mcltspice/tests/test_netlist.py
Ryan Malloy cf8394fa6f Rename mcp-ltspice -> mcltspice, remove stdout banner
Rename package from mcp-ltspice/mcp_ltspice to mcltspice throughout:
source directory, imports, pyproject.toml, tests, and README.

Remove startup banner prints from main() since FastMCP handles
its own banner and stdout is the MCP JSON-RPC transport.

Point repo URL at git.supported.systems/MCP/mcltspice.
2026-02-12 22:53:16 -07:00

198 lines
5.7 KiB
Python

"""Tests for netlist module: builder pattern, rendering, template functions."""
import pytest
from mcltspice.netlist import (
Netlist,
buck_converter,
colpitts_oscillator,
common_emitter_amplifier,
differential_amplifier,
h_bridge,
inverting_amplifier,
ldo_regulator,
non_inverting_amplifier,
rc_lowpass,
voltage_divider,
)
class TestNetlistBuilder:
def test_add_resistor(self):
n = Netlist().add_resistor("R1", "in", "out", "10k")
assert len(n.components) == 1
assert n.components[0].name == "R1"
assert n.components[0].value == "10k"
def test_add_capacitor(self):
n = Netlist().add_capacitor("C1", "out", "0", "100n")
assert len(n.components) == 1
assert n.components[0].value == "100n"
def test_add_inductor(self):
n = Netlist().add_inductor("L1", "a", "b", "10u", series_resistance="0.1")
assert "Rser=0.1" in n.components[0].params
def test_chaining(self):
"""Builder methods return self for chaining."""
n = (
Netlist("Test")
.add_resistor("R1", "a", "b", "1k")
.add_capacitor("C1", "b", "0", "1n")
)
assert len(n.components) == 2
def test_add_voltage_source_dc(self):
n = Netlist().add_voltage_source("V1", "in", "0", dc="5")
assert "5" in n.components[0].value
def test_add_voltage_source_ac(self):
n = Netlist().add_voltage_source("V1", "in", "0", ac="1")
assert "AC 1" in n.components[0].value
def test_add_voltage_source_pulse(self):
n = Netlist().add_voltage_source(
"V1", "g", "0", pulse=("0", "5", "0", "1n", "1n", "5u", "10u")
)
rendered = n.render()
assert "PULSE(" in rendered
def test_add_voltage_source_sin(self):
n = Netlist().add_voltage_source(
"V1", "in", "0", sin=("0", "1", "1k")
)
rendered = n.render()
assert "SIN(" in rendered
def test_add_directive(self):
n = Netlist().add_directive(".tran 10m")
assert ".tran 10m" in n.directives
def test_add_meas(self):
n = Netlist().add_meas("tran", "vmax", "MAX V(out)")
assert any("vmax" in d for d in n.directives)
class TestNetlistRender:
def test_render_contains_title(self):
n = Netlist("My Circuit")
text = n.render()
assert "* My Circuit" in text
def test_render_contains_components(self):
n = (
Netlist()
.add_resistor("R1", "in", "out", "10k")
.add_capacitor("C1", "out", "0", "100n")
)
text = n.render()
assert "R1 in out 10k" in text
assert "C1 out 0 100n" in text
def test_render_contains_backanno_and_end(self):
n = Netlist()
text = n.render()
assert ".backanno" in text
assert ".end" in text
def test_render_includes_directive(self):
n = Netlist().add_directive(".ac dec 100 1 1meg")
text = n.render()
assert ".ac dec 100 1 1meg" in text
def test_render_includes_comment(self):
n = Netlist().add_comment("Test comment")
text = n.render()
assert "* Test comment" in text
def test_render_includes_lib(self):
n = Netlist().add_lib("LT1001")
text = n.render()
assert ".lib LT1001" in text
class TestTemplateNetlists:
"""All template functions should return valid Netlist objects."""
@pytest.mark.parametrize(
"factory",
[
voltage_divider,
rc_lowpass,
inverting_amplifier,
non_inverting_amplifier,
differential_amplifier,
common_emitter_amplifier,
buck_converter,
ldo_regulator,
colpitts_oscillator,
h_bridge,
],
)
def test_template_returns_netlist(self, factory):
n = factory()
assert isinstance(n, Netlist)
@pytest.mark.parametrize(
"factory",
[
voltage_divider,
rc_lowpass,
inverting_amplifier,
non_inverting_amplifier,
differential_amplifier,
common_emitter_amplifier,
buck_converter,
ldo_regulator,
colpitts_oscillator,
h_bridge,
],
)
def test_template_has_backanno_and_end(self, factory):
text = factory().render()
assert ".backanno" in text
assert ".end" in text
@pytest.mark.parametrize(
"factory",
[
voltage_divider,
rc_lowpass,
inverting_amplifier,
non_inverting_amplifier,
differential_amplifier,
common_emitter_amplifier,
buck_converter,
ldo_regulator,
colpitts_oscillator,
h_bridge,
],
)
def test_template_has_components(self, factory):
n = factory()
assert len(n.components) > 0
@pytest.mark.parametrize(
"factory",
[
voltage_divider,
rc_lowpass,
inverting_amplifier,
non_inverting_amplifier,
differential_amplifier,
common_emitter_amplifier,
buck_converter,
ldo_regulator,
colpitts_oscillator,
h_bridge,
],
)
def test_template_has_sim_directive(self, factory):
n = factory()
# Should have at least one directive starting with a sim type
sim_types = [".tran", ".ac", ".dc", ".op", ".noise", ".tf"]
text = n.render()
assert any(sim in text.lower() for sim in sim_types), (
f"No simulation directive found in {factory.__name__}"
)