| 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() | ||