Daemonize the musicbrainz thread #217

Merged
wojtek merged 15 commits from 188---add-option-for-manual-input-during-fetch into main 2024-09-21 23:03:47 +02:00
14 changed files with 1709 additions and 1600 deletions
Showing only changes of commit 593797c8ac - Show all commits

View File

@ -77,105 +77,112 @@ impl IAppInteractBrowse for AppMachine<BrowseState> {
} }
} }
// #[cfg(test)] #[cfg(test)]
// mod tests { mod tests {
// use crate::tui::{ use crate::tui::{
// app::{ app::{
// machine::tests::{inner, inner_with_mb, music_hoard}, machine::tests::{inner, inner_with_mb, music_hoard},
// Category, IApp, IAppAccess, Category, IApp, IAppAccess,
// }, },
// lib::interface::musicbrainz::api::MockIMusicBrainz, lib::interface::musicbrainz::daemon::MockIMbJobSender,
// testmod::COLLECTION, testmod::COLLECTION,
// }; };
// use super::*; use super::*;
// #[test] #[test]
// fn quit() { fn quit() {
// let music_hoard = music_hoard(vec![]); let music_hoard = music_hoard(vec![]);
// let browse = AppMachine::browse_state(inner(music_hoard)); let browse = AppMachine::browse_state(inner(music_hoard));
// let app = browse.quit(); let app = browse.quit();
// assert!(!app.is_running()); assert!(!app.is_running());
// app.unwrap_browse(); app.unwrap_browse();
// } }
// #[test] #[test]
// fn increment_decrement() { fn increment_decrement() {
// let mut browse = AppMachine::browse_state(inner(music_hoard(COLLECTION.to_owned()))); let mut browse = AppMachine::browse_state(inner(music_hoard(COLLECTION.to_owned())));
// let sel = &browse.inner.selection; let sel = &browse.inner.selection;
// assert_eq!(sel.category(), Category::Artist); assert_eq!(sel.category(), Category::Artist);
// assert_eq!(sel.selected(), Some(0)); assert_eq!(sel.selected(), Some(0));
// browse = browse.increment_selection(Delta::Line).unwrap_browse(); browse = browse.increment_selection(Delta::Line).unwrap_browse();
// let sel = &browse.inner.selection; let sel = &browse.inner.selection;
// assert_eq!(sel.category(), Category::Artist); assert_eq!(sel.category(), Category::Artist);
// assert_eq!(sel.selected(), Some(1)); assert_eq!(sel.selected(), Some(1));
// browse = browse.increment_category().unwrap_browse(); browse = browse.increment_category().unwrap_browse();
// let sel = &browse.inner.selection; let sel = &browse.inner.selection;
// assert_eq!(sel.category(), Category::Album); assert_eq!(sel.category(), Category::Album);
// assert_eq!(sel.selected(), Some(0)); assert_eq!(sel.selected(), Some(0));
// browse = browse.increment_selection(Delta::Line).unwrap_browse(); browse = browse.increment_selection(Delta::Line).unwrap_browse();
// let sel = &browse.inner.selection; let sel = &browse.inner.selection;
// assert_eq!(sel.category(), Category::Album); assert_eq!(sel.category(), Category::Album);
// assert_eq!(sel.selected(), Some(1)); assert_eq!(sel.selected(), Some(1));
// browse = browse.decrement_selection(Delta::Line).unwrap_browse(); browse = browse.decrement_selection(Delta::Line).unwrap_browse();
// let sel = &browse.inner.selection; let sel = &browse.inner.selection;
// assert_eq!(sel.category(), Category::Album); assert_eq!(sel.category(), Category::Album);
// assert_eq!(sel.selected(), Some(0)); assert_eq!(sel.selected(), Some(0));
// browse = browse.decrement_category().unwrap_browse(); browse = browse.decrement_category().unwrap_browse();
// let sel = &browse.inner.selection; let sel = &browse.inner.selection;
// assert_eq!(sel.category(), Category::Artist); assert_eq!(sel.category(), Category::Artist);
// assert_eq!(sel.selected(), Some(1)); assert_eq!(sel.selected(), Some(1));
// browse = browse.decrement_selection(Delta::Line).unwrap_browse(); browse = browse.decrement_selection(Delta::Line).unwrap_browse();
// let sel = &browse.inner.selection; let sel = &browse.inner.selection;
// assert_eq!(sel.category(), Category::Artist); assert_eq!(sel.category(), Category::Artist);
// assert_eq!(sel.selected(), Some(0)); assert_eq!(sel.selected(), Some(0));
// } }
// #[test] #[test]
// fn show_info_overlay() { fn show_info_overlay() {
// let browse = AppMachine::browse_state(inner(music_hoard(vec![]))); let browse = AppMachine::browse_state(inner(music_hoard(vec![])));
// let app = browse.show_info_overlay(); let app = browse.show_info_overlay();
// app.unwrap_info(); app.unwrap_info();
// } }
// #[test] #[test]
// fn show_reload_menu() { fn show_reload_menu() {
// let browse = AppMachine::browse_state(inner(music_hoard(vec![]))); let browse = AppMachine::browse_state(inner(music_hoard(vec![])));
// let app = browse.show_reload_menu(); let app = browse.show_reload_menu();
// app.unwrap_reload(); app.unwrap_reload();
// } }
// #[test] #[test]
// fn begin_search() { fn begin_search() {
// let browse = AppMachine::browse_state(inner(music_hoard(vec![]))); let browse = AppMachine::browse_state(inner(music_hoard(vec![])));
// let app = browse.begin_search(); let app = browse.begin_search();
// app.unwrap_search(); app.unwrap_search();
// } }
// #[test] #[test]
// fn fetch_musicbrainz() { fn fetch_musicbrainz() {
// let mb_api = MockIMusicBrainz::new(); let mut mb_job_sender = MockIMbJobSender::new();
// let browse = mb_job_sender
// AppMachine::browse_state(inner_with_mb(music_hoard(COLLECTION.to_owned()), mb_api)); .expect_submit_background_job()
.times(1)
.returning(|_, _| Ok(()));
// // Use the second artist for this test. let browse = AppMachine::browse_state(inner_with_mb(
// let browse = browse.increment_selection(Delta::Line).unwrap_browse(); music_hoard(COLLECTION.to_owned()),
// let mut app = browse.fetch_musicbrainz(); mb_job_sender,
));
// let public = app.get(); // Use the second artist for this test.
let browse = browse.increment_selection(Delta::Line).unwrap_browse();
let mut app = browse.fetch_musicbrainz();
// // Because of fetch's threaded behaviour, this unit test cannot expect one or the other. let public = app.get();
// assert!(
// matches!(public.state, AppState::Match(_)) // Because of fetch's threaded behaviour, this unit test cannot expect one or the other.
// || matches!(public.state, AppState::Fetch(_)) assert!(
// ); matches!(public.state, AppState::Match(_))
// } || matches!(public.state, AppState::Fetch(_))
// } );
}
}

View File

@ -41,16 +41,16 @@ impl IAppInteractError for AppMachine<ErrorState> {
} }
} }
// #[cfg(test)] #[cfg(test)]
// mod tests { mod tests {
// use crate::tui::app::machine::tests::{inner, music_hoard}; use crate::tui::app::machine::tests::{inner, music_hoard};
// use super::*; use super::*;
// #[test] #[test]
// fn dismiss_error() { fn dismiss_error() {
// let error = AppMachine::error_state(inner(music_hoard(vec![])), "get rekt"); let error = AppMachine::error_state(inner(music_hoard(vec![])), "get rekt");
// let app = error.dismiss_error(); let app = error.dismiss_error();
// app.unwrap_browse(); app.unwrap_browse();
// } }
// } }

View File

