mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-08-07 11:48:06 +00:00
The unit-tests CI job ran an explicit 24-file allowlist that had rotted: 63 of 90 test files (display, vegas, store manager, web API, web_interface) never ran on a PR. The job now runs all of test/ (minus test/plugins, which the plugin-safety job owns) so new test files are enrolled by default and any exclusion needs a visible, commented --ignore. The plugin-safety job was a green no-op: plugins/ is empty in CI, so every test skipped with 'Manifest not found'. It now renders a bundled deterministic fixture plugin (test/fixtures/plugins/ci-fixture-plugin, golden images included for all 8 default sizes) via LEDMATRIX_PLUGINS_DIR, and sets LEDMATRIX_REQUIRE_PLUGINS=1 so discovering zero plugins fails loudly instead of skipping green. The per-plugin suites document that they target dev machines with real plugins installed. Coverage is now measured and enforced in exactly one place — the CI unit-tests step (--cov=src --cov=web_interface --cov-fail-under=45, from a measured 47% baseline). pytest.ini previously declared --cov-fail-under=30 but CI always passed --no-cov, so the gate had never run anywhere; local pytest is now coverage-free and fast. Enabling the 63 unenrolled files surfaced three cases of test rot, fixed here: test_display_controller_vegas_tick.py could not collect without the hardware rgbmatrix module (now uses the emulator convention), the state-reconciliation unrecoverable-cache tests broke when production added the is_plugin_uninstalled tombstone check (bare Mock returned truthy), and test_get_system_status assumed the optional psutil dependency (now installed via requirements-test.txt and guarded by importorskip). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NohXi78cwsAKtN1sCfxjUh
39 lines
1.7 KiB
Python
39 lines
1.7 KiB
Python
"""
|
|
CI fixture plugin.
|
|
|
|
Exists so the plugin safety harness (test/plugins/test_plugin_matrix.py and
|
|
the plugin-safety CI job) always has at least one real plugin to load and
|
|
render — without it, an empty plugins/ directory turns the whole job into a
|
|
green no-op. The render is deliberately trivial and fully deterministic:
|
|
a border rectangle plus both diagonals, sized from the display manager's
|
|
declared dimensions. No fonts, no network, no time dependence, so golden
|
|
images are stable across platforms.
|
|
"""
|
|
|
|
from PIL import ImageDraw
|
|
|
|
from src.plugin_system.base_plugin import BasePlugin
|
|
|
|
|
|
class CIFixturePlugin(BasePlugin):
|
|
def update(self) -> None:
|
|
"""Nothing to fetch — the render is self-contained."""
|
|
|
|
def display(self, force_clear: bool = False) -> None:
|
|
width = self.display_manager.matrix.width
|
|
height = self.display_manager.matrix.height
|
|
border = tuple(self.config.get("border_color", [0, 255, 0]))
|
|
diagonal = tuple(self.config.get("diagonal_color", [255, 0, 0]))
|
|
|
|
image = self.display_manager.image
|
|
draw = ImageDraw.Draw(image)
|
|
# Blank only the declared panel area, then draw edge-to-edge content:
|
|
# the border proves the plugin reads dynamic dimensions (any overflow
|
|
# or underfill at any size is a harness bug or a dimensions bug), the
|
|
# diagonals make golden comparisons sensitive to size/offset drift.
|
|
draw.rectangle([0, 0, width - 1, height - 1], fill=(0, 0, 0))
|
|
draw.rectangle([0, 0, width - 1, height - 1], outline=border)
|
|
draw.line([0, 0, width - 1, height - 1], fill=diagonal)
|
|
draw.line([0, height - 1, width - 1, 0], fill=diagonal)
|
|
self.display_manager.update_display()
|