Files
boxsim-fileformat-rev/tests/test_resolver.py
T

133 lines
4.7 KiB
Python
Raw Normal View History

from __future__ import annotations
import pytest
from boxsim import symbols
from boxsim.catalog import SERIES_TO_SYMBOL
from boxsim.model import Orientation, Part, PartType
def test_catalogue_shape(catalog):
"""Guard the figures the resolver design relies on."""
assert len(catalog) == 95
assert len(catalog.series_names) == 11
assert set(catalog.series_names) == set(SERIES_TO_SYMBOL)
# The last catalogue line is a real part; the file has no trailing newline
# and is easy to truncate by one.
assert catalog.find("SP 1,3mm", 2.2e-3) is not None
def test_every_catalogue_part_has_a_symbol(catalog, library):
extras = symbols.cross_check(catalog, library)
assert extras == ["MKT 22μF"], (
"library should contain exactly the catalogue plus Amplifier, Speaker "
"and the radial MKT 22uF that Boxsim cannot select"
)
def test_library_values_are_matched_numerically(library):
"""Irregular library names must still resolve: 1.0uF next to 10uF."""
assert library.lookup("MKP", 1e-6) is not None
assert library.lookup("MKP", 10e-6) is not None
assert library.lookup("SP-Spule", 1e-3, "0.6").name == "SP-Spule 1.0mH (0.6)"
assert library.lookup("LR-Spule", 10e-3).name == "LR-Spule 10mH"
def test_derived_symbols_resolve_geometry(library):
"""Most symbols use `extends`; pins live on the base symbol."""
from boxsim.schematic import symbol_pins
pins = symbol_pins(library, "SP-Spule 0.82mH (0.6)")
assert len(pins.by_number) == 2
assert library.base_of("SP-Spule 0.82mH (0.6)") == "FC-Spule 10mH"
def test_flattened_definition_has_no_extends(library):
body = library.definition("SP-Spule 0.82mH (0.6)")
assert "extends" not in body
assert '(symbol "SP-Spule 0.82mH (0.6)_' in body, "graphics must be inlined"
def test_default_project_fully_resolves(catalog, library, default_project):
resolver = symbols.Resolver(catalog, library)
components = default_project.crossovers[0].components
results = resolver.resolve_all(components)
assert len(results) == len(components) == 22
assert all(r.symbol in library for r in results)
def test_empty_series_is_inferred_from_sibling(catalog, library, default_project):
resolver = symbols.Resolver(catalog, library)
components = default_project.crossovers[0].components
results = {r.part.slot: r for r in resolver.resolve_all(components)}
part231 = results[231]
assert part231.inferred_series is True
assert part231.symbol == "MKT-A 15μF", "sibling Part198 is an MKT-A 15uF"
def test_dcr_deviation_is_reported_not_corrected(catalog, library, default_project):
resolver = symbols.Resolver(catalog, library)
results = {
r.part.slot: r
for r in resolver.resolve_all(default_project.crossovers[0].components)
}
assert results[5].dcr_note is not None, "0.28 vs catalogue 0.33"
assert results[48].dcr_note is not None, "5 vs catalogue 4.8"
assert results[5].part.dcr == 0.28, "the project value must win"
def test_unstocked_value_is_a_hard_error(catalog, library):
resolver = symbols.Resolver(catalog, library)
part = Part(
slot=1,
typ=PartType.CAPACITOR,
orientation=Orientation.HORIZONTAL,
row=0,
col=0,
node_a=0,
node_b=1,
value=13e-6, # not a Visaton value
label="MKT-A",
)
with pytest.raises(symbols.SymbolError, match="not stocked"):
resolver.resolve(part)
def test_unknown_series_is_a_hard_error(catalog, library):
resolver = symbols.Resolver(catalog, library)
part = Part(
slot=2,
typ=PartType.CAPACITOR,
orientation=Orientation.HORIZONTAL,
row=0,
col=0,
node_a=0,
node_b=1,
value=10e-6,
label="Mundorf Supreme",
)
with pytest.raises(symbols.SymbolError, match="not in the Visaton catalogue"):
resolver.resolve(part)
def test_catalogue_shape_check_has_teeth(catalog):
"""A truncated catalogue must be rejected, not silently accepted.
`cross_check` only proves the catalogue is a subset of the library, so a
short catalogue passes it and then rejects valid projects at runtime with
"not stocked by Visaton". This is the check that catches it.
"""
from boxsim import catalog as catalog_mod
catalog_mod.check_shape(catalog) # the real thing must pass
short = catalog_mod.Catalog(catalog.parts[:40])
with pytest.raises(catalog_mod.CatalogError, match="expected"):
catalog_mod.check_shape(short)
# Losing a whole series must also be caught, even at the right total.
without_sp13 = catalog_mod.Catalog(
[p for p in catalog.parts if p.series != "SP 1,3mm"]
)
with pytest.raises(catalog_mod.CatalogError):
catalog_mod.check_shape(without_sp13)