authorgravatar for git@paperclover.netclover caruso <git@paperclover.net> 2026-09-07 19:45:52-07:00
committergravatar for git@paperclover.netclover caruso <git@paperclover.net> 2026-09-07 19:45:52-07:00
log06457c4d53a5d89b0271752c18d7091526928939
treefbc2eb28241e402f9b7f506b00e645fd9eadd870
parentaabbadfaf1e956a3daaa6d59d0a2213d4cf78ffe
signature Signed by SSH key SHA256:52mNGHRsVFBDED9IAX5pe+LRWUefqTbxEReunq21QvU

test: provide an isolated public library regression lane

Run workspace tests, doctests, Clippy, consumer builds and Python regression tests through one command with per-stage logs and explicit completion status. Exclude private input/exporter overrides and document the separate native lab lane. Validated from versioned inputs alone: 171 Rust tests, six doctests, 117 Python regressions and all build/lint checks. Verify missing-tool failure and refusal to overwrite retained results. Assisted-by: gpt-6-astra

2 files changed, 119 insertions(+), 0 deletions(-)

tools/TESTING.md created+57
......@@ -0,0 +1,57 @@
1# Library regression lanes
2
3## Public fixtures
4
5From a checkout with Rust and Python 3 plus Pillow installed:
6
7```sh
8python3 tools/check_public.py /absolute/path/to/new-results
9```
10
11The command runs formatting, all workspace features/targets, doctests, Clippy,
12diagnostic/example builds, and Python regression tests. Each stage has its own
13log; `results.json` records executed commands, elapsed times and exit statuses.
14Only a completed run receives `status: passed`. Existing result directories are
15refused. This lane clears inherited `ONESTORE_*` overrides and uses this
16checkout's binaries so personal captures or external exporters cannot silently
17replace public inputs.
18
19Repeat the same command in a fresh checkout containing only versioned files to
20verify clean-checkout compatibility. Fixture symlinks must remain symlinks; their
21targets are versioned in this repository. Neither `corpus/private`, ignored
22`evidence`, existing `target` outputs, credentials nor running virtual machines
23are required. Cargo's normal dependency cache may be reused.
24
25Rust explicitly reports ignored lab tests and fixture generators. Those cases
26are **not** part of a successful public run. The Python suite tests native/lab
27harness logic using retained synthetic captures and mocks; it does not claim a
28new execution of OneNote or a real server interruption.
29
30## Private and native verification
31
32Private notebooks stay outside versioned fixtures. Materialize a copy before
33editing and retain source hashes; never point an authoring harness at an original
34notebook. Live acceptance uses explicitly owned disposable targets and preserves
35the run's commands, inputs, outputs and teardown evidence.
36
37| Boundary | Entry point | Acceptance evidence |
38| --- | --- | --- |
39| Native authoring and cold reopen | `native_runner.py --help` | Independent OneNote capture; exact expected page count when known; owned clone teardown |
40| Mixed native/Rust/offline writers | `native_collaboration.py --help` | Recorded intents, durable receipts, independent server state and cold native comparison |
41| SMB directory pagination | `test_smb_directory.py --help` | Caller-owned Linux VM, native filesystem oracle, interrupted-page rejection |
42| SMB publication and payload interruptions | Ignored tests in `onestore-smb` | Explicit `ONESTORE_SMB_*` lab inputs, retained protocol traces and independent recovery checks |
43| Retained cache migration | Ignored `migrate_retained_cache_copy` test | New destination, unchanged source, images, intent IDs, attempts and receipts |
44
45To compare an additional **already captured** notebook hierarchy without running
46OneNote:
47
48```sh
49PYTHONPATH=tools ONESTORE_NOTEBOOK_NATIVE=/absolute/path/to/capture \
50 python3 -m unittest test_notebook_discovery
51```
52
53The capture contains `notebook/` and `read/hierarchy.xml`. This comparison is a
54separate private lane and must retain its own log. A missing capture, unavailable
55lab, compilation-only iOS result or ignored test never establishes live native
56compatibility. Process exit, server/VM interruption and physical storage loss
57remain distinct fault models.
tools/check_public.py created+62
......@@ -0,0 +1,62 @@
1#!/usr/bin/env python3
2"""Run the public library regression lane and retain each command's output."""
3import argparse
4from datetime import datetime, timezone
5import json
6import os
7from pathlib import Path
8import subprocess
9import sys
10import time
11
12
13def main():
14 parser = argparse.ArgumentParser(description=__doc__)
15 parser.add_argument('output', type=Path, help='New directory for logs and results')
16 args = parser.parse_args()
17 root = Path(__file__).resolve().parent.parent
18 output = args.output.resolve()
19 output.mkdir(parents=True, exist_ok=False)
20 environment = {key: value for key, value in os.environ.items()
21 if not key.startswith('ONESTORE_')}
22 environment['PYTHONPATH'] = str(root / 'tools')
23 environment['CARGO_TARGET_DIR'] = str(root / 'target')
24 commands = [
25 ('python-dependencies', [sys.executable, '-c', 'import PIL']),
26 ('format', ['cargo', 'fmt', '--all', '--check']),
27 ('rust', ['cargo', 'test', '--locked', '--workspace', '--all-features', '--all-targets']),
28 ('doctests', ['cargo', 'test', '--locked', '--workspace', '--all-features', '--doc']),
29 ('clippy', ['cargo', 'clippy', '--locked', '--workspace', '--all-features', '--all-targets', '--', '-D', 'warnings']),
30 ('tools', ['cargo', 'build', '--locked', '--workspace', '--all-features', '--examples', '--bins']),
31 ('python', [sys.executable, '-m', 'unittest', 'discover', '-s', 'tools', '-p', 'test_*.py', '-v']),
32 ]
33 manifest = {'lane': 'public', 'root': str(root), 'status': 'running',
34 'started': datetime.now(timezone.utc).isoformat(), 'stages': []}
35 result_path = output / 'results.json'
36 result_path.write_text(json.dumps(manifest, indent=2) + '\n')
37 for name, command in commands:
38 print(name, flush=True)
39 started = time.monotonic()
40 log = output / (name + '.log')
41 with log.open('w') as stream:
42 try:
43 status = subprocess.run(command, cwd=root, env=environment,
44 stdout=stream, stderr=subprocess.STDOUT).returncode
45 except OSError as error:
46 stream.write(str(error) + '\n')
47 status = None
48 manifest['stages'].append({'name': name, 'command': command,
49 'exit': status, 'seconds': time.monotonic() - started,
50 'log': log.name})
51 if status != 0:
52 manifest['status'] = 'failed'
53 result_path.write_text(json.dumps(manifest, indent=2) + '\n')
54 if status != 0:
55 raise SystemExit(f'{name} failed: {log}')
56 manifest['status'] = 'passed'
57 result_path.write_text(json.dumps(manifest, indent=2) + '\n')
58 print(result_path)
59
60
61if __name__ == '__main__':
62 main()