@ -87,6 +87,7 @@ impl AppMachine<FetchState> {
let arid = arid.mbid(); let arid = arid.mbid();
let albums = artist.albums.iter(); let albums = artist.albums.iter();
albums albums
.filter(|album| album.meta.musicbrainz.is_none())
.map(|album| MbParams::search_release_group(arid.clone(), album.meta.clone())) .map(|album| MbParams::search_release_group(arid.clone(), album.meta.clone()))
.collect() .collect()
} }
@ -124,299 +125,205 @@ impl IAppEventFetch for AppMachine<FetchState> {
} }
} }
// #[cfg(test)] #[cfg(test)]
// mod tests { mod tests {
// use mockall::{predicate, Sequence}; use mockall::predicate;
// use musichoard::collection::artist::ArtistMeta; use musichoard::collection::{album::AlbumMeta, artist::ArtistMeta, musicbrainz::Mbid};
// use crate::tui::{ use crate::tui::{
// app::{ app::{
// machine::tests::{inner, music_hoard}, machine::tests::{inner, music_hoard},
// AlbumMatches, ArtistMatches, IApp, Delta, IApp, IAppAccess, IAppInteractBrowse, MatchOption, MatchStateInfo,
// }, },
// event::EventReceiver, lib::interface::musicbrainz::{self, api::Match, daemon::MockIMbJobSender},
// lib::interface::musicbrainz::{ testmod::COLLECTION,
// self, };
// api::{Match, MockIMusicBrainz},
// },
// testmod::COLLECTION,
// EventChannel,
// };
// use super::*; use super::*;
// #[test] #[test]
// fn fetch_no_artist() { fn fetch_no_artist() {
// let app = AppMachine::app_fetch_new(inner(music_hoard(vec![]))); let app = AppMachine::app_fetch_new(inner(music_hoard(vec![])));
// assert!(matches!(app.state(), AppState::Error(_))); assert!(matches!(app.state(), AppState::Error(_)));
// } }
// fn event_channel() -> (EventSender, EventReceiver) { fn search_release_group_expectation(
// let event_channel = EventChannel::new(); job_sender: &mut MockIMbJobSender,
// let events_tx = event_channel.sender(); arid: &Mbid,
// let events_rx = event_channel.receiver(); albums: &[AlbumMeta],
// (events_tx, events_rx) ) {
// } let mut requests = VecDeque::new();
for album in albums.iter() {
requests.push_back(MbParams::search_release_group(arid.clone(), album.clone()));
}
job_sender
.expect_submit_background_job()
.with(predicate::always(), predicate::eq(requests))
.times(1)
.return_once(|_, _| Ok(()));
}
// fn album_expectations_1() -> (AlbumMeta, Vec<Match<AlbumMeta>>) { #[test]
// let album_1 = COLLECTION[1].albums[0].meta.clone(); fn fetch_albums() {
// let album_4 = COLLECTION[1].albums[3].meta.clone(); let mut mb_job_sender = MockIMbJobSender::new();
// let album_match_1_1 = Match::new(100, album_1.clone()); let arid: Mbid = "11111111-1111-1111-1111-111111111111".try_into().unwrap();
// 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) let album_1_meta = COLLECTION[1].albums[0].meta.clone();
// } let album_4_meta = COLLECTION[1].albums[3].meta.clone();
// fn album_expectations_4() -> (AlbumMeta, Vec<Match<AlbumMeta>>) { // Other albums have an MBID and so they will be skipped.
// let album_1 = COLLECTION[1].albums[0].meta.clone(); search_release_group_expectation(&mut mb_job_sender, &arid, &[album_1_meta, album_4_meta]);
// let album_4 = COLLECTION[1].albums[3].meta.clone();
// let album_match_4_1 = Match::new(100, album_4.clone()); let music_hoard = music_hoard(COLLECTION.to_owned());
// let album_match_4_2 = Match::new(30, album_1.clone()); let inner = AppInner::new(music_hoard, mb_job_sender);
// let matches_4 = vec![album_match_4_1.clone(), album_match_4_2.clone()];
// (album_4, matches_4) // Use second artist to match the expectation.
// } let browse = AppMachine::browse_state(inner);
let app = browse.increment_selection(Delta::Line);
// fn search_release_group_expectation( let app = app.unwrap_browse().fetch_musicbrainz();
// api: &mut MockIMusicBrainz, assert!(matches!(app, AppState::Match(_)));
// seq: &mut Sequence, }
// arid: &Mbid,
// album: &AlbumMeta,
// matches: &[Match<AlbumMeta>],
// ) {
// 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 search_artist_expectation(job_sender: &mut MockIMbJobSender, artist: &ArtistMeta) {
// fn fetch_albums() { let requests = VecDeque::from([MbParams::search_artist(artist.clone())]);
// let mut mb_api = MockIMusicBrainz::new(); job_sender
.expect_submit_background_job()
.with(predicate::always(), predicate::eq(requests))
.times(1)
.return_once(|_, _| Ok(()));
}
// let arid: Mbid = "11111111-1111-1111-1111-111111111111".try_into().unwrap(); #[test]
fn fetch_artist() {
let mut mb_job_sender = MockIMbJobSender::new();
// let (album_1, matches_1) = album_expectations_1(); let artist = COLLECTION[3].meta.clone();
// let (album_4, matches_4) = album_expectations_4(); search_artist_expectation(&mut mb_job_sender, &artist);
// // Other albums have an MBID and so they will be skipped. let music_hoard = music_hoard(COLLECTION.to_owned());
// let mut seq = Sequence::new(); let inner = AppInner::new(music_hoard, mb_job_sender);
// search_release_group_expectation(&mut mb_api, &mut seq, &arid, &album_1, &matches_1); // Use fourth artist to match the expectation.
// search_release_group_expectation(&mut mb_api, &mut seq, &arid, &album_4, &matches_4); let browse = AppMachine::browse_state(inner);
let browse = browse.increment_selection(Delta::Line).unwrap_browse();
let browse = browse.increment_selection(Delta::Line).unwrap_browse();
let app = browse.increment_selection(Delta::Line);
// let music_hoard = music_hoard(COLLECTION.to_owned()); let app = app.unwrap_browse().fetch_musicbrainz();
// let (events_tx, events_rx) = event_channel(); assert!(matches!(app, AppState::Match(_)));
// let inner = AppInner::new(music_hoard, mb_api, events_tx); }
// let (fetch_tx, fetch_rx) = mpsc::channel(); #[test]
// // Use the second artist for this test. fn fetch_artist_job_sender_err() {
// let handle = AppMachine::spawn_fetch_thread(&inner, &COLLECTION[1], fetch_tx); let mut mb_job_sender = MockIMbJobSender::new();
// handle.join().unwrap();
// assert_eq!(events_rx.try_recv().unwrap(), Event::FetchResultReady); mb_job_sender
// let result = fetch_rx.try_recv().unwrap(); .expect_submit_background_job()
// let expected = Ok(MatchStateInfo::Album(AlbumMatches { .return_once(|_, _| Err(DaemonError::JobChannelDisconnected));
// 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 music_hoard = music_hoard(COLLECTION.to_owned());
// let result = fetch_rx.try_recv().unwrap(); let inner = AppInner::new(music_hoard, mb_job_sender);
// let expected = Ok(MatchStateInfo::Album(AlbumMatches { let browse = AppMachine::browse_state(inner);
// matching: album_4.clone(),
// list: matches_4.iter().cloned().map(Into::into).collect(),
// }));
// assert_eq!(result, expected);
// }
// fn artist_expectations() -> (ArtistMeta, Vec<Match<ArtistMeta>>) { let app = browse.fetch_musicbrainz();
// let artist = COLLECTION[3].meta.clone(); assert!(matches!(app, AppState::Error(_)));
}
// let artist_match_1 = Match::new(100, artist.clone()); #[test]
// let artist_match_2 = Match::new(50, artist.clone()); fn recv_ok_fetch_ok() {
// let matches = vec![artist_match_1.clone(), artist_match_2.clone()]; let (tx, rx) = mpsc::channel::<MbApiResult>();
// (artist, matches) let artist = COLLECTION[3].meta.clone();
// } let artist_match = Match::new(80, COLLECTION[2].meta.clone());
let artist_match_info = MatchStateInfo::artist(artist.clone(), vec![artist_match.clone()]);
let fetch_result = Ok(artist_match_info);
tx.send(fetch_result).unwrap();
// fn search_artist_expectation( let inner = inner(music_hoard(COLLECTION.clone()));
// api: &mut MockIMusicBrainz, let fetch = FetchState::new(rx);
// seq: &mut Sequence, let mut app = AppMachine::app_fetch(inner, fetch, true);
// artist: &ArtistMeta, assert!(matches!(app, AppState::Match(_)));
// matches: &[Match<ArtistMeta>],
// ) {
// let result = Ok(matches.to_owned());
// api.expect_search_artist()
// .with(predicate::eq(artist.clone()))
// .times(1)
// .in_sequence(seq)
// .return_once(|_| result);
// }
// #[test] let public = app.get();
// fn fetch_artist() { let match_state = public.state.unwrap_match();
// let mut mb_api = MockIMusicBrainz::new(); let match_options = vec![
artist_match.into(),
MatchOption::CannotHaveMbid,
MatchOption::ManualInputMbid,
];
let expected = MatchStateInfo::artist(artist, match_options);
assert_eq!(match_state.info, Some(expected).as_ref());
}
// let (artist, matches) = artist_expectations(); #[test]
// let mut seq = Sequence::new(); fn recv_ok_fetch_err() {
// search_artist_expectation(&mut mb_api, &mut seq, &artist, &matches); let (tx, rx) = mpsc::channel::<MbApiResult>();
// let music_hoard = music_hoard(COLLECTION.to_owned()); let fetch_result = Err(musicbrainz::api::Error::RateLimit);
// let (events_tx, events_rx) = event_channel(); tx.send(fetch_result).unwrap();
// let inner = AppInner::new(music_hoard, mb_api, events_tx);
// let (fetch_tx, fetch_rx) = mpsc::channel(); let inner = inner(music_hoard(COLLECTION.clone()));
// // Use the fourth artist for this test as they have no MBID. let fetch = FetchState::new(rx);
// let handle = AppMachine::spawn_fetch_thread(&inner, &COLLECTION[3], fetch_tx); let app = AppMachine::app_fetch(inner, fetch, true);
// handle.join().unwrap(); assert!(matches!(app, AppState::Error(_)));
}
// assert_eq!(events_rx.try_recv().unwrap(), Event::FetchResultReady); #[test]
// let result = fetch_rx.try_recv().unwrap(); fn recv_err_empty() {
// let expected = Ok(MatchStateInfo::Artist(ArtistMatches { let (_tx, rx) = mpsc::channel::<MbApiResult>();
// matching: artist.clone(),
// list: matches.iter().cloned().map(Into::into).collect(),
// }));
// assert_eq!(result, expected);
// }
// #[test] let inner = inner(music_hoard(COLLECTION.clone()));
// fn fetch_artist_fetch_disconnect() { let fetch = FetchState::new(rx);
// let mut mb_api = MockIMusicBrainz::new(); let app = AppMachine::app_fetch(inner, fetch, true);
assert!(matches!(app, AppState::Fetch(_)));
}
// let (artist, matches) = artist_expectations(); #[test]
// let mut seq = Sequence::new(); fn recv_err_disconnected_first() {
// search_artist_expectation(&mut mb_api, &mut seq, &artist, &matches); let (_, rx) = mpsc::channel::<MbApiResult>();
// let music_hoard = music_hoard(COLLECTION.to_owned()); let inner = inner(music_hoard(COLLECTION.clone()));
// let (events_tx, events_rx) = event_channel(); let fetch = FetchState::new(rx);
// let inner = AppInner::new(music_hoard, mb_api, events_tx); let app = AppMachine::app_fetch(inner, fetch, true);
assert!(matches!(app, AppState::Match(_)));
}
// let (fetch_tx, _) = mpsc::channel(); #[test]
// // Use the fourth artist for this test as they have no MBID. fn recv_err_disconnected_next() {
// let handle = AppMachine::spawn_fetch_thread(&inner, &COLLECTION[3], fetch_tx); let (_, rx) = mpsc::channel::<MbApiResult>();
// handle.join().unwrap();
// assert!(events_rx.try_recv().is_err()); let fetch = FetchState::new(rx);
// } let app = AppMachine::app_fetch_next(inner(music_hoard(COLLECTION.clone())), fetch);
assert!(matches!(app, AppState::Browse(_)));
}
// #[test] #[test]
// fn fetch_albums_event_disconnect() { fn empty_first_then_ready() {
// let mut mb_api = MockIMusicBrainz::new(); let (tx, rx) = mpsc::channel::<MbApiResult>();
// let arid: Mbid = "11111111-1111-1111-1111-111111111111".try_into().unwrap(); let inner = inner(music_hoard(COLLECTION.clone()));
let fetch = FetchState::new(rx);
let app = AppMachine::app_fetch(inner, fetch, true);
assert!(matches!(app, AppState::Fetch(_)));
// let (album_1, matches_1) = album_expectations_1(); let artist = COLLECTION[3].meta.clone();
let fetch_result = Ok(MatchStateInfo::artist::<Match<ArtistMeta>>(artist, vec![]));
tx.send(fetch_result).unwrap();
// let mut seq = Sequence::new(); let app = app.unwrap_fetch().fetch_result_ready();
// search_release_group_expectation(&mut mb_api, &mut seq, &arid, &album_1, &matches_1); assert!(matches!(app, AppState::Match(_)));
}
// let music_hoard = music_hoard(COLLECTION.to_owned()); #[test]
// let (events_tx, _) = event_channel(); fn abort() {
// let inner = AppInner::new(music_hoard, mb_api, events_tx); let (_, rx) = mpsc::channel::<MbApiResult>();
// let (fetch_tx, fetch_rx) = mpsc::channel(); let fetch = FetchState::new(rx);
// // Use the second artist for this test. let app = AppMachine::fetch_state(inner(music_hoard(COLLECTION.clone())), fetch);
// let handle = AppMachine::spawn_fetch_thread(&inner, &COLLECTION[1], fetch_tx);
// handle.join().unwrap();
// let result = fetch_rx.try_recv().unwrap(); let app = app.abort();
// let expected = Ok(MatchStateInfo::Album(AlbumMatches { assert!(matches!(app, AppState::Browse(_)));
// 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::<FetchResult>();
// let artist = COLLECTION[3].meta.clone();
// let fetch_result = Ok(MatchStateInfo::artist::<Match<ArtistMeta>>(artist, vec![]));
// tx.send(fetch_result).unwrap();
// let inner = inner(music_hoard(COLLECTION.clone()));
// let fetch = FetchState::new(rx);
// let app = AppMachine::app_fetch(inner, fetch, true);
// assert!(matches!(app, AppState::Match(_)));
// }
// #[test]
// fn recv_ok_fetch_err() {
// let (tx, rx) = mpsc::channel::<FetchResult>();
// let fetch_result = Err(musicbrainz::api::Error::RateLimit);
// tx.send(fetch_result).unwrap();
// let inner = inner(music_hoard(COLLECTION.clone()));
// let fetch = FetchState::new(rx);
// let app = AppMachine::app_fetch(inner, fetch, true);
// assert!(matches!(app, AppState::Error(_)));
// }
// #[test]
// fn recv_err_empty() {
// let (_tx, rx) = mpsc::channel::<FetchResult>();
// let inner = inner(music_hoard(COLLECTION.clone()));
// let fetch = FetchState::new(rx);
// let app = AppMachine::app_fetch(inner, fetch, true);
// assert!(matches!(app, AppState::Fetch(_)));
// }
// #[test]
// fn recv_err_disconnected_first() {
// let (_, rx) = mpsc::channel::<FetchResult>();
// let inner = inner(music_hoard(COLLECTION.clone()));
// let fetch = FetchState::new(rx);
// let app = AppMachine::app_fetch(inner, fetch, true);
// assert!(matches!(app, AppState::Match(_)));
// }
// #[test]
// fn recv_err_disconnected_next() {
// let (_, rx) = mpsc::channel::<FetchResult>();
// let fetch = FetchState::new(rx);
// let app = AppMachine::app_fetch_next(inner(music_hoard(COLLECTION.clone())), fetch);
// assert!(matches!(app, AppState::Browse(_)));
// }
// #[test]
// fn empty_first_then_ready() {
// let (tx, rx) = mpsc::channel::<FetchResult>();
// let inner = inner(music_hoard(COLLECTION.clone()));
// let fetch = FetchState::new(rx);
// let app = AppMachine::app_fetch(inner, fetch, true);
// assert!(matches!(app, AppState::Fetch(_)));
// let artist = COLLECTION[3].meta.clone();
// let fetch_result = Ok(MatchStateInfo::artist::<Match<ArtistMeta>>(artist, vec![]));
// tx.send(fetch_result).unwrap();
// let app = app.unwrap_fetch().fetch_result_ready();
// assert!(matches!(app, AppState::Match(_)));
// }
// #[test]
// fn abort() {
// let (_, rx) = mpsc::channel::<FetchResult>();
// let fetch = FetchState::new(rx);
// let app = AppMachine::fetch_state(inner(music_hoard(COLLECTION.clone())), fetch);
// let app = app.abort();
// assert!(matches!(app, AppState::Browse(_)));
// }
// }

View File

@ -31,16 +31,16 @@ impl IAppInteractInfo for AppMachine<InfoState> {
} }
} }
// #[cfg(test)] #[cfg(test)]
// mod tests { mod tests {
// use crate::tui::app::machine::tests::{inner, music_hoard}; use crate::tui::app::machine::tests::{inner, music_hoard};
// use super::*; use super::*;
// #[test] #[test]
// fn hide_info_overlay() { fn hide_info_overlay() {
// let info = AppMachine::info_state(inner(music_hoard(vec![]))); let info = AppMachine::info_state(inner(music_hoard(vec![])));
// let app = info.hide_info_overlay(); let app = info.hide_info_overlay();
// app.unwrap_browse(); app.unwrap_browse();
// } }
// } }

View File

@ -55,45 +55,45 @@ impl IAppInput for AppInputMode {
} }
} }
// #[cfg(test)] #[cfg(test)]
// mod tests { mod tests {
// use crate::tui::app::{ use crate::tui::app::{
// machine::tests::{events, mb_api, music_hoard_init}, machine::tests::{mb_job_sender, music_hoard_init},
// IApp, IApp,
// }; };
// use super::*; use super::*;
// fn input_event(c: char) -> InputEvent { fn input_event(c: char) -> InputEvent {
// crossterm::event::KeyEvent::new( crossterm::event::KeyEvent::new(
// crossterm::event::KeyCode::Char(c), crossterm::event::KeyCode::Char(c),
// crossterm::event::KeyModifiers::empty(), crossterm::event::KeyModifiers::empty(),
// ) )
// .into() .into()
// } }
// #[test] #[test]
// fn handle_input() { fn handle_input() {
// let mut app = App::new(music_hoard_init(vec![]), mb_api(), events()); let mut app = App::new(music_hoard_init(vec![]), mb_job_sender());
// app.input_mut().replace(Input::default()); app.input_mut().replace(Input::default());
// let input = app.mode().unwrap_input(); let input = app.mode().unwrap_input();
// let app = input.input(input_event('H')); let app = input.input(input_event('H'));
// let input = app.mode().unwrap_input(); let input = app.mode().unwrap_input();
// let app = input.input(input_event('e')); let app = input.input(input_event('e'));
// let input = app.mode().unwrap_input(); let input = app.mode().unwrap_input();
// let app = input.input(input_event('l')); let app = input.input(input_event('l'));
// let input = app.mode().unwrap_input(); let input = app.mode().unwrap_input();
// let app = input.input(input_event('l')); let app = input.input(input_event('l'));
// let input = app.mode().unwrap_input(); let input = app.mode().unwrap_input();
// let app = input.input(input_event('o')); let app = input.input(input_event('o'));
// assert_eq!(app.input_ref().as_ref().unwrap().0.value(), "Hello"); assert_eq!(app.input_ref().as_ref().unwrap().0.value(), "Hello");
// app.mode().unwrap_input().confirm().unwrap_browse(); app.mode().unwrap_input().confirm().unwrap_browse();
// } }
// } }

