135 lines
7.3 KiB
Python
135 lines
7.3 KiB
Python
"""Reconcile committed Windows carried-phase gates against raw logs and prior outcomes."""
|
|
from pathlib import Path
|
|
import hashlib
|
|
import json
|
|
import re
|
|
import subprocess
|
|
import sys
|
|
|
|
base = Path(__file__).resolve().parents[2]
|
|
root = base / '.local/as3batch'
|
|
directory = base / '.local/analysis-carried-phase-gates-20260925'
|
|
prior = base / '.local/analysis-mixed-rename-plan-gates-20260924'
|
|
source, stem, expected_added = sys.argv[1], sys.argv[2], int(sys.argv[3])
|
|
assert 1 <= expected_added <= 100
|
|
assert re.fullmatch(r'[0-9a-f]{40}', source)
|
|
assert re.fullmatch(r'[a-zA-Z0-9-]+', stem)
|
|
sha = lambda raw: hashlib.sha256(raw).hexdigest()
|
|
|
|
def git(*args):
|
|
return subprocess.check_output(['git', '--no-replace-objects', *args], cwd=root)
|
|
|
|
def receipt(folder, name):
|
|
path = folder / (name + '.result.json')
|
|
data = json.loads(path.read_bytes())
|
|
raw = (folder / (name + '.log')).read_bytes()
|
|
assert data['log_sha256'] == sha(raw)
|
|
return data, raw
|
|
|
|
assert git('rev-parse', 'HEAD').decode().strip() == source
|
|
assert not git('status', '--porcelain', '--untracked-files=no').strip()
|
|
scope = ['Cargo.toml', 'Cargo.lock', 'rust-toolchain.toml', '.cargo', 'build', 'crates', 'assets', 'docs', 'examples', 'harness/fixtures', 'vendor']
|
|
paths = git('ls-tree', '-r', '--name-only', source, *scope).decode().splitlines()
|
|
blobs = subprocess.check_output(['git', '--no-replace-objects', 'cat-file', '--batch'], cwd=root,
|
|
input=''.join(f'{source}:{p}\n' for p in paths).encode())
|
|
offset = 0
|
|
expected_inputs = []
|
|
for path in paths:
|
|
end = blobs.index(b'\n', offset)
|
|
_, kind, count = blobs[offset:end].split()
|
|
count = int(count)
|
|
raw = blobs[end + 1:end + 1 + count]
|
|
assert kind == b'blob' and (root / path).read_bytes() == raw, path
|
|
expected_inputs.append({'path': path, 'bytes': count, 'sha256': sha(raw)})
|
|
offset = end + count + 2
|
|
assert offset == len(blobs)
|
|
prefix = ['cargo', 'test', '--locked', '--offline', '-p', 'oa-core', '--lib']
|
|
focus = 'language::analysis_source::mixed::carried::tests::'
|
|
commands = {
|
|
'focused': prefix + [focus, '--', '--test-threads=1'],
|
|
'full': prefix + ['--', '--test-threads=1'],
|
|
'list': prefix + ['--', '--list'],
|
|
'clippy': ['cargo', 'clippy', '--locked', '--offline', '-p', 'oa-core', '--all-targets', '--', '-D', 'warnings'],
|
|
'fmt': ['cargo', 'fmt', '--all', '--', '--check'],
|
|
}
|
|
verified = {}
|
|
all_receipts = {}
|
|
for mode, command in commands.items():
|
|
name = f'carried-{mode}-{stem}-1'
|
|
data, raw = receipt(directory, name)
|
|
assert data['source'] == data['head_after'] == source
|
|
assert data['mode'] == mode and data['command'] == command
|
|
assert not data['changed_inputs'] and not data['verification_failures']
|
|
manifest_raw = (directory / data['inputs']).read_bytes()
|
|
assert sha(manifest_raw) == data['inputs_sha256']
|
|
assert json.loads(manifest_raw) == {'source': source, 'inputs': expected_inputs}
|
|
launcher = (directory / data['launcher_copy']).read_bytes()
|
|
assert sha(launcher) == data['launcher_sha256'] == data['launcher_sha256_after']
|
|
assert source.encode() in launcher
|
|
env = data['environment']
|
|
assert env['CARGO_BUILD_JOBS'] == '1' and env['CARGO_INCREMENTAL'] == '0'
|
|
assert env['ARCADIA_CODEC_LIFECYCLE_PHASE'] is None and env['RUST_MIN_STACK'] is None
|
|
assert data['cwd'] == str(root)
|
|
text = raw.decode()
|
|
outcomes = re.findall(r'^test ([^\r\n]+) \.\.\. (ok|FAILED|ignored)\r?$', text, re.M)
|
|
assert outcomes == [tuple(x) for x in data['outcomes']]
|
|
assert len(outcomes) == len(dict(outcomes))
|
|
members = re.findall(r'^([^\r\n]+): test\r?$', text, re.M) if mode == 'list' else []
|
|
assert members == data['listing_members'] and len(members) == data['listing_count']
|
|
assert len(members) == len(set(members))
|
|
if mode == 'list':
|
|
assert len(members) == 246 + expected_added
|
|
if mode in ('focused', 'full'):
|
|
counts = tuple(sum(v == state for _, v in outcomes) for state in ('ok', 'FAILED', 'ignored'))
|
|
summary = re.findall(r'^test result: (ok|FAILED)\. (\d+) passed; (\d+) failed; (\d+) ignored; 0 measured; (\d+) filtered out;', text, re.M)
|
|
assert len(summary) == 1 and tuple(map(int, summary[0][1:4])) == counts
|
|
assert summary[0][0] == ('FAILED' if counts[1] else 'ok')
|
|
assert int(summary[0][4]) == (246 if mode == 'focused' else 0)
|
|
assert data['exit'] == (101 if counts[1] else 0)
|
|
assert len(data['artifacts']) == 1
|
|
for artifact in data['artifacts']:
|
|
actual = Path(artifact['path']).read_bytes()
|
|
assert len(actual) == artifact['bytes'] and sha(actual) == artifact['sha256']
|
|
assert str(Path(artifact['path'])).replace('\\', '/') in text.replace('\\', '/')
|
|
all_receipts[mode] = data
|
|
verified[name] = {'source': source, 'exit': data['exit'], 'outcomes': len(outcomes), 'listing': len(members), 'log_sha256': sha(raw), 'result_sha256': sha((directory / (name + '.result.json')).read_bytes())}
|
|
|
|
old, old_raw = receipt(prior, 'rename-full-18073a6-1')
|
|
assert old['source'] == '18073a6f2eabce83fc83997f094e331eec835a16'
|
|
old_actual = re.findall(r'^test ([^\r\n]+) \.\.\. (ok|FAILED|ignored)\r?$', old_raw.decode(), re.M)
|
|
assert old_actual == [tuple(x) for x in old['outcomes']]
|
|
assert len(old_actual) == len(dict(old_actual)) == 246
|
|
old_out = dict(old['outcomes'])
|
|
new_out = dict(all_receipts['full']['outcomes'])
|
|
assert len(old_out) == 246 and len(new_out) == 246 + expected_added
|
|
assert all(new_out.get(k) == v for k, v in old_out.items())
|
|
added = set(new_out) - set(old_out)
|
|
assert len(added) == expected_added and all(new_out[k] == 'ok' and k.startswith(focus) for k in added)
|
|
assert set(dict(all_receipts['focused']['outcomes'])) == added
|
|
assert all(status == 'ok' for _, status in all_receipts['focused']['outcomes'])
|
|
assert set(all_receipts['list']['listing_members']) == set(new_out)
|
|
assert all_receipts['clippy']['exit'] == all_receipts['list']['exit'] == all_receipts['focused']['exit'] == 0
|
|
assert all_receipts['fmt']['exit'] == 1
|
|
|
|
def failure_blocks(raw):
|
|
text = raw.decode().replace('\r\n', '\n')
|
|
return {m[1]: re.sub(r"(?m)^(thread '[^\n]*') \(\d+\) panicked", r'\1 (<PID>) panicked', m[2]).strip()
|
|
for m in re.finditer(r'^---- ([^\n]+) stdout ----\n(.*?)(?=^---- |^failures:|\Z)', text, re.M | re.S)}
|
|
|
|
new_raw = (directory / f'carried-full-{stem}-1.log').read_bytes()
|
|
old_blocks, new_blocks = failure_blocks(old_raw), failure_blocks(new_raw)
|
|
assert len(old_blocks) == len(new_blocks) == 16 and old_blocks == new_blocks
|
|
old_fmt, old_fmt_raw = receipt(prior, 'rename-fmt-18073a6-1')
|
|
fmt_raw = (directory / f'carried-fmt-{stem}-1.log').read_bytes()
|
|
assert fmt_raw == old_fmt_raw
|
|
result = {'source': source, 'baseline': old['source'], 'inputs': len(expected_inputs),
|
|
'unchanged_old_outcomes': 246, 'added_passed': expected_added,
|
|
'full_passed': sum(v == 'ok' for v in new_out.values()),
|
|
'identical_failure_blocks_after_pid_only_normalization': 16,
|
|
'identical_fmt_sha256': sha(fmt_raw), 'receipts': verified,
|
|
'scope': 'Read-only committed-input/raw-log/artifact comparison; no Cargo execution.'}
|
|
destination = Path(__file__).with_name('CARRIED-PHASE-RUNTIME-READBACK.json')
|
|
assert not destination.exists(), 'Preserve previous readback; use a separately named checker for another source.'
|
|
destination.write_text(json.dumps(result, indent=2) + '\n', encoding='utf-8')
|
|
print(json.dumps(result, indent=2))
|