Skip to content

Test suite - #85

Merged
sifferhans merged 13 commits into
mainfrom
test/dart-test-suite
Sep 7, 2026
Merged

Test suite#85
sifferhans merged 13 commits into
mainfrom
test/dart-test-suite

Conversation

@sifferhans

@sifferhans sifferhans commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Test suite + CI, and the bugs it found

bccm_player had one Dart test, and nothing in CI ran it. bcc-media-app's Semaphore Test block fires on /submodules/ changes but only runs that app's own suite — so regressions here reached BCC Media, Bible Kids, Play and connect-live unchallenged.

This adds 215 tests and a GitHub Actions workflow (pub get → analyze → test), and fixes the 11 bugs the tests turned up.

Coverage targets where native hands off to Dart, plus the queue — which doc/contributing/audio-support-plan.md step 3 wants covered before it moves native. Queue tests are written against the QueueManager contract rather than the internals, so they survive that move.

Bugs fixed

Each has its own commit, paired with the test that proves it.

User-visible

  • Live seekbar seeked to the wrong place. The thumb accounted for seekableRangeStartMs, the drag handler didn't — on a 100s–200s DVR window the thumb sat at halfway while dragging there jumped to the window start.
  • Shuffle replayed already-played tracks. ShuffleQueueList._orderedItems was only ever written by setItems, so toggling shuffle resurrected consumed items.
  • No history when a track ended naturally. handlePlaybackEnded ignored its argument, so you could skip back after skipping forward but not after letting a track finish.
  • moveQueueItem threw RangeError on stale indices — trivially hit by a reorderable list racing a queue update.

Silent

  • Navigating away mid-scrub raised an unhandled async error, every time. OneAsyncAtATime called completeError on a Completer nothing awaits, and useTimeline calls reset() on dispose. Surfaced in error reporting as a bare disposed string with no useful stack.
  • Controls rebuilt on every millisecond. positionMs ?? 0 / 500 parses as positionMs ?? (0 / 500), so the intended 500 ms throttle never happened.
  • Primary-player changes never reached playerEventStream — the only one of seven callbacks that didn't publish, so controller.events never saw a cast handover.
  • PlayerStateNotifier.dispose(force: true) double-disposed and threw. onDispose re-enters it.
  • getFormattedDuration threw on NaN, which would take the controls overlay down. Unreachable today only because safeDouble guards upstream.
  • setNextUp mutated the caller's list.

⚠️ Behaviour change

  • iconColor was unreachable when primaryColor was set. fillWithDefaults read primaryColor ?? iconColor ?? defaults; now iconColor ?? primaryColor ?? defaults. Anyone setting both now gets the iconColor they asked for.

Also in here

  • flutter analyze runs strict (infos fatal). That meant clearing 25 deprecations — withOpacity, Color.value, cacheExtent, whereNotNull, onReorder. One narrow inline exception: dart:html in the web player.
  • doc/contributing/basics.md gained a "Running tests" section, including the previously undocumented Gradle command for the existing Kotlin tests.

BccmPlayerController turned up no bugs: the initialize memoisation, the disposed-mid-flight window and swapPlayerNotifier's listener swap all hold.

Follow-ups, not in this PR

  • Migrate the web player off dart:html to package:web. ~120 lines of DOM code that only a real web build can verify — the last thing between us and an unqualified strict analyze.
  • Extract track selection out of _SettingsBottomSheet. Pure logic trapped in a widget; the biggest remaining coverage win.
  • tv_controls.dart ignores the seekable range entirely, reimplementing useTimeline inline. DVR-unaware on TV rather than broken.
  • playerProviderFor / primaryPlayerProvider leak a PlayerStateNotifier (with a live 1 s timer) per rebuild.
  • Kotlin tests still aren't in CI; iOS has no test target.

sifferhans and others added 8 commits September 7, 2026 10:42
Nothing ran this package's tests. bcc-media-app's Semaphore `Test` block fires
when `/submodules/` changes but only runs that app's own suite, so the single
Dart test here and the six Robolectric tests were unenforced.

Mirrors the sibling workflow in bccm_flutter (pub get -> analyze -> test,
Flutter 3.44.0 pinned). Uses `--no-fatal-infos` because the package carries ~20
pre-existing deprecation infos; warnings and errors are still fatal, which
required dropping one redundant null assertion in player_controller.

Also adds the shared test infrastructure the following commits build on: a
hand-written FakeBccmPlayerInterface (better than the mockito mocks where a test
needs real state management, since `stateNotifier` is a field on the abstract
class) and pigeon model builders.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
handlePlaybackEnded took a MediaItem and ignored it, so it never pushed onto
history — while skipToNext did. The result was that skipping forward manually
let you skip back, but letting a track finish did not.

Falls back to the current media item when the native side passes null, and
only records when there is actually a next item to move to, matching
skipToNext.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@sifferhans sifferhans changed the title Test/dart test suite Dart test suite Sep 7, 2026
@sifferhans sifferhans changed the title Dart test suite Test suite Sep 7, 2026
@sifferhans
sifferhans merged commit 4bd9caa into main Sep 7, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant