crabidy/quality/mpris.md

77 lines
4.2 KiB
Markdown

# Quality gates — MPRIS
Criteria an implementation of `architecture/mpris.md` must satisfy.
Automated coverage lives in `cbd-tui/src/mpris.rs` (the mapping, as unit
tests) and `cbd-tui/tests/mpris_bus.rs` (the round trip over a real
session bus, skipped when there is none).
## Hard rules (highest priority)
- [x] **G1 — No panic, whatever the bus does.** A missing session bus, a
bus that never answers, a refused name, a failed property emission and a
dropped connection are all logged and survived (D13). The connection
attempt carries a timeout, as an external call must. *(A missing bus is
exercised by every test run: `start` returns `None` and the client runs
on.)*
- [x] **G2 — No secret and no credential-bearing URL reaches the bus.**
Published metadata is title, artist, album, length and a synthetic
trackid — no `xesam:url`, no `mpris:artUrl`, no library path (D8). Every
peer on a session bus can read properties, so this is a redaction
boundary, not a matter of taste. *(tests:
`metadata_names_the_track_and_never_a_url`, and the bus test asserts the
library path is absent from what a client actually receives.)*
- [x] **G3 — The state feed is bounded.** 64 slots, `try_send`, a
`debug!` on overflow; the orchestrator never awaits the bus (D12).
*(test: `a_full_feed_drops_instead_of_blocking`.)*
- [x] **G4 — No lock is held across an `await`.** The published mirror
sits behind a `std::sync::Mutex` whose guard never crosses a suspension
point; a poisoned lock is recovered rather than propagated as a panic.
- [x] **G5 — Nothing is published that the server did not say.** No
optimistic local write on a method call, no interpolated position, no
cached duration carried across a track change (D2).
## Behaviour
- [x] **G6 — A pause key cannot start playback.** `Play`, `Pause` and the
modifier setters consult the published state and send nothing when the
target is already the current value; only `PlayPause` toggles
unconditionally (D3). *(tests:
`pause_never_starts_playback_and_play_never_stops_it`,
`play_pause_always_toggles`, `the_modifiers_toggle_only_when_they_differ`.)*
- [x] **G7 — Mute round-trips.** Volume 0 mutes rather than turning the
level down, so unmuting restores it; a non-zero target unmutes and then
moves by the delta (D4). *(tests:
`volume_is_muted_as_zero_and_restored_by_unmuting`,
`setting_a_volume_sends_the_delta_to_it`.)*
- [x] **G8 — Unrepresentable requests are refused, not approximated.**
`LoopStatus::Track`, a playback rate other than 1.0, `OpenUri`, `Quit`,
`Raise` and `SetFullscreen` answer `NotSupported` and change nothing
(D6, D14). *(test: `a_single_track_loop_is_refused_not_faked`.)*
- [x] **G9 — An unknown duration is omitted, not zero.** A zero
`mpris:length` draws a full progress bar in consumers (D9). *(tests:
`an_unknown_duration_is_omitted_not_zero`,
`the_live_duration_wins_over_the_tracks_own`.)*
- [x] **G10 — `Seeked` fires on a discontinuity and not on the clock or a
track change** (D10). *(test:
`a_position_jump_is_a_seek_but_a_new_track_is_not`.)*
- [x] **G11 — Only real changes are announced.** A `PropertiesChanged`
for a value that did not change wakes every listener for nothing.
*(test: `only_real_changes_are_announced`.)*
- [x] **G12 — A stale `SetPosition` is ignored.** The spec's trackid
argument exists so a seek aimed at a track that has since changed does
not move the one now playing. *(test:
`set_position_becomes_a_delta_and_ignores_a_stale_track`.)*
## Build and shape
- [x] **G13 — The feature carries its own weight and nothing else's.**
`mpris` adds one crate and no system library; `--no-default-features`
compiles, tests and clippies clean through a stub of the same shape, and
the matrix in `check-features` covers `mpris` alone, `notifications`
alone, and neither.
- [x] **G14 — The server is untouched.** No proto change, no server
change; the feature is a client-side translation or it is misplaced.
- [x] **G15 — Verified against a real client, not only a test double.**
`playerctl` must list the player, read its metadata, and drive
play-pause, seek and volume. Re-check on a `mpris-server`/`zbus` bump.