74 lines
5.2 KiB
Python
74 lines
5.2 KiB
Python
"""Compare serde_json Value Debug assertion operands without printing model dumps."""
|
|
from pathlib import Path
|
|
import hashlib,json,re,subprocess
|
|
base=Path(__file__).resolve().parent.parent
|
|
repo=base/'.local/as2-20260913'
|
|
pin='b2c970b4673e062e47eadd12906d2316c4b1a7b3'
|
|
folder=base/'.local/analysis-source-gates-20260913'
|
|
raw=(folder/'legacy-full-b2c970b.log').read_bytes(); log=raw.decode()
|
|
name='original_v2_text_and_json_preserve_identity_and_mixed_scenarios_after_extensions'
|
|
sections=dict(re.findall(r'^---- (\S+) stdout ----\r?\n(.*?)(?=^---- |^failures:|\Z)',log,re.M|re.S))
|
|
assert len(sections)==9
|
|
assert sum('Unsupported schema version: expected arcadia 3, 4, 5, 6 or 7.' in b for b in sections.values())==8
|
|
section=sections[name]
|
|
left=re.search(r'^ left: (.*)$',section,re.M).group(1).rstrip('\r')
|
|
right=re.search(r'^ right: (.*)$',section,re.M).group(1).rstrip('\r')
|
|
class Parser:
|
|
def __init__(self,s): self.s=s; self.i=0; self.nodes=0
|
|
def ws(self):
|
|
while self.i<len(self.s) and self.s[self.i].isspace(): self.i+=1
|
|
def take(self,x):
|
|
self.ws(); assert self.s.startswith(x,self.i),(self.i,x); self.i+=len(x)
|
|
def string(self):
|
|
self.ws(); value,n=json.JSONDecoder().raw_decode(self.s[self.i:]); assert isinstance(value,str); self.i+=n; return value
|
|
def value(self):
|
|
self.ws(); self.nodes+=1
|
|
if self.s.startswith('Object',self.i):
|
|
self.take('Object'); self.take('{'); out={}; self.ws()
|
|
while self.s[self.i]!='}':
|
|
k=self.string(); self.take(':'); assert k not in out; out[k]=self.value(); self.ws()
|
|
if self.s[self.i]=='}': break
|
|
self.take(',')
|
|
self.take('}'); return out
|
|
if self.s.startswith('Array',self.i):
|
|
self.take('Array'); self.take('['); out=[]; self.ws()
|
|
while self.s[self.i]!=']':
|
|
out.append(self.value()); self.ws()
|
|
if self.s[self.i]==']': break
|
|
self.take(',')
|
|
self.take(']'); return out
|
|
if self.s.startswith('String',self.i):
|
|
self.take('String'); self.take('('); v=self.string(); self.take(')'); return v
|
|
if self.s.startswith('Null',self.i): self.take('Null'); return None
|
|
if self.s.startswith('Bool',self.i):
|
|
self.take('Bool'); self.take('('); v=self.s.startswith('true',self.i); self.take('true' if v else 'false'); self.take(')'); return v
|
|
self.take('Number'); self.take('('); end=self.s.index(')',self.i); v=('Number',self.s[self.i:end]); self.i=end; self.take(')'); return v
|
|
def parse(self):
|
|
v=self.value(); self.ws(); assert self.i==len(self.s); return v
|
|
a=Parser(left); retained=a.parse(); b=Parser(right); original=b.parse()
|
|
differences=[]
|
|
def compare(a,b,path=''):
|
|
if isinstance(a,dict) and isinstance(b,dict):
|
|
for k in sorted(a.keys()|b.keys()):
|
|
p=path+'/'+k.replace('~','~0').replace('/','~1')
|
|
if k not in b: differences.append({'path':p,'difference':'left_only','left':a[k]})
|
|
elif k not in a: differences.append({'path':p,'difference':'right_only','right':b[k]})
|
|
else: compare(a[k],b[k],p)
|
|
elif isinstance(a,list) and isinstance(b,list):
|
|
if len(a)!=len(b): differences.append({'path':path,'difference':'array_length','left':len(a),'right':len(b)})
|
|
else:
|
|
for i,(x,y) in enumerate(zip(a,b)): compare(x,y,path+'/'+str(i))
|
|
elif type(a)!=type(b) or a!=b: differences.append({'path':path,'difference':'value','left':a,'right':b})
|
|
compare(retained,original)
|
|
assert differences==[{'path':'/definitions','difference':'left_only','left':{}}]
|
|
assert retained.pop('definitions')=={} and retained==original
|
|
source_path='crates/oa-core/tests/language.rs'
|
|
test_source=subprocess.check_output(['git','show',pin+':'+source_path],cwd=repo)
|
|
fixture_path='crates/oa-core/tests/fixtures/v2-coastal-rescue.json'
|
|
fixture=subprocess.check_output(['git','show',pin+':'+fixture_path],cwd=repo)
|
|
fixture_json=json.loads(fixture,parse_int=lambda n:('Number',n),parse_float=lambda n:('Number',n))
|
|
assert original==fixture_json
|
|
report={'source':pin,'test':name,'legacy_total':16,'legacy_passed':7,'legacy_failed':9,'unsupported8_failure_names':sorted(n for n,b in sections.items() if 'Unsupported schema version:' in b),'assertion_difference':differences,'all_other_values_and_structure_equal':True,'right_matches_pinned_original_fixture':True,'debug_nodes_left':a.nodes,'debug_nodes_right':b.nodes,'log_sha256':hashlib.sha256(raw).hexdigest(),'test_source_path':source_path,'test_source_sha256':hashlib.sha256(test_source).hexdigest(),'fixture_path':fixture_path,'fixture_sha256':hashlib.sha256(fixture).hexdigest(),'interpretation':'The historical retained-vs-original projection omits the newly explicit empty definitions catalog from its compatibility-default normalization. Source-vs-JSON equality earlier in the same test completed before this assertion. This failure is an oracle projection mismatch, not evidence of lost original V2 data; later assertions did not execute and the test remains failed.'}
|
|
with (base/'.local/analysis-source8-s2a-legacy-cause-20260913.json').open('x',encoding='utf-8') as out: json.dump(report,out,indent=2)
|
|
print(json.dumps(report,indent=2))
|