View File

@ -165,218 +165,218 @@ impl IAppInteractMatch for AppMachine<MatchState> {
} }
} }
// #[cfg(test)] #[cfg(test)]
// mod tests { mod tests {
// use std::sync::mpsc; use std::sync::mpsc;
// use musichoard::collection::{ use musichoard::collection::{
// album::{AlbumDate, AlbumId, AlbumMeta, AlbumPrimaryType, AlbumSecondaryType}, album::{AlbumDate, AlbumId, AlbumMeta, AlbumPrimaryType, AlbumSecondaryType},
// artist::{ArtistId, ArtistMeta}, artist::{ArtistId, ArtistMeta},
// }; };
// use crate::tui::{ use crate::tui::{
// app::{ app::{
// machine::tests::{inner, music_hoard}, machine::tests::{inner, music_hoard},
// IApp, IAppAccess, IAppInput, IApp, IAppAccess, IAppInput,
// }, },
// lib::interface::musicbrainz::api::Match, lib::interface::musicbrainz::api::Match,
// }; };
// use super::*; use super::*;
// impl<T> Match<T> { impl<T> Match<T> {
// pub fn new(score: u8, item: T) -> Self { pub fn new(score: u8, item: T) -> Self {
// Match { Match {
// score, score,
// item, item,
// disambiguation: None, disambiguation: None,
// } }
// } }
// } }
// fn artist_match() -> MatchStateInfo { fn artist_match() -> MatchStateInfo {
// let artist = ArtistMeta::new(ArtistId::new("Artist")); let artist = ArtistMeta::new(ArtistId::new("Artist"));
// let artist_1 = artist.clone(); let artist_1 = artist.clone();
// let artist_match_1 = Match::new(100, artist_1); let artist_match_1 = Match::new(100, artist_1);
// let artist_2 = artist.clone(); let artist_2 = artist.clone();
// let mut artist_match_2 = Match::new(100, artist_2); let mut artist_match_2 = Match::new(100, artist_2);
// artist_match_2.disambiguation = Some(String::from("some disambiguation")); artist_match_2.disambiguation = Some(String::from("some disambiguation"));
// let list = vec![artist_match_1.clone(), artist_match_2.clone()]; let list = vec![artist_match_1.clone(), artist_match_2.clone()];
// MatchStateInfo::artist(artist, list) MatchStateInfo::artist(artist, list)
// } }
// fn album_match() -> MatchStateInfo { fn album_match() -> MatchStateInfo {
// let album = AlbumMeta::new( let album = AlbumMeta::new(
// AlbumId::new("Album"), AlbumId::new("Album"),
// AlbumDate::new(Some(1990), Some(5), None), AlbumDate::new(Some(1990), Some(5), None),
// Some(AlbumPrimaryType::Album), Some(AlbumPrimaryType::Album),
// vec![AlbumSecondaryType::Live, AlbumSecondaryType::Compilation], vec![AlbumSecondaryType::Live, AlbumSecondaryType::Compilation],
// ); );
// let album_1 = album.clone(); let album_1 = album.clone();
// let album_match_1 = Match::new(100, album_1); let album_match_1 = Match::new(100, album_1);
// let mut album_2 = album.clone(); let mut album_2 = album.clone();
// album_2.id.title.push_str(" extra title part"); album_2.id.title.push_str(" extra title part");
// album_2.secondary_types.pop(); album_2.secondary_types.pop();
// let album_match_2 = Match::new(100, album_2); let album_match_2 = Match::new(100, album_2);
// let list = vec![album_match_1.clone(), album_match_2.clone()]; let list = vec![album_match_1.clone(), album_match_2.clone()];
// MatchStateInfo::album(album, list) MatchStateInfo::album(album, list)
// } }
// fn fetch_state() -> FetchState { fn fetch_state() -> FetchState {
// let (_, rx) = mpsc::channel(); let (_, rx) = mpsc::channel();
// FetchState::new(rx) FetchState::new(rx)
// } }
// fn match_state(matches_info: Option<MatchStateInfo>) -> MatchState { fn match_state(matches_info: Option<MatchStateInfo>) -> MatchState {
// MatchState::new(matches_info, fetch_state()) MatchState::new(matches_info, fetch_state())
// } }
// #[test] #[test]
// fn create_empty() { fn create_empty() {
// let matches = AppMachine::match_state(inner(music_hoard(vec![])), match_state(None)); let matches = AppMachine::match_state(inner(music_hoard(vec![])), match_state(None));
// let widget_state = WidgetState::default(); let widget_state = WidgetState::default();
// assert_eq!(matches.state.current, None); assert_eq!(matches.state.current, None);
// assert_eq!(matches.state.state, widget_state); assert_eq!(matches.state.state, widget_state);
// let mut app: App = matches.into(); let mut app: App = matches.into();
// let public = app.get(); let public = app.get();
// let public_matches = public.state.unwrap_match(); let public_matches = public.state.unwrap_match();
// assert_eq!(public_matches.info, None); assert_eq!(public_matches.info, None);
// assert_eq!(public_matches.state, &widget_state); assert_eq!(public_matches.state, &widget_state);
// } }
// #[test] #[test]
// fn create_nonempty() { fn create_nonempty() {
// let mut album_match = album_match(); let mut album_match = album_match();
// let matches = AppMachine::match_state( let matches = AppMachine::match_state(
// inner(music_hoard(vec![])), inner(music_hoard(vec![])),
// match_state(Some(album_match.clone())), match_state(Some(album_match.clone())),
// ); );
// album_match.push_cannot_have_mbid(); album_match.push_cannot_have_mbid();
// album_match.push_manual_input_mbid(); album_match.push_manual_input_mbid();
// let mut widget_state = WidgetState::default(); let mut widget_state = WidgetState::default();
// widget_state.list.select(Some(0)); widget_state.list.select(Some(0));
// assert_eq!(matches.state.current.as_ref(), Some(&album_match)); assert_eq!(matches.state.current.as_ref(), Some(&album_match));
// assert_eq!(matches.state.state, widget_state); assert_eq!(matches.state.state, widget_state);
// let mut app: App = matches.into(); let mut app: App = matches.into();
// let public = app.get(); let public = app.get();
// let public_matches = public.state.unwrap_match(); let public_matches = public.state.unwrap_match();
// assert_eq!(public_matches.info, Some(&album_match)); assert_eq!(public_matches.info, Some(&album_match));
// assert_eq!(public_matches.state, &widget_state); assert_eq!(public_matches.state, &widget_state);
// } }
// fn match_state_flow(mut matches_info: MatchStateInfo) { fn match_state_flow(mut matches_info: MatchStateInfo) {
// // tx must exist for rx to return Empty rather than Disconnected. // tx must exist for rx to return Empty rather than Disconnected.
// #[allow(unused_variables)] #[allow(unused_variables)]
// let (tx, rx) = mpsc::channel(); let (tx, rx) = mpsc::channel();
// let app_matches = MatchState::new(Some(matches_info.clone()), FetchState::new(rx)); let app_matches = MatchState::new(Some(matches_info.clone()), FetchState::new(rx));
// let matches = AppMachine::match_state(inner(music_hoard(vec![])), app_matches); let matches = AppMachine::match_state(inner(music_hoard(vec![])), app_matches);
// matches_info.push_cannot_have_mbid(); matches_info.push_cannot_have_mbid();
// matches_info.push_manual_input_mbid(); matches_info.push_manual_input_mbid();
// let mut widget_state = WidgetState::default(); let mut widget_state = WidgetState::default();
// widget_state.list.select(Some(0)); widget_state.list.select(Some(0));
// assert_eq!(matches.state.current.as_ref(), Some(&matches_info)); assert_eq!(matches.state.current.as_ref(), Some(&matches_info));
// assert_eq!(matches.state.state, widget_state); assert_eq!(matches.state.state, widget_state);
// let matches = matches.prev_match().unwrap_match(); let matches = matches.prev_match().unwrap_match();
// assert_eq!(matches.state.current.as_ref(), Some(&matches_info)); assert_eq!(matches.state.current.as_ref(), Some(&matches_info));
// assert_eq!(matches.state.state.list.selected(), Some(0)); assert_eq!(matches.state.state.list.selected(), Some(0));
// let matches = matches.next_match().unwrap_match(); let matches = matches.next_match().unwrap_match();
// assert_eq!(matches.state.current.as_ref(), Some(&matches_info)); assert_eq!(matches.state.current.as_ref(), Some(&matches_info));
// assert_eq!(matches.state.state.list.selected(), Some(1)); assert_eq!(matches.state.state.list.selected(), Some(1));
// // Next is CannotHaveMBID // Next is CannotHaveMBID
// let matches = matches.next_match().unwrap_match(); let matches = matches.next_match().unwrap_match();
// assert_eq!(matches.state.current.as_ref(), Some(&matches_info)); assert_eq!(matches.state.current.as_ref(), Some(&matches_info));
// assert_eq!(matches.state.state.list.selected(), Some(2)); assert_eq!(matches.state.state.list.selected(), Some(2));
// // Next is ManualInputMbid // Next is ManualInputMbid
// let matches = matches.next_match().unwrap_match(); let matches = matches.next_match().unwrap_match();
// assert_eq!(matches.state.current.as_ref(), Some(&matches_info)); assert_eq!(matches.state.current.as_ref(), Some(&matches_info));
// assert_eq!(matches.state.state.list.selected(), Some(3)); assert_eq!(matches.state.state.list.selected(), Some(3));
// let matches = matches.next_match().unwrap_match(); let matches = matches.next_match().unwrap_match();
// assert_eq!(matches.state.current.as_ref(), Some(&matches_info)); assert_eq!(matches.state.current.as_ref(), Some(&matches_info));
// assert_eq!(matches.state.state.list.selected(), Some(3)); assert_eq!(matches.state.state.list.selected(), Some(3));
// // Go prev_match first as selecting on manual input does not go back to fetch. // Go prev_match first as selecting on manual input does not go back to fetch.
// let matches = matches.prev_match().unwrap_match(); let matches = matches.prev_match().unwrap_match();
// matches.select().unwrap_fetch(); matches.select().unwrap_fetch();
// } }
// #[test] #[test]
// fn artist_matches_flow() { fn artist_matches_flow() {
// match_state_flow(artist_match()); match_state_flow(artist_match());
// } }
// #[test] #[test]
// fn album_matches_flow() { fn album_matches_flow() {
// match_state_flow(album_match()); match_state_flow(album_match());
// } }
// #[test] #[test]
// fn abort() { fn abort() {
// let mut album_match = album_match(); let mut album_match = album_match();
// let matches = AppMachine::match_state( let matches = AppMachine::match_state(
// inner(music_hoard(vec![])), inner(music_hoard(vec![])),
// match_state(Some(album_match.clone())), match_state(Some(album_match.clone())),
// ); );
// album_match.push_cannot_have_mbid(); album_match.push_cannot_have_mbid();
// album_match.push_manual_input_mbid(); album_match.push_manual_input_mbid();
// let mut widget_state = WidgetState::default(); let mut widget_state = WidgetState::default();
// widget_state.list.select(Some(0)); widget_state.list.select(Some(0));
// assert_eq!(matches.state.current.as_ref(), Some(&album_match)); assert_eq!(matches.state.current.as_ref(), Some(&album_match));
// assert_eq!(matches.state.state, widget_state); assert_eq!(matches.state.state, widget_state);
// matches.abort().unwrap_browse(); matches.abort().unwrap_browse();
// } }
// #[test] #[test]
// fn select_empty() { fn select_empty() {
// // Note that what really matters in this test is actually that the transmit channel has // Note that what really matters in this test is actually that the transmit channel has
// // disconnected and so the receive within FetchState concludes there are no more matches. // disconnected and so the receive within FetchState concludes there are no more matches.
// let matches = AppMachine::match_state(inner(music_hoard(vec![])), match_state(None)); let matches = AppMachine::match_state(inner(music_hoard(vec![])), match_state(None));
// matches.select().unwrap_browse(); matches.select().unwrap_browse();
// } }
// #[test] #[test]
// fn select_manual_input() { fn select_manual_input() {
// let matches = let matches =
// AppMachine::match_state(inner(music_hoard(vec![])), match_state(Some(album_match()))); AppMachine::match_state(inner(music_hoard(vec![])), match_state(Some(album_match())));
// // album_match has two matches which means that the fourth option should be manual input. // album_match has two matches which means that the fourth option should be manual input.
// let matches = matches.next_match().unwrap_match(); let matches = matches.next_match().unwrap_match();
// let matches = matches.next_match().unwrap_match(); let matches = matches.next_match().unwrap_match();
// let matches = matches.next_match().unwrap_match(); let matches = matches.next_match().unwrap_match();
// let matches = matches.next_match().unwrap_match(); let matches = matches.next_match().unwrap_match();
// let app = matches.select(); let app = matches.select();
// let input = app.mode().unwrap_input(); let input = app.mode().unwrap_input();
// input.confirm().unwrap_match(); input.confirm().unwrap_match();
// } }
// } }

