Add tests
This commit is contained in:
parent
9c5b902a33
commit
f3be3deadb
5
.gitignore
vendored
5
.gitignore
vendored
@ -7,3 +7,8 @@ build
|
||||
dist
|
||||
fatamorgana.egg-info
|
||||
docs
|
||||
|
||||
*.gds
|
||||
*.gds.gz
|
||||
*.oas
|
||||
*.oas.gz
|
||||
|
7
fatamorgana/test/__init__.py
Normal file
7
fatamorgana/test/__init__.py
Normal file
@ -0,0 +1,7 @@
|
||||
"""
|
||||
Tests (run with `python3 -m pytest -rxPXs | tee results.txt`)
|
||||
|
||||
|
||||
The test_files_* modules are meant to mimic the test cases used by KLayout,
|
||||
in order to provide a secondary validation mechanism.
|
||||
"""
|
96
fatamorgana/test/build_testfiles.py
Normal file
96
fatamorgana/test/build_testfiles.py
Normal file
@ -0,0 +1,96 @@
|
||||
'''
|
||||
Build files equivalent to the test cases used by KLayout.
|
||||
'''
|
||||
# type: ignore
|
||||
|
||||
from typing import Callable
|
||||
from io import BufferedIOBase
|
||||
|
||||
|
||||
from . import (
|
||||
test_files_properties, test_files_cblocks, test_files_layernames,
|
||||
test_files_circles, test_files_ctrapezoids, test_files_trapezoids,
|
||||
test_files_placements, test_files_paths, test_files_modals,
|
||||
test_files_polygons, test_files_rectangles, test_files_empty,
|
||||
test_files_texts, test_files_cells)
|
||||
|
||||
|
||||
def build_file(num: str, func: Callable[[BufferedIOBase], BufferedIOBase]) -> None:
|
||||
with open('t' + num + '.oas', 'wb') as f:
|
||||
func(f)
|
||||
|
||||
|
||||
def write_all_files() -> None:
|
||||
build_file('1.1', test_files_empty.write_file_1)
|
||||
build_file('1.2', test_files_empty.write_file_2)
|
||||
build_file('1.3', test_files_empty.write_file_3)
|
||||
build_file('1.4', test_files_empty.write_file_4)
|
||||
build_file('1.5', test_files_empty.write_file_5)
|
||||
|
||||
build_file('2.1', test_files_cells.write_file_1)
|
||||
build_file('2.2', test_files_cells.write_file_2)
|
||||
build_file('2.3', test_files_cells.write_file_3)
|
||||
build_file('2.4', test_files_cells.write_file_4)
|
||||
build_file('2.5', test_files_cells.write_file_5)
|
||||
build_file('2.6', test_files_cells.write_file_6)
|
||||
build_file('2.7', test_files_cells.write_file_7)
|
||||
|
||||
build_file('3.1', lambda f: test_files_texts.write_file_common(f, 1))
|
||||
build_file('3.2', lambda f: test_files_texts.write_file_common(f, 2))
|
||||
build_file('3.3', test_files_texts.write_file_3)
|
||||
build_file('3.4', test_files_texts.write_file_4)
|
||||
build_file('3.5', lambda f: test_files_texts.write_file_common(f, 5))
|
||||
build_file('3.6', test_files_texts.write_file_6)
|
||||
build_file('3.7', test_files_texts.write_file_7)
|
||||
build_file('3.8', test_files_texts.write_file_8)
|
||||
build_file('3.9', test_files_texts.write_file_9)
|
||||
build_file('3.10', test_files_texts.write_file_10)
|
||||
build_file('3.11', test_files_texts.write_file_11)
|
||||
|
||||
build_file('4.1', lambda f: test_files_rectangles.write_file_common(f, 1))
|
||||
build_file('4.2', lambda f: test_files_rectangles.write_file_common(f, 2))
|
||||
|
||||
build_file('5.1', lambda f: test_files_polygons.write_file_common(f, 1))
|
||||
build_file('5.2', test_files_polygons.write_file_2)
|
||||
build_file('5.3', lambda f: test_files_polygons.write_file_common(f, 3))
|
||||
|
||||
build_file('6.1', test_files_paths.write_file_1)
|
||||
|
||||
build_file('7.1', test_files_trapezoids.write_file_1)
|
||||
|
||||
build_file('8.1', test_files_placements.write_file_1)
|
||||
build_file('8.2', lambda f: test_files_placements.write_file_common(f, 2))
|
||||
build_file('8.3', lambda f: test_files_placements.write_file_common(f, 3))
|
||||
build_file('8.4', test_files_placements.write_file_4)
|
||||
build_file('8.5', lambda f: test_files_placements.write_file_common(f, 5))
|
||||
build_file('8.6', test_files_placements.write_file_6)
|
||||
build_file('8.7', lambda f: test_files_placements.write_file_common(f, 7))
|
||||
build_file('8.8', test_files_placements.write_file_8)
|
||||
|
||||
build_file('9.1', test_files_ctrapezoids.write_file_1)
|
||||
build_file('9.2', test_files_ctrapezoids.write_file_2)
|
||||
|
||||
build_file('10.1', test_files_modals.write_file_1)
|
||||
|
||||
build_file('11.1', lambda f: test_files_properties.write_file_common(f, 1))
|
||||
build_file('11.2', lambda f: test_files_properties.write_file_common(f, 2))
|
||||
build_file('11.3', test_files_properties.write_file_3)
|
||||
build_file('11.4', lambda f: test_files_properties.write_file_4_6(f, 4))
|
||||
build_file('11.5', lambda f: test_files_properties.write_file_common(f, 5))
|
||||
build_file('11.6', lambda f: test_files_properties.write_file_4_6(f, 6))
|
||||
build_file('11.7', lambda f: test_files_properties.write_file_7_8_9(f, 7))
|
||||
build_file('11.8', lambda f: test_files_properties.write_file_7_8_9(f, 8))
|
||||
build_file('11.9', lambda f: test_files_properties.write_file_7_8_9(f, 9))
|
||||
|
||||
build_file('12.1', test_files_circles.write_file_1)
|
||||
|
||||
build_file('13.1', test_files_layernames.write_file_1)
|
||||
build_file('13.2', test_files_layernames.write_file_2)
|
||||
build_file('13.3', test_files_layernames.write_file_3)
|
||||
build_file('13.4', test_files_layernames.write_file_4)
|
||||
|
||||
build_file('14.1', test_files_cblocks.write_file_1)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
write_all_files()
|
183
fatamorgana/test/test_files_cblocks.py
Normal file
183
fatamorgana/test/test_files_cblocks.py
Normal file
@ -0,0 +1,183 @@
|
||||
# type: ignore
|
||||
|
||||
from typing import List, Tuple, Iterable
|
||||
from itertools import chain
|
||||
from io import BytesIO, BufferedIOBase
|
||||
import struct
|
||||
|
||||
import pytest # type: ignore
|
||||
import numpy
|
||||
from numpy.testing import assert_equal
|
||||
|
||||
from .utils import HEADER, FOOTER
|
||||
from ..basic import write_uint, write_sint, read_uint, read_sint, write_bstring, write_byte, PathExtensionScheme
|
||||
from ..basic import InvalidRecordError, InvalidDataError
|
||||
from ..main import OasisLayout
|
||||
|
||||
|
||||
def base_tests(layout: OasisLayout) -> None:
|
||||
assert layout.version.string == '1.0'
|
||||
assert layout.unit == 1000
|
||||
assert layout.validation.checksum_type == 0
|
||||
|
||||
assert not layout.properties
|
||||
assert not layout.propnames
|
||||
assert not layout.xnames
|
||||
assert not layout.textstrings
|
||||
assert not layout.cellnames
|
||||
assert not layout.layers
|
||||
|
||||
assert len(layout.cells) == 1
|
||||
assert layout.cells[0].name.string == 'ABCDH'
|
||||
assert not layout.cells[0].properties
|
||||
|
||||
|
||||
def write_file_1(buf: BufferedIOBase) -> BufferedIOBase:
|
||||
'''
|
||||
'''
|
||||
buf.write(HEADER)
|
||||
|
||||
write_uint(buf, 14) # CELL record (explicit)
|
||||
write_bstring(buf, b'ABCDH') # Cell name
|
||||
|
||||
cblock_data = bytes.fromhex('''
|
||||
22 00 b0 02 b2 02 13
|
||||
a9 66 60 98 c3 32 89 e5 0e e3 1b 61 91 4a c6 15
|
||||
ac 8f 58 3a f8 be f0 8a 5a b0 30 57 5f 64 6d e4
|
||||
4f bd c8 7a 87 ed 81 f8 02 79 a0 88 68 f5 42 b6
|
||||
4e be 80 99 4c 3b 99 35 97 30 4f 14 d7 3c 14 f4
|
||||
52 50 e4 24 e3 0b f6 9b c2 1a 9a 27 18 57 4b 8a
|
||||
04 ae 65 3f 12 04 24 36 0b 8b 2c f2 e9 14 16 3d
|
||||
c6 73 92 4d 64 21 e3 0b 9e cf 9a 15 4f 59 6f 08
|
||||
83 cc 5d c8 f8 91 7b 25 7f ea 4e e6 03 3c 5b a4
|
||||
66 88 01 85 d8 37 b2 fc 64 bd c8 25 5a 79 92 b1
|
||||
99 4b a3 93 65 26 7b 33 bf e6 69 b6 39 7c a9 4b
|
||||
40 2e e1 3b 28 a6 79 82 69 41 98 f6 14 ae 60 9b
|
||||
d7 4c a2 9a 3d 8c 37 f9 6c 03 3f 32 b6 68 2c 64
|
||||
5c cb f3 9a 49 f3 33 e3 0c a6 dd da 29 2f 98 76
|
||||
80 d4 73 df 64 f9 cb b3 58 33 60 36 d3 13 d6 9b
|
||||
9c b6 9a 3b 98 5f b2 07 2e 64 dc c9 7c 91 4b 24
|
||||
f8 08 cb 6e 45 8d 47 32 1d 12 77 b8 81 4a 59 17
|
||||
68 6a 1f 60 df 28 ac a9 3d 85 b5 5b b6 62 0a ff
|
||||
0c 69 90 7b 36 b3 6c 65 d3 9c c9 f4 40 b1 93 a5
|
||||
47 e0 32 7f 8a e6 54 d6 93 6c a2 0f 14 17 c8 03
|
||||
00''')
|
||||
for byte in cblock_data:
|
||||
write_byte(buf, byte)
|
||||
|
||||
buf.write(FOOTER)
|
||||
return buf
|
||||
|
||||
|
||||
def test_file_1() -> None:
|
||||
buf = write_file_1(BytesIO())
|
||||
|
||||
buf.seek(0)
|
||||
layout = OasisLayout.read(buf)
|
||||
|
||||
base_tests(layout)
|
||||
|
||||
geometry = layout.cells[0].geometry
|
||||
assert len(geometry) == 10
|
||||
|
||||
for ii, gg in enumerate(geometry):
|
||||
msg = f'Failed on geometry {ii}'
|
||||
assert gg.x == [110, 900, 1520, -370, 1690, -50, 180, 1540, 970, 2160][ii], msg
|
||||
assert gg.y == [1270, 890, 2000, 1260, 1420, 850, 860, 750, 1740, 2000][ii], msg
|
||||
if ii == 0:
|
||||
assert gg.layer == 0, msg
|
||||
else:
|
||||
assert gg.layer == 1, msg
|
||||
assert gg.datatype == 0, msg
|
||||
|
||||
assert not gg.properties, msg
|
||||
assert gg.repetition is None, msg
|
||||
|
||||
assert geometry[0].height == 530
|
||||
assert geometry[0].width == 540
|
||||
assert geometry[1].height == 610
|
||||
assert geometry[1].width == 680
|
||||
|
||||
assert_equal(geometry[2].point_list,
|
||||
[[-30, -360], [480, -50], [180, 430], [-630, -20]])
|
||||
|
||||
assert_equal(geometry[3].point_list,
|
||||
[[-30, -400],
|
||||
[450, 40],
|
||||
[70, -220],
|
||||
[10, 210],
|
||||
[740, -20],
|
||||
[0, 660],
|
||||
[570, 10],
|
||||
[50, 500],
|
||||
[630, 20],
|
||||
[10, 100],
|
||||
[-810, 10],
|
||||
[20, -470],
|
||||
[-660, 0],
|
||||
[20, -470],
|
||||
[-620, 10],
|
||||
[0, 610],
|
||||
[610, -10],
|
||||
[0, -100],
|
||||
[210, 10],
|
||||
[40, 820],
|
||||
[-1340, 60],
|
||||
[30, -1370]])
|
||||
|
||||
assert_equal(geometry[4].point_list,
|
||||
[[40, -760], [490, -50], [110, 800], [-640, 10]])
|
||||
|
||||
assert_equal(geometry[5].point_list,
|
||||
[[140, -380],
|
||||
[340, -10],
|
||||
[30, -100],
|
||||
[-320, 20],
|
||||
[130, -460],
|
||||
[-480, -20],
|
||||
[-210, 910],
|
||||
[370, 40]])
|
||||
|
||||
assert_equal(geometry[6].point_list,
|
||||
[[720, -20],
|
||||
[20, 20],
|
||||
[690, 0],
|
||||
[-10, 650],
|
||||
[-20, 30],
|
||||
[-90, -10],
|
||||
[10, 70],
|
||||
[470, -30],
|
||||
[20, -120],
|
||||
[-320, 0],
|
||||
[40, -790],
|
||||
[-90, -20],
|
||||
[-60, 140],
|
||||
[-1390, 50],
|
||||
[10, 30]])
|
||||
|
||||
assert_equal(geometry[7].point_list,
|
||||
[[150, -830],
|
||||
[-1320, 40],
|
||||
[-70, 370],
|
||||
[310, -30],
|
||||
[10, 220],
|
||||
[250, -40],
|
||||
[40, -220],
|
||||
[340, 10],
|
||||
[-20, 290],
|
||||
[-1070, 20],
|
||||
[0, 230],
|
||||
[1380, -60]])
|
||||
|
||||
assert_equal(geometry[8].point_list,
|
||||
[[330, 0], [-10, 480], [620, -20], [-10, 330], [-930, 60], [0, -850]])
|
||||
|
||||
assert_equal(geometry[9].point_list,
|
||||
[[-140, -410],
|
||||
[10, -140],
|
||||
[270, 0],
|
||||
[130, 1030],
|
||||
[-500, 50],
|
||||
[10, -330],
|
||||
[210, -10],
|
||||
[10, -190]])
|
275
fatamorgana/test/test_files_cells.py
Normal file
275
fatamorgana/test/test_files_cells.py
Normal file
@ -0,0 +1,275 @@
|
||||
# type: ignore
|
||||
|
||||
from typing import List, Tuple, Iterable
|
||||
from itertools import chain
|
||||
from io import BytesIO, BufferedIOBase
|
||||
import struct
|
||||
|
||||
import pytest # type: ignore
|
||||
|
||||
from .utils import HEADER, FOOTER
|
||||
from ..basic import write_uint, write_sint, read_uint, read_sint, write_bstring
|
||||
from ..basic import InvalidRecordError, InvalidDataError
|
||||
from ..main import OasisLayout
|
||||
|
||||
|
||||
def base_tests(layout: OasisLayout) -> None:
|
||||
assert layout.version.string == '1.0'
|
||||
assert layout.unit == 1000
|
||||
assert layout.validation.checksum_type == 0
|
||||
|
||||
assert not layout.properties
|
||||
assert not layout.propnames
|
||||
assert not layout.xnames
|
||||
assert not layout.textstrings
|
||||
assert not layout.propstrings
|
||||
assert not layout.layers
|
||||
|
||||
|
||||
def write_file_1(buf: BufferedIOBase) -> BufferedIOBase:
|
||||
'''
|
||||
Single cell with explicit name 'XYZ'
|
||||
'''
|
||||
buf.write(HEADER)
|
||||
|
||||
write_uint(buf, 14) # CELL record (explicit)
|
||||
write_bstring(buf, b'XYZ') # Cell name
|
||||
|
||||
buf.write(FOOTER)
|
||||
return buf
|
||||
|
||||
|
||||
def test_file_1() -> None:
|
||||
buf = write_file_1(BytesIO())
|
||||
|
||||
buf.seek(0)
|
||||
layout = OasisLayout.read(buf)
|
||||
|
||||
base_tests(layout)
|
||||
assert len(layout.cells) == 1
|
||||
assert layout.cells[0].name.string == 'XYZ'
|
||||
assert not layout.cellnames
|
||||
|
||||
|
||||
def write_file_2(buf: BufferedIOBase) -> BufferedIOBase:
|
||||
'''
|
||||
Two cellnames ('XYZ', 'ABC') and two cells with name references.
|
||||
'''
|
||||
buf.write(HEADER)
|
||||
|
||||
write_uint(buf, 3) # CELLNAME record (implicit id 0)
|
||||
write_bstring(buf, b'XYZ')
|
||||
|
||||
write_uint(buf, 3) # CELLNAME record (implicit id 1)
|
||||
write_bstring(buf, b'ABC')
|
||||
|
||||
write_uint(buf, 13) # CELL record (name ref.)
|
||||
write_uint(buf, 0) # Cell name 0 (XYZ)
|
||||
|
||||
write_uint(buf, 13) # CELL record (name ref.)
|
||||
write_uint(buf, 1) # Cell name 1 (ABC)
|
||||
|
||||
buf.write(FOOTER)
|
||||
return buf
|
||||
|
||||
|
||||
def test_file_2() -> None:
|
||||
buf = write_file_2(BytesIO())
|
||||
|
||||
buf.seek(0)
|
||||
layout = OasisLayout.read(buf)
|
||||
|
||||
base_tests(layout)
|
||||
assert len(layout.cellnames) == 2
|
||||
assert len(layout.cells) == 2
|
||||
assert layout.cellnames[0].nstring.string == 'XYZ'
|
||||
assert layout.cellnames[1].nstring.string == 'ABC'
|
||||
assert layout.cells[0].name == 0
|
||||
assert layout.cells[1].name == 1
|
||||
|
||||
|
||||
def write_file_3(buf: BufferedIOBase) -> BufferedIOBase:
|
||||
'''
|
||||
Invalid file, contains a mix of explicit and implicit cellnames
|
||||
'''
|
||||
buf.write(HEADER)
|
||||
|
||||
write_uint(buf, 4) # CELLNAME record (explicit id)
|
||||
write_bstring(buf, b'ABC')
|
||||
write_uint(buf, 1) # id 1
|
||||
|
||||
write_uint(buf, 3) # CELLNAME record (implicit id 0) -- Expect failure due to mix of explicit/implicit ids
|
||||
write_bstring(buf, b'XYZ')
|
||||
|
||||
write_uint(buf, 13) # CELL record (name ref.)
|
||||
write_uint(buf, 0) # Cell name 0 (XYZ)
|
||||
|
||||
write_uint(buf, 13) # CELL record (name ref.)
|
||||
write_uint(buf, 1) # Cell name 1 (ABC)
|
||||
|
||||
buf.write(FOOTER)
|
||||
return buf
|
||||
|
||||
|
||||
def test_file_3() -> None:
|
||||
buf = write_file_3(BytesIO())
|
||||
|
||||
buf.seek(0)
|
||||
with pytest.raises(InvalidRecordError):
|
||||
layout = OasisLayout.read(buf)
|
||||
|
||||
|
||||
def write_file_4(buf: BufferedIOBase) -> BufferedIOBase:
|
||||
'''
|
||||
Two cells referencing two names with explicit ids (unsorted)
|
||||
'''
|
||||
buf.write(HEADER)
|
||||
|
||||
write_uint(buf, 4) # CELLNAME record (explicit id)
|
||||
write_bstring(buf, b'ABC')
|
||||
write_uint(buf, 1) # id 1
|
||||
|
||||
write_uint(buf, 4) # CELLNAME record (explicit id)
|
||||
write_bstring(buf, b'XYZ')
|
||||
write_uint(buf, 0) # id 0
|
||||
|
||||
write_uint(buf, 13) # CELL record (name ref.)
|
||||
write_uint(buf, 0) # Cell name 0 (XYZ)
|
||||
|
||||
write_uint(buf, 13) # CELL record (name ref.)
|
||||
write_uint(buf, 1) # Cell name 1 (ABC)
|
||||
|
||||
buf.write(FOOTER)
|
||||
return buf
|
||||
|
||||
|
||||
def test_file_4() -> None:
|
||||
buf = write_file_4(BytesIO())
|
||||
|
||||
buf.seek(0)
|
||||
layout = OasisLayout.read(buf)
|
||||
|
||||
base_tests(layout)
|
||||
assert len(layout.cellnames) == 2
|
||||
assert len(layout.cells) == 2
|
||||
assert layout.cellnames[0].nstring.string == 'XYZ'
|
||||
assert layout.cellnames[1].nstring.string == 'ABC'
|
||||
assert layout.cells[0].name == 0
|
||||
assert layout.cells[1].name == 1
|
||||
|
||||
|
||||
def write_file_5(buf: BufferedIOBase) -> BufferedIOBase:
|
||||
'''
|
||||
Reference to non-existent cell name.
|
||||
'''
|
||||
buf.write(HEADER)
|
||||
|
||||
write_uint(buf, 4) # CELLNAME record (explicit id)
|
||||
write_bstring(buf, b'ABC')
|
||||
write_uint(buf, 1) # id 1
|
||||
|
||||
write_uint(buf, 4) # CELLNAME record (explicit id)
|
||||
write_bstring(buf, b'XYZ')
|
||||
write_uint(buf, 0) # id 0
|
||||
|
||||
write_uint(buf, 13) # CELL record (name ref.)
|
||||
write_uint(buf, 0) # Cell name 0 (XYZ)
|
||||
|
||||
write_uint(buf, 13) # CELL record (name ref.)
|
||||
write_uint(buf, 2) # Cell name 2 -- Reference to non-existent CELLNAME!!!
|
||||
|
||||
buf.write(FOOTER)
|
||||
return buf
|
||||
|
||||
|
||||
def test_file_5() -> None:
|
||||
buf = write_file_5(BytesIO())
|
||||
|
||||
buf.seek(0)
|
||||
layout = OasisLayout.read(buf)
|
||||
|
||||
base_tests(layout)
|
||||
assert len(layout.cellnames) == 2
|
||||
assert len(layout.cells) == 2
|
||||
assert layout.cellnames[0].nstring.string == 'XYZ'
|
||||
assert layout.cellnames[1].nstring.string == 'ABC'
|
||||
assert layout.cells[0].name == 0
|
||||
assert layout.cells[1].name == 2
|
||||
|
||||
#TODO add optional error checking for this case
|
||||
|
||||
|
||||
def write_file_6(buf: BufferedIOBase) -> BufferedIOBase:
|
||||
'''
|
||||
Cellname with invalid n-string.
|
||||
'''
|
||||
buf.write(HEADER)
|
||||
|
||||
write_uint(buf, 4) # CELLNAME record (explicit id)
|
||||
write_bstring(buf, b'ABC')
|
||||
write_uint(buf, 1) # id 1
|
||||
|
||||
write_uint(buf, 4) # CELLNAME record (explicit id)
|
||||
write_bstring(buf, b' XYZ')
|
||||
write_uint(buf, 0) # id 0
|
||||
|
||||
write_uint(buf, 13) # CELL record (name ref.)
|
||||
write_uint(buf, 0) # Cell name 0 (XYZ)
|
||||
|
||||
write_uint(buf, 13) # CELL record (name ref.)
|
||||
write_uint(buf, 1) # Cell name 1
|
||||
|
||||
buf.write(FOOTER)
|
||||
return buf
|
||||
|
||||
|
||||
def test_file_6() -> None:
|
||||
buf = write_file_6(BytesIO())
|
||||
|
||||
buf.seek(0)
|
||||
|
||||
with pytest.raises(InvalidDataError):
|
||||
layout = OasisLayout.read(buf)
|
||||
|
||||
#base_tests(layout)
|
||||
#assert len(layout.cellnames) == 2
|
||||
#assert len(layout.cells) == 2
|
||||
#assert layout.cellnames[0].nstring.string == ' XYZ'
|
||||
#assert layout.cellnames[1].nstring.string == 'ABC'
|
||||
#assert layout.cells[0].name == 0
|
||||
#assert layout.cells[1].name == 1
|
||||
|
||||
|
||||
def write_file_7(buf: BufferedIOBase) -> BufferedIOBase:
|
||||
'''
|
||||
Unused cellname.
|
||||
'''
|
||||
buf.write(HEADER)
|
||||
|
||||
write_uint(buf, 4) # CELLNAME record (explicit id)
|
||||
write_bstring(buf, b'ABC')
|
||||
write_uint(buf, 1) # id 1
|
||||
|
||||
write_uint(buf, 4) # CELLNAME record (explicit id)
|
||||
write_bstring(buf, b'XYZ')
|
||||
write_uint(buf, 0) # id 0
|
||||
|
||||
write_uint(buf, 13) # CELL record (name ref.)
|
||||
write_uint(buf, 0) # Cell name 0 (XYZ)
|
||||
|
||||
buf.write(FOOTER)
|
||||
return buf
|
||||
|
||||
|
||||
def test_file_7() -> None:
|
||||
buf = write_file_7(BytesIO())
|
||||
|
||||
buf.seek(0)
|
||||
layout = OasisLayout.read(buf)
|
||||
|
||||
base_tests(layout)
|
||||
assert len(layout.cellnames) == 2
|
||||
assert len(layout.cells) == 1
|
||||
assert layout.cellnames[0].nstring.string == 'XYZ'
|
||||
assert layout.cellnames[1].nstring.string == 'ABC'
|
||||
assert layout.cells[0].name == 0
|
117
fatamorgana/test/test_files_circles.py
Normal file
117
fatamorgana/test/test_files_circles.py
Normal file
@ -0,0 +1,117 @@
|
||||
# type: ignore
|
||||
|
||||
from typing import List, Tuple, Iterable
|
||||
from itertools import chain
|
||||
from io import BytesIO, BufferedIOBase
|
||||
import struct
|
||||
|
||||
import pytest # type: ignore
|
||||
import numpy
|
||||
from numpy.testing import assert_equal
|
||||
|
||||
from .utils import HEADER, FOOTER
|
||||
from ..basic import write_uint, write_sint, read_uint, read_sint, write_bstring, write_byte, PathExtensionScheme
|
||||
from ..basic import InvalidRecordError, InvalidDataError
|
||||
from ..main import OasisLayout
|
||||
|
||||
|
||||
def base_tests(layout: OasisLayout) -> None:
|
||||
assert layout.version.string == '1.0'
|
||||
assert layout.unit == 1000
|
||||
assert layout.validation.checksum_type == 0
|
||||
|
||||
assert not layout.properties
|
||||
assert not layout.propnames
|
||||
assert not layout.xnames
|
||||
assert not layout.textstrings
|
||||
assert not layout.cellnames
|
||||
assert not layout.layers
|
||||
|
||||
assert len(layout.cells) == 1
|
||||
assert layout.cells[0].name.string == 'A'
|
||||
assert not layout.cells[0].properties
|
||||
|
||||
|
||||
def write_file_1(buf: BufferedIOBase) -> BufferedIOBase:
|
||||
'''
|
||||
'''
|
||||
buf.write(HEADER)
|
||||
|
||||
write_uint(buf, 14) # CELL record (explicit)
|
||||
write_bstring(buf, b'A') # Cell name
|
||||
|
||||
write_uint(buf, 27) # CIRCLE record
|
||||
write_byte(buf, 0b0011_1011) # 00rX_YRDL
|
||||
write_uint(buf, 1) # layer
|
||||
write_uint(buf, 2) # datatype
|
||||
write_uint(buf, 150) # radius
|
||||
write_sint(buf, -100) # geometry-x (absolute)
|
||||
write_sint(buf, 200) # geometry-y (absolute)
|
||||
|
||||
write_uint(buf, 16) # XYRELATIVE record
|
||||
|
||||
write_uint(buf, 27) # CIRCLE record
|
||||
write_byte(buf, 0b0000_1000) # 00rX_YRDL
|
||||
write_sint(buf, 400) # geometry-y (relative)
|
||||
|
||||
write_uint(buf, 27) # CIRCLE record
|
||||
write_byte(buf, 0b0010_1000) # 00rX_YRDL
|
||||
write_uint(buf, 0) # radius
|
||||
write_sint(buf, 400) # geometry-y (relative)
|
||||
|
||||
write_uint(buf, 27) # CIRCLE record
|
||||
write_byte(buf, 0b0010_1000) # 00rX_YRDL
|
||||
write_uint(buf, 1) # radius
|
||||
write_sint(buf, 400) # geometry-y (relative)
|
||||
|
||||
write_uint(buf, 27) # CIRCLE record
|
||||
write_byte(buf, 0b0010_1000) # 00rX_YRDL
|
||||
write_uint(buf, 6) # radius
|
||||
write_sint(buf, 400) # geometry-y (relative)
|
||||
|
||||
write_uint(buf, 27) # CIRCLE record
|
||||
write_byte(buf, 0b0010_1000) # 00rX_YRDL
|
||||
write_uint(buf, 20) # radius
|
||||
write_sint(buf, 400) # geometry-y (relative)
|
||||
|
||||
write_uint(buf, 27) # CIRCLE record
|
||||
write_byte(buf, 0b0010_1100) # 00rX_YRDL
|
||||
write_uint(buf, 100) # radius
|
||||
write_sint(buf, 400) # geometry-y (relative)
|
||||
write_uint(buf, 1) # repetition (3x4 matrix)
|
||||
write_uint(buf, 1) # (repetition) x-dimension
|
||||
write_uint(buf, 2) # (repetition) y-dimension
|
||||
write_uint(buf, 400) # (repetition) x-spacing
|
||||
write_uint(buf, 300) # (repetition) y-spacing
|
||||
|
||||
buf.write(FOOTER)
|
||||
return buf
|
||||
|
||||
|
||||
def test_file_1() -> None:
|
||||
buf = write_file_1(BytesIO())
|
||||
|
||||
buf.seek(0)
|
||||
layout = OasisLayout.read(buf)
|
||||
|
||||
base_tests(layout)
|
||||
|
||||
geometry = layout.cells[0].geometry
|
||||
assert len(geometry) == 7
|
||||
for ii, gg in enumerate(geometry):
|
||||
msg = f'Failed on circle {ii}'
|
||||
assert gg.x == -100, msg
|
||||
assert gg.y == 200 + 400 * ii, msg
|
||||
|
||||
assert gg.layer == 1, msg
|
||||
assert gg.datatype == 2, msg
|
||||
assert not gg.properties, msg
|
||||
assert gg.radius == [150, 150, 0, 1, 6, 20, 100][ii], msg
|
||||
|
||||
if ii != 6:
|
||||
assert gg.repetition is None, msg
|
||||
|
||||
assert geometry[6].repetition.a_count == 3, msg
|
||||
assert geometry[6].repetition.b_count == 4, msg
|
||||
assert geometry[6].repetition.a_vector == [400, 0], msg
|
||||
assert geometry[6].repetition.b_vector == [0, 300], msg
|
247
fatamorgana/test/test_files_ctrapezoids.py
Normal file
247
fatamorgana/test/test_files_ctrapezoids.py
Normal file
@ -0,0 +1,247 @@
|
||||
# type: ignore
|
||||
|
||||
from typing import List, Tuple, Iterable
|
||||
from itertools import chain
|
||||
from io import BytesIO, BufferedIOBase
|
||||
import struct
|
||||
|
||||
import pytest # type: ignore
|
||||
import numpy
|
||||
from numpy.testing import assert_equal
|
||||
|
||||
from .utils import HEADER, FOOTER
|
||||
from ..basic import write_uint, write_sint, read_uint, read_sint, write_bstring, write_byte, PathExtensionScheme
|
||||
from ..basic import InvalidRecordError, InvalidDataError
|
||||
from ..main import OasisLayout
|
||||
|
||||
|
||||
def base_tests(layout: OasisLayout) -> None:
|
||||
assert layout.version.string == '1.0'
|
||||
assert layout.unit == 1000
|
||||
assert layout.validation.checksum_type == 0
|
||||
|
||||
assert not layout.properties
|
||||
assert not layout.propnames
|
||||
assert not layout.xnames
|
||||
assert not layout.textstrings
|
||||
assert not layout.cellnames
|
||||
assert not layout.layers
|
||||
|
||||
|
||||
def write_file_1(buf: BufferedIOBase) -> BufferedIOBase:
|
||||
'''
|
||||
'''
|
||||
buf.write(HEADER)
|
||||
|
||||
write_uint(buf, 14) # CELL record (explicit)
|
||||
write_bstring(buf, b'A') # Cell name
|
||||
|
||||
write_uint(buf, 26) # CTRAPEZOID record
|
||||
write_byte(buf, 0b1111_1011) # TWHX_YRDL
|
||||
write_uint(buf, 1) # layer
|
||||
write_uint(buf, 2) # datatype
|
||||
write_uint(buf, 24) # ctrapezoid type
|
||||
write_uint(buf, 100) # width
|
||||
write_uint(buf, 200) # height
|
||||
write_sint(buf, -100) # geometry-x (absolute)
|
||||
write_sint(buf, 200) # geometry-y (absolute)
|
||||
|
||||
write_uint(buf, 16) # XYRELATIVE record
|
||||
|
||||
write_uint(buf, 26) # CTRAPEZOID record
|
||||
write_byte(buf, 0b0000_1000) # TWHX_YRDL
|
||||
write_sint(buf, 400) # geometry-y (relative)
|
||||
|
||||
write_uint(buf, 20) # RECTANGLE record
|
||||
write_byte(buf, 0b0000_0011) # SWHX_YRDL
|
||||
write_uint(buf, 2) # layer
|
||||
write_uint(buf, 3) # datatype
|
||||
|
||||
h = [250, 100]
|
||||
v = [100, 250]
|
||||
|
||||
wh = [h] * 8 + [v] * 8 + [h] * 6 + [v] * 2 + [h] * 2
|
||||
|
||||
wh_en = ([0b11] * 16
|
||||
+ [0b10] * 4
|
||||
+ [0b01] * 2
|
||||
+ [0b10] * 2
|
||||
+ [0b11, 0b10])
|
||||
|
||||
for t, (x, x_en) in enumerate(zip(wh, wh_en)):
|
||||
write_uint(buf, 26) # CTRAPEZOID record
|
||||
write_byte(buf, 0b1000_1011 | (x_en << 5)) # TWHX_YRDL
|
||||
write_uint(buf, 1) # layer
|
||||
write_uint(buf, 2) # datatype
|
||||
write_uint(buf, t) # ctrapezoid type
|
||||
if x_en & 0b10:
|
||||
write_uint(buf, x[0]) # width
|
||||
if x_en & 0b01:
|
||||
write_uint(buf, x[1]) # height
|
||||
write_sint(buf, 400) # geometry-y (relative)
|
||||
|
||||
write_uint(buf, 20) # RECTANGLE record
|
||||
write_byte(buf, 0b0000_0011) # SWHX_YRDL
|
||||
write_uint(buf, 2) # layer
|
||||
write_uint(buf, 3) # datatype
|
||||
|
||||
write_uint(buf, 26) # CTRAPEZOID record
|
||||
write_byte(buf, 0b0000_1100) # TWHX_YRDL
|
||||
write_sint(buf, 400) # geometry-y (relative)
|
||||
write_uint(buf, 1) # repetition (3x4 matrix)
|
||||
write_uint(buf, 1) # (repetition) x-dimension
|
||||
write_uint(buf, 2) # (repetition) y-dimension
|
||||
write_uint(buf, 400) # (repetition) x-spacing
|
||||
write_uint(buf, 300) # (repetition) y-spacing
|
||||
|
||||
buf.write(FOOTER)
|
||||
return buf
|
||||
|
||||
|
||||
def test_file_1() -> None:
|
||||
buf = write_file_1(BytesIO())
|
||||
|
||||
buf.seek(0)
|
||||
layout = OasisLayout.read(buf)
|
||||
|
||||
base_tests(layout)
|
||||
|
||||
assert len(layout.cells) == 1
|
||||
assert layout.cells[0].name.string == 'A'
|
||||
assert not layout.cells[0].properties
|
||||
|
||||
geometry = layout.cells[0].geometry
|
||||
assert len(geometry) == 3 + 26 * 2 + 1
|
||||
|
||||
for ii, gg in enumerate(geometry):
|
||||
msg = f'Failed on shape {ii}'
|
||||
assert gg.x == -100, msg
|
||||
assert gg.y == 200 + 400 * ((ii + 1) // 2), msg
|
||||
|
||||
if ii < 2 or (3 <= ii < 55 and ii % 2 == 1):
|
||||
assert gg.layer == 1, msg
|
||||
assert gg.datatype == 2, msg
|
||||
else:
|
||||
assert gg.layer == 2, msg
|
||||
assert gg.datatype == 3, msg
|
||||
|
||||
if ii < 3:
|
||||
assert gg.width == 100, msg
|
||||
assert gg.height == 200, msg
|
||||
|
||||
assert not gg.properties, msg
|
||||
|
||||
if 3 <= ii < 55:
|
||||
ct_type = (ii - 3) // 2
|
||||
is_ctrapz = ii % 2 == 1
|
||||
if is_ctrapz:
|
||||
assert gg.ctrapezoid_type == ct_type, msg
|
||||
if ct_type in range(16, 20):
|
||||
assert gg.height == [250, None][is_ctrapz], msg
|
||||
elif ct_type in (20, 21):
|
||||
assert gg.width == [250, None][is_ctrapz], msg
|
||||
elif ct_type in range(22, 24) or ct_type == 25:
|
||||
assert gg.height == [100, None][is_ctrapz], msg
|
||||
else:
|
||||
if ct_type < 8 or 16 <= ct_type < 25 or 26 <= ct_type :
|
||||
assert gg.width == 250, msg
|
||||
assert gg.height == 100, msg
|
||||
else:
|
||||
assert gg.width == 100, msg
|
||||
assert gg.height == 250, msg
|
||||
elif ii < 3 and ii % 2:
|
||||
assert gg.ctrapezoid_type == 24, msg
|
||||
elif ii == 55:
|
||||
assert gg.ctrapezoid_type == 25, msg
|
||||
|
||||
assert geometry[55].repetition.a_count == 3
|
||||
assert geometry[55].repetition.b_count == 4
|
||||
assert geometry[55].repetition.a_vector == [400, 0]
|
||||
assert geometry[55].repetition.b_vector == [0, 300]
|
||||
|
||||
|
||||
def write_file_2(buf: BufferedIOBase) -> BufferedIOBase:
|
||||
'''
|
||||
'''
|
||||
buf.write(HEADER)
|
||||
|
||||
write_uint(buf, 14) # CELL record (explicit)
|
||||
write_bstring(buf, b'A') # Cell name
|
||||
|
||||
# Shouldn't access (undefined) height modal, despite not having a height.
|
||||
write_uint(buf, 26) # CTRAPEZOID record
|
||||
write_byte(buf, 0b1101_1011) # TWHX_YRDL
|
||||
write_uint(buf, 1) # layer
|
||||
write_uint(buf, 2) # datatype
|
||||
write_uint(buf, 16) # ctrapezoid type
|
||||
write_uint(buf, 200) # width
|
||||
write_sint(buf, -100) # geometry-x (absolute)
|
||||
write_sint(buf, 200) # geometry-y (absolute)
|
||||
|
||||
write_uint(buf, 16) # XYRELATIVE record
|
||||
|
||||
write_uint(buf, 26) # CTRAPEZOID record
|
||||
write_byte(buf, 0b0000_1000) # TWHX_YRDL
|
||||
write_sint(buf, 400) # geometry-y (relative)
|
||||
|
||||
write_uint(buf, 14) # CELL record (explicit)
|
||||
write_bstring(buf, b'B') # Cell name
|
||||
|
||||
# Shouldn't access (undefined) width modal, despite not having a width.
|
||||
write_uint(buf, 26) # CTRAPEZOID record
|
||||
write_byte(buf, 0b1011_1011) # TWHX_YRDL
|
||||
write_uint(buf, 1) # layer
|
||||
write_uint(buf, 2) # datatype
|
||||
write_uint(buf, 20) # ctrapezoid type
|
||||
write_uint(buf, 200) # height
|
||||
write_sint(buf, -100) # geometry-x (absolute)
|
||||
write_sint(buf, 200) # geometry-y (absolute)
|
||||
|
||||
write_uint(buf, 16) # XYRELATIVE record
|
||||
|
||||
write_uint(buf, 26) # CTRAPEZOID record
|
||||
write_byte(buf, 0b0000_1000) # TWHX_YRDL
|
||||
write_sint(buf, 400) # geometry-y (relative)
|
||||
|
||||
buf.write(FOOTER)
|
||||
return buf
|
||||
|
||||
|
||||
def test_file_2() -> None:
|
||||
buf = write_file_2(BytesIO())
|
||||
|
||||
buf.seek(0)
|
||||
layout = OasisLayout.read(buf)
|
||||
|
||||
base_tests(layout)
|
||||
|
||||
assert len(layout.cells) == 2
|
||||
assert layout.cells[0].name.string == 'A'
|
||||
assert layout.cells[1].name.string == 'B'
|
||||
assert not layout.cells[0].properties
|
||||
assert not layout.cells[1].properties
|
||||
|
||||
for ii, cc in enumerate(layout.cells):
|
||||
for jj, gg in enumerate(cc.geometry):
|
||||
msg = f'Fail in cell {ii}, ctrapezoid {jj}'
|
||||
assert not gg.properties, msg
|
||||
assert gg.layer == 1, msg
|
||||
assert gg.datatype == 2, msg
|
||||
assert gg.x == -100, msg
|
||||
|
||||
geometry = layout.cells[0].geometry
|
||||
assert geometry[0].width == 200
|
||||
assert geometry[1].width == 200
|
||||
assert geometry[0].ctrapezoid_type == 16
|
||||
assert geometry[1].ctrapezoid_type == 16
|
||||
assert geometry[0].y == 200
|
||||
assert geometry[1].y == 600
|
||||
|
||||
geometry = layout.cells[1].geometry
|
||||
assert geometry[0].height == 200
|
||||
assert geometry[1].height == 200
|
||||
assert geometry[0].ctrapezoid_type == 20
|
||||
assert geometry[1].ctrapezoid_type == 20
|
||||
assert geometry[0].y == 200
|
||||
assert geometry[1].y == 600
|
||||
|
185
fatamorgana/test/test_files_empty.py
Normal file
185
fatamorgana/test/test_files_empty.py
Normal file
@ -0,0 +1,185 @@
|
||||
# type: ignore
|
||||
|
||||
from typing import List, Tuple, Iterable
|
||||
from itertools import chain
|
||||
from io import BytesIO, BufferedIOBase
|
||||
import struct
|
||||
|
||||
import pytest # type: ignore
|
||||
|
||||
from .utils import MAGIC_BYTES, FOOTER
|
||||
from ..basic import write_uint, write_sint, read_uint, read_sint, write_bstring
|
||||
from ..main import OasisLayout
|
||||
|
||||
|
||||
def base_tests(layout: OasisLayout) -> None:
|
||||
assert layout.version.string == '1.0'
|
||||
assert layout.validation.checksum_type == 0
|
||||
|
||||
assert not layout.properties
|
||||
assert not layout.cells
|
||||
assert not layout.cellnames
|
||||
assert not layout.propnames
|
||||
assert not layout.xnames
|
||||
assert not layout.textstrings
|
||||
assert not layout.propstrings
|
||||
assert not layout.layers
|
||||
|
||||
|
||||
def write_file_1(buf: BufferedIOBase) -> BufferedIOBase:
|
||||
'''
|
||||
File contains one PAD record.
|
||||
1000 units/micron
|
||||
Offset table inside START.
|
||||
'''
|
||||
buf.write(MAGIC_BYTES)
|
||||
|
||||
write_uint(buf, 1) # START record
|
||||
write_bstring(buf, b'1.0') # version
|
||||
write_uint(buf, 0) # dbu real type: uint
|
||||
write_uint(buf, 1000) # dbu value: 1000 per micron
|
||||
write_uint(buf, 0) # offset table is present here
|
||||
for _ in range(6):
|
||||
write_uint(buf, 0) # offset table (0: not strict)
|
||||
write_uint(buf, 0) # offset table (0: no entry present)
|
||||
|
||||
write_uint(buf, 0) # PAD record
|
||||
|
||||
buf.write(FOOTER)
|
||||
return buf
|
||||
|
||||
|
||||
def test_file_1() -> None:
|
||||
buf = write_file_1(BytesIO())
|
||||
|
||||
buf.seek(0)
|
||||
layout = OasisLayout.read(buf)
|
||||
|
||||
base_tests(layout)
|
||||
assert layout.unit == 1000
|
||||
|
||||
|
||||
|
||||
def write_file_2(buf: BufferedIOBase) -> BufferedIOBase:
|
||||
'''
|
||||
File contains no records.
|
||||
1/2 unit/micron
|
||||
Offset table inside START.
|
||||
'''
|
||||
buf.write(MAGIC_BYTES)
|
||||
|
||||
write_uint(buf, 1) # START record
|
||||
write_bstring(buf, b'1.0') # version
|
||||
write_uint(buf, 2) # dbu real type: fraction 1/x
|
||||
write_uint(buf, 2) # dbu value: 1/2 per micron
|
||||
write_uint(buf, 0) # offset table is present here
|
||||
for _ in range(6):
|
||||
write_uint(buf, 0) # offset table (0: not strict)
|
||||
write_uint(buf, 0) # offset table (0: no entry present)
|
||||
|
||||
buf.write(FOOTER)
|
||||
return buf
|
||||
|
||||
|
||||
def test_file_2() -> None:
|
||||
buf = write_file_2(BytesIO())
|
||||
|
||||
buf.seek(0)
|
||||
layout = OasisLayout.read(buf)
|
||||
|
||||
base_tests(layout)
|
||||
assert layout.unit == 0.5
|
||||
|
||||
|
||||
def write_file_3(buf: BufferedIOBase) -> BufferedIOBase:
|
||||
'''
|
||||
File contains no records.
|
||||
10/4 unit/micron
|
||||
Offset table inside START.
|
||||
'''
|
||||
buf.write(MAGIC_BYTES)
|
||||
|
||||
write_uint(buf, 1) # START record
|
||||
write_bstring(buf, b'1.0') # version
|
||||
write_uint(buf, 4) # dbu real type: fraction a/b
|
||||
write_uint(buf, 10) # dbu value a
|
||||
write_uint(buf, 4) # dbu value b: 10/4 per micron
|
||||
write_uint(buf, 0) # offset table is present here
|
||||
for _ in range(6):
|
||||
write_uint(buf, 0) # offset table (0: not strict)
|
||||
write_uint(buf, 0) # offset table (0: no entry present)
|
||||
|
||||
buf.write(FOOTER)
|
||||
return buf
|
||||
|
||||
|
||||
def test_file_3() -> None:
|
||||
buf = write_file_3(BytesIO())
|
||||
|
||||
buf.seek(0)
|
||||
layout = OasisLayout.read(buf)
|
||||
|
||||
base_tests(layout)
|
||||
assert layout.unit == 10 / 4
|
||||
|
||||
|
||||
def write_file_4(buf: BufferedIOBase) -> BufferedIOBase:
|
||||
'''
|
||||
File contains no records.
|
||||
12.5 unit/micron (float32)
|
||||
Offset table inside START.
|
||||
'''
|
||||
buf.write(MAGIC_BYTES)
|
||||
|
||||
write_uint(buf, 1) # START record
|
||||
write_bstring(buf, b'1.0') # version
|
||||
write_uint(buf, 6) # dbu real type: float32
|
||||
buf.write(struct.pack("<f", 12.5)) # dbu value: 12.5
|
||||
write_uint(buf, 0) # offset table is present here
|
||||
for _ in range(6):
|
||||
write_uint(buf, 0) # offset table (0: not strict)
|
||||
write_uint(buf, 0) # offset table (0: no entry present)
|
||||
|
||||
buf.write(FOOTER)
|
||||
return buf
|
||||
|
||||
|
||||
def test_file_4() -> None:
|
||||
buf = write_file_4(BytesIO())
|
||||
|
||||
buf.seek(0)
|
||||
layout = OasisLayout.read(buf)
|
||||
|
||||
base_tests(layout)
|
||||
assert layout.unit == 12.5
|
||||
|
||||
|
||||
def write_file_5(buf: BufferedIOBase) -> BufferedIOBase:
|
||||
'''
|
||||
File contains no records.
|
||||
12.5 unit/micron (float64)
|
||||
Offset table inside START.
|
||||
'''
|
||||
buf.write(MAGIC_BYTES)
|
||||
|
||||
write_uint(buf, 1) # START record
|
||||
write_bstring(buf, b'1.0') # version
|
||||
write_uint(buf, 7) # dbu real type: float64
|
||||
buf.write(struct.pack("<d", 12.5)) # dbu value: 12.5
|
||||
write_uint(buf, 0) # offset table is present here
|
||||
for _ in range(6):
|
||||
write_uint(buf, 0) # offset table (0: not strict)
|
||||
write_uint(buf, 0) # offset table (0: no entry present)
|
||||
|
||||
buf.write(FOOTER)
|
||||
return buf
|
||||
|
||||
|
||||
def test_file_5() -> None:
|
||||
buf = write_file_5(BytesIO())
|
||||
|
||||
buf.seek(0)
|
||||
layout = OasisLayout.read(buf)
|
||||
|
||||
base_tests(layout)
|
||||
assert layout.unit == 12.5
|
338
fatamorgana/test/test_files_layernames.py
Normal file
338
fatamorgana/test/test_files_layernames.py
Normal file
@ -0,0 +1,338 @@
|
||||
# type: ignore
|
||||
|
||||
from typing import List, Tuple, Iterable, Sequence
|
||||
from itertools import chain
|
||||
from io import BytesIO, BufferedIOBase
|
||||
|
||||
import pytest # type: ignore
|
||||
import numpy
|
||||
from numpy.testing import assert_equal
|
||||
|
||||
from .utils import HEADER, FOOTER
|
||||
from ..basic import write_uint, write_sint, read_uint, read_sint, write_bstring, write_byte, PathExtensionScheme
|
||||
from ..basic import InvalidRecordError, InvalidDataError
|
||||
from ..main import OasisLayout
|
||||
|
||||
|
||||
LAYERS = [(1, 2), (1, 5), (1, 6), (1, 8),
|
||||
(5, 2), (5, 5), (5, 6), (5, 8),
|
||||
(6, 2), (6, 5), (6, 6), (6, 8),
|
||||
(7, 2), (7, 5), (7, 6), (7, 8),
|
||||
]
|
||||
|
||||
def base_tests(layout: OasisLayout) -> None:
|
||||
assert layout.version.string == '1.0'
|
||||
assert layout.unit == 1000
|
||||
assert layout.validation.checksum_type == 0
|
||||
|
||||
assert not layout.properties
|
||||
assert not layout.propnames
|
||||
assert not layout.xnames
|
||||
assert not layout.textstrings
|
||||
assert not layout.cellnames
|
||||
|
||||
assert len(layout.cells) == 1
|
||||
assert layout.cells[0].name.string == 'A'
|
||||
assert not layout.cells[0].properties
|
||||
|
||||
|
||||
def write_names_geom(buf: BufferedIOBase, short: bool = False) -> BufferedIOBase:
|
||||
write_uint(buf, 11) # LAYERNAME record (geometry)
|
||||
write_bstring(buf, b'AA') # name
|
||||
write_uint(buf, 0) # all layers
|
||||
write_uint(buf, 0) # all datatypes
|
||||
|
||||
write_uint(buf, 11) # LAYERNAME record (geometry)
|
||||
write_bstring(buf, b'L5A') # name
|
||||
write_uint(buf, 1) # layer <=5
|
||||
write_uint(buf, 5) # (...)
|
||||
write_uint(buf, 0) # all datatypes
|
||||
|
||||
write_uint(buf, 11) # LAYERNAME record (geometry)
|
||||
write_bstring(buf, b'H5A') # name
|
||||
write_uint(buf, 2) # layer >=5
|
||||
write_uint(buf, 5) # (...)
|
||||
write_uint(buf, 0) # all datatypes
|
||||
|
||||
write_uint(buf, 11) # LAYERNAME record (geometry)
|
||||
write_bstring(buf, b'E5A') # name
|
||||
write_uint(buf, 3) # layer ==5
|
||||
write_uint(buf, 5) # (...)
|
||||
write_uint(buf, 0) # all datatypes
|
||||
|
||||
write_uint(buf, 11) # LAYERNAME record (geometry)
|
||||
write_bstring(buf, b'I56A') # name
|
||||
write_uint(buf, 4) # layer 5 to 6
|
||||
write_uint(buf, 5) # (...)
|
||||
write_uint(buf, 6) # (...)
|
||||
write_uint(buf, 0) # all datatypes
|
||||
|
||||
if short:
|
||||
return buf
|
||||
|
||||
write_uint(buf, 11) # LAYERNAME record (geometry)
|
||||
write_bstring(buf, b'E5L4') # name
|
||||
write_uint(buf, 3) # layer ==5
|
||||
write_uint(buf, 5) # (...)
|
||||
write_uint(buf, 1) # datatype <=4
|
||||
write_uint(buf, 4) # (...)
|
||||
|
||||
write_uint(buf, 11) # LAYERNAME record (geometry)
|
||||
write_bstring(buf, b'E5H4') # name
|
||||
write_uint(buf, 3) # layer ==5
|
||||
write_uint(buf, 5) # (...)
|
||||
write_uint(buf, 2) # datatype >=4
|
||||
write_uint(buf, 4) # (...)
|
||||
|
||||
write_uint(buf, 11) # LAYERNAME record (geometry)
|
||||
write_bstring(buf, b'E5E4') # name
|
||||
write_uint(buf, 3) # layer ==5
|
||||
write_uint(buf, 5) # (...)
|
||||
write_uint(buf, 3) # datatype ==4
|
||||
write_uint(buf, 4) # (...)
|
||||
|
||||
write_uint(buf, 11) # LAYERNAME record (geometry)
|
||||
write_bstring(buf, b'E5I47') # name
|
||||
write_uint(buf, 3) # layer ==5
|
||||
write_uint(buf, 5) # (...)
|
||||
write_uint(buf, 4) # datatype 4 to 7
|
||||
write_uint(buf, 4) # (...)
|
||||
write_uint(buf, 7) # (...)
|
||||
|
||||
return buf
|
||||
|
||||
|
||||
def write_names_text(buf: BufferedIOBase, prefix: bytes = b'') -> BufferedIOBase:
|
||||
write_uint(buf, 12) # LAYERNAME record (geometry)
|
||||
write_bstring(buf, prefix + b'AA') # name
|
||||
write_uint(buf, 0) # all layers
|
||||
write_uint(buf, 0) # all datatypes
|
||||
|
||||
write_uint(buf, 12) # LAYERNAME record (geometry)
|
||||
write_bstring(buf, prefix + b'L5A') # name
|
||||
write_uint(buf, 1) # layer <=5
|
||||
write_uint(buf, 5) # (...)
|
||||
write_uint(buf, 0) # all datatypes
|
||||
|
||||
write_uint(buf, 12) # LAYERNAME record (geometry)
|
||||
write_bstring(buf, prefix + b'H5A') # name
|
||||
write_uint(buf, 2) # layer >=5
|
||||
write_uint(buf, 5) # (...)
|
||||
write_uint(buf, 0) # all datatypes
|
||||
|
||||
write_uint(buf, 12) # LAYERNAME record (geometry)
|
||||
write_bstring(buf, prefix + b'E5A') # name
|
||||
write_uint(buf, 3) # layer ==5
|
||||
write_uint(buf, 5) # (...)
|
||||
write_uint(buf, 0) # all datatypes
|
||||
|
||||
write_uint(buf, 12) # LAYERNAME record (geometry)
|
||||
write_bstring(buf, prefix + b'I56A') # name
|
||||
write_uint(buf, 4) # layer 5 to 6
|
||||
write_uint(buf, 5) # (...)
|
||||
write_uint(buf, 6) # (...)
|
||||
write_uint(buf, 0) # all datatypes
|
||||
return buf
|
||||
|
||||
def write_geom(buf: BufferedIOBase) -> BufferedIOBase:
|
||||
for ll, dt in LAYERS:
|
||||
write_uint(buf, 27) # CIRCLE record
|
||||
write_byte(buf, 0b0011_1011) # 00rX_YRDL
|
||||
write_uint(buf, ll) # layer
|
||||
write_uint(buf, dt) # datatype
|
||||
write_uint(buf, 150) # radius
|
||||
write_sint(buf, ll * 1000) # geometry-x (absolute)
|
||||
write_sint(buf, dt * 1000) # geometry-y (absolute)
|
||||
return buf
|
||||
|
||||
|
||||
def write_text(buf: BufferedIOBase) -> BufferedIOBase:
|
||||
for ll, dt in LAYERS:
|
||||
write_uint(buf, 19) # TEXT record
|
||||
write_byte(buf, 0b0101_1011) # 0CNX_YRTL
|
||||
write_bstring(buf, b'A') # text-string
|
||||
write_uint(buf, ll) # text-layer
|
||||
write_uint(buf, dt) # text-datatype
|
||||
write_sint(buf, ll * 1000) # geometry-x
|
||||
write_sint(buf, dt * 1000) # geometry-y
|
||||
return buf
|
||||
|
||||
|
||||
def name_test(layers: Sequence, is_textlayer: bool) -> None:
|
||||
for ii, nn in enumerate(layers):
|
||||
assert is_textlayer == nn.is_textlayer, f'Fail on layername {ii}'
|
||||
|
||||
assert nn.nstring.string == ['AA', 'L5A', 'H5A', 'E5A', 'I56A',
|
||||
'E5L4', 'E5H4', 'E5E4', 'E5I47'][ii], msg
|
||||
assert nn.layer_interval[0] == [None, None, 5, 5, 5, 5, 5, 5, 5][ii], msg
|
||||
assert nn.layer_interval[1] == [None, 5, None, 5, 6, 5, 5, 5, 5][ii], msg
|
||||
assert nn.type_interval[0] == [None, None, None, None, None, None, 4, 4, 4][ii], msg
|
||||
assert nn.type_interval[1] == [None, None, None, None, None, 4, None, 4, 7][ii], msg
|
||||
|
||||
|
||||
def name_test_text(layers: Sequence) -> None:
|
||||
for ii, nn in enumerate(layers):
|
||||
assert nn.is_textlayer, f'Fail on layername {ii}'
|
||||
|
||||
assert nn.nstring.string == ['TAA', 'TL5A', 'TH5A', 'TE5A', 'TI56A'][ii], msg
|
||||
assert nn.layer_interval[0] == [None, None, 5, 5, 5][ii], msg
|
||||
assert nn.layer_interval[1] == [None, 5, None, 5, 6][ii], msg
|
||||
assert nn.type_interval[0] == [None, None, None, None, None][ii], msg
|
||||
assert nn.type_interval[1] == [None, None, None, None, None][ii], msg
|
||||
|
||||
|
||||
def elem_test_geom(geometry: Sequence) -> None:
|
||||
for ii, gg in enumerate(geometry):
|
||||
msg = f'Failed on circle ({ii})'
|
||||
assert gg.x == 1000 * LAYERS[ii][0], msg
|
||||
assert gg.y == 1000 * LAYERS[ii][1], msg
|
||||
assert gg.radius == 150, msg
|
||||
|
||||
assert gg.layer == LAYERS[ii][0], msg
|
||||
assert gg.datatype == LAYERS[ii][1], msg
|
||||
|
||||
assert gg.repetition is None, msg
|
||||
assert not gg.properties, msg
|
||||
|
||||
|
||||
def elem_test_text(geometry: Sequence) -> None:
|
||||
for ii, gg in enumerate(geometry):
|
||||
msg = f'Failed on text ({ii})'
|
||||
assert gg.x == 1000 * LAYERS[ii][0], msg
|
||||
assert gg.y == 1000 * LAYERS[ii][1], msg
|
||||
assert gg.string.string == 'A', msg
|
||||
|
||||
assert gg.layer == LAYERS[ii][0], msg
|
||||
assert gg.datatype == LAYERS[ii][1], msg
|
||||
|
||||
assert gg.repetition is None, msg
|
||||
assert not gg.properties, msg
|
||||
|
||||
|
||||
def write_file_1(buf: BufferedIOBase) -> BufferedIOBase:
|
||||
'''
|
||||
'''
|
||||
buf.write(HEADER)
|
||||
write_names_geom(buf)
|
||||
|
||||
write_uint(buf, 14) # CELL record (explicit)
|
||||
write_bstring(buf, b'A') # Cell name
|
||||
|
||||
write_geom(buf)
|
||||
buf.write(FOOTER)
|
||||
return buf
|
||||
|
||||
|
||||
def test_file_1() -> None:
|
||||
buf = write_file_1(BytesIO())
|
||||
|
||||
buf.seek(0)
|
||||
layout = OasisLayout.read(buf)
|
||||
|
||||
base_tests(layout)
|
||||
|
||||
geometry = layout.cells[0].geometry
|
||||
assert len(geometry) == len(LAYERS)
|
||||
elem_test_geom(geometry)
|
||||
|
||||
assert len(layout.layers) == 9
|
||||
name_test(layout.layers, is_textlayer=False)
|
||||
|
||||
|
||||
def write_file_2(buf: BufferedIOBase) -> BufferedIOBase:
|
||||
'''
|
||||
'''
|
||||
buf.write(HEADER)
|
||||
write_names_text(buf)
|
||||
|
||||
write_uint(buf, 14) # CELL record (explicit)
|
||||
write_bstring(buf, b'A') # Cell name
|
||||
|
||||
write_text(buf)
|
||||
buf.write(FOOTER)
|
||||
return buf
|
||||
|
||||
|
||||
def test_file_2() -> None:
|
||||
buf = write_file_2(BytesIO())
|
||||
|
||||
buf.seek(0)
|
||||
layout = OasisLayout.read(buf)
|
||||
|
||||
base_tests(layout)
|
||||
|
||||
geometry = layout.cells[0].geometry
|
||||
assert len(geometry) == len(LAYERS)
|
||||
elem_test_text(geometry)
|
||||
|
||||
assert len(layout.layers) == 5
|
||||
name_test(layout.layers, is_textlayer=True)
|
||||
|
||||
|
||||
def write_file_3(buf: BufferedIOBase) -> BufferedIOBase:
|
||||
'''
|
||||
'''
|
||||
buf.write(HEADER)
|
||||
write_names_text(buf, prefix=b'T')
|
||||
write_names_geom(buf, short=True)
|
||||
|
||||
write_uint(buf, 14) # CELL record (explicit)
|
||||
write_bstring(buf, b'A') # Cell name
|
||||
|
||||
write_text(buf)
|
||||
write_geom(buf)
|
||||
buf.write(FOOTER)
|
||||
return buf
|
||||
|
||||
|
||||
def write_file_4(buf: BufferedIOBase) -> BufferedIOBase:
|
||||
'''
|
||||
'''
|
||||
buf.write(HEADER)
|
||||
|
||||
write_uint(buf, 14) # CELL record (explicit)
|
||||
write_bstring(buf, b'A') # Cell name
|
||||
|
||||
write_text(buf)
|
||||
write_geom(buf)
|
||||
|
||||
write_names_text(buf, prefix=b'T')
|
||||
write_names_geom(buf, short=True)
|
||||
buf.write(FOOTER)
|
||||
return buf
|
||||
|
||||
|
||||
def test_file_3() -> None:
|
||||
buf = write_file_3(BytesIO())
|
||||
|
||||
buf.seek(0)
|
||||
layout = OasisLayout.read(buf)
|
||||
|
||||
base_tests(layout)
|
||||
|
||||
geometry = layout.cells[0].geometry
|
||||
assert len(geometry) == 2 * len(LAYERS)
|
||||
elem_test_text(geometry[:len(LAYERS)])
|
||||