77 lines
4.2 KiB
Markdown
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.
|