Files
TheTree_mock/tools/strict_test_markers.py
Takeshi b84bade045 Save The Tree implementation and acceptance work in progress
Preserve production code, regression fixtures, audio assets and provenance, test infrastructure, and the original-completion plan and progress ledger. Final game and Windows acceptance remain pending; excluded local artifacts and native experiments are not part of this snapshot.
2026-10-11 15:58:00 +09:00

106 lines
9.4 KiB
Python

"""Exact per-suite completion schemas. Counts come only from immutable raw logs."""
from pathlib import Path
import hashlib,json,re
FIELDS=('checks','failures','passed','failed','skipped')
KNOWN_LEAK_WARNING=re.compile(r'^\s*(?:WARNING:[^\r\n]*(?:ObjectDB instances leaked|resources? still in use at exit)|Leaked instance:)[^\r\n]*$')
def sha(path):return hashlib.sha256(Path(path).read_bytes()).hexdigest()
def load_contracts(path):
data=json.loads(Path(path).read_text())
if data.get('schema_version')!=1:raise ValueError('marker contract schema')
rows=data.get('rows',[]);by_test={row['test']:row for row in rows}
if len(rows)!=len(by_test):raise ValueError('duplicate suite marker contract')
for row in rows:
pattern(row['template'])
for auxiliary in row.get('auxiliary',[]):pattern(auxiliary['template'])
return by_test
def pattern(template):
escaped=re.escape(template)
for field in FIELDS:escaped=escaped.replace(re.escape('<'+field+'>'),'(?P<'+field+'>[0-9]+)')
compiled=re.compile(escaped)
if 'checks' not in compiled.groupindex:raise ValueError('missing named checks schema')
return compiled
def marker_like(line):
return bool(re.match(r'^(?:PASS|FAIL):\s.*checks',line) or re.match(r'^.*?:\s*[0-9]+(?: [A-Za-z]+)* checks(?:[,; /].*)?$',line) or re.match(r'^[A-Z][A-Z0-9_]*(?: phase=[a-z]+)? checks=',line) or line.startswith(' checks='))
def integer(value):return type(value) is int
def parse_raw(test,content,contracts,source_sha256=None):
errors=[];spec=contracts.get(test)
if spec is None:return {'passed':False,'checks':None,'failures':None,'marker':None,'errors':['unknown suite marker schema']}
lines=content.splitlines(keepends=True)
banners=[line for line in lines if line.startswith('Godot Engine v')]
if len(banners)!=1 or banners[0] not in ['Godot Engine v4.7.2.stable.official.ed1daf0bf - https://godotengine.org\n','Godot Engine v4.7.2.stable.official.ed1daf0bf - https://godotengine.org\r\n']:errors.append('missing/duplicate/wrong engine banner')
diagnostics=[line.rstrip('\r\n') for line in lines if any(token in line for token in ['ERROR:','SCRIPT ERROR:','Parse Error:']) or re.match(r'^\s*(?:FAIL:|FAILED:|FAIL )',line)]
if diagnostics:errors.append('raw diagnostics or FAIL lines')
leak_warnings=[line.rstrip('\r\n') for line in lines if KNOWN_LEAK_WARNING.fullmatch(line.rstrip('\r\n'))]
if leak_warnings:errors.append('known exit object/resource leak warning')
unicode_lines=[line.rstrip('\r\n') for line in lines if 'Unicode parsing error' in line]
known_unicode=spec.get('known_unicode_diagnostic',{})
allowed_unicode=unicode_lines and source_sha256==known_unicode.get('source_sha256') and all(line==known_unicode.get('exact_line') for line in unicode_lines)
if unicode_lines and not allowed_unicode:errors.append('unrecognized Unicode diagnostic or source pin')
schemas=[dict(spec,primary=True)]+[dict(row,primary=False) for row in spec.get('auxiliary',[])]
observed=[];recognized_indices=set()
for schema in schemas:
compiled=pattern(schema['template']);matches=[]
prefix=schema['template'].split('<checks>')[0]
for index,line in enumerate(lines):
text=line.rstrip('\r\n');match=compiled.fullmatch(text)
if match:
if not line.endswith('\n'):errors.append('completion line missing newline')
matches.append((index,line,{key:int(value) for key,value in match.groupdict().items()}))
elif prefix and text.startswith(prefix) and 'checks' in text:errors.append('malformed expected completion schema')
required=schema.get('required',True)
if len(matches)!=(1 if required else len(matches)) or len(matches)>1:errors.append('missing/duplicate '+('primary' if schema['primary'] else 'auxiliary')+' completion marker')
if not matches:
if schema['primary']:observed.append(None)
continue
index,line,values=matches[0];recognized_indices.add(index)
if values['checks']<=0:errors.append('nonpositive raw check count')
for field in ['failures','failed','skipped']:
if values.get(field,0)!=0:errors.append('raw nonzero '+field)
if 'passed' in values and values['passed']!=values['checks']:errors.append('raw passed/check count mismatch')
observed.append({'line':line,'values':values,'primary':schema['primary']})
for index,line in enumerate(lines):
if marker_like(line.rstrip('\r\n')) and index not in recognized_indices:errors.append('unexpected other-label/schema completion line')
primary=next((row for row in observed if row and row['primary']),None)
if primary:
for row in observed:
if row and row['values']['checks']!=primary['values']['checks']:errors.append('raw auxiliary/primary check count mismatch')
if spec.get('json_report'):
prefix=spec['json_report']['prefix'];reports=[line[len(prefix):].strip() for line in lines if line.startswith(prefix)]
if len(reports)!=1:errors.append('missing/duplicate raw JSON report')
else:
try:
report=json.loads(reports[0]);summary=report['check_summary']
if not primary or any(not integer(summary.get(field)) for field in ['total','passed','failed','skipped']) or summary!={'total':primary['values']['checks'],'passed':primary['values']['checks'],'failed':0,'skipped':0}:errors.append('raw JSON/marker check count mismatch')
if report.get('status') not in ['PASS','SMOKE_PASS'] or report.get('runtime_errors')!=[] or report.get('unmet_fixture_fields')!=[]:errors.append('raw JSON report not complete and error-free')
except (ValueError,KeyError,TypeError):errors.append('malformed raw JSON report')
return {'passed':not errors,'checks':primary['values']['checks'] if primary else None,'failures':primary['values'].get('failures',primary['values'].get('failed',0)) if primary else None,'marker':primary['line'] if primary else None,'errors':sorted(set(errors)),'diagnostic_lines':diagnostics,'known_leak_warning_lines':leak_warnings,'known_leak_warning_count':len(leak_warnings),'known_unicode_diagnostic_count':len(unicode_lines) if allowed_unicode else 0,'unrecognized_unicode_diagnostic_count':0 if allowed_unicode else len(unicode_lines)}
def review_row(row,content,contracts,source_map,project_root,run_id,start,end,raw_sha):
parsed=parse_raw(row.get('test',''),content,contracts,source_map.get(row.get('test')));errors=list(parsed['errors'])
test=row.get('test');expected_command=[row.get('godot'),'--headless','--path',str(Path(project_root).resolve()),'--script','res://'+str(test)]
if not integer(row.get('exit_code')) or row.get('exit_code')!=0 or row.get('failed') is not False or row.get('engine_ended') is not True:errors.append('process failure or not ended')
if not integer(row.get('actual_pid')) or row['actual_pid']<=0:errors.append('actual PID missing/invalid')
if row.get('command')!=expected_command:errors.append('actual command/test/root mismatch')
if not integer(row.get('reported_checks')) or row.get('reported_checks')!=parsed['checks']:errors.append('reported/raw check count mismatch')
if not integer(row.get('reported_failures')) or row.get('reported_failures')!=parsed['failures'] or row.get('completion_marker')!=parsed['marker']:errors.append('reported/raw marker mismatch')
if test not in source_map or row.get('source_sha256')!=source_map.get(test) or row.get('source_sha256_end')!=source_map.get(test):errors.append('suite source pin mismatch')
for receipt in [start,end]:
if not isinstance(receipt,dict) or receipt.get('test')!=test or receipt.get('run_id')!=run_id or receipt.get('sequence')!=row.get('sequence') or receipt.get('actual_pid')!=row.get('actual_pid') or receipt.get('command')!=expected_command or receipt.get('source_sha256')!=source_map.get(test):errors.append('start/end PID/input receipt mismatch')
if isinstance(start,dict) and isinstance(end,dict):
if not integer(start.get('owner_runner_pid')) or start['owner_runner_pid']<=0 or start.get('owner_runner_pid')!=end.get('owner_runner_pid'):errors.append('runner owner PID mismatch')
if not integer(start.get('monotonic_ns')) or not integer(end.get('monotonic_ns')) or end.get('monotonic_ns',-1)<start.get('monotonic_ns',0):errors.append('receipt time invalid')
if start.get('event')!='ENGINE_STARTED' or end.get('event')!='ENGINE_ENDED' or end.get('engine_ended') is not True or not integer(end.get('exit_code')) or end.get('exit_code')!=row.get('exit_code'):errors.append('process lifecycle receipt mismatch')
if end.get('raw_sha256')!=raw_sha or row.get('log_sha256')!=raw_sha:errors.append('raw SHA receipt mismatch')
result=dict(parsed);result.update(passed=not errors,errors=sorted(set(errors)),actual_pid=row.get('actual_pid'),test=test)
return result
def review_roster(roster,rows):
names=[row.get('test') for row in rows]
errors=[]
if len(roster)!=len(set(roster)):errors.append('duplicate expected roster')
if names!=roster or len(names)!=len(set(names)):errors.append('missing/duplicate/extra/reordered actual roster')
if any(not integer(row.get('sequence')) for row in rows) or [row.get('sequence') for row in rows]!=list(range(len(roster))):errors.append('suite sequence mismatch')
if len([row.get('log') for row in rows])!=len(set(row.get('log') for row in rows)):errors.append('duplicate suite log binding')
return {'passed':not errors,'errors':errors}