Files
altium-render-engine/backend/tests/test_parser.py
T
nearology 56c5ee23d3
Verify / test (push) Failing after 2m54s
first commit
2026-11-05 17:31:17 +03:30

134 خطوط
6.9 KiB
Python

import math
import struct
import pytest
from app.parser.geometry import point,coordinate,transform_point,color
from app.models.schematic import Point
from app.parser.record_decoder import detect_format,binary_records,properties,ParseError
from app.parser.schdoc_parser import parse_schdoc
from app.parser.ole_reader import read_document
@pytest.mark.parametrize('data,expected',[(b'\xd0\xcf\x11\xe0\xa1\xb1\x1a\xe1','ole'),(b'|HEADER=x','ascii'),(b'\xef\xbb\xbf|HEADER=x','ascii')])
def test_detect(data,expected): assert detect_format(data)==expected
@pytest.mark.parametrize('data',[b'',b'<html/>',b'PK\x03\x04',b'\0'*1024])
def test_unknown_format(data):
with pytest.raises(ParseError): detect_format(data)
def test_length_prefix_and_indexing():
payloads=[b'|HEADER=Example\0',b'|RECORD=31\0',b'|RECORD=27|X1=42\0']
stream=b''.join(struct.pack('<I',len(p))+p for p in payloads)
records=binary_records(stream,'FileHeader')
assert [r.index for r in records]==[-1,0,1]
assert records[2].properties['X1']=='42'
@pytest.mark.parametrize('data',[b'\x05',struct.pack('<I',100)+b'x',struct.pack('<I',0),struct.pack('<I',0x7fffff)+b'x'])
def test_truncated_or_excessive_record(data):
with pytest.raises(ParseError): binary_records(data,'FileHeader')
def test_binary_record_does_not_shift_owners():
p=b'|HEADER=x\0';s=struct.pack('<I',len(p))+p+struct.pack('<I',0x1000002)+b'aa'
r=binary_records(s,'FileHeader');assert r[1].binary and r[1].index==0
def test_utf8_precedence_and_case():
p=properties('|%UTF8%Comment=مقاومت Ω|Comment=legacy|Text=a=b'.encode())
assert p['COMMENT']=='مقاومت Ω' and p['TEXT']=='a=b'
def test_fraction_and_axis():
p={'LOCATION.X':'12','LOCATION.X_FRAC':'50000','LOCATION.Y':'-3','LOCATION.Y_FRAC':'25000'}
assert coordinate(p,'LOCATION.X')==12.5
assert point(p)==Point(x=12.5,y=2.75)
assert color('8388608')=='#000080'
@pytest.mark.parametrize('rotation,expected',[(0,(12,20)),(90,(10,18)),(180,(8,20)),(270,(10,22))])
def test_rotation(rotation,expected):
p=transform_point(Point(x=2,y=0),Point(x=10,y=20),rotation)
assert p.x==pytest.approx(expected[0]) and p.y==pytest.approx(expected[1])
def test_mirror_then_rotate():
p=transform_point(Point(x=2,y=1),Point(x=10,y=20),90,True)
assert p==Point(x=11,y=22)
def test_component_extraction_and_absolute_geometry(ascii_schematic):
d=parse_schdoc(ascii_schematic,'test.SchDoc','test')
c=d.components[0]
assert (c.designator,c.value,c.comment,c.library_reference)==('R1','4K7','4K7','R_Symbol')
assert c.rotation==90 and c.mirrored and c.parameters['Manufacturer']=='Test'
assert c.pins[0].number=='1' and c.pins[0].name=='A'
assert c.pins[0].connection==Point(x=100,y=-230)
# Placed child geometry must not be rotated/mirrored again.
line=next(p for p in d.primitives if p.object_id=='FileHeader:2')
assert line.path=='M100,-200 L100,-220'
assert line.component_id==c.id
assert len({x.id for x in d.objects})==len(d.objects)
assert set(c.primitive_ids)==set(d.symbols[0].primitive_ids)
assert not any(p.text=='Test' for p in d.primitives)
def test_wire_path_and_connection_vertices(ascii_schematic):
d=parse_schdoc(ascii_schematic,'test.SchDoc','test')
o=next(o for o in d.objects if o.kind=='wire')
p=next(p for p in d.primitives if p.object_id==o.id)
assert p.path=='M100,-230 L150,-230'
assert o.connection_points==[Point(x=100,y=-230),Point(x=150,y=-230)]
def test_part_visibility(ascii_schematic):
data=ascii_schematic+b'|RECORD=13|OwnerIndex=1|OwnerPartId=2|Location.X=2|Corner.X=3\n'
d=parse_schdoc(data,'test.SchDoc','test')
assert not d.objects[-1].visible and not d.objects[-1].primitive_ids
def test_unsupported_preserved(ascii_schematic):
d=parse_schdoc(ascii_schematic+b'|RECORD=999|UnknownProperty=important\n','test.SchDoc','test')
assert d.objects[-1].properties['UNKNOWNPROPERTY']=='important'
assert any(x.code=='unsupported_record' and x.record_type==999 for x in d.diagnostics)
def test_bad_numeric_geometry_reports_partial(ascii_schematic):
d=parse_schdoc(ascii_schematic+b'|RECORD=13|Location.X=nan\n','test.SchDoc','test')
assert any(x.code=='malformed_record' and x.severity=='error' for x in d.diagnostics)
assert d.objects[-1].properties['LOCATION.X']=='nan'
def test_unknown_version(ascii_schematic):
with pytest.raises(ParseError,match='Unsupported schematic version'):
parse_schdoc(ascii_schematic.replace(b'Version 5.0',b'Version 8.0'),'test.SchDoc','test')
def test_ole_malformed_sector_table():
with pytest.raises(ParseError): read_document(b'\xd0\xcf\x11\xe0\xa1\xb1\x1a\xe1'+b'\0'*504)
def test_owner_cycle(ascii_schematic):
d=parse_schdoc(ascii_schematic+b'|RECORD=13|OwnerIndex=9\n','test.SchDoc','test')
assert any(x.code=='malformed_record' for x in d.diagnostics)
@pytest.mark.parametrize('typ,props',[
(5,'|LocationCount=4|X1=0|Y1=0|X2=5|Y2=9|X3=10|Y3=9|X4=15|Y4=0'),
(7,'|LocationCount=3|X1=0|Y1=0|X2=5|Y2=9|X3=10|Y3=0|IsSolid=T'),
(8,'|Radius=5|SecondaryRadius=10'),(9,'|Radius=5|StartAngle=30|EndAngle=180'),
(10,'|Corner.X=20|Corner.Y=10|CornerXRadius=2|CornerYRadius=3'),
(11,'|Radius=10|SecondaryRadius=5|StartAngle=45|EndAngle=200'),
(12,'|Radius=5'),(14,'|Corner.X=10|Corner.Y=20'),
(15,'|XSize=100|YSize=60'),(18,'|Name=DATA|Width=50|Style=3'),
(22,''),(26,'|LocationCount=2|X1=0|Y1=0|X2=20|Y2=30'),(29,''),
(37,'|Corner.X=10|Corner.Y=20'),(225,'|Corner.X=100|Corner.Y=50')])
def test_supported_shapes(ascii_schematic,typ,props):
d=parse_schdoc(ascii_schematic+f'|RECORD={typ}{props}\n'.encode(),'test.SchDoc','test')
obj=d.objects[-1]
assert obj.primitive_ids
assert not any(x.code=='malformed_record' for x in d.diagnostics)
assert all(math.isfinite(p.bounds.width) for p in d.primitives)
def test_clipped_text_does_not_expand_fit_bounds(ascii_schematic):
line='|RECORD=28|Location.X=10|Location.Y=10|Corner.X=60|Corner.Y=40|FontID=1|WordWrap=T|ClipToRect=T|Text='+('Long note text '*100)+'\n'
d=parse_schdoc(ascii_schematic+line.encode(),'test.SchDoc','test')
obj=d.objects[-1]
assert obj.bounds.x>=10 and obj.bounds.x+obj.bounds.width<=60
assert obj.bounds.y>=-40 and obj.bounds.y+obj.bounds.height<=-10
assert any(p.clip for p in d.primitives if p.object_id==obj.id)
def test_invalid_record_type_is_preserved(ascii_schematic):
d=parse_schdoc(ascii_schematic+b'|RECORD=abc|OriginalField=value\n','test.SchDoc','test')
assert d.objects[-1].properties['ORIGINALFIELD']=='value'
assert any(w.code=='malformed_record' for w in d.diagnostics)
def test_implementation_parameter_is_not_drawn(ascii_schematic):
data=ascii_schematic+b'|RECORD=48|OwnerIndex=1\n|RECORD=41|OwnerIndex=9|Name=ModelParam|Text=must-not-render\n'
d=parse_schdoc(data,'test.SchDoc','test')
assert not d.objects[-1].visible
assert not any(p.text=='must-not-render' for p in d.primitives)