testsAndMisc/python_pkg/brother_printer/tests/test_cups_status_mapping.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

164 lines
6.4 KiB
Python

"""Tests for brother_printer._cups_fallback - status mapping and discovery.
Split from test_cups_service_part2.py under the 250-line cap. These cover the
mapping from what CUPS says went wrong onto the Brother PJL status codes the
report displays, plus finding the Brother queue name.
"""
from __future__ import annotations
import subprocess
from unittest.mock import MagicMock, patch
from python_pkg.brother_printer._cups_fallback import (
_cups_reasons_to_error,
_port_status_to_status_code,
find_cups_printer_name,
)
from python_pkg.brother_printer.constants import (
DERIVED_CUPS_ERROR,
get_status_info,
)
from python_pkg.brother_printer.data_classes import USBPortStatus
MOD = "python_pkg.brother_printer._cups_fallback"
class TestCupsReasonsToError:
"""Tests for _cups_reasons_to_error."""
def test_media_jam(self) -> None:
"""A real jam is 40022. It used to map to 40000, which is Sleep."""
code, display = _cups_reasons_to_error("media-jam-report")
assert code == "40022"
assert display == "Paper Jam"
def test_cover_open(self) -> None:
code, _ = _cups_reasons_to_error("cover-open")
assert code == "40021"
def test_door_open(self) -> None:
code, _ = _cups_reasons_to_error("door-open")
assert code == "40021"
def test_toner_empty(self) -> None:
code, _ = _cups_reasons_to_error("toner-empty")
assert code == "40010"
def test_toner_low(self) -> None:
code, _ = _cups_reasons_to_error("toner-low")
assert code == "40038"
def test_unknown_reason_shows_what_cups_said(self) -> None:
"""'Printer Error' alone is useless; quote the reason we were given."""
code, display = _cups_reasons_to_error("something-weird")
assert code == DERIVED_CUPS_ERROR
assert "something-weird" in display
def test_no_reason_at_all(self) -> None:
code, display = _cups_reasons_to_error("none")
assert code == DERIVED_CUPS_ERROR
assert "no reason" in display.lower()
def test_every_error_code_is_resolvable(self) -> None:
for reason in ("media-jam", "cover-open", "toner-empty", "toner-low"):
code, _ = _cups_reasons_to_error(reason)
_, text, _ = get_status_info(code)
assert "Unknown" not in text
# ── _port_status_to_status_code ──────────────────────────────────────
class TestPortStatusToStatusCode:
"""Tests for _port_status_to_status_code.
The port status only exposes paper/error/online bits, so it must not
pretend to know more than that - it previously reported "Cover Open" for
any error on an offline printer, which was a guess.
"""
def test_error_and_paper_empty(self) -> None:
ps = USBPortStatus(error=True, paper_empty=True, online=True)
code, display = _port_status_to_status_code(ps, "none")
assert code == "41000"
assert display == "No Paper"
def test_error_and_not_online_defers_to_cups(self) -> None:
ps = USBPortStatus(error=True, paper_empty=False, online=False)
code, display = _port_status_to_status_code(ps, "cover-open")
assert code == "40021"
assert display == "Cover Open"
def test_error_with_no_clue_does_not_guess(self) -> None:
ps = USBPortStatus(error=True, paper_empty=False, online=False)
code, _ = _port_status_to_status_code(ps, "none")
assert code == DERIVED_CUPS_ERROR
def test_error_only(self) -> None:
ps = USBPortStatus(error=True, paper_empty=False, online=True)
code, _ = _port_status_to_status_code(ps, "media-jam")
assert code == "40022"
def test_paper_empty_no_error(self) -> None:
ps = USBPortStatus(error=False, paper_empty=True, online=True)
code, _ = _port_status_to_status_code(ps, "none")
assert code == "41000"
def test_not_online_no_error(self) -> None:
ps = USBPortStatus(error=False, paper_empty=False, online=False)
code, display = _port_status_to_status_code(ps, "none")
assert code == "10002"
assert display == "Offline / Sleep"
def test_all_ok(self) -> None:
ps = USBPortStatus(error=False, paper_empty=False, online=True)
code, display = _port_status_to_status_code(ps, "none")
assert code == ""
assert display == ""
# ── find_cups_printer_name ───────────────────────────────────────────
class TestFindCupsPrinterName:
"""Tests for find_cups_printer_name."""
@patch(f"{MOD}.shutil.which", return_value=None)
def test_no_lpstat(self, m: MagicMock) -> None:
assert find_cups_printer_name() == ""
@patch("python_pkg.brother_printer._query.subprocess.run")
@patch(f"{MOD}.shutil.which", return_value="/usr/bin/lpstat")
def test_found(self, w: MagicMock, mock_run: MagicMock) -> None:
mock_run.return_value = MagicMock(
stdout="device for BrotherHL1110: usb://Brother/HL-1110\n"
)
assert find_cups_printer_name() == "BrotherHL1110"
@patch("python_pkg.brother_printer._query.subprocess.run")
@patch(f"{MOD}.shutil.which", return_value="/usr/bin/lpstat")
def test_no_brother(self, w: MagicMock, mock_run: MagicMock) -> None:
mock_run.return_value = MagicMock(stdout="device for HP: ipp://hp.local\n")
assert find_cups_printer_name() == ""
@patch("python_pkg.brother_printer._query.subprocess.run")
@patch(f"{MOD}.shutil.which", return_value="/usr/bin/lpstat")
def test_brother_no_match(self, w: MagicMock, mock_run: MagicMock) -> None:
mock_run.return_value = MagicMock(
stdout="brother printer found but format unexpected\n"
)
assert find_cups_printer_name() == ""
@patch("python_pkg.brother_printer._query.subprocess.run")
@patch(f"{MOD}.shutil.which", return_value="/usr/bin/lpstat")
def test_timeout(self, w: MagicMock, mock_run: MagicMock) -> None:
mock_run.side_effect = subprocess.TimeoutExpired("lpstat", 5)
assert find_cups_printer_name() == ""
@patch("python_pkg.brother_printer._query.subprocess.run")
@patch(f"{MOD}.shutil.which", return_value="/usr/bin/lpstat")
def test_oserror(self, w: MagicMock, mock_run: MagicMock) -> None:
mock_run.side_effect = OSError("fail")
assert find_cups_printer_name() == ""