From a67f9036b2aa05fdb766c63db47fb60cd6069497 Mon Sep 17 00:00:00 2001 From: jan Date: Mon, 9 Mar 2026 01:01:07 -0700 Subject: [PATCH] [tests] add some more tests --- klamath/test_elements.py | 46 ++++++++++++++++ klamath/test_library.py | 24 +++++++++ klamath/test_records.py | 110 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 180 insertions(+) create mode 100644 klamath/test_records.py diff --git a/klamath/test_elements.py b/klamath/test_elements.py index c1993c2..ae3df18 100644 --- a/klamath/test_elements.py +++ b/klamath/test_elements.py @@ -108,3 +108,49 @@ def test_node_roundtrip() -> None: n2 = Node.read(stream) assert n2.layer == n.layer assert_array_equal(n2.xy, n.xy) + +def test_reference_check() -> None: + import pytest + from klamath.basic import KlamathError + # SREF with too many points + xy = numpy.array([[0, 0], [10, 10]], dtype=numpy.int32) + r = Reference(struct_name=b"CELL", xy=xy, colrow=None, properties={}, invert_y=False, mag=1.0, angle_deg=0.0) + with pytest.raises(KlamathError, match="Expected size-2 xy"): + r.check() + + # AREF with too few points + xy = numpy.array([[0, 0]], dtype=numpy.int32) + r = Reference(struct_name=b"CELL", xy=xy, colrow=(2, 2), properties={}, invert_y=False, mag=1.0, angle_deg=0.0) + with pytest.raises(KlamathError, match="colrow is not None, so expected size-6 xy"): + r.check() + +def test_read_properties_duplicate() -> None: + import pytest + from klamath.basic import KlamathError + from klamath.records import PROPATTR, PROPVALUE, ENDEL + stream = io.BytesIO() + PROPATTR.write(stream, 1) + PROPVALUE.write(stream, b"val1") + PROPATTR.write(stream, 1) # DUPLICATE + PROPVALUE.write(stream, b"val2") + ENDEL.write(stream, None) + stream.seek(0) + + from klamath.elements import read_properties + with pytest.raises(KlamathError, match="Duplicate property key"): + read_properties(stream) + +def test_element_read_unexpected_tag() -> None: + import pytest + from klamath.basic import KlamathError + from klamath.records import SREF, SNAME, HEADER, XY, ENDEL + stream = io.BytesIO() + SREF.write(stream, None) + SNAME.write(stream, b"CELL") + HEADER.write(stream, 123) # UNEXPECTED TAG for Reference.read + XY.write(stream, [0, 0]) + ENDEL.write(stream, None) + stream.seek(0) + + with pytest.raises(KlamathError, match="Unexpected tag"): + Reference.read(stream) diff --git a/klamath/test_library.py b/klamath/test_library.py index 27ee41b..a27c65c 100644 --- a/klamath/test_library.py +++ b/klamath/test_library.py @@ -76,3 +76,27 @@ def test_scan_hierarchy() -> None: assert hierarchy[b"A"] == {b"B": 2} assert hierarchy[b"B"] == {b"C": 6} assert hierarchy[b"C"] == {} + +def test_scan_structs_duplicate() -> None: + import pytest + from klamath.basic import KlamathError + stream = io.BytesIO() + write_struct(stream, name=b"CELL_A", elements=[]) + write_struct(stream, name=b"CELL_A", elements=[]) + ENDLIB.write(stream, None) + stream.seek(0) + + with pytest.raises(KlamathError, match="Duplicate structure name"): + scan_structs(stream) + +def test_scan_hierarchy_duplicate() -> None: + import pytest + from klamath.basic import KlamathError + stream = io.BytesIO() + write_struct(stream, name=b"CELL_A", elements=[]) + write_struct(stream, name=b"CELL_A", elements=[]) + ENDLIB.write(stream, None) + stream.seek(0) + + with pytest.raises(KlamathError, match="Duplicate structure name"): + scan_hierarchy(stream) diff --git a/klamath/test_records.py b/klamath/test_records.py new file mode 100644 index 0000000..fd07b44 --- /dev/null +++ b/klamath/test_records.py @@ -0,0 +1,110 @@ +import io +import pytest +import numpy +from datetime import datetime +from klamath.basic import KlamathError +from klamath import records + +def test_record_tags() -> None: + assert records.HEADER.tag == 0x0002 + assert records.BGNLIB.tag == 0x0102 + assert records.LIBNAME.tag == 0x0206 + assert records.UNITS.tag == 0x0305 + assert records.ENDLIB.tag == 0x0400 + assert records.BGNSTR.tag == 0x0502 + assert records.STRNAME.tag == 0x0606 + assert records.ENDSTR.tag == 0x0700 + assert records.BOUNDARY.tag == 0x0800 + assert records.PATH.tag == 0x0900 + assert records.SREF.tag == 0x0a00 + assert records.AREF.tag == 0x0b00 + assert records.TEXT.tag == 0x0c00 + assert records.LAYER.tag == 0x0d02 + assert records.DATATYPE.tag == 0x0e02 + assert records.WIDTH.tag == 0x0f03 + assert records.XY.tag == 0x1003 + assert records.ENDEL.tag == 0x1100 + assert records.SNAME.tag == 0x1206 + assert records.COLROW.tag == 0x1302 + assert records.NODE.tag == 0x1500 + assert records.TEXTTYPE.tag == 0x1602 + assert records.PRESENTATION.tag == 0x1701 + assert records.STRING.tag == 0x1906 + assert records.STRANS.tag == 0x1a01 + assert records.MAG.tag == 0x1b05 + assert records.ANGLE.tag == 0x1c05 + assert records.REFLIBS.tag == 0x1f06 + assert records.FONTS.tag == 0x2006 + assert records.PATHTYPE.tag == 0x2102 + assert records.GENERATIONS.tag == 0x2202 + assert records.ATTRTABLE.tag == 0x2306 + assert records.ELFLAGS.tag == 0x2601 + assert records.NODETYPE.tag == 0x2a02 + assert records.PROPATTR.tag == 0x2b02 + assert records.PROPVALUE.tag == 0x2c06 + assert records.BOX.tag == 0x2d00 + assert records.BOXTYPE.tag == 0x2e02 + assert records.PLEX.tag == 0x2f03 + assert records.BGNEXTN.tag == 0x3003 + assert records.ENDEXTN.tag == 0x3103 + assert records.TAPENUM.tag == 0x3202 + assert records.TAPECODE.tag == 0x3302 + assert records.FORMAT.tag == 0x3602 + assert records.MASK.tag == 0x3706 + assert records.ENDMASKS.tag == 0x3800 + assert records.LIBDIRSIZE.tag == 0x3902 + assert records.SRFNAME.tag == 0x3a06 + assert records.LIBSECUR.tag == 0x3b02 + +def test_header_validation() -> None: + # Correct size + records.HEADER.check_size(2) + + # Incorrect size + with pytest.raises(KlamathError, match="Expected size 2, got 4"): + records.HEADER.check_size(4) + +def test_bgnlib_validation() -> None: + now = datetime(2023, 10, 27, 12, 30, 45) + # Correct size (2 datetimes = 24 bytes) + records.BGNLIB.check_size(24) + + # Incorrect size + with pytest.raises(KlamathError, match="Expected size 24, got 12"): + records.BGNLIB.check_size(12) + +def test_reflibs_fonts_validation() -> None: + # REFLIBS must be multiple of 44 + records.REFLIBS.check_size(44) + records.REFLIBS.check_size(88) + records.REFLIBS.check_size(0) + + with pytest.raises(KlamathError, match="Expected size to be multiple of 44"): + records.REFLIBS.check_size(10) + +def test_generations_format_validation() -> None: + # GENERATIONS expects exactly one integer + records.GENERATIONS.check_data(3) + records.GENERATIONS.check_data([1]) + + with pytest.raises(KlamathError, match="Expected exactly one integer"): + records.GENERATIONS.check_data([1, 2]) + +def test_attrtable_validation() -> None: + # ATTRTABLE size <= 44 + records.ATTRTABLE.check_size(44) + records.ATTRTABLE.check_size(10) + + with pytest.raises(KlamathError, match="Expected size <= 44"): + records.ATTRTABLE.check_size(45) + +def test_nodata_records() -> None: + stream = io.BytesIO() + records.ENDLIB.write(stream, None) + stream.seek(0) + assert records.ENDLIB.read(stream) is None + + stream = io.BytesIO() + records.BOUNDARY.write(stream, None) + stream.seek(0) + assert records.BOUNDARY.read(stream) is None