1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
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()
|