View File

@ -215,378 +215,376 @@ where
} }
} }
// #[cfg(test)] #[cfg(test)]
// mod tests { mod tests {
// use std::sync::mpsc; use std::sync::mpsc;
// use musichoard::collection::Collection; use musichoard::collection::Collection;
// use crate::tui::{ use crate::tui::{
// app::{AppState, IApp, IAppInput, IAppInteractBrowse}, app::{AppState, IApp, IAppInput, IAppInteractBrowse},
// lib::{interface::musicbrainz::api::MockIMusicBrainz, MockIMusicHoard}, lib::{interface::musicbrainz::daemon::MockIMbJobSender, MockIMusicHoard},
// EventChannel, };
// };
use super::*;
// use super::*;
impl<StateMode, InputMode> AppMode<StateMode, InputMode> {
// impl<StateMode, InputMode> AppMode<StateMode, InputMode> { fn unwrap_state(self) -> StateMode {
// fn unwrap_state(self) -> StateMode { match self {
// match self { AppMode::State(state) => state,
// AppMode::State(state) => state, _ => panic!(),
// _ => panic!(), }
// } }
// }
pub fn unwrap_input(self) -> InputMode {
// pub fn unwrap_input(self) -> InputMode { match self {
// match self { AppMode::Input(input) => input,
// AppMode::Input(input) => input, _ => panic!(),
// _ => panic!(), }
// } }
// } }
// }
impl<
// impl< BrowseState,
// BrowseState, InfoState,
// InfoState, ReloadState,
// ReloadState, SearchState,
// SearchState, FetchState,
// FetchState, MatchState,
// MatchState, ErrorState,
// ErrorState, CriticalState,
// CriticalState, >
// > AppState<
// AppState< BrowseState,
// BrowseState, InfoState,
// InfoState, ReloadState,
// ReloadState, SearchState,
// SearchState, FetchState,
// FetchState, MatchState,
// MatchState, ErrorState,
// ErrorState, CriticalState,
// CriticalState, >
// > {
// { pub fn unwrap_browse(self) -> BrowseState {
// pub fn unwrap_browse(self) -> BrowseState { match self {
// match self { AppState::Browse(browse) => browse,
// AppState::Browse(browse) => browse, _ => panic!(),
// _ => panic!(), }
// } }
// }
pub fn unwrap_info(self) -> InfoState {
// pub fn unwrap_info(self) -> InfoState { match self {
// match self { AppState::Info(info) => info,
// AppState::Info(info) => info, _ => panic!(),
// _ => panic!(), }
// } }
// }
pub fn unwrap_reload(self) -> ReloadState {
// pub fn unwrap_reload(self) -> ReloadState { match self {
// match self { AppState::Reload(reload) => reload,
// AppState::Reload(reload) => reload, _ => panic!(),
// _ => panic!(), }
// } }
// }
pub fn unwrap_search(self) -> SearchState {
// pub fn unwrap_search(self) -> SearchState { match self {
// match self { AppState::Search(search) => search,
// AppState::Search(search) => search, _ => panic!(),
// _ => panic!(), }
// } }
// }
pub fn unwrap_fetch(self) -> FetchState {
// pub fn unwrap_fetch(self) -> FetchState { match self {
// match self { AppState::Fetch(fetch) => fetch,
// AppState::Fetch(fetch) => fetch, _ => panic!(),
// _ => panic!(), }
// } }
// }
pub fn unwrap_match(self) -> MatchState {
// pub fn unwrap_match(self) -> MatchState { match self {
// match self { AppState::Match(matches) => matches,
// AppState::Match(matches) => matches, _ => panic!(),
// _ => panic!(), }
// } }
// }
pub fn unwrap_error(self) -> ErrorState {
// pub fn unwrap_error(self) -> ErrorState { match self {
// match self { AppState::Error(error) => error,
// AppState::Error(error) => error, _ => panic!(),
// _ => panic!(), }
// } }
// }
pub fn unwrap_critical(self) -> CriticalState {
// pub fn unwrap_critical(self) -> CriticalState { match self {
// match self { AppState::Critical(critical) => critical,
// AppState::Critical(critical) => critical, _ => panic!(),
// _ => panic!(), }
// } }
// } }
// }
pub fn music_hoard(collection: Collection) -> MockIMusicHoard {
// pub fn music_hoard(collection: Collection) -> MockIMusicHoard { let mut music_hoard = MockIMusicHoard::new();
// let mut music_hoard = MockIMusicHoard::new(); music_hoard.expect_get_collection().return_const(collection);
// music_hoard.expect_get_collection().return_const(collection);
music_hoard
// music_hoard }
// }
pub fn music_hoard_init(collection: Collection) -> MockIMusicHoard {
// pub fn music_hoard_init(collection: Collection) -> MockIMusicHoard { let mut music_hoard = music_hoard(collection);
// let mut music_hoard = music_hoard(collection);
music_hoard
// music_hoard .expect_rescan_library()
// .expect_rescan_library() .times(1)
// .times(1) .return_once(|| Ok(()));
// .return_once(|| Ok(()));
music_hoard
// music_hoard }
// }
pub fn mb_job_sender() -> MockIMbJobSender {
// pub fn mb_api() -> MockIMusicBrainz { MockIMbJobSender::new()
// MockIMusicBrainz::new() }
// }
pub fn inner(music_hoard: MockIMusicHoard) -> AppInner {
// pub fn events() -> EventSender { AppInner::new(music_hoard, mb_job_sender())
// EventChannel::new().sender() }
// }
pub fn inner_with_mb(
// pub fn inner(music_hoard: MockIMusicHoard) -> AppInner { music_hoard: MockIMusicHoard,
// AppInner::new(music_hoard, mb_api(), events()) mb_job_sender: MockIMbJobSender,
// } ) -> AppInner {
AppInner::new(music_hoard, mb_job_sender)
// pub fn inner_with_mb(music_hoard: MockIMusicHoard, mb_api: MockIMusicBrainz) -> AppInner { }
// AppInner::new(music_hoard, mb_api, events())
// } #[test]
fn input_mode() {
// #[test] let app = App::new(music_hoard_init(vec![]), mb_job_sender());
// fn input_mode() { assert!(app.is_running());
// let app = App::new(music_hoard_init(vec![]), mb_api(), events());
// assert!(app.is_running()); let mode = app.mode();
assert!(matches!(mode, AppMode::State(_)));
// let mode = app.mode();
// assert!(matches!(mode, AppMode::State(_))); let state = mode.unwrap_state();
assert!(matches!(state, AppState::Browse(_)));
// let state = mode.unwrap_state();
// assert!(matches!(state, AppState::Browse(_))); let mut app = state;
app.input_mut().replace(Input::default());
// let mut app = state;
// app.input_mut().replace(Input::default()); let public = app.get();
assert!(public.input.is_some());
// let public = app.get();
// assert!(public.input.is_some()); let mode = app.mode();
assert!(matches!(mode, AppMode::Input(_)));
// let mode = app.mode();
// assert!(matches!(mode, AppMode::Input(_))); let mut app = mode.unwrap_input().cancel();
assert!(matches!(app, AppState::Browse(_)));
// let mut app = mode.unwrap_input().cancel();
// assert!(matches!(app, AppState::Browse(_))); let public = app.get();
assert!(public.input.is_none());
// let public = app.get(); }
// assert!(public.input.is_none());
// } #[test]
fn state_browse() {
// #[test] let mut app = App::new(music_hoard_init(vec![]), mb_job_sender());
// fn state_browse() { assert!(app.is_running());
// let mut app = App::new(music_hoard_init(vec![]), mb_api(), events());
// assert!(app.is_running()); let state = app.state();
assert!(matches!(state, AppState::Browse(_)));
// let state = app.state(); app = state;
// assert!(matches!(state, AppState::Browse(_)));
// app = state; app = app.no_op();
let state = app.state();
// app = app.no_op(); assert!(matches!(state, AppState::Browse(_)));
// let state = app.state(); app = state;
// assert!(matches!(state, AppState::Browse(_)));
// app = state; let public = app.get();
assert!(matches!(public.state, AppState::Browse(_)));
// let public = app.get();
// assert!(matches!(public.state, AppState::Browse(_))); let app = app.force_quit();
assert!(!app.is_running());
}
#[test]
fn state_info() {
let mut app = App::new(music_hoard_init(vec![]), mb_job_sender());
assert!(app.is_running());
app = app.unwrap_browse().show_info_overlay();
let state = app.state();
assert!(matches!(state, AppState::Info(_)));
app = state;
// let app = app.force_quit(); app = app.no_op();
// assert!(!app.is_running()); let state = app.state();
// } assert!(matches!(state, AppState::Info(_)));
app = state;
// #[test] let public = app.get();
// fn state_info() { assert!(matches!(public.state, AppState::Info(_)));
// let mut app = App::new(music_hoard_init(vec![]), mb_api(), events());
// assert!(app.is_running());
// app = app.unwrap_browse().show_info_overlay(); let app = app.force_quit();
assert!(!app.is_running());
}
// let state = app.state(); #[test]
// assert!(matches!(state, AppState::Info(_))); fn state_reload() {
// app = state; let mut app = App::new(music_hoard_init(vec![]), mb_job_sender());
assert!(app.is_running());
// app = app.no_op(); app = app.unwrap_browse().show_reload_menu();
// let state = app.state();
// assert!(matches!(state, AppState::Info(_)));
// app = state;
// let public = app.get(); let state = app.state();
// assert!(matches!(public.state, AppState::Info(_))); assert!(matches!(state, AppState::Reload(_)));
app = state;
// let app = app.force_quit(); app = app.no_op();
// assert!(!app.is_running()); let state = app.state();
// } assert!(matches!(state, AppState::Reload(_)));
app = state;
// #[test] let public = app.get();
// fn state_reload() { assert!(matches!(public.state, AppState::Reload(_)));
// let mut app = App::new(music_hoard_init(vec![]), mb_api(), events());
// assert!(app.is_running());
// app = app.unwrap_browse().show_reload_menu(); let app = app.force_quit();
assert!(!app.is_running());
}
// let state = app.state(); #[test]
// assert!(matches!(state, AppState::Reload(_))); fn state_search() {
// app = state; let mut app = App::new(music_hoard_init(vec![]), mb_job_sender());
assert!(app.is_running());
// app = app.no_op(); app = app.unwrap_browse().begin_search();
// let state = app.state();
// assert!(matches!(state, AppState::Reload(_)));
// app = state;
// let public = app.get(); let state = app.state();
// assert!(matches!(public.state, AppState::Reload(_))); assert!(matches!(state, AppState::Search(_)));
app = state;
// let app = app.force_quit(); app = app.no_op();
// assert!(!app.is_running()); let state = app.state();
// } assert!(matches!(state, AppState::Search(_)));
app = state;
// #[test] let public = app.get();
// fn state_search() { assert!(matches!(public.state, AppState::Search("")));
// let mut app = App::new(music_hoard_init(vec![]), mb_api(), events());
// assert!(app.is_running());
// app = app.unwrap_browse().begin_search(); let app = app.force_quit();
assert!(!app.is_running());
}
// let state = app.state(); #[test]
// assert!(matches!(state, AppState::Search(_))); fn state_fetch() {
// app = state; let mut app = App::new(music_hoard_init(vec![]), mb_job_sender());
assert!(app.is_running());
// app = app.no_op(); let (_, rx) = mpsc::channel();
// let state = app.state(); let inner = app.unwrap_browse().inner;
// assert!(matches!(state, AppState::Search(_))); let state = FetchState::new(rx);
// app = state; app = AppMachine::new(inner, state).into();
// let public = app.get(); let state = app.state();
// assert!(matches!(public.state, AppState::Search(""))); assert!(matches!(state, AppState::Fetch(_)));
app = state;
app = app.no_op();
let state = app.state();
assert!(matches!(state, AppState::Fetch(_)));
app = state;
// let app = app.force_quit(); let public = app.get();
// assert!(!app.is_running()); assert!(matches!(public.state, AppState::Fetch(_)));
// }
let app = app.force_quit();
assert!(!app.is_running());
}
#[test]
fn state_match() {
let mut app = App::new(music_hoard_init(vec![]), mb_job_sender());
assert!(app.is_running());
// #[test] let (_, rx) = mpsc::channel();
// fn state_fetch() { let fetch = FetchState::new(rx);
// let mut app = App::new(music_hoard_init(vec![]), mb_api(), events()); app =
// assert!(app.is_running()); AppMachine::match_state(app.unwrap_browse().inner, MatchState::new(None, fetch)).into();
// let (_, rx) = mpsc::channel(); let state = app.state();
// let inner = app.unwrap_browse().inner; assert!(matches!(state, AppState::Match(_)));
// let state = FetchState::new(rx); app = state;
// app = AppMachine::new(inner, state).into();
app = app.no_op();
let state = app.state();
assert!(matches!(state, AppState::Match(_)));
app = state;
let public = app.get();
assert!(matches!(public.state, AppState::Match(_)));
let app = app.force_quit();
assert!(!app.is_running());
}
#[test]
fn state_error() {
let mut app = App::new(music_hoard_init(vec![]), mb_job_sender());
assert!(app.is_running());
app = AppMachine::error_state(app.unwrap_browse().inner, "get rekt").into();
// let state = app.state(); let state = app.state();
// assert!(matches!(state, AppState::Fetch(_))); assert!(matches!(state, AppState::Error(_)));
// app = state; app = state;
// app = app.no_op(); app = app.no_op();
// let state = app.state(); let state = app.state();
// assert!(matches!(state, AppState::Fetch(_))); assert!(matches!(state, AppState::Error(_)));
// app = state; app = state;
// let public = app.get(); let public = app.get();
// assert!(matches!(public.state, AppState::Fetch(_))); assert!(matches!(public.state, AppState::Error("get rekt")));
// let app = app.force_quit(); app = app.force_quit();
// assert!(!app.is_running()); assert!(!app.is_running());
// } }
// #[test] #[test]
// fn state_match() { fn state_critical() {
// let mut app = App::new(music_hoard_init(vec![]), mb_api(), events()); let mut app = App::new(music_hoard_init(vec![]), mb_job_sender());
// assert!(app.is_running()); assert!(app.is_running());
// let (_, rx) = mpsc::channel(); app = AppMachine::critical_state(app.unwrap_browse().inner, "get rekt").into();
// let fetch = FetchState::new(rx);
// app = let state = app.state();
// AppMachine::match_state(app.unwrap_browse().inner, MatchState::new(None, fetch)).into(); assert!(matches!(state, AppState::Critical(_)));
app = state;
// let state = app.state();
// assert!(matches!(state, AppState::Match(_))); app = app.no_op();
// app = state; let state = app.state();
assert!(matches!(state, AppState::Critical(_)));
// app = app.no_op(); app = state;
// let state = app.state();
// assert!(matches!(state, AppState::Match(_))); let public = app.get();
// app = state; assert!(matches!(public.state, AppState::Critical("get rekt")));
// let public = app.get(); app = app.force_quit();
// assert!(matches!(public.state, AppState::Match(_))); assert!(!app.is_running());
}
// let app = app.force_quit();
// assert!(!app.is_running()); #[test]
// } fn init_error() {
let mut music_hoard = MockIMusicHoard::new();
// #[test]
// fn state_error() { music_hoard
// let mut app = App::new(music_hoard_init(vec![]), mb_api(), events()); .expect_rescan_library()
// assert!(app.is_running()); .times(1)
.return_once(|| Err(musichoard::Error::LibraryError(String::from("get rekt"))));
// app = AppMachine::error_state(app.unwrap_browse().inner, "get rekt").into(); music_hoard.expect_get_collection().return_const(vec![]);
// let state = app.state(); let app = App::new(music_hoard, mb_job_sender());
// assert!(matches!(state, AppState::Error(_))); assert!(app.is_running());
// app = state; app.unwrap_critical();
}
// app = app.no_op(); }
// let state = app.state();
// assert!(matches!(state, AppState::Error(_)));
// app = state;
// let public = app.get();
// assert!(matches!(public.state, AppState::Error("get rekt")));
// app = app.force_quit();
// assert!(!app.is_running());
// }
// #[test]
// fn state_critical() {
// let mut app = App::new(music_hoard_init(vec![]), mb_api(), events());
// assert!(app.is_running());
// app = AppMachine::critical_state(app.unwrap_browse().inner, "get rekt").into();
// let state = app.state();
// assert!(matches!(state, AppState::Critical(_)));
// app = state;
// app = app.no_op();
// let state = app.state();
// assert!(matches!(state, AppState::Critical(_)));
// app = state;
// let public = app.get();
// assert!(matches!(public.state, AppState::Critical("get rekt")));
// app = app.force_quit();
// assert!(!app.is_running());
// }
// #[test]
// fn init_error() {
// let mut music_hoard = MockIMusicHoard::new();
// music_hoard
// .expect_rescan_library()
// .times(1)
// .return_once(|| Err(musichoard::Error::LibraryError(String::from("get rekt"))));
// music_hoard.expect_get_collection().return_const(vec![]);
// let app = App::new(music_hoard, mb_api(), events());
// assert!(app.is_running());
// app.unwrap_critical();
// }
// }
#[cfg(nightly)] #[cfg(nightly)]
#[cfg(test)] #[cfg(test)]

View File

@ -68,58 +68,58 @@ impl IAppInteractReloadPrivate for AppMachine<ReloadState> {
} }
} }
// #[cfg(test)] #[cfg(test)]
// mod tests { mod tests {
// use crate::tui::app::machine::tests::{inner, music_hoard}; use crate::tui::app::machine::tests::{inner, music_hoard};
// use super::*; use super::*;
// #[test] #[test]
// fn hide_reload_menu() { fn hide_reload_menu() {
// let reload = AppMachine::reload_state(inner(music_hoard(vec![]))); let reload = AppMachine::reload_state(inner(music_hoard(vec![])));
// let app = reload.hide_reload_menu(); let app = reload.hide_reload_menu();
// app.unwrap_browse(); app.unwrap_browse();
// } }
// #[test] #[test]
// fn reload_database() { fn reload_database() {
// let mut music_hoard = music_hoard(vec![]); let mut music_hoard = music_hoard(vec![]);
// music_hoard music_hoard
// .expect_reload_database() .expect_reload_database()
// .times(1) .times(1)
// .return_once(|| Ok(())); .return_once(|| Ok(()));
// let reload = AppMachine::reload_state(inner(music_hoard)); let reload = AppMachine::reload_state(inner(music_hoard));
// let app = reload.reload_database(); let app = reload.reload_database();
// app.unwrap_browse(); app.unwrap_browse();
// } }
// #[test] #[test]
// fn reload_library() { fn reload_library() {
// let mut music_hoard = music_hoard(vec![]); let mut music_hoard = music_hoard(vec![]);
// music_hoard music_hoard
// .expect_rescan_library() .expect_rescan_library()
// .times(1) .times(1)
// .return_once(|| Ok(())); .return_once(|| Ok(()));
// let reload = AppMachine::reload_state(inner(music_hoard)); let reload = AppMachine::reload_state(inner(music_hoard));
// let app = reload.reload_library(); let app = reload.reload_library();
// app.unwrap_browse(); app.unwrap_browse();
// } }
// #[test] #[test]
// fn reload_error() { fn reload_error() {
// let mut music_hoard = music_hoard(vec![]); let mut music_hoard = music_hoard(vec![]);
// music_hoard music_hoard
// .expect_reload_database() .expect_reload_database()
// .times(1) .times(1)
// .return_once(|| Err(musichoard::Error::DatabaseError(String::from("get rekt")))); .return_once(|| Err(musichoard::Error::DatabaseError(String::from("get rekt"))));
// let reload = AppMachine::reload_state(inner(music_hoard)); let reload = AppMachine::reload_state(inner(music_hoard));
// let app = reload.reload_database(); let app = reload.reload_database();
// app.unwrap_error(); app.unwrap_error();
// } }
// } }

View File

@ -229,343 +229,343 @@ impl IAppInteractSearchPrivate for AppMachine<SearchState> {
} }
} }
// #[cfg(test)] #[cfg(test)]
// mod tests { mod tests {
// use ratatui::widgets::ListState; use ratatui::widgets::ListState;
// use crate::tui::{ use crate::tui::{
// app::machine::tests::{inner, music_hoard}, app::machine::tests::{inner, music_hoard},
// testmod::COLLECTION, testmod::COLLECTION,
// }; };
// use super::*; use super::*;
// fn orig(index: Option<usize>) -> ListSelection { fn orig(index: Option<usize>) -> ListSelection {
// let mut artist = ListState::default(); let mut artist = ListState::default();
// artist.select(index); artist.select(index);
// ListSelection { ListSelection {
// artist, artist,
// album: ListState::default(), album: ListState::default(),
// track: ListState::default(), track: ListState::default(),
// } }
// } }
// #[test] #[test]
// fn artist_incremental_search() { fn artist_incremental_search() {
// // Empty collection. // Empty collection.
// let mut search = AppMachine::search_state(inner(music_hoard(vec![])), orig(None)); let mut search = AppMachine::search_state(inner(music_hoard(vec![])), orig(None));
// assert_eq!(search.inner.selection.selected(), None); assert_eq!(search.inner.selection.selected(), None);
// search.state.string = String::from("album_artist 'a'"); search.state.string = String::from("album_artist 'a'");
// search.incremental_search(false); search.incremental_search(false);
// assert_eq!(search.inner.selection.selected(), None); assert_eq!(search.inner.selection.selected(), None);
// // Basic test, first element. // Basic test, first element.
// let mut search = let mut search =
// AppMachine::search_state(inner(music_hoard(COLLECTION.to_owned())), orig(Some(1))); AppMachine::search_state(inner(music_hoard(COLLECTION.to_owned())), orig(Some(1)));
// assert_eq!(search.inner.selection.selected(), Some(0)); assert_eq!(search.inner.selection.selected(), Some(0));
// search.state.string = String::from(""); search.state.string = String::from("");
// search.incremental_search(false); search.incremental_search(false);
// assert_eq!(search.inner.selection.selected(), Some(0)); assert_eq!(search.inner.selection.selected(), Some(0));
// search.state.string = String::from("album_artist "); search.state.string = String::from("album_artist ");
// search.incremental_search(false); search.incremental_search(false);
// assert_eq!(search.inner.selection.selected(), Some(0)); assert_eq!(search.inner.selection.selected(), Some(0));
// search.state.string = String::from("album_artist 'a'"); search.state.string = String::from("album_artist 'a'");
// search.incremental_search(false); search.incremental_search(false);
// assert_eq!(search.inner.selection.selected(), Some(0)); assert_eq!(search.inner.selection.selected(), Some(0));
// // Basic test, non-first element. // Basic test, non-first element.
// let mut search = let mut search =
// AppMachine::search_state(inner(music_hoard(COLLECTION.to_owned())), orig(Some(1))); AppMachine::search_state(inner(music_hoard(COLLECTION.to_owned())), orig(Some(1)));
// assert_eq!(search.inner.selection.selected(), Some(0)); assert_eq!(search.inner.selection.selected(), Some(0));
// search.state.string = String::from("album_artist "); search.state.string = String::from("album_artist ");
// search.incremental_search(false); search.incremental_search(false);
// assert_eq!(search.inner.selection.selected(), Some(0)); assert_eq!(search.inner.selection.selected(), Some(0));
// search.state.string = String::from("album_artist 'c'"); search.state.string = String::from("album_artist 'c'");
// search.incremental_search(false); search.incremental_search(false);
// assert_eq!(search.inner.selection.selected(), Some(2)); assert_eq!(search.inner.selection.selected(), Some(2));
// // Non-lowercase. // Non-lowercase.
// let mut search = let mut search =
// AppMachine::search_state(inner(music_hoard(COLLECTION.to_owned())), orig(Some(1))); AppMachine::search_state(inner(music_hoard(COLLECTION.to_owned())), orig(Some(1)));
// assert_eq!(search.inner.selection.selected(), Some(0)); assert_eq!(search.inner.selection.selected(), Some(0));
// search.state.string = String::from("Album_Artist "); search.state.string = String::from("Album_Artist ");
// search.incremental_search(false); search.incremental_search(false);
// assert_eq!(search.inner.selection.selected(), Some(0)); assert_eq!(search.inner.selection.selected(), Some(0));
// search.state.string = String::from("Album_Artist 'C'"); search.state.string = String::from("Album_Artist 'C'");
// search.incremental_search(false); search.incremental_search(false);
// assert_eq!(search.inner.selection.selected(), Some(2)); assert_eq!(search.inner.selection.selected(), Some(2));
// // Non-ascii. // Non-ascii.
// let mut search = let mut search =
// AppMachine::search_state(inner(music_hoard(COLLECTION.to_owned())), orig(Some(1))); AppMachine::search_state(inner(music_hoard(COLLECTION.to_owned())), orig(Some(1)));
// assert_eq!(search.inner.selection.selected(), Some(0)); assert_eq!(search.inner.selection.selected(), Some(0));
// search.state.string = String::from("album_artist "); search.state.string = String::from("album_artist ");
// search.incremental_search(false); search.incremental_search(false);
// assert_eq!(search.inner.selection.selected(), Some(0)); assert_eq!(search.inner.selection.selected(), Some(0));
// search.state.string = String::from("album_artist c"); search.state.string = String::from("album_artist c");
// search.incremental_search(false); search.incremental_search(false);
// assert_eq!(search.inner.selection.selected(), Some(2)); assert_eq!(search.inner.selection.selected(), Some(2));
// // Non-lowercase, non-ascii. // Non-lowercase, non-ascii.
// let mut search = let mut search =
// AppMachine::search_state(inner(music_hoard(COLLECTION.to_owned())), orig(Some(1))); AppMachine::search_state(inner(music_hoard(COLLECTION.to_owned())), orig(Some(1)));
// assert_eq!(search.inner.selection.selected(), Some(0)); assert_eq!(search.inner.selection.selected(), Some(0));
// search.state.string = String::from("Album_Artist "); search.state.string = String::from("Album_Artist ");
// search.incremental_search(false); search.incremental_search(false);
// assert_eq!(search.inner.selection.selected(), Some(0)); assert_eq!(search.inner.selection.selected(), Some(0));
// search.state.string = String::from("Album_Artist C"); search.state.string = String::from("Album_Artist C");
// search.incremental_search(false); search.incremental_search(false);
// assert_eq!(search.inner.selection.selected(), Some(2)); assert_eq!(search.inner.selection.selected(), Some(2));
// // Stop at name, not sort name. // Stop at name, not sort name.
// let mut search = let mut search =
// AppMachine::search_state(inner(music_hoard(COLLECTION.to_owned())), orig(Some(1))); AppMachine::search_state(inner(music_hoard(COLLECTION.to_owned())), orig(Some(1)));
// assert_eq!(search.inner.selection.selected(), Some(0)); assert_eq!(search.inner.selection.selected(), Some(0));
// search.state.string = String::from("the "); search.state.string = String::from("the ");
// search.incremental_search(false); search.incremental_search(false);
// assert_eq!(search.inner.selection.selected(), Some(2)); assert_eq!(search.inner.selection.selected(), Some(2));
// search.state.string = String::from("the album_artist 'c'"); search.state.string = String::from("the album_artist 'c'");
// search.incremental_search(false); search.incremental_search(false);
// assert_eq!(search.inner.selection.selected(), Some(2)); assert_eq!(search.inner.selection.selected(), Some(2));
// // Search next with common prefix. // Search next with common prefix.
// let mut search = let mut search =
// AppMachine::search_state(inner(music_hoard(COLLECTION.to_owned())), orig(Some(1))); AppMachine::search_state(inner(music_hoard(COLLECTION.to_owned())), orig(Some(1)));
// assert_eq!(search.inner.selection.selected(), Some(0)); assert_eq!(search.inner.selection.selected(), Some(0));
// search.state.string = String::from("album_artist"); search.state.string = String::from("album_artist");
// search.incremental_search(false); search.incremental_search(false);
// assert_eq!(search.inner.selection.selected(), Some(0)); assert_eq!(search.inner.selection.selected(), Some(0));
// search.incremental_search(true); search.incremental_search(true);
// assert_eq!(search.inner.selection.selected(), Some(1)); assert_eq!(search.inner.selection.selected(), Some(1));
// search.incremental_search(true); search.incremental_search(true);
// assert_eq!(search.inner.selection.selected(), Some(2)); assert_eq!(search.inner.selection.selected(), Some(2));
// search.incremental_search(true); search.incremental_search(true);
// assert_eq!(search.inner.selection.selected(), Some(3)); assert_eq!(search.inner.selection.selected(), Some(3));
// search.incremental_search(true); search.incremental_search(true);
// assert_eq!(search.inner.selection.selected(), Some(3)); assert_eq!(search.inner.selection.selected(), Some(3));
// } }
// #[test] #[test]
// fn album_incremental_search() { fn album_incremental_search() {
// let mut search = let mut search =
// AppMachine::search_state(inner(music_hoard(COLLECTION.to_owned())), orig(Some(1))); AppMachine::search_state(inner(music_hoard(COLLECTION.to_owned())), orig(Some(1)));
// search.inner.selection.increment_category(); search.inner.selection.increment_category();
// assert_eq!(search.inner.selection.category(), Category::Album); assert_eq!(search.inner.selection.category(), Category::Album);
// let sel = &search.inner.selection; let sel = &search.inner.selection;
// assert_eq!(sel.selected(), Some(0)); assert_eq!(sel.selected(), Some(0));
// search.state.string = String::from("album_title "); search.state.string = String::from("album_title ");
// search.incremental_search(false); search.incremental_search(false);
// let sel = &search.inner.selection; let sel = &search.inner.selection;
// assert_eq!(sel.selected(), Some(0)); assert_eq!(sel.selected(), Some(0));
// let search = search.append_character('a').unwrap_search(); let search = search.append_character('a').unwrap_search();
// let search = search.append_character('.').unwrap_search(); let search = search.append_character('.').unwrap_search();
// let search = search.append_character('b').unwrap_search(); let search = search.append_character('b').unwrap_search();
// let sel = &search.inner.selection; let sel = &search.inner.selection;
// assert_eq!(sel.selected(), Some(1)); assert_eq!(sel.selected(), Some(1));
// } }
// #[test] #[test]
// fn track_incremental_search() { fn track_incremental_search() {
// let mut search = let mut search =
// AppMachine::search_state(inner(music_hoard(COLLECTION.to_owned())), orig(Some(1))); AppMachine::search_state(inner(music_hoard(COLLECTION.to_owned())), orig(Some(1)));
// search.inner.selection.increment_category(); search.inner.selection.increment_category();
// search.inner.selection.increment_category(); search.inner.selection.increment_category();
// assert_eq!(search.inner.selection.category(), Category::Track); assert_eq!(search.inner.selection.category(), Category::Track);
// let sel = &search.inner.selection; let sel = &search.inner.selection;
// assert_eq!(sel.selected(), Some(0)); assert_eq!(sel.selected(), Some(0));
// search.state.string = String::from("track "); search.state.string = String::from("track ");
// search.incremental_search(false); search.incremental_search(false);
// let sel = &search.inner.selection; let sel = &search.inner.selection;
// assert_eq!(sel.selected(), Some(0)); assert_eq!(sel.selected(), Some(0));
// let search = search.append_character('a').unwrap_search(); let search = search.append_character('a').unwrap_search();
// let search = search.append_character('.').unwrap_search(); let search = search.append_character('.').unwrap_search();
// let search = search.append_character('a').unwrap_search(); let search = search.append_character('a').unwrap_search();
// let search = search.append_character('.').unwrap_search(); let search = search.append_character('.').unwrap_search();
// let search = search.append_character('2').unwrap_search(); let search = search.append_character('2').unwrap_search();
// let sel = &search.inner.selection; let sel = &search.inner.selection;
// assert_eq!(sel.selected(), Some(1)); assert_eq!(sel.selected(), Some(1));
// } }
// #[test] #[test]
// fn search() { fn search() {
// let search = let search =
// AppMachine::search_state(inner(music_hoard(COLLECTION.to_owned())), orig(Some(2))); AppMachine::search_state(inner(music_hoard(COLLECTION.to_owned())), orig(Some(2)));
// assert_eq!(search.inner.selection.selected(), Some(0)); assert_eq!(search.inner.selection.selected(), Some(0));
// let search = search.append_character('a').unwrap_search(); let search = search.append_character('a').unwrap_search();
// let search = search.append_character('l').unwrap_search(); let search = search.append_character('l').unwrap_search();
// let search = search.append_character('b').unwrap_search(); let search = search.append_character('b').unwrap_search();
// let search = search.append_character('u').unwrap_search(); let search = search.append_character('u').unwrap_search();
// let search = search.append_character('m').unwrap_search(); let search = search.append_character('m').unwrap_search();
// let search = search.append_character('_').unwrap_search(); let search = search.append_character('_').unwrap_search();
// let search = search.append_character('a').unwrap_search(); let search = search.append_character('a').unwrap_search();
// let search = search.append_character('r').unwrap_search(); let search = search.append_character('r').unwrap_search();
// let search = search.append_character('t').unwrap_search(); let search = search.append_character('t').unwrap_search();
// let search = search.append_character('i').unwrap_search(); let search = search.append_character('i').unwrap_search();
// let search = search.append_character('s').unwrap_search(); let search = search.append_character('s').unwrap_search();
// let search = search.append_character('t').unwrap_search(); let search = search.append_character('t').unwrap_search();
// let search = search.append_character(' ').unwrap_search(); let search = search.append_character(' ').unwrap_search();
// assert_eq!(search.inner.selection.selected(), Some(0)); assert_eq!(search.inner.selection.selected(), Some(0));
// let search = search.append_character('\'').unwrap_search(); let search = search.append_character('\'').unwrap_search();
// let search = search.append_character('c').unwrap_search(); let search = search.append_character('c').unwrap_search();
// let search = search.append_character('\'').unwrap_search(); let search = search.append_character('\'').unwrap_search();
// assert_eq!(search.inner.selection.selected(), Some(2)); assert_eq!(search.inner.selection.selected(), Some(2));
// let search = search.step_back().unwrap_search(); let search = search.step_back().unwrap_search();
// let search = search.step_back().unwrap_search(); let search = search.step_back().unwrap_search();
// let search = search.step_back().unwrap_search(); let search = search.step_back().unwrap_search();
// assert_eq!(search.inner.selection.selected(), Some(0)); assert_eq!(search.inner.selection.selected(), Some(0));
// let search = search.append_character('\'').unwrap_search(); let search = search.append_character('\'').unwrap_search();
// let search = search.append_character('b').unwrap_search(); let search = search.append_character('b').unwrap_search();
// let search = search.append_character('\'').unwrap_search(); let search = search.append_character('\'').unwrap_search();
// assert_eq!(search.inner.selection.selected(), Some(1)); assert_eq!(search.inner.selection.selected(), Some(1));
// let app = search.finish_search(); let app = search.finish_search();
// let browse = app.unwrap_browse(); let browse = app.unwrap_browse();
// assert_eq!(browse.inner.selection.selected(), Some(1)); assert_eq!(browse.inner.selection.selected(), Some(1));
// } }
// #[test] #[test]
// fn search_next_step_back() { fn search_next_step_back() {
// let search = let search =
// AppMachine::search_state(inner(music_hoard(COLLECTION.to_owned())), orig(Some(2))); AppMachine::search_state(inner(music_hoard(COLLECTION.to_owned())), orig(Some(2)));
// assert_eq!(search.inner.selection.selected(), Some(0)); assert_eq!(search.inner.selection.selected(), Some(0));
// let search = search.step_back().unwrap_search(); let search = search.step_back().unwrap_search();
// assert_eq!(search.inner.selection.selected(), Some(0)); assert_eq!(search.inner.selection.selected(), Some(0));
// let search = search.append_character('a').unwrap_search(); let search = search.append_character('a').unwrap_search();
// let search = search.search_next().unwrap_search(); let search = search.search_next().unwrap_search();
// assert_eq!(search.inner.selection.selected(), Some(1)); assert_eq!(search.inner.selection.selected(), Some(1));
// let search = search.search_next().unwrap_search(); let search = search.search_next().unwrap_search();
// assert_eq!(search.inner.selection.selected(), Some(2)); assert_eq!(search.inner.selection.selected(), Some(2));
// let search = search.search_next().unwrap_search(); let search = search.search_next().unwrap_search();
// assert_eq!(search.inner.selection.selected(), Some(3)); assert_eq!(search.inner.selection.selected(), Some(3));
// let search = search.search_next().unwrap_search(); let search = search.search_next().unwrap_search();
// assert_eq!(search.inner.selection.selected(), Some(3)); assert_eq!(search.inner.selection.selected(), Some(3));
// let search = search.step_back().unwrap_search(); let search = search.step_back().unwrap_search();
// assert_eq!(search.inner.selection.selected(), Some(3)); assert_eq!(search.inner.selection.selected(), Some(3));
// let search = search.step_back().unwrap_search(); let search = search.step_back().unwrap_search();
// assert_eq!(search.inner.selection.selected(), Some(2)); assert_eq!(search.inner.selection.selected(), Some(2));
// let search = search.step_back().unwrap_search(); let search = search.step_back().unwrap_search();
// assert_eq!(search.inner.selection.selected(), Some(1)); assert_eq!(search.inner.selection.selected(), Some(1));
// let search = search.step_back().unwrap_search(); let search = search.step_back().unwrap_search();
// assert_eq!(search.inner.selection.selected(), Some(0)); assert_eq!(search.inner.selection.selected(), Some(0));
// let search = search.step_back().unwrap_search(); let search = search.step_back().unwrap_search();
// assert_eq!(search.inner.selection.selected(), Some(0)); assert_eq!(search.inner.selection.selected(), Some(0));
// } }
// #[test] #[test]
// fn cancel_search() { fn cancel_search() {
// let search = let search =
// AppMachine::search_state(inner(music_hoard(COLLECTION.to_owned())), orig(Some(2))); AppMachine::search_state(inner(music_hoard(COLLECTION.to_owned())), orig(Some(2)));
// assert_eq!(search.inner.selection.selected(), Some(0)); assert_eq!(search.inner.selection.selected(), Some(0));
// let search = search.append_character('a').unwrap_search(); let search = search.append_character('a').unwrap_search();
// let search = search.append_character('l').unwrap_search(); let search = search.append_character('l').unwrap_search();
// let search = search.append_character('b').unwrap_search(); let search = search.append_character('b').unwrap_search();
// let search = search.append_character('u').unwrap_search(); let search = search.append_character('u').unwrap_search();
// let search = search.append_character('m').unwrap_search(); let search = search.append_character('m').unwrap_search();
// let search = search.append_character('_').unwrap_search(); let search = search.append_character('_').unwrap_search();
// let search = search.append_character('a').unwrap_search(); let search = search.append_character('a').unwrap_search();
// let search = search.append_character('r').unwrap_search(); let search = search.append_character('r').unwrap_search();
// let search = search.append_character('t').unwrap_search(); let search = search.append_character('t').unwrap_search();
// let search = search.append_character('i').unwrap_search(); let search = search.append_character('i').unwrap_search();
// let search = search.append_character('s').unwrap_search(); let search = search.append_character('s').unwrap_search();
// let search = search.append_character('t').unwrap_search(); let search = search.append_character('t').unwrap_search();
// let search = search.append_character(' ').unwrap_search(); let search = search.append_character(' ').unwrap_search();
// let search = search.append_character('\'').unwrap_search(); let search = search.append_character('\'').unwrap_search();
// let search = search.append_character('b').unwrap_search(); let search = search.append_character('b').unwrap_search();
// let search = search.append_character('\'').unwrap_search(); let search = search.append_character('\'').unwrap_search();
// assert_eq!(search.inner.selection.selected(), Some(1)); assert_eq!(search.inner.selection.selected(), Some(1));
// let browse = search.cancel_search().unwrap_browse(); let browse = search.cancel_search().unwrap_browse();
// assert_eq!(browse.inner.selection.selected(), Some(2)); assert_eq!(browse.inner.selection.selected(), Some(2));
// } }
// #[test] #[test]
// fn empty_search() { fn empty_search() {
// let search = AppMachine::search_state(inner(music_hoard(vec![])), orig(None)); let search = AppMachine::search_state(inner(music_hoard(vec![])), orig(None));
// assert_eq!(search.inner.selection.selected(), None); assert_eq!(search.inner.selection.selected(), None);
// let search = search.append_character('a').unwrap_search(); let search = search.append_character('a').unwrap_search();
// assert_eq!(search.inner.selection.selected(), None); assert_eq!(search.inner.selection.selected(), None);
// let search = search.search_next().unwrap_search(); let search = search.search_next().unwrap_search();
// assert_eq!(search.inner.selection.selected(), None); assert_eq!(search.inner.selection.selected(), None);
// let search = search.step_back().unwrap_search(); let search = search.step_back().unwrap_search();
// assert_eq!(search.inner.selection.selected(), None); assert_eq!(search.inner.selection.selected(), None);
// let search = search.step_back().unwrap_search(); let search = search.step_back().unwrap_search();
// assert_eq!(search.inner.selection.selected(), None); assert_eq!(search.inner.selection.selected(), None);
// let browse = search.cancel_search().unwrap_browse(); let browse = search.cancel_search().unwrap_browse();
// assert_eq!(browse.inner.selection.selected(), None); assert_eq!(browse.inner.selection.selected(), None);
// } }
// } }
// #[cfg(nightly)] #[cfg(nightly)]
// #[cfg(test)] #[cfg(test)]
// mod benches { mod benches {
// // The purpose of these benches was to evaluate the benefit of AhoCorasick over std solutions. // The purpose of these benches was to evaluate the benefit of AhoCorasick over std solutions.
// use test::Bencher; use test::Bencher;
// use crate::tui::{app::machine::benchmod::ARTISTS, lib::MockIMusicHoard}; use crate::tui::{app::machine::benchmod::ARTISTS, lib::MockIMusicHoard};
// use super::*; use super::*;
// type Search = AppMachine<MockIMusicHoard, SearchState>; type Search = AppMachine<MockIMusicHoard, SearchState>;
// #[bench] #[bench]
// fn is_char_sensitive(b: &mut Bencher) { fn is_char_sensitive(b: &mut Bencher) {
// let mut iter = ARTISTS.iter().cycle(); let mut iter = ARTISTS.iter().cycle();
// b.iter(|| test::black_box(Search::is_char_sensitive(&iter.next().unwrap()))) b.iter(|| test::black_box(Search::is_char_sensitive(&iter.next().unwrap())))
// } }
// #[bench] #[bench]
// fn normalize_search(b: &mut Bencher) { fn normalize_search(b: &mut Bencher) {
// let mut iter = ARTISTS.iter().cycle(); let mut iter = ARTISTS.iter().cycle();
// b.iter(|| test::black_box(Search::normalize_search(&iter.next().unwrap(), true, true))) b.iter(|| test::black_box(Search::normalize_search(&iter.next().unwrap(), true, true)))
// } }
// } }

View File

@ -2,6 +2,9 @@ use crossterm::event::KeyEvent;
use std::fmt; use std::fmt;
use std::sync::mpsc; use std::sync::mpsc;
#[cfg(test)]
use mockall::automock;
#[derive(Debug)] #[derive(Debug)]
pub enum EventError { pub enum EventError {
Send(Event), Send(Event),
@ -50,6 +53,7 @@ pub struct EventChannel {
receiver: mpsc::Receiver<Event>, receiver: mpsc::Receiver<Event>,
} }
#[cfg_attr(test, automock)]
pub trait IKeyEventSender { pub trait IKeyEventSender {
fn send_key(&self, key_event: KeyEvent) -> Result<(), EventError>; fn send_key(&self, key_event: KeyEvent) -> Result<(), EventError>;
} }

View File

@ -296,3 +296,193 @@ impl JobQueue {
} }
} }
} }
#[cfg(test)]
mod tests {
// 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<Match<AlbumMeta>>) {
// 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<Match<AlbumMeta>>) {
// 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<AlbumMeta>],
// ) {
// 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(MatchStateInfo::Album(AlbumMatches {
// 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(MatchStateInfo::Album(AlbumMatches {
// matching: album_4.clone(),
// list: matches_4.iter().cloned().map(Into::into).collect(),
// }));
// assert_eq!(result, expected);
// }
// fn artist_expectations() -> (ArtistMeta, Vec<Match<ArtistMeta>>) {
// 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<ArtistMeta>],
// ) {
// 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(MatchStateInfo::Artist(ArtistMatches {
// 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(MatchStateInfo::Album(AlbumMatches {
// 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);
// }
}

View File

@ -4,6 +4,9 @@ use musichoard::collection::{album::AlbumMeta, artist::ArtistMeta, musicbrainz::
use crate::tui::{app::MatchStateInfo, lib::interface::musicbrainz::api::Error as MbApiError}; use crate::tui::{app::MatchStateInfo, lib::interface::musicbrainz::api::Error as MbApiError};
#[cfg(test)]
use mockall::automock;
pub enum Error { pub enum Error {
EventChannelDisconnected, EventChannelDisconnected,
JobChannelDisconnected, JobChannelDisconnected,
@ -21,6 +24,7 @@ impl fmt::Display for Error {
pub type MbApiResult = Result<MatchStateInfo, MbApiError>; pub type MbApiResult = Result<MatchStateInfo, MbApiError>;
pub type ResultSender = mpsc::Sender<MbApiResult>; pub type ResultSender = mpsc::Sender<MbApiResult>;
#[cfg_attr(test, automock)]
pub trait IMbJobSender { pub trait IMbJobSender {
fn submit_background_job( fn submit_background_job(
&self, &self,
@ -29,19 +33,23 @@ pub trait IMbJobSender {
) -> Result<(), Error>; ) -> Result<(), Error>;
} }
#[derive(Debug, PartialEq, Eq)]
pub enum MbParams { pub enum MbParams {
Search(SearchParams), Search(SearchParams),
} }
#[derive(Debug, PartialEq, Eq)]
pub enum SearchParams { pub enum SearchParams {
Artist(SearchArtistParams), Artist(SearchArtistParams),
ReleaseGroup(SearchReleaseGroupParams), ReleaseGroup(SearchReleaseGroupParams),
} }
#[derive(Debug, PartialEq, Eq)]
pub struct SearchArtistParams { pub struct SearchArtistParams {
pub artist: ArtistMeta, pub artist: ArtistMeta,
} }
#[derive(Debug, PartialEq, Eq)]
pub struct SearchReleaseGroupParams { pub struct SearchReleaseGroupParams {
pub arid: Mbid, pub arid: Mbid,
pub album: AlbumMeta, pub album: AlbumMeta,

View File

@ -173,154 +173,149 @@ impl<B: Backend, UI: IUi, APP: IApp + IAppAccess> Tui<B, UI, APP> {
#[cfg(test)] #[cfg(test)]
mod testmod; mod testmod;
// #[cfg(test)] #[cfg(test)]
// mod tests { mod tests {
// use std::{io, thread}; use std::{io, thread};
// use event::EventSender; use lib::interface::musicbrainz::daemon::MockIMbJobSender;
// use lib::interface::musicbrainz::api::MockIMusicBrainz; use ratatui::{backend::TestBackend, Terminal};
// use ratatui::{backend::TestBackend, Terminal};
// use musichoard::collection::Collection; use musichoard::collection::Collection;
// use crate::tui::{ use crate::tui::{
// app::App, handler::MockIEventHandler, lib::MockIMusicHoard, listener::MockIEventListener, app::App, handler::MockIEventHandler, lib::MockIMusicHoard, listener::MockIEventListener,
// ui::Ui, ui::Ui,
// }; };
// use super::*; use super::*;
// use testmod::COLLECTION; use testmod::COLLECTION;
// pub fn terminal() -> Terminal<TestBackend> { pub fn terminal() -> Terminal<TestBackend> {
// let backend = TestBackend::new(150, 30); let backend = TestBackend::new(150, 30);
// Terminal::new(backend).unwrap() Terminal::new(backend).unwrap()
// } }
// fn music_hoard(collection: Collection) -> MockIMusicHoard { fn music_hoard(collection: Collection) -> MockIMusicHoard {
// let mut music_hoard = MockIMusicHoard::new(); let mut music_hoard = MockIMusicHoard::new();
// music_hoard.expect_reload_database().returning(|| Ok(())); music_hoard.expect_reload_database().returning(|| Ok(()));
// music_hoard.expect_rescan_library().returning(|| Ok(())); music_hoard.expect_rescan_library().returning(|| Ok(()));
// music_hoard.expect_get_collection().return_const(collection); music_hoard.expect_get_collection().return_const(collection);
// music_hoard music_hoard
// } }
// fn events() -> EventSender { fn app(collection: Collection) -> App {
// EventChannel::new().sender() App::new(music_hoard(collection), MockIMbJobSender::new())
// } }
// fn app(collection: Collection) -> App { fn listener() -> MockIEventListener {
// App::new(music_hoard(collection), MockIMusicBrainz::new(), events()) let mut listener = MockIEventListener::new();
// } listener.expect_spawn().return_once(|| {
thread::spawn(|| {
thread::park();
EventError::Io(io::Error::new(io::ErrorKind::Interrupted, "unparked"))
})
});
listener
}
// fn listener() -> MockIEventListener { fn handler() -> MockIEventHandler<App> {
// let mut listener = MockIEventListener::new(); let mut handler = MockIEventHandler::new();
// listener.expect_spawn().return_once(|| { handler
// thread::spawn(|| { .expect_handle_next_event()
// thread::park(); .return_once(|app: App| Ok(app.force_quit()));
// EventError::Io(io::Error::new(io::ErrorKind::Interrupted, "unparked")) handler
// }) }
// });
// listener
// }
// fn handler() -> MockIEventHandler<App> { #[test]
// let mut handler = MockIEventHandler::new(); fn run() {
// handler let terminal = terminal();
// .expect_handle_next_event() let app = app(COLLECTION.to_owned());
// .return_once(|app: App| Ok(app.force_quit())); let ui = Ui;
// handler
// }
// #[test] let listener = listener();
// fn run() { let handler = handler();
// let terminal = terminal();
// let app = app(COLLECTION.to_owned());
// let ui = Ui;
// let listener = listener(); let result = Tui::main(terminal, app, ui, handler, listener);
// let handler = handler(); assert!(result.is_ok());
}
// let result = Tui::main(terminal, app, ui, handler, listener); #[test]
// assert!(result.is_ok()); fn event_error() {
// } let terminal = terminal();
let app = app(COLLECTION.to_owned());
let ui = Ui;
// #[test] let listener = listener();
// fn event_error() {
// let terminal = terminal();
// let app = app(COLLECTION.to_owned());
// let ui = Ui;
// let listener = listener(); let mut handler = MockIEventHandler::new();
handler
.expect_handle_next_event()
.return_once(|_| Err(EventError::Recv));
// let mut handler = MockIEventHandler::new(); let result = Tui::main(terminal, app, ui, handler, listener);
// handler assert!(result.is_err());
// .expect_handle_next_event()
// .return_once(|_| Err(EventError::Recv));
// let result = Tui::main(terminal, app, ui, handler, listener); let error = EventError::Recv;
// assert!(result.is_err()); assert_eq!(result.unwrap_err(), Error::Event(error.to_string()));
}
// let error = EventError::Recv; #[test]
// assert_eq!(result.unwrap_err(), Error::Event(error.to_string())); fn listener_error() {
// } let terminal = terminal();
let app = app(COLLECTION.to_owned());
let ui = Ui;
// #[test] let error = EventError::Io(io::Error::new(io::ErrorKind::Interrupted, "error"));
// fn listener_error() { let listener_handle: thread::JoinHandle<EventError> = thread::spawn(|| error);
// let terminal = terminal(); while !listener_handle.is_finished() {}
// let app = app(COLLECTION.to_owned());
// let ui = Ui;
// let error = EventError::Io(io::Error::new(io::ErrorKind::Interrupted, "error")); let mut listener = MockIEventListener::new();
// let listener_handle: thread::JoinHandle<EventError> = thread::spawn(|| error); listener.expect_spawn().return_once(|| listener_handle);
// while !listener_handle.is_finished() {}
// let mut listener = MockIEventListener::new(); let mut handler = MockIEventHandler::new();
// listener.expect_spawn().return_once(|| listener_handle); handler
.expect_handle_next_event()
.return_once(|_| Err(EventError::Recv));
// let mut handler = MockIEventHandler::new(); let result = Tui::main(terminal, app, ui, handler, listener);
// handler assert!(result.is_err());
// .expect_handle_next_event()
// .return_once(|_| Err(EventError::Recv));
// let result = Tui::main(terminal, app, ui, handler, listener); let error = EventError::Io(io::Error::new(io::ErrorKind::Interrupted, "error"));
// assert!(result.is_err()); assert_eq!(result.unwrap_err(), Error::Event(error.to_string()));
}
// let error = EventError::Io(io::Error::new(io::ErrorKind::Interrupted, "error")); #[test]
// assert_eq!(result.unwrap_err(), Error::Event(error.to_string())); fn listener_panic() {
// } let terminal = terminal();
let app = app(COLLECTION.to_owned());
let ui = Ui;
// #[test] let listener_handle: thread::JoinHandle<EventError> = thread::spawn(|| panic!());
// fn listener_panic() { while !listener_handle.is_finished() {}
// let terminal = terminal();
// let app = app(COLLECTION.to_owned());
// let ui = Ui;
// let listener_handle: thread::JoinHandle<EventError> = thread::spawn(|| panic!()); let mut listener = MockIEventListener::new();
// while !listener_handle.is_finished() {} listener.expect_spawn().return_once(|| listener_handle);
// let mut listener = MockIEventListener::new(); let mut handler = MockIEventHandler::new();
// listener.expect_spawn().return_once(|| listener_handle); handler
.expect_handle_next_event()
.return_once(|_| Err(EventError::Recv));
// let mut handler = MockIEventHandler::new(); let result = Tui::main(terminal, app, ui, handler, listener);
// handler assert!(result.is_err());
// .expect_handle_next_event() assert_eq!(result.unwrap_err(), Error::ListenerPanic);
// .return_once(|_| Err(EventError::Recv)); }
// let result = Tui::main(terminal, app, ui, handler, listener); #[test]
// assert!(result.is_err()); fn errors() {
// assert_eq!(result.unwrap_err(), Error::ListenerPanic); let io_err: Error = io::Error::new(io::ErrorKind::Interrupted, "error").into();
// } let event_err: Error = EventError::Recv.into();
let listener_err = Error::ListenerPanic;
// #[test] assert!(!format!("{:?}", io_err).is_empty());
// fn errors() { assert!(!format!("{:?}", event_err).is_empty());
// let io_err: Error = io::Error::new(io::ErrorKind::Interrupted, "error").into(); assert!(!format!("{:?}", listener_err).is_empty());
// let event_err: Error = EventError::Recv.into(); }
// let listener_err = Error::ListenerPanic; }
// assert!(!format!("{:?}", io_err).is_empty());
// assert!(!format!("{:?}", event_err).is_empty());
// assert!(!format!("{:?}", listener_err).is_empty());
// }
// }

View File

@ -198,232 +198,232 @@ impl IUi for Ui {
} }
} }
// #[cfg(test)] #[cfg(test)]
// mod tests { mod tests {
// use musichoard::collection::{ use musichoard::collection::{
// album::{AlbumDate, AlbumId, AlbumMeta, AlbumPrimaryType, AlbumSecondaryType}, album::{AlbumDate, AlbumId, AlbumMeta, AlbumPrimaryType, AlbumSecondaryType},
// artist::{Artist, ArtistId, ArtistMeta}, artist::{Artist, ArtistId, ArtistMeta},
// }; };
// use crate::tui::{ use crate::tui::{
// app::{AppPublic, AppPublicInner, Delta, MatchOption, MatchStatePublic}, app::{AppPublic, AppPublicInner, Delta, MatchOption, MatchStatePublic},
// lib::interface::musicbrainz::api::Match, lib::interface::musicbrainz::api::Match,
// testmod::COLLECTION, testmod::COLLECTION,
// tests::terminal, tests::terminal,
// }; };
// use super::*; use super::*;
// // Automock does not support returning types with generic lifetimes. // Automock does not support returning types with generic lifetimes.
// impl<'app> IAppAccess for AppPublic<'app> { impl<'app> IAppAccess for AppPublic<'app> {
// fn get(&mut self) -> AppPublic { fn get(&mut self) -> AppPublic {
// AppPublic { AppPublic {
// inner: AppPublicInner { inner: AppPublicInner {
// collection: self.inner.collection, collection: self.inner.collection,
// selection: self.inner.selection, selection: self.inner.selection,
// }, },
// state: match self.state { state: match self.state {
// AppState::Browse(()) => AppState::Browse(()), AppState::Browse(()) => AppState::Browse(()),
// AppState::Info(()) => AppState::Info(()), AppState::Info(()) => AppState::Info(()),
// AppState::Reload(()) => AppState::Reload(()), AppState::Reload(()) => AppState::Reload(()),
// AppState::Search(s) => AppState::Search(s), AppState::Search(s) => AppState::Search(s),
// AppState::Fetch(()) => AppState::Fetch(()), AppState::Fetch(()) => AppState::Fetch(()),
// AppState::Match(ref mut m) => AppState::Match(MatchStatePublic { AppState::Match(ref mut m) => AppState::Match(MatchStatePublic {
// info: m.info, info: m.info,
// state: m.state, state: m.state,
// }), }),
// AppState::Error(s) => AppState::Error(s), AppState::Error(s) => AppState::Error(s),
// AppState::Critical(s) => AppState::Critical(s), AppState::Critical(s) => AppState::Critical(s),
// }, },
// input: self.input, input: self.input,
// } }
// } }
// } }
// fn public_inner<'app>( fn public_inner<'app>(
// collection: &'app Collection, collection: &'app Collection,
// selection: &'app mut Selection, selection: &'app mut Selection,
// ) -> AppPublicInner<'app> { ) -> AppPublicInner<'app> {
// AppPublicInner { AppPublicInner {
// collection, collection,
// selection, selection,
// } }
// } }
// fn artist_matches(matching: ArtistMeta, list: Vec<Match<ArtistMeta>>) -> MatchStateInfo { fn artist_matches(matching: ArtistMeta, list: Vec<Match<ArtistMeta>>) -> MatchStateInfo {
// let mut list: Vec<MatchOption<ArtistMeta>> = list.into_iter().map(Into::into).collect(); let mut list: Vec<MatchOption<ArtistMeta>> = list.into_iter().map(Into::into).collect();
// list.push(MatchOption::CannotHaveMbid); list.push(MatchOption::CannotHaveMbid);
// list.push(MatchOption::ManualInputMbid); list.push(MatchOption::ManualInputMbid);
// MatchStateInfo::artist(matching, list) MatchStateInfo::artist(matching, list)
// } }
// fn album_matches(matching: AlbumMeta, list: Vec<Match<AlbumMeta>>) -> MatchStateInfo { fn album_matches(matching: AlbumMeta, list: Vec<Match<AlbumMeta>>) -> MatchStateInfo {
// let mut list: Vec<MatchOption<AlbumMeta>> = list.into_iter().map(Into::into).collect(); let mut list: Vec<MatchOption<AlbumMeta>> = list.into_iter().map(Into::into).collect();
// list.push(MatchOption::CannotHaveMbid); list.push(MatchOption::CannotHaveMbid);
// list.push(MatchOption::ManualInputMbid); list.push(MatchOption::ManualInputMbid);
// MatchStateInfo::album(matching, list) MatchStateInfo::album(matching, list)
// } }
// fn draw_test_suite(collection: &Collection, selection: &mut Selection) { fn draw_test_suite(collection: &Collection, selection: &mut Selection) {
// let mut terminal = terminal(); let mut terminal = terminal();
// let mut app = AppPublic { let mut app = AppPublic {
// inner: public_inner(collection, selection), inner: public_inner(collection, selection),
// state: AppState::Browse(()), state: AppState::Browse(()),
// input: None, input: None,
// }; };
// terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap(); terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap();
// app.state = AppState::Info(()); app.state = AppState::Info(());
// terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap(); terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap();
// app.state = AppState::Reload(()); app.state = AppState::Reload(());
// terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap(); terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap();
// app.state = AppState::Search(""); app.state = AppState::Search("");
// terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap(); terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap();
// app.state = AppState::Fetch(()); app.state = AppState::Fetch(());
// terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap(); terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap();
// app.state = AppState::Error("get rekt scrub"); app.state = AppState::Error("get rekt scrub");
// terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap(); terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap();
// app.state = AppState::Critical("get critically rekt scrub"); app.state = AppState::Critical("get critically rekt scrub");
// terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap(); terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap();
// } }
// #[test] #[test]
// fn empty() { fn empty() {
// let artists: Vec<Artist> = vec![]; let artists: Vec<Artist> = vec![];
// let mut selection = Selection::new(&artists); let mut selection = Selection::new(&artists);
// draw_test_suite(&artists, &mut selection); draw_test_suite(&artists, &mut selection);
// } }
// #[test] #[test]
// fn empty_album() { fn empty_album() {
// let mut artists: Vec<Artist> = vec![Artist::new(ArtistId::new("An artist"))]; let mut artists: Vec<Artist> = vec![Artist::new(ArtistId::new("An artist"))];
// artists[0] artists[0]
// .albums .albums
// .push(Album::new("An album", AlbumDate::default(), None, vec![])); .push(Album::new("An album", AlbumDate::default(), None, vec![]));
// let mut selection = Selection::new(&artists); let mut selection = Selection::new(&artists);
// draw_test_suite(&artists, &mut selection); draw_test_suite(&artists, &mut selection);
// } }
// #[test] #[test]
// fn collection() { fn collection() {
// let artists = &COLLECTION; let artists = &COLLECTION;
// let mut selection = Selection::new(artists); let mut selection = Selection::new(artists);
// draw_test_suite(artists, &mut selection); draw_test_suite(artists, &mut selection);
// // Change the track (which has a different track format). // Change the track (which has a different track format).
// selection.increment_category(); selection.increment_category();
// selection.increment_category(); selection.increment_category();
// selection.increment_selection(artists, Delta::Line); selection.increment_selection(artists, Delta::Line);
// draw_test_suite(artists, &mut selection); draw_test_suite(artists, &mut selection);
// // Change the artist (which has a multi-link entry). // Change the artist (which has a multi-link entry).
// selection.decrement_category(); selection.decrement_category();
// selection.decrement_category(); selection.decrement_category();
// selection.increment_selection(artists, Delta::Line); selection.increment_selection(artists, Delta::Line);
// draw_test_suite(artists, &mut selection); draw_test_suite(artists, &mut selection);
// } }
// #[test] #[test]
// fn draw_empty_matches() { fn draw_empty_matches() {
// let collection = &COLLECTION; let collection = &COLLECTION;
// let mut selection = Selection::new(collection); let mut selection = Selection::new(collection);
// let mut terminal = terminal(); let mut terminal = terminal();
// let mut widget_state = WidgetState::default(); let mut widget_state = WidgetState::default();
// let mut app = AppPublic { let mut app = AppPublic {
// inner: public_inner(collection, &mut selection), inner: public_inner(collection, &mut selection),
// state: AppState::Match(MatchStatePublic { state: AppState::Match(MatchStatePublic {
// info: None, info: None,
// state: &mut widget_state, state: &mut widget_state,
// }), }),
// input: None, input: None,
// }; };
// terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap(); terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap();
// } }
// #[test] #[test]
// fn draw_artist_matches() { fn draw_artist_matches() {
// let collection = &COLLECTION; let collection = &COLLECTION;
// let mut selection = Selection::new(collection); let mut selection = Selection::new(collection);
// let mut terminal = terminal(); let mut terminal = terminal();
// let artist = ArtistMeta::new(ArtistId::new("an artist")); let artist = ArtistMeta::new(ArtistId::new("an artist"));
// let artist_match = Match { let artist_match = Match {
// score: 80, score: 80,
// item: artist.clone(), item: artist.clone(),
// disambiguation: None, disambiguation: None,
// }; };
// let list = vec![artist_match.clone(), artist_match.clone()]; let list = vec![artist_match.clone(), artist_match.clone()];
// let artist_matches = artist_matches(artist, list); let artist_matches = artist_matches(artist, list);
// let mut widget_state = WidgetState::default(); let mut widget_state = WidgetState::default();
// widget_state.list.select(Some(0)); widget_state.list.select(Some(0));
// let mut app = AppPublic { let mut app = AppPublic {
// inner: public_inner(collection, &mut selection), inner: public_inner(collection, &mut selection),
// state: AppState::Match(MatchStatePublic { state: AppState::Match(MatchStatePublic {
// info: Some(&artist_matches), info: Some(&artist_matches),
// state: &mut widget_state, state: &mut widget_state,
// }), }),
// input: None, input: None,
// }; };
// terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap(); terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap();
// let input = tui_input::Input::default(); let input = tui_input::Input::default();
// app.input = Some(&input); app.input = Some(&input);
// terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap(); terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap();
// } }
// #[test] #[test]
// fn draw_album_matches() { fn draw_album_matches() {
// let collection = &COLLECTION; let collection = &COLLECTION;
// let mut selection = Selection::new(collection); let mut selection = Selection::new(collection);
// let mut terminal = terminal(); let mut terminal = terminal();
// let album = AlbumMeta::new( let album = AlbumMeta::new(
// AlbumId::new("An Album"), AlbumId::new("An Album"),
// AlbumDate::new(Some(1990), Some(5), None), AlbumDate::new(Some(1990), Some(5), None),
// Some(AlbumPrimaryType::Album), Some(AlbumPrimaryType::Album),
// vec![AlbumSecondaryType::Live, AlbumSecondaryType::Compilation], vec![AlbumSecondaryType::Live, AlbumSecondaryType::Compilation],
// ); );
// let album_match = Match { let album_match = Match {
// score: 80, score: 80,
// item: album.clone(), item: album.clone(),
// disambiguation: None, disambiguation: None,
// }; };
// let list = vec![album_match.clone(), album_match.clone()]; let list = vec![album_match.clone(), album_match.clone()];
// let album_matches = album_matches(album, list); let album_matches = album_matches(album, list);
// let mut widget_state = WidgetState::default(); let mut widget_state = WidgetState::default();
// widget_state.list.select(Some(0)); widget_state.list.select(Some(0));
// let mut app = AppPublic { let mut app = AppPublic {
// inner: public_inner(collection, &mut selection), inner: public_inner(collection, &mut selection),
// state: AppState::Match(MatchStatePublic { state: AppState::Match(MatchStatePublic {
// info: Some(&album_matches), info: Some(&album_matches),
// state: &mut widget_state, state: &mut widget_state,
// }), }),
// input: None, input: None,
// }; };
// terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap(); terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap();
// let input = tui_input::Input::default(); let input = tui_input::Input::default();
// app.input = Some(&input); app.input = Some(&input);
// terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap(); terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap();
// } }
// } }