"""The router prefers the native freeroute CLI and falls back to the JAR.""" from __future__ import annotations import os from unittest.mock import MagicMock, patch import pytest from mckicad.utils.freerouting import FreeRoutingEngine, FreeRoutingError def _jar_run_writes_ses(cmd, **_kwargs): """Simulate the FreeRouting JAR: write a .ses into the -do output directory.""" outdir = cmd[cmd.index("-do") + 1] with open(os.path.join(outdir, "board.ses"), "w") as f: f.write("(session board)") return MagicMock(returncode=0, stdout="", stderr="") def test_prefers_freeroute_when_available(tmp_path): eng = FreeRoutingEngine(freerouting_jar_path="/fake/freerouting.jar") with ( patch("mckicad.utils.freerouting.find_freeroute_cli", return_value="/usr/bin/freeroute"), patch.object(eng, "_run_freeroute_cli", return_value=(True, "/out/board.ses")) as fr, patch("mckicad.utils.freerouting.subprocess.run") as jar_run, ): ok, ses = eng.run_freerouting("/tmp/board.dsn", "/out") assert ok and ses == "/out/board.ses" fr.assert_called_once() jar_run.assert_not_called() # the JAR is never invoked when freeroute succeeds def test_falls_back_to_jar_when_freeroute_fails(tmp_path): jar = tmp_path / "freerouting.jar" jar.write_text("x") eng = FreeRoutingEngine(freerouting_jar_path=str(jar)) with ( patch("mckicad.utils.freerouting.find_freeroute_cli", return_value="/usr/bin/freeroute"), patch.object(eng, "_run_freeroute_cli", return_value=(False, None)), patch("mckicad.utils.freerouting.subprocess.run", side_effect=_jar_run_writes_ses), ): ok, ses = eng.run_freerouting("/tmp/board.dsn", str(tmp_path)) assert ok and ses.endswith("board.ses") def test_uses_jar_when_freeroute_absent(tmp_path): jar = tmp_path / "freerouting.jar" jar.write_text("x") eng = FreeRoutingEngine(freerouting_jar_path=str(jar)) with ( patch("mckicad.utils.freerouting.find_freeroute_cli", return_value=None), patch("mckicad.utils.freerouting.subprocess.run", side_effect=_jar_run_writes_ses), ): ok, ses = eng.run_freerouting("/tmp/board.dsn", str(tmp_path)) assert ok def test_raises_when_no_router_available(tmp_path): eng = FreeRoutingEngine() with ( patch("mckicad.utils.freerouting.find_freeroute_cli", return_value=None), patch.object(eng, "find_freerouting_jar", return_value=None), pytest.raises(FreeRoutingError), ): eng.run_freerouting("/tmp/board.dsn", str(tmp_path))