"""Tests for the 5 new circuit templates (netlist + asc generator).""" import pytest from mcp_ltspice.asc_generator import ( AscSchematic, generate_boost_converter, generate_current_mirror, generate_instrumentation_amp, generate_sallen_key_lowpass, generate_transimpedance_amp, ) from mcp_ltspice.netlist import ( Netlist, boost_converter, current_mirror, instrumentation_amplifier, sallen_key_lowpass, transimpedance_amplifier, ) # --------------------------------------------------------------------------- # Netlist template tests # --------------------------------------------------------------------------- class TestSallenKeyLowpassNetlist: def test_returns_netlist(self): n = sallen_key_lowpass() assert isinstance(n, Netlist) def test_component_count(self): n = sallen_key_lowpass() # V1, Vpos, Vneg, R1, R2, C1, C2, X1 = 8 components assert len(n.components) == 8 def test_render_contains_key_components(self): text = sallen_key_lowpass().render() assert "R1" in text assert "R2" in text assert "C1" in text assert "C2" in text assert "X1" in text assert "LT1001" in text assert ".ac" in text def test_custom_params(self): n = sallen_key_lowpass(r1="4.7k", r2="4.7k", c1="22n", c2="22n") text = n.render() assert "4.7k" in text assert "22n" in text def test_has_backanno_and_end(self): text = sallen_key_lowpass().render() assert ".backanno" in text assert ".end" in text class TestBoostConverterNetlist: def test_returns_netlist(self): n = boost_converter() assert isinstance(n, Netlist) def test_component_count(self): n = boost_converter() # Vin, Vgate, L1, M1, D1, Cout, Rload = 7 components assert len(n.components) == 7 def test_render_contains_key_components(self): text = boost_converter().render() assert "L1" in text assert "M1" in text assert "D1" in text assert "Cout" in text assert "Rload" in text assert "PULSE(" in text assert ".tran" in text def test_custom_params(self): n = boost_converter(ind="22u", r_load="100", v_in="3.3", duty_cycle=0.6) text = n.render() assert "22u" in text assert "100" in text assert "3.3" in text def test_has_backanno_and_end(self): text = boost_converter().render() assert ".backanno" in text assert ".end" in text class TestInstrumentationAmplifierNetlist: def test_returns_netlist(self): n = instrumentation_amplifier() assert isinstance(n, Netlist) def test_component_count(self): n = instrumentation_amplifier() # V1, V2, Vpos, Vneg, X1, X2, R1, R1b, Rgain, X3, R2, R3, R2b, R3b = 14 assert len(n.components) == 14 def test_render_contains_key_components(self): text = instrumentation_amplifier().render() assert "X1" in text assert "X2" in text assert "X3" in text assert "Rgain" in text assert "LT1001" in text assert ".ac" in text def test_custom_params(self): n = instrumentation_amplifier(r1="20k", r_gain="1k") text = n.render() assert "20k" in text assert "1k" in text def test_has_backanno_and_end(self): text = instrumentation_amplifier().render() assert ".backanno" in text assert ".end" in text class TestCurrentMirrorNetlist: def test_returns_netlist(self): n = current_mirror() assert isinstance(n, Netlist) def test_component_count(self): n = current_mirror() # Vcc, Rref, Rload, Q1, Q2 = 5 components assert len(n.components) == 5 def test_render_contains_key_components(self): text = current_mirror().render() assert "Q1" in text assert "Q2" in text assert "Rref" in text assert "Rload" in text assert "2N2222" in text assert ".op" in text assert ".tran" in text def test_custom_params(self): n = current_mirror(r_ref="4.7k", r_load="2.2k", vcc="5") text = n.render() assert "4.7k" in text assert "2.2k" in text assert "5" in text def test_has_backanno_and_end(self): text = current_mirror().render() assert ".backanno" in text assert ".end" in text class TestTransimpedanceAmplifierNetlist: def test_returns_netlist(self): n = transimpedance_amplifier() assert isinstance(n, Netlist) def test_component_count(self): n = transimpedance_amplifier() # I1, Vpos, Vneg, Rf, Cf, X1 = 6 components assert len(n.components) == 6 def test_render_contains_key_components(self): text = transimpedance_amplifier().render() assert "I1" in text assert "Rf" in text assert "Cf" in text assert "X1" in text assert "LT1001" in text assert ".ac" in text def test_custom_params(self): n = transimpedance_amplifier(rf="1Meg", cf="0.5p", i_source="10u") text = n.render() assert "1Meg" in text assert "0.5p" in text assert "10u" in text def test_has_backanno_and_end(self): text = transimpedance_amplifier().render() assert ".backanno" in text assert ".end" in text # --------------------------------------------------------------------------- # ASC generator template tests # --------------------------------------------------------------------------- class TestSallenKeyLowpassAsc: def test_returns_schematic(self): sch = generate_sallen_key_lowpass() assert isinstance(sch, AscSchematic) def test_render_valid(self): text = generate_sallen_key_lowpass().render() assert text.startswith("Version 4\n") assert "SHEET" in text assert len(text) > 100 def test_contains_expected_symbols(self): text = generate_sallen_key_lowpass().render() assert "SYMBOL res" in text assert "SYMBOL cap" in text assert "SYMBOL OpAmps/UniversalOpamp2" in text assert "SYMBOL voltage" in text def test_custom_params(self): text = generate_sallen_key_lowpass(r1="4.7k", c1="22n").render() assert "4.7k" in text assert "22n" in text def test_has_simulation_directive(self): text = generate_sallen_key_lowpass().render() assert ".ac" in text class TestBoostConverterAsc: def test_returns_schematic(self): sch = generate_boost_converter() assert isinstance(sch, AscSchematic) def test_render_valid(self): text = generate_boost_converter().render() assert text.startswith("Version 4\n") assert "SHEET" in text assert len(text) > 100 def test_contains_expected_symbols(self): text = generate_boost_converter().render() assert "SYMBOL ind" in text assert "SYMBOL nmos" in text assert "SYMBOL diode" in text assert "SYMBOL cap" in text assert "SYMBOL res" in text def test_custom_params(self): text = generate_boost_converter(ind="22u", r_load="100").render() assert "22u" in text assert "100" in text def test_has_simulation_directive(self): text = generate_boost_converter().render() assert ".tran" in text class TestInstrumentationAmpAsc: def test_returns_schematic(self): sch = generate_instrumentation_amp() assert isinstance(sch, AscSchematic) def test_render_valid(self): text = generate_instrumentation_amp().render() assert text.startswith("Version 4\n") assert "SHEET" in text assert len(text) > 100 def test_contains_expected_symbols(self): text = generate_instrumentation_amp().render() # Should have 3 opamps assert text.count("SYMBOL OpAmps/UniversalOpamp2") == 3 # Should have multiple resistors assert text.count("SYMBOL res") >= 7 def test_custom_params(self): text = generate_instrumentation_amp(r1="20k", r_gain="1k").render() assert "20k" in text assert "1k" in text def test_has_simulation_directive(self): text = generate_instrumentation_amp().render() assert ".ac" in text class TestCurrentMirrorAsc: def test_returns_schematic(self): sch = generate_current_mirror() assert isinstance(sch, AscSchematic) def test_render_valid(self): text = generate_current_mirror().render() assert text.startswith("Version 4\n") assert "SHEET" in text assert len(text) > 100 def test_contains_expected_symbols(self): text = generate_current_mirror().render() # Should have 2 NPN transistors assert text.count("SYMBOL npn") == 2 assert "SYMBOL res" in text assert "SYMBOL voltage" in text def test_custom_params(self): text = generate_current_mirror(r_ref="4.7k", r_load="2.2k").render() assert "4.7k" in text assert "2.2k" in text def test_has_simulation_directive(self): text = generate_current_mirror().render() assert ".op" in text assert ".tran" in text class TestTransimpedanceAmpAsc: def test_returns_schematic(self): sch = generate_transimpedance_amp() assert isinstance(sch, AscSchematic) def test_render_valid(self): text = generate_transimpedance_amp().render() assert text.startswith("Version 4\n") assert "SHEET" in text assert len(text) > 100 def test_contains_expected_symbols(self): text = generate_transimpedance_amp().render() assert "SYMBOL OpAmps/UniversalOpamp2" in text assert "SYMBOL res" in text assert "SYMBOL cap" in text def test_custom_params(self): text = generate_transimpedance_amp(rf="1Meg", cf="0.5p").render() assert "1Meg" in text assert "0.5p" in text def test_has_simulation_directive(self): text = generate_transimpedance_amp().render() assert ".ac" in text # --------------------------------------------------------------------------- # Parametrized cross-cutting tests for all 5 new netlist templates # --------------------------------------------------------------------------- class TestNewNetlistTemplatesCommon: @pytest.mark.parametrize( "factory", [ sallen_key_lowpass, boost_converter, instrumentation_amplifier, current_mirror, transimpedance_amplifier, ], ) def test_returns_netlist(self, factory): assert isinstance(factory(), Netlist) @pytest.mark.parametrize( "factory", [ sallen_key_lowpass, boost_converter, instrumentation_amplifier, current_mirror, transimpedance_amplifier, ], ) def test_has_backanno_and_end(self, factory): text = factory().render() assert ".backanno" in text assert ".end" in text @pytest.mark.parametrize( "factory", [ sallen_key_lowpass, boost_converter, instrumentation_amplifier, current_mirror, transimpedance_amplifier, ], ) def test_has_components(self, factory): n = factory() assert len(n.components) > 0 @pytest.mark.parametrize( "factory", [ sallen_key_lowpass, boost_converter, instrumentation_amplifier, current_mirror, transimpedance_amplifier, ], ) def test_has_sim_directive(self, factory): text = factory().render() sim_types = [".tran", ".ac", ".dc", ".op", ".noise", ".tf"] assert any(sim in text.lower() for sim in sim_types), ( f"No simulation directive found in {factory.__name__}" ) # --------------------------------------------------------------------------- # Parametrized cross-cutting tests for all 5 new ASC templates # --------------------------------------------------------------------------- class TestNewAscTemplatesCommon: @pytest.mark.parametrize( "factory", [ generate_sallen_key_lowpass, generate_boost_converter, generate_instrumentation_amp, generate_current_mirror, generate_transimpedance_amp, ], ) def test_returns_schematic(self, factory): assert isinstance(factory(), AscSchematic) @pytest.mark.parametrize( "factory", [ generate_sallen_key_lowpass, generate_boost_converter, generate_instrumentation_amp, generate_current_mirror, generate_transimpedance_amp, ], ) def test_render_nonempty(self, factory): text = factory().render() assert len(text) > 50 assert "SYMBOL" in text @pytest.mark.parametrize( "factory", [ generate_sallen_key_lowpass, generate_boost_converter, generate_instrumentation_amp, generate_current_mirror, generate_transimpedance_amp, ], ) def test_has_version_and_sheet(self, factory): text = factory().render() assert "Version 4" in text assert "SHEET" in text