Files
kicad-zone-resistance/tests/test_platform_fallbacks.py
T
janik 23edb39f52
Build PCM package / build (push) Successful in 5s
Fix the two macOS field-test failures: backend order, RO board dir
Second Mac run (KiCad demo board opened from the mounted installer
image) got past the dialog and died twice:

1. make_output_dir crashed with OSError 30: the demos volume is a
   read-only filesystem. Now falls back to a temp directory named
   after the board, with the path printed (the Messages panel showed
   it is actually read there).

2. The error figure - and every other figure - could never render:
   matplotlib refused with Cannot load backend TkAgg ... as qt is
   currently running. macOS bundled Python ships tkinter, so the
   old tk-first probe picked TkAgg while the PySide6 dialog and
   progress window had already made this a Qt process. Windows never
   saw it because KiCad Python there has no tkinter. Qt now comes
   first - PySide6 is a hard dependency, so it is always there.

Both covered by tests that fail on the old code: the dev env has both
toolkits installed, so the backend-order assertion is exercised for
real, and the RO fallback is simulated by denying mkdir under the
board dir. 140 passed on the dev stack and on the Python 3.9 +
numpy 2.0 / scipy 1.13 / matplotlib 3.9 / PySide6 6.10 stack that a
Mac venv resolves.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 13:29:22 +07:00

46 lines
1.7 KiB
Python

"""Regressions from the first macOS field test.
Two failures that only a non-Windows KiCad could produce: the board
directory was read-only (demo project opened straight from the mounted
installer image), and matplotlib picked TkAgg - macOS' bundled Python
ships tkinter, unlike KiCad's Windows Python - then refused to create
any figure because the PySide6 dialog already had a Qt event loop in
the process.
"""
import pathlib
from fill_resistance import plots, report
def test_backend_prefers_qt_over_tk():
# The dev environment has both toolkits installed, so this asserts
# the preference order for real: Qt must win, because the selection
# dialog / progress window make the process a Qt process before the
# first figure exists.
assert plots._pick_backend() in ("QtAgg", "Qt5Agg")
def test_output_dir_falls_back_when_board_dir_unwritable(
tmp_path, monkeypatch, capsys):
board_dir = tmp_path / "board"
board_dir.mkdir()
real_mkdir = pathlib.Path.mkdir
def deny_under_board(self, *args, **kwargs):
if str(self).startswith(str(board_dir)):
raise OSError(30, "Read-only file system", str(self))
return real_mkdir(self, *args, **kwargs)
monkeypatch.setattr(pathlib.Path, "mkdir", deny_under_board)
out = report.make_output_dir(board_dir)
assert out.is_dir()
assert not str(out).startswith(str(board_dir))
assert board_dir.name in out.name # traceable back to the board
assert "not writable" in capsys.readouterr().out
def test_output_dir_normal_case_unchanged(tmp_path):
out = report.make_output_dir(tmp_path)
assert out.is_dir()
assert out.parent.parent == tmp_path