diff --git a/src/tui/app/machine/browse.rs b/src/tui/app/machine/browse.rs index 6e12584..e8ea66a 100644 --- a/src/tui/app/machine/browse.rs +++ b/src/tui/app/machine/browse.rs @@ -1,26 +1,9 @@ -use std::{ - sync::{mpsc, Arc, Mutex}, - thread, time, +use crate::tui::app::{ + machine::{App, AppInner, AppMachine}, + selection::{Delta, ListSelection}, + AppPublic, AppState, IAppInteractBrowse, }; -use musichoard::collection::{ - album::AlbumMeta, - artist::{Artist, ArtistMeta}, - musicbrainz::{IMusicBrainzRef, Mbid}, -}; - -use crate::tui::{ - app::{ - machine::{App, AppInner, AppMachine}, - selection::{Delta, ListSelection}, - AppMatchesInfo, AppPublic, AppState, IAppInteractBrowse, - }, - event::{Event, EventSender}, - lib::interface::musicbrainz::{self, IMusicBrainz}, -}; - -use super::fetch::{FetchResult, FetchSender}; - pub struct AppBrowse; impl AppMachine { @@ -30,80 +13,6 @@ impl AppMachine { state: AppBrowse, } } - - fn spawn_fetch_thread( - inner: &AppInner, - artist: &Artist, - tx: FetchSender, - ) -> thread::JoinHandle<()> { - match artist.meta.musicbrainz { - Some(ref arid) => { - let musicbrainz = Arc::clone(&inner.musicbrainz); - let events = inner.events.clone(); - let arid = arid.mbid().clone(); - let albums = artist.albums.iter().map(|a| &a.meta).cloned().collect(); - thread::spawn(|| Self::fetch_albums(musicbrainz, tx, events, arid, albums)) - } - None => { - let musicbrainz = Arc::clone(&inner.musicbrainz); - let events = inner.events.clone(); - let artist = artist.meta.clone(); - thread::spawn(|| Self::fetch_artist(musicbrainz, tx, events, artist)) - } - } - } - - fn fetch_artist( - musicbrainz: Arc>, - fetch_tx: FetchSender, - events: EventSender, - artist: ArtistMeta, - ) { - let result = musicbrainz.lock().unwrap().search_artist(&artist); - let result = result.map(|list| AppMatchesInfo::artist(artist, list)); - Self::send_fetch_result(&fetch_tx, &events, result).ok(); - } - - fn fetch_albums( - musicbrainz: Arc>, - fetch_tx: FetchSender, - events: EventSender, - arid: Mbid, - albums: Vec, - ) { - let mut musicbrainz = musicbrainz.lock().unwrap(); - let mut album_iter = albums.into_iter().peekable(); - while let Some(album) = album_iter.next() { - if album.musicbrainz.is_some() { - continue; - } - - let result = musicbrainz.search_release_group(&arid, &album); - let result = result.map(|list| AppMatchesInfo::album(album, list)); - if Self::send_fetch_result(&fetch_tx, &events, result).is_err() { - return; - }; - - if album_iter.peek().is_some() { - thread::sleep(time::Duration::from_secs(1)); - } - } - } - - fn send_fetch_result( - fetch_tx: &FetchSender, - events: &EventSender, - result: Result, - ) -> Result<(), ()> { - // If receiver disconnects just drop the rest. - fetch_tx.send(result).map_err(|_| ())?; - - // If this send fails the event listener is dead. Don't panic as this function runs in a - // detached thread so this might be happening during normal shut down. - events.send(Event::FetchResultReady).map_err(|_| ())?; - - Ok(()) - } } impl From> for App { @@ -170,35 +79,19 @@ impl IAppInteractBrowse for AppMachine { } fn fetch_musicbrainz(self) -> Self::APP { - let coll = self.inner.music_hoard.get_collection(); - let artist = match self.inner.selection.state_artist(coll) { - Some(artist_state) => &coll[artist_state.index], - None => { - return AppMachine::error(self.inner, "cannot fetch: no artist selected").into() - } - }; - - let (fetch_tx, fetch_rx) = mpsc::channel::(); - Self::spawn_fetch_thread(&self.inner, artist, fetch_tx); - - AppMachine::app_fetch_new(self.inner, fetch_rx) + AppMachine::app_fetch_new(self.inner) } } #[cfg(test)] mod tests { - use mockall::{predicate, Sequence}; - use mpsc::TryRecvError; - use crate::tui::{ app::{ machine::tests::{inner, inner_with_mb, music_hoard}, - AppAlbumMatches, AppArtistMatches, Category, IAppAccess, IAppInteract, + Category, IAppAccess, IAppInteract, }, - event::EventReceiver, - lib::interface::musicbrainz::{Match, MockIMusicBrainz}, + lib::interface::musicbrainz::MockIMusicBrainz, testmod::COLLECTION, - EventChannel, }; use super::*; @@ -291,195 +184,10 @@ mod tests { let public = app.get(); - // Because of threaded behaviour, this unit test cannot expect one or the other. + // Because of fetch's threaded behaviour, this unit test cannot expect one or the other. assert!( matches!(public.state, AppState::Matches(_)) || matches!(public.state, AppState::Fetch(_)) ); } - - fn event_channel() -> (EventSender, EventReceiver) { - let event_channel = EventChannel::new(); - let events_tx = event_channel.sender(); - let events_rx = event_channel.receiver(); - (events_tx, events_rx) - } - - fn album_expectations_1() -> (AlbumMeta, Vec>) { - let album_1 = COLLECTION[1].albums[0].meta.clone(); - let album_4 = COLLECTION[1].albums[3].meta.clone(); - - let album_match_1_1 = Match::new(100, album_1.clone()); - let album_match_1_2 = Match::new(50, album_4.clone()); - let matches_1 = vec![album_match_1_1.clone(), album_match_1_2.clone()]; - - (album_1, matches_1) - } - - fn album_expectations_4() -> (AlbumMeta, Vec>) { - let album_1 = COLLECTION[1].albums[0].meta.clone(); - let album_4 = COLLECTION[1].albums[3].meta.clone(); - - let album_match_4_1 = Match::new(100, album_4.clone()); - let album_match_4_2 = Match::new(30, album_1.clone()); - let matches_4 = vec![album_match_4_1.clone(), album_match_4_2.clone()]; - - (album_4, matches_4) - } - - fn search_release_group_expectation( - api: &mut MockIMusicBrainz, - seq: &mut Sequence, - arid: &Mbid, - album: &AlbumMeta, - matches: &[Match], - ) { - let result = Ok(matches.to_owned()); - api.expect_search_release_group() - .with(predicate::eq(arid.clone()), predicate::eq(album.clone())) - .times(1) - .in_sequence(seq) - .return_once(|_, _| result); - } - - #[test] - fn fetch_albums() { - let mut mb_api = MockIMusicBrainz::new(); - - let arid: Mbid = "11111111-1111-1111-1111-111111111111".try_into().unwrap(); - - let (album_1, matches_1) = album_expectations_1(); - let (album_4, matches_4) = album_expectations_4(); - - // Other albums have an MBID and so they will be skipped. - let mut seq = Sequence::new(); - - search_release_group_expectation(&mut mb_api, &mut seq, &arid, &album_1, &matches_1); - search_release_group_expectation(&mut mb_api, &mut seq, &arid, &album_4, &matches_4); - - let music_hoard = music_hoard(COLLECTION.to_owned()); - let (events_tx, events_rx) = event_channel(); - let inner = AppInner::new(music_hoard, mb_api, events_tx); - - let (fetch_tx, fetch_rx) = mpsc::channel(); - // Use the second artist for this test. - let handle = AppMachine::spawn_fetch_thread(&inner, &COLLECTION[1], fetch_tx); - handle.join().unwrap(); - - assert_eq!(events_rx.try_recv().unwrap(), Event::FetchResultReady); - let result = fetch_rx.try_recv().unwrap(); - let expected = Ok(AppMatchesInfo::Album(AppAlbumMatches { - matching: album_1.clone(), - list: matches_1.iter().cloned().map(Into::into).collect(), - })); - assert_eq!(result, expected); - - assert_eq!(events_rx.try_recv().unwrap(), Event::FetchResultReady); - let result = fetch_rx.try_recv().unwrap(); - let expected = Ok(AppMatchesInfo::Album(AppAlbumMatches { - matching: album_4.clone(), - list: matches_4.iter().cloned().map(Into::into).collect(), - })); - assert_eq!(result, expected); - } - - fn artist_expectations() -> (ArtistMeta, Vec>) { - let artist = COLLECTION[3].meta.clone(); - - let artist_match_1 = Match::new(100, artist.clone()); - let artist_match_2 = Match::new(50, artist.clone()); - let matches = vec![artist_match_1.clone(), artist_match_2.clone()]; - - (artist, matches) - } - - fn search_artist_expectation( - api: &mut MockIMusicBrainz, - seq: &mut Sequence, - artist: &ArtistMeta, - matches: &[Match], - ) { - let result = Ok(matches.to_owned()); - api.expect_search_artist() - .with(predicate::eq(artist.clone())) - .times(1) - .in_sequence(seq) - .return_once(|_| result); - } - - #[test] - fn fetch_artist() { - let mut mb_api = MockIMusicBrainz::new(); - - let (artist, matches) = artist_expectations(); - let mut seq = Sequence::new(); - search_artist_expectation(&mut mb_api, &mut seq, &artist, &matches); - - let music_hoard = music_hoard(COLLECTION.to_owned()); - let (events_tx, events_rx) = event_channel(); - let inner = AppInner::new(music_hoard, mb_api, events_tx); - - let (fetch_tx, fetch_rx) = mpsc::channel(); - // Use the fourth artist for this test as they have no MBID. - let handle = AppMachine::spawn_fetch_thread(&inner, &COLLECTION[3], fetch_tx); - handle.join().unwrap(); - - assert_eq!(events_rx.try_recv().unwrap(), Event::FetchResultReady); - let result = fetch_rx.try_recv().unwrap(); - let expected = Ok(AppMatchesInfo::Artist(AppArtistMatches { - matching: artist.clone(), - list: matches.iter().cloned().map(Into::into).collect(), - })); - assert_eq!(result, expected); - } - - #[test] - fn fetch_artist_fetch_disconnect() { - let mut mb_api = MockIMusicBrainz::new(); - - let (artist, matches) = artist_expectations(); - let mut seq = Sequence::new(); - search_artist_expectation(&mut mb_api, &mut seq, &artist, &matches); - - let music_hoard = music_hoard(COLLECTION.to_owned()); - let (events_tx, events_rx) = event_channel(); - let inner = AppInner::new(music_hoard, mb_api, events_tx); - - let (fetch_tx, _) = mpsc::channel(); - // Use the fourth artist for this test as they have no MBID. - let handle = AppMachine::spawn_fetch_thread(&inner, &COLLECTION[3], fetch_tx); - handle.join().unwrap(); - - assert!(events_rx.try_recv().is_err()); - } - - #[test] - fn fetch_albums_event_disconnect() { - let mut mb_api = MockIMusicBrainz::new(); - - let arid: Mbid = "11111111-1111-1111-1111-111111111111".try_into().unwrap(); - - let (album_1, matches_1) = album_expectations_1(); - - let mut seq = Sequence::new(); - search_release_group_expectation(&mut mb_api, &mut seq, &arid, &album_1, &matches_1); - - let music_hoard = music_hoard(COLLECTION.to_owned()); - let (events_tx, _) = event_channel(); - let inner = AppInner::new(music_hoard, mb_api, events_tx); - - let (fetch_tx, fetch_rx) = mpsc::channel(); - // Use the second artist for this test. - let handle = AppMachine::spawn_fetch_thread(&inner, &COLLECTION[1], fetch_tx); - handle.join().unwrap(); - - let result = fetch_rx.try_recv().unwrap(); - let expected = Ok(AppMatchesInfo::Album(AppAlbumMatches { - matching: album_1.clone(), - list: matches_1.iter().cloned().map(Into::into).collect(), - })); - assert_eq!(result, expected); - - assert_eq!(fetch_rx.try_recv().unwrap_err(), TryRecvError::Disconnected); - } } diff --git a/src/tui/app/machine/fetch.rs b/src/tui/app/machine/fetch.rs index 5d73fdb..b0e3c4e 100644 --- a/src/tui/app/machine/fetch.rs +++ b/src/tui/app/machine/fetch.rs @@ -1,11 +1,24 @@ -use std::sync::mpsc::{self, TryRecvError}; +use std::{ + sync::{ + mpsc::{self, TryRecvError}, + Arc, Mutex, + }, + thread, time, +}; + +use musichoard::collection::{ + album::AlbumMeta, + artist::{Artist, ArtistMeta}, + musicbrainz::{IMusicBrainzRef, Mbid}, +}; use crate::tui::{ app::{ machine::{App, AppInner, AppMachine}, AppMatchesInfo, AppPublic, AppState, IAppEventFetch, IAppInteractFetch, }, - lib::interface::musicbrainz::Error as MbError, + event::{Event, EventSender}, + lib::interface::musicbrainz::{self, Error as MbError, IMusicBrainz}, }; use super::matches::AppMatches; @@ -30,7 +43,16 @@ impl AppMachine { AppMachine { inner, state } } - pub fn app_fetch_new(inner: AppInner, fetch_rx: FetchReceiver) -> App { + pub fn app_fetch_new(inner: AppInner) -> App { + let coll = inner.music_hoard.get_collection(); + let artist = match inner.selection.state_artist(coll) { + Some(artist_state) => &coll[artist_state.index], + None => return AppMachine::error(inner, "cannot fetch: no artist selected").into(), + }; + + let (fetch_tx, fetch_rx) = mpsc::channel::(); + Self::spawn_fetch_thread(&inner, artist, fetch_tx); + let fetch = AppFetch::new(fetch_rx); AppMachine::app_fetch(inner, fetch, true) } @@ -62,6 +84,80 @@ impl AppMachine { }, } } + + fn spawn_fetch_thread( + inner: &AppInner, + artist: &Artist, + tx: FetchSender, + ) -> thread::JoinHandle<()> { + match artist.meta.musicbrainz { + Some(ref arid) => { + let musicbrainz = Arc::clone(&inner.musicbrainz); + let events = inner.events.clone(); + let arid = arid.mbid().clone(); + let albums = artist.albums.iter().map(|a| &a.meta).cloned().collect(); + thread::spawn(|| Self::fetch_albums(musicbrainz, tx, events, arid, albums)) + } + None => { + let musicbrainz = Arc::clone(&inner.musicbrainz); + let events = inner.events.clone(); + let artist = artist.meta.clone(); + thread::spawn(|| Self::fetch_artist(musicbrainz, tx, events, artist)) + } + } + } + + fn fetch_artist( + musicbrainz: Arc>, + fetch_tx: FetchSender, + events: EventSender, + artist: ArtistMeta, + ) { + let result = musicbrainz.lock().unwrap().search_artist(&artist); + let result = result.map(|list| AppMatchesInfo::artist(artist, list)); + Self::send_fetch_result(&fetch_tx, &events, result).ok(); + } + + fn fetch_albums( + musicbrainz: Arc>, + fetch_tx: FetchSender, + events: EventSender, + arid: Mbid, + albums: Vec, + ) { + let mut musicbrainz = musicbrainz.lock().unwrap(); + let mut album_iter = albums.into_iter().peekable(); + while let Some(album) = album_iter.next() { + if album.musicbrainz.is_some() { + continue; + } + + let result = musicbrainz.search_release_group(&arid, &album); + let result = result.map(|list| AppMatchesInfo::album(album, list)); + if Self::send_fetch_result(&fetch_tx, &events, result).is_err() { + return; + }; + + if album_iter.peek().is_some() { + thread::sleep(time::Duration::from_secs(1)); + } + } + } + + fn send_fetch_result( + fetch_tx: &FetchSender, + events: &EventSender, + result: Result, + ) -> Result<(), ()> { + // If receiver disconnects just drop the rest. + fetch_tx.send(result).map_err(|_| ())?; + + // If this send fails the event listener is dead. Don't panic as this function runs in a + // detached thread so this might be happening during normal shut down. + events.send(Event::FetchResultReady).map_err(|_| ())?; + + Ok(()) + } } impl From> for App { @@ -97,16 +193,207 @@ impl IAppEventFetch for AppMachine { #[cfg(test)] mod tests { + use mockall::{predicate, Sequence}; use musichoard::collection::artist::ArtistMeta; use crate::tui::{ - app::machine::tests::{inner, music_hoard}, - lib::interface::musicbrainz::{self, Match}, + app::{ + machine::tests::{inner, music_hoard}, + AppAlbumMatches, AppArtistMatches, + }, + event::EventReceiver, + lib::interface::musicbrainz::{self, Match, MockIMusicBrainz}, testmod::COLLECTION, + EventChannel, }; use super::*; + fn event_channel() -> (EventSender, EventReceiver) { + let event_channel = EventChannel::new(); + let events_tx = event_channel.sender(); + let events_rx = event_channel.receiver(); + (events_tx, events_rx) + } + + fn album_expectations_1() -> (AlbumMeta, Vec>) { + let album_1 = COLLECTION[1].albums[0].meta.clone(); + let album_4 = COLLECTION[1].albums[3].meta.clone(); + + let album_match_1_1 = Match::new(100, album_1.clone()); + let album_match_1_2 = Match::new(50, album_4.clone()); + let matches_1 = vec![album_match_1_1.clone(), album_match_1_2.clone()]; + + (album_1, matches_1) + } + + fn album_expectations_4() -> (AlbumMeta, Vec>) { + let album_1 = COLLECTION[1].albums[0].meta.clone(); + let album_4 = COLLECTION[1].albums[3].meta.clone(); + + let album_match_4_1 = Match::new(100, album_4.clone()); + let album_match_4_2 = Match::new(30, album_1.clone()); + let matches_4 = vec![album_match_4_1.clone(), album_match_4_2.clone()]; + + (album_4, matches_4) + } + + fn search_release_group_expectation( + api: &mut MockIMusicBrainz, + seq: &mut Sequence, + arid: &Mbid, + album: &AlbumMeta, + matches: &[Match], + ) { + let result = Ok(matches.to_owned()); + api.expect_search_release_group() + .with(predicate::eq(arid.clone()), predicate::eq(album.clone())) + .times(1) + .in_sequence(seq) + .return_once(|_, _| result); + } + + #[test] + fn fetch_albums() { + let mut mb_api = MockIMusicBrainz::new(); + + let arid: Mbid = "11111111-1111-1111-1111-111111111111".try_into().unwrap(); + + let (album_1, matches_1) = album_expectations_1(); + let (album_4, matches_4) = album_expectations_4(); + + // Other albums have an MBID and so they will be skipped. + let mut seq = Sequence::new(); + + search_release_group_expectation(&mut mb_api, &mut seq, &arid, &album_1, &matches_1); + search_release_group_expectation(&mut mb_api, &mut seq, &arid, &album_4, &matches_4); + + let music_hoard = music_hoard(COLLECTION.to_owned()); + let (events_tx, events_rx) = event_channel(); + let inner = AppInner::new(music_hoard, mb_api, events_tx); + + let (fetch_tx, fetch_rx) = mpsc::channel(); + // Use the second artist for this test. + let handle = AppMachine::spawn_fetch_thread(&inner, &COLLECTION[1], fetch_tx); + handle.join().unwrap(); + + assert_eq!(events_rx.try_recv().unwrap(), Event::FetchResultReady); + let result = fetch_rx.try_recv().unwrap(); + let expected = Ok(AppMatchesInfo::Album(AppAlbumMatches { + matching: album_1.clone(), + list: matches_1.iter().cloned().map(Into::into).collect(), + })); + assert_eq!(result, expected); + + assert_eq!(events_rx.try_recv().unwrap(), Event::FetchResultReady); + let result = fetch_rx.try_recv().unwrap(); + let expected = Ok(AppMatchesInfo::Album(AppAlbumMatches { + matching: album_4.clone(), + list: matches_4.iter().cloned().map(Into::into).collect(), + })); + assert_eq!(result, expected); + } + + fn artist_expectations() -> (ArtistMeta, Vec>) { + let artist = COLLECTION[3].meta.clone(); + + let artist_match_1 = Match::new(100, artist.clone()); + let artist_match_2 = Match::new(50, artist.clone()); + let matches = vec![artist_match_1.clone(), artist_match_2.clone()]; + + (artist, matches) + } + + fn search_artist_expectation( + api: &mut MockIMusicBrainz, + seq: &mut Sequence, + artist: &ArtistMeta, + matches: &[Match], + ) { + let result = Ok(matches.to_owned()); + api.expect_search_artist() + .with(predicate::eq(artist.clone())) + .times(1) + .in_sequence(seq) + .return_once(|_| result); + } + + #[test] + fn fetch_artist() { + let mut mb_api = MockIMusicBrainz::new(); + + let (artist, matches) = artist_expectations(); + let mut seq = Sequence::new(); + search_artist_expectation(&mut mb_api, &mut seq, &artist, &matches); + + let music_hoard = music_hoard(COLLECTION.to_owned()); + let (events_tx, events_rx) = event_channel(); + let inner = AppInner::new(music_hoard, mb_api, events_tx); + + let (fetch_tx, fetch_rx) = mpsc::channel(); + // Use the fourth artist for this test as they have no MBID. + let handle = AppMachine::spawn_fetch_thread(&inner, &COLLECTION[3], fetch_tx); + handle.join().unwrap(); + + assert_eq!(events_rx.try_recv().unwrap(), Event::FetchResultReady); + let result = fetch_rx.try_recv().unwrap(); + let expected = Ok(AppMatchesInfo::Artist(AppArtistMatches { + matching: artist.clone(), + list: matches.iter().cloned().map(Into::into).collect(), + })); + assert_eq!(result, expected); + } + + #[test] + fn fetch_artist_fetch_disconnect() { + let mut mb_api = MockIMusicBrainz::new(); + + let (artist, matches) = artist_expectations(); + let mut seq = Sequence::new(); + search_artist_expectation(&mut mb_api, &mut seq, &artist, &matches); + + let music_hoard = music_hoard(COLLECTION.to_owned()); + let (events_tx, events_rx) = event_channel(); + let inner = AppInner::new(music_hoard, mb_api, events_tx); + + let (fetch_tx, _) = mpsc::channel(); + // Use the fourth artist for this test as they have no MBID. + let handle = AppMachine::spawn_fetch_thread(&inner, &COLLECTION[3], fetch_tx); + handle.join().unwrap(); + + assert!(events_rx.try_recv().is_err()); + } + + #[test] + fn fetch_albums_event_disconnect() { + let mut mb_api = MockIMusicBrainz::new(); + + let arid: Mbid = "11111111-1111-1111-1111-111111111111".try_into().unwrap(); + + let (album_1, matches_1) = album_expectations_1(); + + let mut seq = Sequence::new(); + search_release_group_expectation(&mut mb_api, &mut seq, &arid, &album_1, &matches_1); + + let music_hoard = music_hoard(COLLECTION.to_owned()); + let (events_tx, _) = event_channel(); + let inner = AppInner::new(music_hoard, mb_api, events_tx); + + let (fetch_tx, fetch_rx) = mpsc::channel(); + // Use the second artist for this test. + let handle = AppMachine::spawn_fetch_thread(&inner, &COLLECTION[1], fetch_tx); + handle.join().unwrap(); + + let result = fetch_rx.try_recv().unwrap(); + let expected = Ok(AppMatchesInfo::Album(AppAlbumMatches { + matching: album_1.clone(), + list: matches_1.iter().cloned().map(Into::into).collect(), + })); + assert_eq!(result, expected); + + assert_eq!(fetch_rx.try_recv().unwrap_err(), TryRecvError::Disconnected); + } + #[test] fn recv_ok_fetch_ok() { let (tx, rx) = mpsc::channel::(); @@ -115,7 +402,9 @@ mod tests { let fetch_result = Ok(AppMatchesInfo::artist::>(artist, vec![])); tx.send(fetch_result).unwrap(); - let app = AppMachine::app_fetch_new(inner(music_hoard(COLLECTION.clone())), rx); + let inner = inner(music_hoard(COLLECTION.clone())); + let fetch = AppFetch::new(rx); + let app = AppMachine::app_fetch(inner, fetch, true); assert!(matches!(app, AppState::Matches(_))); } @@ -126,7 +415,9 @@ mod tests { let fetch_result = Err(musicbrainz::Error::RateLimit); tx.send(fetch_result).unwrap(); - let app = AppMachine::app_fetch_new(inner(music_hoard(COLLECTION.clone())), rx); + let inner = inner(music_hoard(COLLECTION.clone())); + let fetch = AppFetch::new(rx); + let app = AppMachine::app_fetch(inner, fetch, true); assert!(matches!(app, AppState::Error(_))); } @@ -134,7 +425,9 @@ mod tests { fn recv_err_empty() { let (_tx, rx) = mpsc::channel::(); - let app = AppMachine::app_fetch_new(inner(music_hoard(COLLECTION.clone())), rx); + let inner = inner(music_hoard(COLLECTION.clone())); + let fetch = AppFetch::new(rx); + let app = AppMachine::app_fetch(inner, fetch, true); assert!(matches!(app, AppState::Fetch(_))); } @@ -142,7 +435,9 @@ mod tests { fn recv_err_disconnected_first() { let (_, rx) = mpsc::channel::(); - let app = AppMachine::app_fetch_new(inner(music_hoard(COLLECTION.clone())), rx); + let inner = inner(music_hoard(COLLECTION.clone())); + let fetch = AppFetch::new(rx); + let app = AppMachine::app_fetch(inner, fetch, true); assert!(matches!(app, AppState::Matches(_))); } @@ -159,7 +454,9 @@ mod tests { fn empty_first_then_ready() { let (tx, rx) = mpsc::channel::(); - let app = AppMachine::app_fetch_new(inner(music_hoard(COLLECTION.clone())), rx); + let inner = inner(music_hoard(COLLECTION.clone())); + let fetch = AppFetch::new(rx); + let app = AppMachine::app_fetch(inner, fetch, true); assert!(matches!(app, AppState::Fetch(_))); let artist = COLLECTION[3].meta.clone();