mirror of
https://github.com/kuhyx/screen-locker.git
synced 2026-11-10 22:27:12 +01:00
A self-contained prompt for a fresh session to finish the last 10 files over the 250-line cap, all Dart. It carries the parts that are expensive to rediscover: - scripts/split_dart_test.py, which works, plus the envelope it works in: one pass from the pristine file, no post-main() declarations, no local function declarations in the preamble. All four remaining test files sit outside that envelope, and the fix (hoist shared fakes into a _fixtures.dart sibling first, as its own commit) is written down. - The three Dart constraints each of which cost a debugging cycle here: setState is @protected so it cannot be called from an extension; extension methods dispatch statically so a method a test fake overrides must stay in the class (this hung four tests on pumpAndSettle rather than failing an assertion); static members need qualifying inside an extension. - 402 as the test-count invariant, and the note that dart analyze is a no-op on test/ so the count is the only signal there. - That the local gatelock (0.5.0) is ahead of the pin (0.4.0), so the popup gate's failure here is environment drift and must NOT be "fixed" in code. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EveB813BQ8abAP68VSqZ5Y
76 lines
2.4 KiB
Python
76 lines
2.4 KiB
Python
"""Split a Dart test file at top-level `group(` boundaries.
|
|
|
|
Each output file reuses the original's imports and its main() setup preamble
|
|
(everything between `void main() {` and the first top-level group), so every
|
|
part runs the same fixtures the original did.
|
|
|
|
Usage:
|
|
split_dart_test.py <file> <out1>:<firstGroupIdx> [<out2>:<idx> ...]
|
|
|
|
Indices are 0-based positions in the top-level group list; each output takes
|
|
groups[idx:next_idx]. The source file keeps groups[:first_idx].
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
import re
|
|
import sys
|
|
|
|
|
|
def top_level_groups(lines: list[str]) -> list[int]:
|
|
"""Return 0-based line indices of every top-level test boundary.
|
|
|
|
Screen tests are often a flat list of `testWidgets(` calls with no
|
|
`group(` at all, so both count as a split point.
|
|
"""
|
|
return [
|
|
i
|
|
for i, ln in enumerate(lines)
|
|
if re.match(r"^ (group|testWidgets|test)\(", ln)
|
|
]
|
|
|
|
|
|
def split(path: Path, specs: list[tuple[Path, int]]) -> None:
|
|
"""Write each spec's group range to its own file, trimming the source."""
|
|
lines = path.read_text().splitlines(keepends=True)
|
|
groups = top_level_groups(lines)
|
|
main_idx = next(i for i, ln in enumerate(lines) if ln.startswith("void main("))
|
|
header = "".join(lines[:main_idx])
|
|
preamble = "".join(lines[main_idx + 1 : groups[0]])
|
|
|
|
# Fixtures and fake classes often sit AFTER main(). Every split file needs
|
|
# them too, or it fails to compile on a helper the original defined once.
|
|
main_end = next(
|
|
(i for i in range(len(lines) - 1, main_idx, -1) if lines[i].startswith("}")),
|
|
len(lines) - 1,
|
|
)
|
|
trailer = "".join(lines[main_end + 1 :])
|
|
|
|
bounds = [i for _, i in specs] + [len(groups)]
|
|
for (out, start), end in zip(specs, bounds[1:], strict=False):
|
|
first = groups[start]
|
|
last = groups[end] if end < len(groups) else main_end
|
|
body = "".join(lines[first:last]).rstrip("\n")
|
|
out.write_text(f"{header}void main() {{\n{preamble}{body}\n}}\n{trailer}")
|
|
|
|
keep = groups[specs[0][1]]
|
|
path.write_text(
|
|
"".join(lines[:keep]).rstrip("\n") + "\n}\n" + trailer,
|
|
)
|
|
|
|
|
|
def main() -> int:
|
|
"""Parse argv and run the split."""
|
|
src = Path(sys.argv[1])
|
|
specs = []
|
|
for arg in sys.argv[2:]:
|
|
name, idx = arg.rsplit(":", 1)
|
|
specs.append((src.parent / name, int(idx)))
|
|
split(src, specs)
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|