Test coverage
Regression tests are defined in the SiFab PDK in order to catch undesired changes and to check desired changes carefully. This is done using the Luceda IP Manager module.
You can use the SiFab tests as an example for adding test coverage to your PDK or component library. Look at the documentation of IP Manager for detailed information on how to define tests, run them, and inspect the results.
File structure
The tests are located in a folder tests
at the same level as the main si_fab folder and the building folder:
The tests
folder contains:
Configuration files:
pytest.ini
andconftest.py
.Test files:
test_awg_designer.py
,test_components.py
,test_layers.py
,test_materials.py
andtest_waveguides.py
.A script
generate_reffiles.py
to (re)generate the known-good reference files.
Configuration files
The conftest.py
file contains definitions common to all tests.
In this case, it defines the TECH
fixture: the technology to load and use for all tests, which is the si_fab
technology.
Pytest will execute this fixture before all tests and will make it available to all tests (autouse=True
) in the session (scope='session'
).
import pytest
@pytest.fixture(scope="session", autouse=True)
def TECH():
from si_fab.technology import TECH
return TECH
The pytest.ini
file contains the configuration of pytest itself.
We specify that we want to obtain an HTML report of the tests that have been run together with their results:
[pytest]
addopts = '--html=report.html' '--self-contained-html'
Layer tests
In tests/test_layers.py
, we define a layout that incorporates each layer in the technology.
This can be used for cross-checking the layer table and GDSII layer numbers and data types.
A regression test on this full layout catches any changes:
@pytest.mark.comparisons([Compare.LayoutToGds, Compare.LayoutToXML])
class TestLayers(ComponentReferenceTest):
@pytest.fixture
def component(self, layer_test_patch):
return layer_test_patch
Component tests
The file tests/test_waveguides.py
contains layout regression tests on the strip and rib waveguides of SiFab.
The test is defined on a small cell that contains two waveguides and a transition.
from ip_manager.testing import ComponentReferenceTest, VFab2DReferenceTest, Compare
import pytest
comparisons_layout = [Compare.LayoutToGds, Compare.LayoutToXML]
@pytest.mark.comparisons(comparisons_layout)
class TestWire(ComponentReferenceTest, VFab2DReferenceTest):
@pytest.fixture
def component(self):
from si_fab.all import SiWireWaveguideTemplate, LinearWireTransition
import ipkiss3.all as i3
wire_tt = SiWireWaveguideTemplate()
wire_tt.Layout(core_width=0.4, cladding_width=8.0)
wire2_tt = SiWireWaveguideTemplate()
wire2_tt.Layout(core_width=1.0, cladding_width=6.0)
wg_wire = i3.Waveguide(trace_template=wire_tt)
wg_wire.Layout(shape=[(0.0, 0.0), (5.0, 0.0)])
wg_wire2 = i3.Waveguide(trace_template=wire2_tt)
wg_wire2.Layout(shape=[(25.0, 0.0), (30.0, 0.0)])
trans = LinearWireTransition(start_trace_template=wire_tt, end_trace_template=wire2_tt)
trans.Layout(start_position=(5.0, 0.0), end_position=(25.0, 0.0))
class TestWireCell(i3.PCell):
class Layout(i3.LayoutView):
def _generate_instances(self, insts):
insts += i3.SRef(wg_wire, name="wire")
insts += i3.SRef(trans)
insts += i3.SRef(wg_wire2, name="wire2")
return insts
def _generate_ports(self, ports):
ports += self.instances["wire"].ports["in"]
ports += self.instances["wire2"].ports["out"]
return ports
class NetList(i3.NetlistFromLayout):
pass
return TestWireCell(name="TestWire")
In this way, we cover a few different aspects with one test, such as layout drawing and ports as well as the virtual fabrication.
The file tests/test_components.py
contains regression tests for the library cells in SiFab.
Currently, we implemented the following tests:
Layout regression tests for components that only have a layout (e.g.
BONDPAD_5050
).Layout and model tests for components that have both a layout and a circuit model (e.g.
SiDirectionalCouplerS
).
from ip_manager.testing import ComponentReferenceTest, CircuitModelTest, Compare
import pytest
layout_comparisons = [Compare.LayoutToGds, Compare.LayoutToXML]
circuit_comparisons = [Compare.SMatrix, Compare.NetlistToXML]
For each PCell in the library, there is a test defined that instantiates the cell with default parameters:
@pytest.mark.comparisons(layout_comparisons)
class TestMMI1x2(ComponentReferenceTest):
@pytest.fixture
def component(self):
from si_fab.components.mmi.pcell.cell import MMI1x2
sl = MMI1x2(name="TestMMI1x2")
sl.Layout()
return sl
Some of the tests are parametric and implement custom checks.
For example, for TestFixedPortWithLengthSpiral
, we instantiate the PCell for a wide range of lengths and test that the length of the spiral is correct.
class TestFixedPortWithLengthSpiral(ComponentReferenceTest):
@pytest.fixture
def component(self, total_length):
from si_fab.components.spiral.pcell.cell import FixedPortWithLengthSpiral
sl = FixedPortWithLengthSpiral(total_length=total_length)
sl.Layout()
return sl
@pytest.fixture
def reference_name(self, total_length):
return "TestFixedPortWithLengthSpiral_" + str(total_length)
@pytest.fixture(params=[4000, 5000, 6000])
def total_length(self, request):
return request.param
def test_total_length(self, component, total_length):
from ipkiss3.pcell.layout.view import LayoutView
lv = component.get_default_view(LayoutView)
trace_length = lv.trace_length()
assert trace_length == pytest.approx(total_length)
AWG Designer tests
SiFab also has support for Luceda AWG Designer.
Currently, tests/test_awg_designer.py
contains a regression test for the layout of the rib waveguide and rib MMI apertures.
It can be extended to cover more components (such as a star coupler) and aspects (such as the netlist or model).
Generating reference files
The tests/generate_reffiles.py
file uses the IP Manager function generate_reference_files
to generate the known-good reference files.
from ip_manager.testing import generate_reference_files
generate_reference_files()
Calling generate_reference_files
without arguments would cause the reference files to be generated for every component reference tests found in the folder in which the script is placed.
Note
The functionality to regenerate reference files should be used with care. Use it only if you are sure you want to overwrite the reference files!