DR-243. One set of behaviours every engine must satisfy, written before the
second engine exists so it cannot encode whatever the first happens to do —
which is how three playback implementations drifted apart in the first place.
FakePlayer models the one behaviour that matters most: opening is not
instantaneous. `open` parks in Phase::Opening until complete_open() is called,
so a test can put a seek into that window deliberately. That window is where
DR-241 lived, and it was previously unreachable from any test.
The suite drives readiness through a Harness rather than sleeping — the fake
completes on demand, a real engine waits for its own readiness event. A
timing-dependent suite is worse than none, because it teaches people to
re-run until green.
Nine cases, each naming the defect it prevents:
opens_at_a_start_position DR-241 - starts there, never at zero
seek_while_opening_is_honoured DR-241 - held, not discarded
seek_while_opening_overrides_start later intent wins
pause_and_play_are_observable DR-239 - state an engine cannot hide
close_is_silent_and_idempotent stopped must mean silent
close_during_open_never_plays an open cancelled by close
must not come back to life
`audible()` may return None for engines that cannot answer, which skips the
silence assertions rather than passing them vacuously — an assertion that
cannot fail is worse than an absent one.
Also adds MediaItem::sample: the struct has twenty-odd fields, almost none of
which a given test cares about, and repeating the literal per test is how a
new field ends up added in thirty places.
57 lines
1.5 KiB
Rust
57 lines
1.5 KiB
Rust
//! `FakePlayer` runs the conformance suite.
|
|
//!
|
|
//! It is correct by construction, so a failure here means the *suite* is wrong,
|
|
//! not an engine. That is what makes it safe to trust the same cases when they
|
|
//! fail against a real one.
|
|
//!
|
|
//! TRACES: UR-081 | DR-243 | UT-220
|
|
|
|
use std::time::Duration;
|
|
|
|
use super::conformance::Harness;
|
|
use super::fake_player::FakePlayer;
|
|
use super::media::MediaItem;
|
|
use super::media_player::OpenRequest;
|
|
use crate::repository::stream_selection::StreamSelection;
|
|
|
|
struct FakeHarness {
|
|
player: FakePlayer,
|
|
}
|
|
|
|
impl FakeHarness {
|
|
fn new() -> Self {
|
|
Self {
|
|
player: FakePlayer::new().with_duration(Duration::from_secs(7200)),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Harness for FakeHarness {
|
|
type Player = FakePlayer;
|
|
|
|
fn player(&mut self) -> &mut FakePlayer {
|
|
&mut self.player
|
|
}
|
|
|
|
fn request(&self, start: Duration) -> OpenRequest {
|
|
let selection = StreamSelection::local_file("http://example.invalid/stream.mp4");
|
|
let media = MediaItem::sample("fake-item", &selection.url);
|
|
OpenRequest::new(media, selection).starting_at(start)
|
|
}
|
|
|
|
fn settle(&mut self) {
|
|
self.player.complete_open();
|
|
}
|
|
|
|
fn audible(&mut self) -> Option<bool> {
|
|
Some(self.player.audible)
|
|
}
|
|
|
|
/// Exact: the fake has no keyframes to round to, so any drift is a bug.
|
|
fn seek_tolerance(&self) -> Duration {
|
|
Duration::ZERO
|
|
}
|
|
}
|
|
|
|
crate::media_player_conformance!(fake, FakeHarness::new());
|