summaryrefslogtreecommitdiff
path: root/spike/settings/test-strings/make_test_strings.py
diff options
context:
space:
mode:
authorcirakg <cirakg@cirakg.xyz>2026-10-18 23:44:18 +0200
committercirakg <cirakg@cirakg.xyz>2026-10-18 23:44:18 +0200
commitac60fb060414261935ac5e158b26b34e211e1e06 (patch)
treea8f97e1c4b0bf8f1b433d3e8f33a5d30994e7c58 /spike/settings/test-strings/make_test_strings.py
parent93aa7e3aa291f3328cf34a5a2f86172f41d0affd (diff)
feat(spike): add the M1 settings spike app and make targets
A separate throwaway app (own app ID, no permissions) that shows what the Garmin Connect app delivers per settings field and writes values back, to measure the undocumented settings limits on the real phone and watch. Includes length-marked test strings, `make spike` / `make spike-sim`, `make vectors`, and step-by-step instructions in docs/SETTINGS_SPIKE.md. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'spike/settings/test-strings/make_test_strings.py')
-rw-r--r--spike/settings/test-strings/make_test_strings.py92
1 files changed, 92 insertions, 0 deletions
diff --git a/spike/settings/test-strings/make_test_strings.py b/spike/settings/test-strings/make_test_strings.py
new file mode 100644
index 0000000..8d43977
--- /dev/null
+++ b/spike/settings/test-strings/make_test_strings.py
@@ -0,0 +1,92 @@
+#!/usr/bin/env python3
+# Author: cirakg & claude ai
+# Summary: Generate the settings-spike test strings: one .txt per string and an index.html with copy buttons.
+"""Generate the test strings for the M1 settings spike (docs/SETTINGS_SPIKE.md).
+
+Length strings look like
+ N=1000|abcdef...P0050...P0100...xyzEND
+"N=<length>|" says how long the string should be, a marker "P<position>"
+sits at every 50th character, and "END" closes it. If the phone app cuts a
+string, the watch shows a wrong length and no "END"; the last marker it shows
+tells roughly where the cut happened. Only letters, digits, "=", "|" are
+used, so the length tests don't also test character handling.
+
+special.txt holds the characters that may be rejected (& < > \\ " { } %),
+non-ASCII text, a fake otpauth URI and a JSON fragment.
+
+Usage: python3 make_test_strings.py (writes into its own folder)
+"""
+
+from __future__ import annotations
+
+import html
+from pathlib import Path
+
+LENGTHS = (100, 255, 256, 500, 800, 801, 1000, 2000, 4000, 8000)
+SPECIAL = (
+ 'special: & < > \\ " { } % | non-ASCII: äöü ß € 😀 | '
+ "otpauth://totp/Fake:alice?secret=JBSWY3DPEHPK3PXP&issuer=Fake&digits=6 | "
+ '{"name":"Fake","path":"a\\/b"} END'
+)
+HEADER = (
+ "# Author: cirakg & claude ai\n# Summary: {summary}\n"
+ "# Copy ONLY the last line (the test string) into a settings field.\n\n"
+)
+LETTERS = "abcdefghijklmnopqrstuvwxyz"
+
+
+def marker_string(length: int) -> str:
+ """A string of exactly `length` characters with position markers."""
+ text = f"N={length}|"
+ i = 0
+ while len(text) < length - 3:
+ pos = len(text)
+ if pos % 50 == 0 and pos + 5 <= length - 3:
+ text += f"P{pos:04d}"
+ else:
+ text += LETTERS[i % len(LETTERS)]
+ i += 1
+ text += "END"
+ assert len(text) == length, (length, len(text))
+ return text
+
+
+def main() -> None:
+ here = Path(__file__).resolve().parent
+ entries = [(f"len-{n:04d}", f"{n} characters", marker_string(n)) for n in LENGTHS]
+ entries.append(("special", "special characters", SPECIAL))
+ for name, label, value in entries:
+ (here / f"{name}.txt").write_text(HEADER.format(summary=f"Settings spike test string: {label}.") + value + "\n")
+ cards = "\n".join(
+ f"<section><h2>{html.escape(label)}</h2>"
+ f'<textarea id="{name}" readonly rows="3">{html.escape(value)}</textarea>'
+ f"<button onclick=\"copyText('{name}')\">Copy</button></section>"
+ for name, label, value in entries
+ )
+ page = f"""<!doctype html>
+<!-- Author: cirakg & claude ai -->
+<!-- Summary: Settings spike test strings with copy buttons; open on the phone, copy, paste into a settings field. -->
+<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
+<title>Settings spike test strings</title>
+<style>body{{font-family:sans-serif;margin:16px}}textarea{{width:100%;font-family:monospace}}
+button{{font-size:1.1em;padding:6px 16px;margin:4px 0 16px}}</style></head>
+<body><h1>Settings spike test strings</h1>
+<p>Tap Copy, then paste into one of the Text fields in the app settings. Expected lengths are in each heading.</p>
+{cards}
+<script>
+function copyText(id) {{
+ const area = document.getElementById(id);
+ area.select();
+ const done = () => alert("Copied " + area.value.length + " characters");
+ if (navigator.clipboard && window.isSecureContext) {{
+ navigator.clipboard.writeText(area.value).then(done, () => {{ document.execCommand("copy"); done(); }});
+ }} else {{ document.execCommand("copy"); done(); }}
+}}
+</script></body></html>
+"""
+ (here / "index.html").write_text(page)
+ print(f"wrote {len(entries)} test strings and index.html into {here}")
+
+
+if __name__ == "__main__":
+ main()