| author | |
| committer | |
| log | 06457c4d53a5d89b0271752c18d7091526928939 |
| tree | fbc2eb28241e402f9b7f506b00e645fd9eadd870 |
| parent | aabbadfaf1e956a3daaa6d59d0a2213d4cf78ffe |
| signature | Signed by SSH key SHA256:52mNGHRsVFBDED9IAX5pe+LRWUefqTbxEReunq21QvU |
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-astra2 files changed, 119 insertions(+), 0 deletions(-)
tools/TESTING.md created+57| ... | ... | @@ -0,0 +1,57 @@ |
| 1 | # Library regression lanes | |
| 2 | ||
| 3 | ## Public fixtures | |
| 4 | ||
| 5 | From a checkout with Rust and Python 3 plus Pillow installed: | |
| 6 | ||
| 7 | ```sh | |
| 8 | python3 tools/check_public.py /absolute/path/to/new-results | |
| 9 | ``` | |
| 10 | ||
| 11 | The command runs formatting, all workspace features/targets, doctests, Clippy, | |
| 12 | diagnostic/example builds, and Python regression tests. Each stage has its own | |
| 13 | log; `results.json` records executed commands, elapsed times and exit statuses. | |
| 14 | Only a completed run receives `status: passed`. Existing result directories are | |
| 15 | refused. This lane clears inherited `ONESTORE_*` overrides and uses this | |
| 16 | checkout's binaries so personal captures or external exporters cannot silently | |
| 17 | replace public inputs. | |
| 18 | ||
| 19 | Repeat the same command in a fresh checkout containing only versioned files to | |
| 20 | verify clean-checkout compatibility. Fixture symlinks must remain symlinks; their | |
| 21 | targets are versioned in this repository. Neither `corpus/private`, ignored | |
| 22 | `evidence`, existing `target` outputs, credentials nor running virtual machines | |
| 23 | are required. Cargo's normal dependency cache may be reused. | |
| 24 | ||
| 25 | Rust explicitly reports ignored lab tests and fixture generators. Those cases | |
| 26 | are **not** part of a successful public run. The Python suite tests native/lab | |
| 27 | harness logic using retained synthetic captures and mocks; it does not claim a | |
| 28 | new execution of OneNote or a real server interruption. | |
| 29 | ||
| 30 | ## Private and native verification | |
| 31 | ||
| 32 | Private notebooks stay outside versioned fixtures. Materialize a copy before | |
| 33 | editing and retain source hashes; never point an authoring harness at an original | |
| 34 | notebook. Live acceptance uses explicitly owned disposable targets and preserves | |
| 35 | the 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 | ||
| 45 | To compare an additional **already captured** notebook hierarchy without running | |
| 46 | OneNote: | |
| 47 | ||
| 48 | ```sh | |
| 49 | PYTHONPATH=tools ONESTORE_NOTEBOOK_NATIVE=/absolute/path/to/capture \ | |
| 50 | python3 -m unittest test_notebook_discovery | |
| 51 | ``` | |
| 52 | ||
| 53 | The capture contains `notebook/` and `read/hierarchy.xml`. This comparison is a | |
| 54 | separate private lane and must retain its own log. A missing capture, unavailable | |
| 55 | lab, compilation-only iOS result or ignored test never establishes live native | |
| 56 | compatibility. Process exit, server/VM interruption and physical storage loss | |
| 57 | remain 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.""" | |
| 3 | import argparse | |
| 4 | from datetime import datetime, timezone | |
| 5 | import json | |
| 6 | import os | |
| 7 | from pathlib import Path | |
| 8 | import subprocess | |
| 9 | import sys | |
| 10 | import time | |
| 11 | ||
| 12 | ||
| 13 | def 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 | ||
| 61 | if __name__ == '__main__': | |
| 62 | main() |