mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-10-18 12:59:12 +00:00
fix(sports): declare the constants the mixins read, and test the contract
CodeRabbit found _QUALITY_CHOICES and _RANKING_COVERAGE_SECONDS read by _normalise_quality and _check_ranking_coverage but never defined on a mixin. Confirmed: both are declared by all eight scoreboards, so nothing fails today -- it would only have bitten the ninth plugin to adopt this, at runtime, mid-render. Both are identical everywhere, so they get defaults here; each plugin's own copy still shadows them. Auditing for others showed those two were the only ones, but also that the host-contract docstring was substantially incomplete: it listed 21 attributes where the mixins actually read about 40, and omitted five hooks (_is_favorite_game, _is_game_really_over, _is_ranked_game, _passes_other_filters, _get_timezone). The section is now derived from that audit rather than remembered. test_sports_shared.py covers what is genuinely new, not the moved bodies: - The contract itself. It parses the module for every ALL-CAPS `self.X` the mixins read and asserts each is defined, so the next omission fails here rather than in the field. - _plugin_dir, the only new logic in the move. Including the case that made it necessary: SportsCore is an ABC, so a subclass built with type(name, bases, ns) -- which the plugins' own tests build -- reports __module__ as "abc". The test asserts that precondition before asserting the walk steps past it. - The three SportsLive bodies. Hockey and lacrosse disable live mode in their harness fixtures, so the 176 renders never reach this path; testing the mixin directly means coverage no longer depends on which plugin happens to have a unit test. Two of those tests pin things that would otherwise be silently undone. SportsRecentSharedMixin does carry an __init__ -- SportsRecent.__init__ was one of the 48 byte-identical bodies. Its bare super() binds to where it is defined, now the mixin, so it only reaches the host because the mixin is listed first in the bases. One test proves the chain runs; the next proves that reversing the order silently skips the host constructor.
This commit is contained in:
@@ -37,20 +37,30 @@ needs it -- so it is inlined below rather than left behind.
|
||||
|
||||
WHAT A HOST MUST PROVIDE
|
||||
------------------------
|
||||
Enumerated by walking every ``self.<attr>`` the mixins read and subtracting what
|
||||
they define, so this list is derived rather than remembered. Everything below is
|
||||
supplied by all eight scoreboards today.
|
||||
|
||||
State: ``config``, ``fonts``, ``logger``, ``display_width``, ``display_height``,
|
||||
``display_manager``, ``league``, ``sport``, ``mode_config``, ``session``,
|
||||
``headers``, ``schedule_lookback_days``, ``schedule_lookahead_days``,
|
||||
``game_update_timestamps``, ``_zero_clock_timestamps``, ``_logo_cache``,
|
||||
``_selection_pools``, ``_ranking_coverage_logged_at``, ``_empty_live_streak``,
|
||||
``_last_warning_time``, ``_score_grew``.
|
||||
``headers``, ``favorite_teams``, ``games_list``, ``current_game_index``,
|
||||
``last_game_switch``, ``last_update``, ``update_interval``,
|
||||
``no_data_interval``, ``game_display_duration``, ``stale_game_timeout``,
|
||||
``other_games_min_quality``, ``schedule_lookback_days``,
|
||||
``schedule_lookahead_days``, ``game_update_timestamps``,
|
||||
``_zero_clock_timestamps``, ``_logo_cache``, ``_selection_pools``,
|
||||
``_ranking_coverage_logged_at``, ``_empty_live_streak``, ``_last_warning_time``,
|
||||
``_score_grew``.
|
||||
|
||||
Methods that stay per-plugin, because they are not identical across the eight:
|
||||
Methods that stay per-plugin, because they are not identical across the eight
|
||||
(or, for ``_get_timezone``, because they bind per-plugin modules):
|
||||
``_get_layout_offset``, ``_by_importance``, ``_other_games_window``,
|
||||
``_upcoming_date_and_time_text``, ``_extract_game_details_common``,
|
||||
``_load_division_team_ids``.
|
||||
``_load_division_team_ids``, ``_get_timezone``, ``_is_favorite_game``,
|
||||
``_is_game_really_over``, ``_is_ranked_game``, ``_passes_other_filters``.
|
||||
|
||||
Of the twelve shared class constants, eleven are identical everywhere and live
|
||||
here. Only ``_SCORE_PROBE_TEXT`` varies -- afl and basketball reach three digits
|
||||
Of the fourteen shared class constants, thirteen are identical everywhere and
|
||||
live here. Only ``_SCORE_PROBE_TEXT`` varies -- afl and basketball reach three digits
|
||||
a side and override it, the same two that override ``_SCORE_PROBE`` on
|
||||
``SportsGameRendererMixin``.
|
||||
|
||||
@@ -168,6 +178,10 @@ class SportsCoreSharedMixin:
|
||||
"PressStart2P-Regular.ttf": 8, "4x6-font.ttf": 7}
|
||||
_FONT_NAME_ALIASES: ClassVar[Dict[str, str]] = {
|
||||
"press_start": "PressStart2P-Regular.ttf", "four_by_six": "4x6-font.ttf"}
|
||||
#: Accepted values for the other-games quality filter.
|
||||
_QUALITY_CHOICES: ClassVar[frozenset] = frozenset({"any", "ranked"})
|
||||
#: How long to stay quiet between ranking-coverage warnings.
|
||||
_RANKING_COVERAGE_SECONDS: ClassVar[int] = 60 * 60
|
||||
|
||||
def _get_season_schedule_dates(self) -> tuple[str, str]:
|
||||
return "", ""
|
||||
|
||||
Reference in New Issue
Block a user