mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-10-06 07:15:09 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6b6c4d2daf | ||
|
|
e4f5e49ff7 |
@@ -19,6 +19,16 @@ accepts both, but the store flags the old spelling as deprecated
|
||||
|
||||
## Unreleased
|
||||
|
||||
### Tooling
|
||||
|
||||
- `test/test_sports_helpers.py`'s parity tests pass again with
|
||||
`LEDMATRIX_PLUGINS` set. The scoreboards deleted their copies of the
|
||||
`sports_helpers` bodies and constants when they adopted `SportsHelpersMixin`
|
||||
(ledmatrix-plugins #563/#564), and the 19 tests still expected them. A copy
|
||||
that is gone now counts as adopted when the plugin imports
|
||||
`src.common.sports_helpers`, as the stage 3/4 and game-over parity tests
|
||||
already do; a copy that remains must still match.
|
||||
|
||||
## 3.8.2
|
||||
|
||||
The display hands freed memory back to the OS (#774), and sports consolidation
|
||||
|
||||
@@ -91,7 +91,10 @@ if [ -r "$LM_OS_RELEASE_FILE" ]; then
|
||||
DESKTOP_DETECTED=0
|
||||
# grep without -q: -q exits at the first match, dpkg then dies of SIGPIPE,
|
||||
# and pipefail turns a found desktop into "not found".
|
||||
if dpkg -l | grep -E "^ii.*raspberrypi-ui-mods|^ii.*lxde|^ii.*xfce|^ii.*gnome|^ii.*kde" >/dev/null; then
|
||||
# Match on the package name only, anchored: `dpkg -l` lines include the
|
||||
# description, so an unanchored ".*kde" matched libblockdev-* on Lite.
|
||||
if dpkg-query -W -f='${db:Status-Abbrev} ${binary:Package}\n' 2>/dev/null \
|
||||
| grep -E "^ii +(raspberrypi-ui-mods|lxde|xfce|gnome|kde)" >/dev/null; then
|
||||
DESKTOP_DETECTED=1
|
||||
fi
|
||||
if systemctl list-units --type=service --state=running 2>/dev/null | grep -qE "lightdm|gdm3|sddm|lxdm"; then
|
||||
|
||||
+41
-11
@@ -8,7 +8,9 @@ loses those tests with it.
|
||||
The parity class is what keeps "byte-identical" true after this lands. Point
|
||||
LEDMATRIX_PLUGINS at a ledmatrix-plugins checkout and every promoted body is
|
||||
compared, as a docstring-stripped AST, against every plugin copy that carries
|
||||
it. Without the variable it skips rather than fails, since core CI has no
|
||||
it. A copy that is gone counts as adopted when the plugin imports
|
||||
src.common.sports_helpers (plugins#563/#564 did that for every scoreboard).
|
||||
Without the variable it skips rather than fails, since core CI has no
|
||||
plugins checkout; ledmatrix-plugins CI runs the same comparison against core
|
||||
(scripts/check_sports_helpers_parity.py, ledmatrix-plugins#495).
|
||||
"""
|
||||
@@ -572,6 +574,24 @@ def _core_definitions():
|
||||
return out
|
||||
|
||||
|
||||
def _sports_source(root, sport):
|
||||
return (root / f"{sport}-scoreboard" / "sports.py").read_text(encoding="utf-8")
|
||||
|
||||
|
||||
def _adopted(source):
|
||||
"""Gone is fine once the plugin uses the module; otherwise the finder is
|
||||
not seeing its copy."""
|
||||
name = sports_helpers.__name__
|
||||
for node in ast.walk(ast.parse(source)):
|
||||
if isinstance(node, ast.ImportFrom):
|
||||
if node.module == name or any(
|
||||
f"{node.module}.{a.name}" == name for a in node.names):
|
||||
return True
|
||||
elif isinstance(node, ast.Import) and any(a.name == name for a in node.names):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
class TestParityWithPlugins:
|
||||
@pytest.mark.parametrize("name", sorted(PROMOTED))
|
||||
def test_body_matches_every_plugin_copy(self, name):
|
||||
@@ -580,10 +600,10 @@ class TestParityWithPlugins:
|
||||
ours = _dump(_core_definitions()[name])
|
||||
drifted, missing = [], []
|
||||
for sport in carriers:
|
||||
defs = _definitions(ast.parse(
|
||||
(root / f"{sport}-scoreboard" / "sports.py").read_text(encoding="utf-8")))
|
||||
theirs = defs[where].get(plugin_name)
|
||||
source = _sports_source(root, sport)
|
||||
theirs = _definitions(ast.parse(source))[where].get(plugin_name)
|
||||
if theirs is None:
|
||||
if not _adopted(source):
|
||||
missing.append(sport)
|
||||
elif _dump(theirs) != ours:
|
||||
drifted.append(sport)
|
||||
@@ -594,10 +614,20 @@ class TestParityWithPlugins:
|
||||
|
||||
@pytest.mark.parametrize("sport", SCOREBOARDS)
|
||||
def test_constants_match(self, sport):
|
||||
root = _plugins_root()
|
||||
defs = _definitions(ast.parse(
|
||||
(root / f"{sport}-scoreboard" / "sports.py").read_text(encoding="utf-8")))
|
||||
assert ast.literal_eval(defs["module"]["_MIN_WINDOW_DAYS"].value) == MIN_WINDOW_DAYS
|
||||
assert ast.literal_eval(defs["module"]["_MAX_WINDOW_DAYS"].value) == MAX_WINDOW_DAYS
|
||||
gap = defs["SportsCore"]["_DWELL_REENTRY_GAP_SECONDS"].value
|
||||
assert math.isclose(ast.literal_eval(gap), SportsHelpersMixin._DWELL_REENTRY_GAP_SECONDS)
|
||||
source = _sports_source(_plugins_root(), sport)
|
||||
defs = _definitions(ast.parse(source))
|
||||
expected = {
|
||||
("module", "_MIN_WINDOW_DAYS"): MIN_WINDOW_DAYS,
|
||||
("module", "_MAX_WINDOW_DAYS"): MAX_WINDOW_DAYS,
|
||||
("SportsCore", "_DWELL_REENTRY_GAP_SECONDS"):
|
||||
SportsHelpersMixin._DWELL_REENTRY_GAP_SECONDS,
|
||||
}
|
||||
missing = []
|
||||
for (where, name), value in expected.items():
|
||||
node = defs[where].get(name)
|
||||
if node is None:
|
||||
if not _adopted(source):
|
||||
missing.append(name)
|
||||
else:
|
||||
assert math.isclose(ast.literal_eval(node.value), value), name
|
||||
assert missing == [], f"not found in {sport}: {missing}"
|
||||
|
||||
Reference in New Issue
Block a user