testsAndMisc/python_pkg/brother_printer/tests/test_cups_economode.py
Krzysztof kuhy Rudnicki 607ba98c85 Split the brother_printer cups_service test modules under the cap
test_cups_service.py 390 -> 172 (usblp port status to _port),
part2 261 -> 115 (status mapping and discovery out), part3 260 -> 105
(the four healthy-port cases out). part3 was one 235-line class, so it
split by method rather than by class.

The four part-numbered files are renamed for what they cover; three of
them now name _cups_fallback, matching where the code moved earlier.

The first run after the part2 split reported 400, not 402: the moved
classes needed subprocess and get_status_info that my hand-written
import block had dropped. Fixed, and the count is 402 again.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011w2Pj4Ek3ZNMQVYcFHgXDY
2026-08-16 17:19:55 +02:00

116 lines
4.7 KiB
Python

"""Tests for brother_printer._cups_fallback - economode and state mapping."""
from __future__ import annotations
import subprocess
from unittest.mock import MagicMock, patch
from python_pkg.brother_printer._cups_fallback import (
_get_cups_economode,
_map_cups_to_status_code,
)
from python_pkg.brother_printer.constants import (
_CUPS_REASONS_TO_STATUS,
get_status_info,
)
MOD = "python_pkg.brother_printer._cups_fallback"
# ── _get_cups_economode ──────────────────────────────────────────────
class TestGetCupsEconomode:
"""Tests for _get_cups_economode."""
@patch(f"{MOD}.shutil.which", return_value=None)
def test_no_lpoptions(self, m: MagicMock) -> None:
assert _get_cups_economode("Brother") == ""
@patch("python_pkg.brother_printer._query.subprocess.run")
@patch(f"{MOD}.shutil.which", return_value="/usr/bin/lpoptions")
def test_economode_on(self, w: MagicMock, mock_run: MagicMock) -> None:
mock_run.return_value = MagicMock(
stdout="BREconomode/Toner Save Mode: *True False\n"
)
assert _get_cups_economode("Brother") == "ON"
@patch("python_pkg.brother_printer._query.subprocess.run")
@patch(f"{MOD}.shutil.which", return_value="/usr/bin/lpoptions")
def test_economode_off(self, w: MagicMock, mock_run: MagicMock) -> None:
mock_run.return_value = MagicMock(
stdout="BREconomode/Toner Save Mode: True *False\n"
)
assert _get_cups_economode("Brother") == "OFF"
@patch("python_pkg.brother_printer._query.subprocess.run")
@patch(f"{MOD}.shutil.which", return_value="/usr/bin/lpoptions")
def test_no_economode_line(self, w: MagicMock, mock_run: MagicMock) -> None:
mock_run.return_value = MagicMock(
stdout="Resolution/Output Resolution: 600dpi *1200dpi\n"
)
assert _get_cups_economode("Brother") == ""
@patch("python_pkg.brother_printer._query.subprocess.run")
@patch(f"{MOD}.shutil.which", return_value="/usr/bin/lpoptions")
def test_economode_no_star_match(self, w: MagicMock, mock_run: MagicMock) -> None:
mock_run.return_value = MagicMock(
stdout="BREconomode/Toner Save Mode: True False\n"
)
assert _get_cups_economode("Brother") == ""
@patch("python_pkg.brother_printer._query.subprocess.run")
@patch(f"{MOD}.shutil.which", return_value="/usr/bin/lpoptions")
def test_timeout(self, w: MagicMock, mock_run: MagicMock) -> None:
mock_run.side_effect = subprocess.TimeoutExpired("lpoptions", 5)
assert _get_cups_economode("Brother") == ""
@patch("python_pkg.brother_printer._query.subprocess.run")
@patch(f"{MOD}.shutil.which", return_value="/usr/bin/lpoptions")
def test_oserror(self, w: MagicMock, mock_run: MagicMock) -> None:
mock_run.side_effect = OSError("fail")
assert _get_cups_economode("Brother") == ""
# ── _map_cups_to_status_code ─────────────────────────────────────────
class TestMapCupsToStatusCode:
"""Tests for _map_cups_to_status_code.
Every code here must exist in BROTHER_STATUS_CODES; mapping CUPS onto a
code the table does not know silently degrades to "unknown status".
"""
def test_reason_match(self) -> None:
assert _map_cups_to_status_code("idle", "toner-low-report") == "40038"
def test_state_match(self) -> None:
assert _map_cups_to_status_code("idle", "none") == "10001"
def test_processing_state(self) -> None:
assert _map_cups_to_status_code("processing", "none") == "10023"
def test_stopped_state(self) -> None:
assert _map_cups_to_status_code("stopped", "none") == "40079"
def test_unknown_state(self) -> None:
assert _map_cups_to_status_code("mystery", "none") == "10001"
def test_state_with_parenthetical(self) -> None:
assert _map_cups_to_status_code("idle (on fire)", "none") == "10001"
def test_every_mapped_code_is_resolvable(self) -> None:
"""Guard the class of bug where a mapping points at a deleted code."""
for state in ("idle", "processing", "stopped"):
code = _map_cups_to_status_code(state, "none")
_, text, _ = get_status_info(code)
assert "Unknown" not in text
for reason in _CUPS_REASONS_TO_STATUS:
code = _map_cups_to_status_code("idle", reason)
_, text, _ = get_status_info(code)
assert "Unknown" not in text
# ── _cups_reasons_to_error ───────────────────────────────────────────