147 lines
4.9 KiB
Rust
147 lines
4.9 KiB
Rust
use super::*;
|
|
use ferese_ipc::media::Playback;
|
|
use policy::PlayersState;
|
|
|
|
fn player(name: &str, status: Playback) -> Player {
|
|
Player {
|
|
name: name.into(),
|
|
owner: format!(":{name}"),
|
|
playing: status == Playback::Playing,
|
|
view: ferese_ipc::media::Player {
|
|
name: name.into(),
|
|
status,
|
|
..Default::default()
|
|
},
|
|
..Default::default()
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn selection_tracks_starts_not_metadata_or_volume_changes() {
|
|
let mut state = PlayersState::default();
|
|
state.update(player("first", Playback::Playing));
|
|
state.update(player("second", Playback::Playing));
|
|
let mut first = state.players["first"].clone();
|
|
first.view.title = "New track".into();
|
|
first.view.volume = Some(0.5);
|
|
state.update(first);
|
|
assert_eq!(state.selected().unwrap().name, "second");
|
|
|
|
state.update(player("second", Playback::Paused));
|
|
assert_eq!(state.selected().unwrap().name, "first");
|
|
state.update(player("first", Playback::Paused));
|
|
assert_eq!(state.selected().unwrap().name, "first");
|
|
state.update(player("second", Playback::Playing));
|
|
assert_eq!(state.selected().unwrap().name, "second");
|
|
}
|
|
|
|
#[test]
|
|
fn pin_ignore_and_departure_do_not_leave_a_ghost_player() {
|
|
let mut state = PlayersState::default();
|
|
state.update(player("first", Playback::Paused));
|
|
state.update(player("second", Playback::Playing));
|
|
state.pinned = Some("first".into());
|
|
assert_eq!(state.selected().unwrap().name, "first");
|
|
state.ignored.insert("first".into());
|
|
assert_eq!(state.selected().unwrap().name, "second");
|
|
state.remove("first");
|
|
assert!(state.pinned.is_none());
|
|
assert!(state.ignored.is_empty());
|
|
state.remove("second");
|
|
assert!(state.selected().is_none());
|
|
}
|
|
|
|
#[test]
|
|
fn initial_ties_are_deterministic_and_stopped_players_are_hidden() {
|
|
let mut state = PlayersState::default();
|
|
for name in ["z", "a"] {
|
|
state
|
|
.players
|
|
.insert(name.into(), player(name, Playback::Playing));
|
|
}
|
|
assert_eq!(state.selected().unwrap().name, "a");
|
|
state.update(player("a", Playback::Stopped));
|
|
state.update(player("z", Playback::Stopped));
|
|
assert!(state.selected().is_none());
|
|
}
|
|
|
|
#[test]
|
|
fn watch_waits_for_changes_deduplicates_and_drops_disconnected_clients() {
|
|
let mut engine = Engine::default();
|
|
let (send, receive) = mpsc::sync_channel(1);
|
|
engine.watch(1, 42, 0, send);
|
|
engine.update(Snapshot::default());
|
|
assert!(receive.try_recv().is_err());
|
|
let mut state = PlayersState::default();
|
|
state.update(player("first", Playback::Playing));
|
|
engine.update(state.update_snapshot().snapshot);
|
|
let response = receive.try_recv().unwrap();
|
|
assert_eq!(response.id, 42);
|
|
assert_eq!(response.result.unwrap()["revision"], 1);
|
|
let (send, receive) = mpsc::sync_channel(1);
|
|
engine.watch(2, 43, 1, send);
|
|
engine.remove(2);
|
|
state.remove("first");
|
|
engine.update(state.update_snapshot().snapshot);
|
|
assert!(receive.try_recv().is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn metadata_is_bounded_and_control_characters_are_removed() {
|
|
assert_eq!(bus::clean(" Track\nname\0 ", 80), "Trackname");
|
|
assert_eq!(bus::clean(&"é".repeat(500), 80).chars().count(), 80);
|
|
}
|
|
|
|
#[test]
|
|
fn metadata_and_position_updates_do_not_recompute_idle_policy() {
|
|
let before = player("first", Playback::Playing);
|
|
let mut after = before.clone();
|
|
after.view.title = "New track".into();
|
|
after.view.position_us = Some(10);
|
|
after.view.volume = Some(0.8);
|
|
assert!(!inhibition_changed(
|
|
std::slice::from_ref(&before),
|
|
std::slice::from_ref(&after)
|
|
));
|
|
after.playing = false;
|
|
assert!(inhibition_changed(
|
|
std::slice::from_ref(&before),
|
|
std::slice::from_ref(&after)
|
|
));
|
|
after.playing = true;
|
|
after.pid = Some(42);
|
|
assert!(inhibition_changed(
|
|
std::slice::from_ref(&before),
|
|
std::slice::from_ref(&after)
|
|
));
|
|
assert!(inhibition_changed(std::slice::from_ref(&before), &[]));
|
|
}
|
|
|
|
#[test]
|
|
fn session_lock_cancels_pending_and_rejects_new_media_watches() {
|
|
let mut engine = Engine::default();
|
|
let (send, receive) = mpsc::sync_channel(1);
|
|
engine.watch(1, 42, 0, send);
|
|
engine.set_locked(true);
|
|
let reply = receive.recv().unwrap();
|
|
assert_eq!(reply.id, 42);
|
|
assert!(reply.result.is_none());
|
|
assert_eq!(reply.error.unwrap().code, "session_locked");
|
|
engine.update(Snapshot {
|
|
selected: Some(player("private", Playback::Playing).view),
|
|
..Default::default()
|
|
});
|
|
for since in [0, engine.snapshot.revision] {
|
|
let (send, receive) = mpsc::sync_channel(1);
|
|
engine.watch(2, 43, since, send);
|
|
assert_eq!(
|
|
receive.recv().unwrap().error.unwrap().code,
|
|
"session_locked"
|
|
);
|
|
}
|
|
engine.set_locked(false);
|
|
let (send, receive) = mpsc::sync_channel(1);
|
|
engine.watch(3, 44, 0, send);
|
|
assert!(receive.recv().unwrap().result.is_some());
|
|
}
|