Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 26
Test suite overhaul + fixes for the three bugs it uncovered#441
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
911c559ca04820dfba88bb8b0c8ed159f28dc643b4206eca0d97a299File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -2969,16 +2969,27 @@ def update_plugin(self, plugin_id: str) -> bool: | ||
| remote_branch = plugin_info_remote.get('branch') or plugin_info_remote.get('default_branch') | ||
| # Compare local manifest version against registry latest_version | ||
| # to avoid unnecessary reinstalls for monorepo plugins | ||
| # to avoid unnecessary reinstalls for monorepo plugins. Uses the | ||
| # same semantic comparator as the web UI's update badge, so | ||
| # equivalent spellings ("v1.2.0" vs "1.2.0") never trigger a | ||
| # reinstall and a locally-ahead version is never downgraded. | ||
| try: | ||
| local_manifest_path = plugin_path / "manifest.json" | ||
| if local_manifest_path.exists(): | ||
| with open(local_manifest_path, 'r', encoding='utf-8') as f: | ||
| local_manifest = json.load(f) | ||
| local_version = local_manifest.get('version', '') | ||
| remote_version = plugin_info_remote.get('latest_version', '') | ||
| if local_version and remote_version and local_version == remote_version: | ||
| self.logger.info(f"Plugin {plugin_id} already at latest version {local_version}") | ||
| from src.plugin_system.compatibility import is_update_available | ||
| # No truthiness gate: the shared comparator already treats | ||
| # a missing version on either side as "no update", and the | ||
| # store must agree with the UI badge in that case too. A | ||
| # missing manifest (not just a missing version field) | ||
| # still falls through to the reinstall recovery path. | ||
| if not is_update_available(local_version, remote_version): | ||
| self.logger.info( | ||
| f"Plugin {plugin_id} already at latest version " | ||
| f"(installed {local_version}, registry {remote_version})") | ||
coderabbitai[bot] marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| return True | ||
| except Exception as e: | ||
| self.logger.debug(f"Could not compare versions for {plugin_id}: {e}") | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| { | ||
| "$schema": "http://json-schema.org/draft-07/schema#", | ||
| "title": "CI Fixture Plugin", | ||
| "type": "object", | ||
| "properties": { | ||
| "enabled": { | ||
| "type": "boolean", | ||
| "default": true | ||
| }, | ||
| "display_duration": { | ||
| "type": "number", | ||
| "default": 5 | ||
| }, | ||
| "border_color": { | ||
| "type": "array", | ||
| "items": {"type": "integer", "minimum": 0, "maximum": 255}, | ||
| "minItems": 3, | ||
| "maxItems": 3, | ||
| "default": [0, 255, 0], | ||
| "description": "RGB color of the border rectangle." | ||
| }, | ||
| "diagonal_color": { | ||
| "type": "array", | ||
| "items": {"type": "integer", "minimum": 0, "maximum": 255}, | ||
| "minItems": 3, | ||
| "maxItems": 3, | ||
| "default": [255, 0, 0], | ||
| "description": "RGB color of the diagonals." | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| """ | ||
| 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): | ||
coderabbitai[bot] marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| """Deterministic CI-only fixture plugin: renders a border + diagonals | ||
| pattern sized from the display's declared dimensions. Never shipped to | ||
| devices; exists solely so the plugin safety harness has a real plugin | ||
| to exercise in CI.""" | ||
| def update(self) -> None: | ||
| """Nothing to fetch — the render is self-contained.""" | ||
| def display(self, force_clear: bool = False) -> None: | ||
| self.display_manager.clear() | ||
| 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() | ||
coderabbitai[bot] marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| { | ||
| "id": "ci-fixture-plugin", | ||
| "name": "CI Fixture Plugin", | ||
| "version": "1.0.0", | ||
| "description": "Bundled test fixture so the plugin safety harness always has at least one real plugin to render in CI. Draws a deterministic border + diagonals pattern at any panel size. Not installable from the store and never shipped to devices.", | ||
| "author": "LEDMatrix", | ||
| "entry_point": "manager.py", | ||
| "class_name": "CIFixturePlugin", | ||
| "display_modes": ["ci-fixture"], | ||
| "update_interval": 3600, | ||
| "min_ledmatrix_version": "2.0.0", | ||
| "compatible_versions": [">=2.0.0"] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| # No dependencies — the fixture must load in any environment. | ||
coderabbitai[bot] marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| # | ||
| # Pillow is deliberately NOT pinned here even though manager.py imports | ||
| # PIL: it is a core LEDMatrix dependency (see the repo-root | ||
| # requirements.txt), so it is always present wherever the harness runs, | ||
| # and the harness loads plugins with install_deps=False anyway. Pinning | ||
| # it here would only invite a needless pip install during test runs. | ||
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.