Compare commits

..

No commits in common. "593797c8accf38b225f507991385508083681116" and "39bd5e7935c4f73759f4c9d8289b8149556735b4" have entirely different histories.

14 changed files with 1605 additions and 1714 deletions

View File

@ -77,112 +77,105 @@ 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::daemon::MockIMbJobSender, // lib::interface::musicbrainz::api::MockIMusicBrainz,
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 mut mb_job_sender = MockIMbJobSender::new(); // let mb_api = MockIMusicBrainz::new();
mb_job_sender // let browse =
.expect_submit_background_job() // AppMachine::browse_state(inner_with_mb(music_hoard(COLLECTION.to_owned()), mb_api));
.times(1)
.returning(|_, _| Ok(()));
let browse = AppMachine::browse_state(inner_with_mb( // // Use the second artist for this test.
music_hoard(COLLECTION.to_owned()), // let browse = browse.increment_selection(Delta::Line).unwrap_browse();
mb_job_sender, // let mut app = browse.fetch_musicbrainz();
));
// Use the second artist for this test. // let public = app.get();
let browse = browse.increment_selection(Delta::Line).unwrap_browse();
let mut app = browse.fetch_musicbrainz();
let public = app.get(); // // Because of fetch's threaded behaviour, this unit test cannot expect one or the other.
// assert!(
// Because of fetch's threaded behaviour, this unit test cannot expect one or the other. // matches!(public.state, AppState::Match(_))
assert!( // || matches!(public.state, AppState::Fetch(_))
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,7 +87,6 @@ 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()
} }
@ -125,205 +124,299 @@ impl IAppEventFetch for AppMachine<FetchState> {
} }
} }
#[cfg(test)] // #[cfg(test)]
mod tests { // mod tests {
use mockall::predicate; // use mockall::{predicate, Sequence};
use musichoard::collection::{album::AlbumMeta, artist::ArtistMeta, musicbrainz::Mbid}; // use musichoard::collection::artist::ArtistMeta;
use crate::tui::{ // use crate::tui::{
app::{ // app::{
machine::tests::{inner, music_hoard}, // machine::tests::{inner, music_hoard},
Delta, IApp, IAppAccess, IAppInteractBrowse, MatchOption, MatchStateInfo, // AlbumMatches, ArtistMatches, IApp,
}, // },
lib::interface::musicbrainz::{self, api::Match, daemon::MockIMbJobSender}, // event::EventReceiver,
testmod::COLLECTION, // lib::interface::musicbrainz::{
}; // 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 search_release_group_expectation( // fn event_channel() -> (EventSender, EventReceiver) {
job_sender: &mut MockIMbJobSender, // let event_channel = EventChannel::new();
arid: &Mbid, // let events_tx = event_channel.sender();
albums: &[AlbumMeta], // let events_rx = event_channel.receiver();
) { // (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(()));
}
#[test] // fn album_expectations_1() -> (AlbumMeta, Vec<Match<AlbumMeta>>) {
fn fetch_albums() { // let album_1 = COLLECTION[1].albums[0].meta.clone();
let mut mb_job_sender = MockIMbJobSender::new(); // let album_4 = COLLECTION[1].albums[3].meta.clone();
let arid: Mbid = "11111111-1111-1111-1111-111111111111".try_into().unwrap(); // 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()];
let album_1_meta = COLLECTION[1].albums[0].meta.clone(); // (album_1, matches_1)
let album_4_meta = COLLECTION[1].albums[3].meta.clone(); // }
// Other albums have an MBID and so they will be skipped. // fn album_expectations_4() -> (AlbumMeta, Vec<Match<AlbumMeta>>) {
search_release_group_expectation(&mut mb_job_sender, &arid, &[album_1_meta, album_4_meta]); // let album_1 = COLLECTION[1].albums[0].meta.clone();
// let album_4 = COLLECTION[1].albums[3].meta.clone();
let music_hoard = music_hoard(COLLECTION.to_owned()); // let album_match_4_1 = Match::new(100, album_4.clone());
let inner = AppInner::new(music_hoard, mb_job_sender); // 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()];
// Use second artist to match the expectation. // (album_4, matches_4)
let browse = AppMachine::browse_state(inner); // }
let app = browse.increment_selection(Delta::Line);
let app = app.unwrap_browse().fetch_musicbrainz(); // fn search_release_group_expectation(
assert!(matches!(app, AppState::Match(_))); // 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);
// }
fn search_artist_expectation(job_sender: &mut MockIMbJobSender, artist: &ArtistMeta) { // #[test]
let requests = VecDeque::from([MbParams::search_artist(artist.clone())]); // fn fetch_albums() {
job_sender // let mut mb_api = MockIMusicBrainz::new();
.expect_submit_background_job()
.with(predicate::always(), predicate::eq(requests))
.times(1)
.return_once(|_, _| Ok(()));
}
#[test] // let arid: Mbid = "11111111-1111-1111-1111-111111111111".try_into().unwrap();
fn fetch_artist() {
let mut mb_job_sender = MockIMbJobSender::new();
let artist = COLLECTION[3].meta.clone(); // let (album_1, matches_1) = album_expectations_1();
search_artist_expectation(&mut mb_job_sender, &artist); // let (album_4, matches_4) = album_expectations_4();
let music_hoard = music_hoard(COLLECTION.to_owned()); // // Other albums have an MBID and so they will be skipped.
let inner = AppInner::new(music_hoard, mb_job_sender); // let mut seq = Sequence::new();
// Use fourth artist to match the expectation. // search_release_group_expectation(&mut mb_api, &mut seq, &arid, &album_1, &matches_1);
let browse = AppMachine::browse_state(inner); // search_release_group_expectation(&mut mb_api, &mut seq, &arid, &album_4, &matches_4);
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 app = app.unwrap_browse().fetch_musicbrainz(); // let music_hoard = music_hoard(COLLECTION.to_owned());
assert!(matches!(app, AppState::Match(_))); // let (events_tx, events_rx) = event_channel();
} // let inner = AppInner::new(music_hoard, mb_api, events_tx);
#[test] // let (fetch_tx, fetch_rx) = mpsc::channel();
fn fetch_artist_job_sender_err() { // // Use the second artist for this test.
let mut mb_job_sender = MockIMbJobSender::new(); // let handle = AppMachine::spawn_fetch_thread(&inner, &COLLECTION[1], fetch_tx);
// handle.join().unwrap();
mb_job_sender // assert_eq!(events_rx.try_recv().unwrap(), Event::FetchResultReady);
.expect_submit_background_job() // let result = fetch_rx.try_recv().unwrap();
.return_once(|_, _| Err(DaemonError::JobChannelDisconnected)); // let expected = Ok(MatchStateInfo::Album(AlbumMatches {
// matching: album_1.clone(),
// list: matches_1.iter().cloned().map(Into::into).collect(),
// }));
// assert_eq!(result, expected);
let music_hoard = music_hoard(COLLECTION.to_owned()); // assert_eq!(events_rx.try_recv().unwrap(), Event::FetchResultReady);
let inner = AppInner::new(music_hoard, mb_job_sender); // let result = fetch_rx.try_recv().unwrap();
let browse = AppMachine::browse_state(inner); // let expected = Ok(MatchStateInfo::Album(AlbumMatches {
// matching: album_4.clone(),
// list: matches_4.iter().cloned().map(Into::into).collect(),
// }));
// assert_eq!(result, expected);
// }
let app = browse.fetch_musicbrainz(); // fn artist_expectations() -> (ArtistMeta, Vec<Match<ArtistMeta>>) {
assert!(matches!(app, AppState::Error(_))); // let artist = COLLECTION[3].meta.clone();
}
#[test] // let artist_match_1 = Match::new(100, artist.clone());
fn recv_ok_fetch_ok() { // let artist_match_2 = Match::new(50, artist.clone());
let (tx, rx) = mpsc::channel::<MbApiResult>(); // let matches = vec![artist_match_1.clone(), artist_match_2.clone()];
let artist = COLLECTION[3].meta.clone(); // (artist, matches)
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();
let inner = inner(music_hoard(COLLECTION.clone())); // fn search_artist_expectation(
let fetch = FetchState::new(rx); // api: &mut MockIMusicBrainz,
let mut app = AppMachine::app_fetch(inner, fetch, true); // seq: &mut Sequence,
assert!(matches!(app, AppState::Match(_))); // 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);
// }
let public = app.get(); // #[test]
let match_state = public.state.unwrap_match(); // fn fetch_artist() {
let match_options = vec![ // let mut mb_api = MockIMusicBrainz::new();
artist_match.into(),
MatchOption::CannotHaveMbid,
MatchOption::ManualInputMbid,
];
let expected = MatchStateInfo::artist(artist, match_options);
assert_eq!(match_state.info, Some(expected).as_ref());
}
#[test] // let (artist, matches) = artist_expectations();
fn recv_ok_fetch_err() { // let mut seq = Sequence::new();
let (tx, rx) = mpsc::channel::<MbApiResult>(); // search_artist_expectation(&mut mb_api, &mut seq, &artist, &matches);
let fetch_result = Err(musicbrainz::api::Error::RateLimit); // let music_hoard = music_hoard(COLLECTION.to_owned());
tx.send(fetch_result).unwrap(); // let (events_tx, events_rx) = event_channel();
// let inner = AppInner::new(music_hoard, mb_api, events_tx);
let inner = inner(music_hoard(COLLECTION.clone())); // let (fetch_tx, fetch_rx) = mpsc::channel();
let fetch = FetchState::new(rx); // // Use the fourth artist for this test as they have no MBID.
let app = AppMachine::app_fetch(inner, fetch, true); // let handle = AppMachine::spawn_fetch_thread(&inner, &COLLECTION[3], fetch_tx);
assert!(matches!(app, AppState::Error(_))); // handle.join().unwrap();
}
#[test] // assert_eq!(events_rx.try_recv().unwrap(), Event::FetchResultReady);
fn recv_err_empty() { // let result = fetch_rx.try_recv().unwrap();
let (_tx, rx) = mpsc::channel::<MbApiResult>(); // let expected = Ok(MatchStateInfo::Artist(ArtistMatches {
// matching: artist.clone(),
// list: matches.iter().cloned().map(Into::into).collect(),
// }));
// assert_eq!(result, expected);
// }
let inner = inner(music_hoard(COLLECTION.clone())); // #[test]
let fetch = FetchState::new(rx); // fn fetch_artist_fetch_disconnect() {
let app = AppMachine::app_fetch(inner, fetch, true); // let mut mb_api = MockIMusicBrainz::new();
assert!(matches!(app, AppState::Fetch(_)));
}
#[test] // let (artist, matches) = artist_expectations();
fn recv_err_disconnected_first() { // let mut seq = Sequence::new();
let (_, rx) = mpsc::channel::<MbApiResult>(); // search_artist_expectation(&mut mb_api, &mut seq, &artist, &matches);
let inner = inner(music_hoard(COLLECTION.clone())); // let music_hoard = music_hoard(COLLECTION.to_owned());
let fetch = FetchState::new(rx); // let (events_tx, events_rx) = event_channel();
let app = AppMachine::app_fetch(inner, fetch, true); // let inner = AppInner::new(music_hoard, mb_api, events_tx);
assert!(matches!(app, AppState::Match(_)));
}
#[test] // let (fetch_tx, _) = mpsc::channel();
fn recv_err_disconnected_next() { // // Use the fourth artist for this test as they have no MBID.
let (_, rx) = mpsc::channel::<MbApiResult>(); // let handle = AppMachine::spawn_fetch_thread(&inner, &COLLECTION[3], fetch_tx);
// handle.join().unwrap();
let fetch = FetchState::new(rx); // assert!(events_rx.try_recv().is_err());
let app = AppMachine::app_fetch_next(inner(music_hoard(COLLECTION.clone())), fetch); // }
assert!(matches!(app, AppState::Browse(_)));
}
#[test] // #[test]
fn empty_first_then_ready() { // fn fetch_albums_event_disconnect() {
let (tx, rx) = mpsc::channel::<MbApiResult>(); // let mut mb_api = MockIMusicBrainz::new();
let inner = inner(music_hoard(COLLECTION.clone())); // let arid: Mbid = "11111111-1111-1111-1111-111111111111".try_into().unwrap();
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 (album_1, matches_1) = album_expectations_1();
let fetch_result = Ok(MatchStateInfo::artist::<Match<ArtistMeta>>(artist, vec![]));
tx.send(fetch_result).unwrap();
let app = app.unwrap_fetch().fetch_result_ready(); // let mut seq = Sequence::new();
assert!(matches!(app, AppState::Match(_))); // search_release_group_expectation(&mut mb_api, &mut seq, &arid, &album_1, &matches_1);
}
#[test] // let music_hoard = music_hoard(COLLECTION.to_owned());
fn abort() { // let (events_tx, _) = event_channel();
let (_, rx) = mpsc::channel::<MbApiResult>(); // let inner = AppInner::new(music_hoard, mb_api, events_tx);
let fetch = FetchState::new(rx); // let (fetch_tx, fetch_rx) = mpsc::channel();
let app = AppMachine::fetch_state(inner(music_hoard(COLLECTION.clone())), fetch); // // Use the second artist for this test.
// let handle = AppMachine::spawn_fetch_thread(&inner, &COLLECTION[1], fetch_tx);
// handle.join().unwrap();
let app = app.abort(); // let result = fetch_rx.try_recv().unwrap();
assert!(matches!(app, AppState::Browse(_))); // 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);
// }
// #[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::{mb_job_sender, music_hoard_init}, // machine::tests::{events, mb_api, 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_job_sender()); // let mut app = App::new(music_hoard_init(vec![]), mb_api(), events());
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,376 +215,378 @@ 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::daemon::MockIMbJobSender, MockIMusicHoard}, // lib::{interface::musicbrainz::api::MockIMusicBrainz, MockIMusicHoard},
}; // EventChannel,
// };
use super::*;
// use super::*;
impl<StateMode, InputMode> AppMode<StateMode, InputMode> {
fn unwrap_state(self) -> StateMode { // impl<StateMode, InputMode> AppMode<StateMode, InputMode> {
match self { // fn unwrap_state(self) -> StateMode {
AppMode::State(state) => state, // match self {
_ => panic!(), // AppMode::State(state) => state,
} // _ => panic!(),
} // }
// }
pub fn unwrap_input(self) -> InputMode {
match self { // pub fn unwrap_input(self) -> InputMode {
AppMode::Input(input) => input, // match self {
_ => panic!(), // AppMode::Input(input) => input,
} // _ => panic!(),
} // }
} // }
// }
impl<
BrowseState, // impl<
InfoState, // BrowseState,
ReloadState, // InfoState,
SearchState, // ReloadState,
FetchState, // SearchState,
MatchState, // FetchState,
ErrorState, // MatchState,
CriticalState, // ErrorState,
> // CriticalState,
AppState< // >
BrowseState, // AppState<
InfoState, // BrowseState,
ReloadState, // InfoState,
SearchState, // ReloadState,
FetchState, // SearchState,
MatchState, // FetchState,
ErrorState, // MatchState,
CriticalState, // ErrorState,
> // CriticalState,
{ // >
pub fn unwrap_browse(self) -> BrowseState { // {
match self { // pub fn unwrap_browse(self) -> BrowseState {
AppState::Browse(browse) => browse, // match self {
_ => panic!(), // AppState::Browse(browse) => browse,
} // _ => panic!(),
} // }
// }
pub fn unwrap_info(self) -> InfoState {
match self { // pub fn unwrap_info(self) -> InfoState {
AppState::Info(info) => info, // match self {
_ => panic!(), // AppState::Info(info) => info,
} // _ => panic!(),
} // }
// }
pub fn unwrap_reload(self) -> ReloadState {
match self { // pub fn unwrap_reload(self) -> ReloadState {
AppState::Reload(reload) => reload, // match self {
_ => panic!(), // AppState::Reload(reload) => reload,
} // _ => panic!(),
} // }
// }
pub fn unwrap_search(self) -> SearchState {
match self { // pub fn unwrap_search(self) -> SearchState {
AppState::Search(search) => search, // match self {
_ => panic!(), // AppState::Search(search) => search,
} // _ => panic!(),
} // }
// }
pub fn unwrap_fetch(self) -> FetchState {
match self { // pub fn unwrap_fetch(self) -> FetchState {
AppState::Fetch(fetch) => fetch, // match self {
_ => panic!(), // AppState::Fetch(fetch) => fetch,
} // _ => panic!(),
} // }
// }
pub fn unwrap_match(self) -> MatchState {
match self { // pub fn unwrap_match(self) -> MatchState {
AppState::Match(matches) => matches, // match self {
_ => panic!(), // AppState::Match(matches) => matches,
} // _ => panic!(),
} // }
// }
pub fn unwrap_error(self) -> ErrorState {
match self { // pub fn unwrap_error(self) -> ErrorState {
AppState::Error(error) => error, // match self {
_ => panic!(), // AppState::Error(error) => error,
} // _ => panic!(),
} // }
// }
pub fn unwrap_critical(self) -> CriticalState {
match self { // pub fn unwrap_critical(self) -> CriticalState {
AppState::Critical(critical) => critical, // match self {
_ => panic!(), // AppState::Critical(critical) => critical,
} // _ => panic!(),
} // }
} // }
// }
pub fn music_hoard(collection: Collection) -> MockIMusicHoard {
let mut music_hoard = MockIMusicHoard::new(); // pub fn music_hoard(collection: Collection) -> MockIMusicHoard {
music_hoard.expect_get_collection().return_const(collection); // let mut music_hoard = MockIMusicHoard::new();
// music_hoard.expect_get_collection().return_const(collection);
music_hoard
} // music_hoard
// }
pub fn music_hoard_init(collection: Collection) -> MockIMusicHoard {
let mut music_hoard = music_hoard(collection); // pub fn music_hoard_init(collection: Collection) -> MockIMusicHoard {
// let mut music_hoard = music_hoard(collection);
music_hoard
.expect_rescan_library() // music_hoard
.times(1) // .expect_rescan_library()
.return_once(|| Ok(())); // .times(1)
// .return_once(|| Ok(()));
music_hoard
} // music_hoard
// }
pub fn mb_job_sender() -> MockIMbJobSender {
MockIMbJobSender::new() // pub fn mb_api() -> MockIMusicBrainz {
} // MockIMusicBrainz::new()
// }
pub fn inner(music_hoard: MockIMusicHoard) -> AppInner {
AppInner::new(music_hoard, mb_job_sender()) // pub fn events() -> EventSender {
} // EventChannel::new().sender()
// }
pub fn inner_with_mb(
music_hoard: MockIMusicHoard, // pub fn inner(music_hoard: MockIMusicHoard) -> AppInner {
mb_job_sender: MockIMbJobSender, // AppInner::new(music_hoard, mb_api(), events())
) -> 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() {
let app = App::new(music_hoard_init(vec![]), mb_job_sender()); // #[test]
assert!(app.is_running()); // fn input_mode() {
// let app = App::new(music_hoard_init(vec![]), mb_api(), events());
let mode = app.mode(); // assert!(app.is_running());
assert!(matches!(mode, AppMode::State(_)));
// let mode = app.mode();
let state = mode.unwrap_state(); // assert!(matches!(mode, AppMode::State(_)));
assert!(matches!(state, AppState::Browse(_)));
// let state = mode.unwrap_state();
let mut app = state; // assert!(matches!(state, AppState::Browse(_)));
app.input_mut().replace(Input::default());
// let mut app = state;
let public = app.get(); // app.input_mut().replace(Input::default());
assert!(public.input.is_some());
// let public = app.get();
let mode = app.mode(); // assert!(public.input.is_some());
assert!(matches!(mode, AppMode::Input(_)));
// let mode = app.mode();
let mut app = mode.unwrap_input().cancel(); // assert!(matches!(mode, AppMode::Input(_)));
assert!(matches!(app, AppState::Browse(_)));
// let mut app = mode.unwrap_input().cancel();
let public = app.get(); // assert!(matches!(app, AppState::Browse(_)));
assert!(public.input.is_none());
} // let public = app.get();
// assert!(public.input.is_none());
#[test] // }
fn state_browse() {
let mut app = App::new(music_hoard_init(vec![]), mb_job_sender()); // #[test]
assert!(app.is_running()); // fn state_browse() {
// let mut app = App::new(music_hoard_init(vec![]), mb_api(), events());
let state = app.state(); // assert!(app.is_running());
assert!(matches!(state, AppState::Browse(_)));
app = state; // let state = app.state();
// assert!(matches!(state, AppState::Browse(_)));
app = app.no_op(); // app = state;
let state = app.state();
assert!(matches!(state, AppState::Browse(_))); // app = app.no_op();
app = state; // let state = app.state();
// assert!(matches!(state, AppState::Browse(_)));
let public = app.get(); // app = state;
assert!(matches!(public.state, AppState::Browse(_)));
// let public = app.get();
let app = app.force_quit(); // assert!(matches!(public.state, AppState::Browse(_)));
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;
app = app.no_op(); // let app = app.force_quit();
let state = app.state(); // assert!(!app.is_running());
assert!(matches!(state, AppState::Info(_))); // }
app = state;
let public = app.get(); // #[test]
assert!(matches!(public.state, AppState::Info(_))); // fn state_info() {
// let mut app = App::new(music_hoard_init(vec![]), mb_api(), events());
// assert!(app.is_running());
let app = app.force_quit(); // app = app.unwrap_browse().show_info_overlay();
assert!(!app.is_running());
}
#[test] // let state = app.state();
fn state_reload() { // assert!(matches!(state, AppState::Info(_)));
let mut app = App::new(music_hoard_init(vec![]), mb_job_sender()); // app = state;
assert!(app.is_running());
app = app.unwrap_browse().show_reload_menu(); // app = app.no_op();
// let state = app.state();
// assert!(matches!(state, AppState::Info(_)));
// app = state;
let state = app.state(); // let public = app.get();
assert!(matches!(state, AppState::Reload(_))); // assert!(matches!(public.state, AppState::Info(_)));
app = state;
app = app.no_op(); // let app = app.force_quit();
let state = app.state(); // assert!(!app.is_running());
assert!(matches!(state, AppState::Reload(_))); // }
app = state;
let public = app.get(); // #[test]
assert!(matches!(public.state, AppState::Reload(_))); // fn state_reload() {
// let mut app = App::new(music_hoard_init(vec![]), mb_api(), events());
// assert!(app.is_running());
let app = app.force_quit(); // app = app.unwrap_browse().show_reload_menu();
assert!(!app.is_running());
}
#[test] // let state = app.state();
fn state_search() { // assert!(matches!(state, AppState::Reload(_)));
let mut app = App::new(music_hoard_init(vec![]), mb_job_sender()); // app = state;
assert!(app.is_running());
app = app.unwrap_browse().begin_search(); // app = app.no_op();
// let state = app.state();
// assert!(matches!(state, AppState::Reload(_)));
// app = state;
let state = app.state(); // let public = app.get();
assert!(matches!(state, AppState::Search(_))); // assert!(matches!(public.state, AppState::Reload(_)));
app = state;
app = app.no_op(); // let app = app.force_quit();
let state = app.state(); // assert!(!app.is_running());
assert!(matches!(state, AppState::Search(_))); // }
app = state;
let public = app.get(); // #[test]
assert!(matches!(public.state, AppState::Search(""))); // fn state_search() {
// let mut app = App::new(music_hoard_init(vec![]), mb_api(), events());
// assert!(app.is_running());
let app = app.force_quit(); // app = app.unwrap_browse().begin_search();
assert!(!app.is_running());
}
#[test] // let state = app.state();
fn state_fetch() { // assert!(matches!(state, AppState::Search(_)));
let mut app = App::new(music_hoard_init(vec![]), mb_job_sender()); // app = state;
assert!(app.is_running());
let (_, rx) = mpsc::channel(); // app = app.no_op();
let inner = app.unwrap_browse().inner; // let state = app.state();
let state = FetchState::new(rx); // assert!(matches!(state, AppState::Search(_)));
app = AppMachine::new(inner, state).into(); // app = state;
let state = app.state(); // let public = app.get();
assert!(matches!(state, AppState::Fetch(_))); // assert!(matches!(public.state, AppState::Search("")));
app = state;
app = app.no_op();
let state = app.state();
assert!(matches!(state, AppState::Fetch(_)));
app = state;
let public = app.get(); // let app = app.force_quit();
assert!(matches!(public.state, AppState::Fetch(_))); // assert!(!app.is_running());
// }
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());
let (_, rx) = mpsc::channel(); // #[test]
let fetch = FetchState::new(rx); // fn state_fetch() {
app = // let mut app = App::new(music_hoard_init(vec![]), mb_api(), events());
AppMachine::match_state(app.unwrap_browse().inner, MatchState::new(None, fetch)).into(); // assert!(app.is_running());
let state = app.state(); // let (_, rx) = mpsc::channel();
assert!(matches!(state, AppState::Match(_))); // let inner = app.unwrap_browse().inner;
app = state; // let state = FetchState::new(rx);
// 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::Error(_))); // assert!(matches!(state, AppState::Fetch(_)));
app = state; // app = state;
app = app.no_op(); // app = app.no_op();
let state = app.state(); // let state = app.state();
assert!(matches!(state, AppState::Error(_))); // assert!(matches!(state, AppState::Fetch(_)));
app = state; // app = state;
let public = app.get(); // let public = app.get();
assert!(matches!(public.state, AppState::Error("get rekt"))); // assert!(matches!(public.state, AppState::Fetch(_)));
app = app.force_quit(); // let app = app.force_quit();
assert!(!app.is_running()); // assert!(!app.is_running());
} // }
#[test] // #[test]
fn state_critical() { // fn state_match() {
let mut app = App::new(music_hoard_init(vec![]), mb_job_sender()); // let mut app = App::new(music_hoard_init(vec![]), mb_api(), events());
assert!(app.is_running()); // assert!(app.is_running());
app = AppMachine::critical_state(app.unwrap_browse().inner, "get rekt").into(); // let (_, rx) = mpsc::channel();
// let fetch = FetchState::new(rx);
let state = app.state(); // app =
assert!(matches!(state, AppState::Critical(_))); // AppMachine::match_state(app.unwrap_browse().inner, MatchState::new(None, fetch)).into();
app = state;
// let state = app.state();
app = app.no_op(); // assert!(matches!(state, AppState::Match(_)));
let state = app.state(); // app = state;
assert!(matches!(state, AppState::Critical(_)));
app = state; // app = app.no_op();
// let state = app.state();
let public = app.get(); // assert!(matches!(state, AppState::Match(_)));
assert!(matches!(public.state, AppState::Critical("get rekt"))); // app = state;
app = app.force_quit(); // let public = app.get();
assert!(!app.is_running()); // assert!(matches!(public.state, AppState::Match(_)));
}
// let app = app.force_quit();
#[test] // assert!(!app.is_running());
fn init_error() { // }
let mut music_hoard = MockIMusicHoard::new();
// #[test]
music_hoard // fn state_error() {
.expect_rescan_library() // let mut app = App::new(music_hoard_init(vec![]), mb_api(), events());
.times(1) // assert!(app.is_running());
.return_once(|| Err(musichoard::Error::LibraryError(String::from("get rekt"))));
music_hoard.expect_get_collection().return_const(vec![]); // app = AppMachine::error_state(app.unwrap_browse().inner, "get rekt").into();
let app = App::new(music_hoard, mb_job_sender()); // let state = app.state();
assert!(app.is_running()); // assert!(matches!(state, AppState::Error(_)));
app.unwrap_critical(); // app = state;
}
} // 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,9 +2,6 @@ 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),
@ -53,7 +50,6 @@ 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

@ -9,6 +9,11 @@ use crate::tui::{
}, },
}; };
enum JobError {
JobQueueEmpty,
EventChannelDisconnected,
}
pub struct MusicBrainzDaemon { pub struct MusicBrainzDaemon {
musicbrainz: Box<dyn IMusicBrainz>, musicbrainz: Box<dyn IMusicBrainz>,
job_receiver: mpsc::Receiver<Job>, job_receiver: mpsc::Receiver<Job>,
@ -61,11 +66,6 @@ impl JobInstance {
} }
} }
enum JobError {
JobQueueEmpty,
EventChannelDisconnected,
}
pub struct JobChannel { pub struct JobChannel {
sender: mpsc::Sender<Job>, sender: mpsc::Sender<Job>,
receiver: mpsc::Receiver<Job>, receiver: mpsc::Receiver<Job>,
@ -296,193 +296,3 @@ 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,9 +4,6 @@ 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,
@ -24,7 +21,6 @@ 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,
@ -33,23 +29,19 @@ 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,149 +173,154 @@ 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 lib::interface::musicbrainz::daemon::MockIMbJobSender; // use event::EventSender;
use ratatui::{backend::TestBackend, Terminal}; // use lib::interface::musicbrainz::api::MockIMusicBrainz;
// 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 app(collection: Collection) -> App { // fn events() -> EventSender {
App::new(music_hoard(collection), MockIMbJobSender::new()) // EventChannel::new().sender()
} // }
fn listener() -> MockIEventListener { // fn app(collection: Collection) -> App {
let mut listener = MockIEventListener::new(); // App::new(music_hoard(collection), MockIMusicBrainz::new(), events())
listener.expect_spawn().return_once(|| { // }
thread::spawn(|| {
thread::park();
EventError::Io(io::Error::new(io::ErrorKind::Interrupted, "unparked"))
})
});
listener
}
fn handler() -> MockIEventHandler<App> { // fn listener() -> MockIEventListener {
let mut handler = MockIEventHandler::new(); // let mut listener = MockIEventListener::new();
handler // listener.expect_spawn().return_once(|| {
.expect_handle_next_event() // thread::spawn(|| {
.return_once(|app: App| Ok(app.force_quit())); // thread::park();
handler // EventError::Io(io::Error::new(io::ErrorKind::Interrupted, "unparked"))
} // })
// });
// listener
// }
#[test] // fn handler() -> MockIEventHandler<App> {
fn run() { // let mut handler = MockIEventHandler::new();
let terminal = terminal(); // handler
let app = app(COLLECTION.to_owned()); // .expect_handle_next_event()
let ui = Ui; // .return_once(|app: App| Ok(app.force_quit()));
// handler
// }
let listener = listener(); // #[test]
let handler = handler(); // fn run() {
// let terminal = terminal();
// let app = app(COLLECTION.to_owned());
// let ui = Ui;
let result = Tui::main(terminal, app, ui, handler, listener); // let listener = listener();
assert!(result.is_ok()); // let handler = handler();
}
#[test] // let result = Tui::main(terminal, app, ui, handler, listener);
fn event_error() { // assert!(result.is_ok());
let terminal = terminal(); // }
let app = app(COLLECTION.to_owned());
let ui = Ui;
let listener = listener(); // #[test]
// fn event_error() {
// let terminal = terminal();
// let app = app(COLLECTION.to_owned());
// let ui = Ui;
let mut handler = MockIEventHandler::new(); // let listener = listener();
handler
.expect_handle_next_event()
.return_once(|_| Err(EventError::Recv));
let result = Tui::main(terminal, app, ui, handler, listener); // let mut handler = MockIEventHandler::new();
assert!(result.is_err()); // handler
// .expect_handle_next_event()
// .return_once(|_| Err(EventError::Recv));
let error = EventError::Recv; // let result = Tui::main(terminal, app, ui, handler, listener);
assert_eq!(result.unwrap_err(), Error::Event(error.to_string())); // assert!(result.is_err());
}
#[test] // let error = EventError::Recv;
fn listener_error() { // assert_eq!(result.unwrap_err(), Error::Event(error.to_string()));
let terminal = terminal(); // }
let app = app(COLLECTION.to_owned());
let ui = Ui;
let error = EventError::Io(io::Error::new(io::ErrorKind::Interrupted, "error")); // #[test]
let listener_handle: thread::JoinHandle<EventError> = thread::spawn(|| error); // fn listener_error() {
while !listener_handle.is_finished() {} // let terminal = terminal();
// let app = app(COLLECTION.to_owned());
// let ui = Ui;
let mut listener = MockIEventListener::new(); // let error = EventError::Io(io::Error::new(io::ErrorKind::Interrupted, "error"));
listener.expect_spawn().return_once(|| listener_handle); // let listener_handle: thread::JoinHandle<EventError> = thread::spawn(|| error);
// while !listener_handle.is_finished() {}
let mut handler = MockIEventHandler::new(); // let mut listener = MockIEventListener::new();
handler // listener.expect_spawn().return_once(|| listener_handle);
.expect_handle_next_event()
.return_once(|_| Err(EventError::Recv));
let result = Tui::main(terminal, app, ui, handler, listener); // let mut handler = MockIEventHandler::new();
assert!(result.is_err()); // handler
// .expect_handle_next_event()
// .return_once(|_| Err(EventError::Recv));
let error = EventError::Io(io::Error::new(io::ErrorKind::Interrupted, "error")); // let result = Tui::main(terminal, app, ui, handler, listener);
assert_eq!(result.unwrap_err(), Error::Event(error.to_string())); // assert!(result.is_err());
}
#[test] // let error = EventError::Io(io::Error::new(io::ErrorKind::Interrupted, "error"));
fn listener_panic() { // assert_eq!(result.unwrap_err(), Error::Event(error.to_string()));
let terminal = terminal(); // }
let app = app(COLLECTION.to_owned());
let ui = Ui;
let listener_handle: thread::JoinHandle<EventError> = thread::spawn(|| panic!()); // #[test]
while !listener_handle.is_finished() {} // fn listener_panic() {
// let terminal = terminal();
// let app = app(COLLECTION.to_owned());
// let ui = Ui;
let mut listener = MockIEventListener::new(); // let listener_handle: thread::JoinHandle<EventError> = thread::spawn(|| panic!());
listener.expect_spawn().return_once(|| listener_handle); // while !listener_handle.is_finished() {}
let mut handler = MockIEventHandler::new(); // let mut listener = MockIEventListener::new();
handler // listener.expect_spawn().return_once(|| listener_handle);
.expect_handle_next_event()
.return_once(|_| Err(EventError::Recv));
let result = Tui::main(terminal, app, ui, handler, listener); // let mut handler = MockIEventHandler::new();
assert!(result.is_err()); // handler
assert_eq!(result.unwrap_err(), Error::ListenerPanic); // .expect_handle_next_event()
} // .return_once(|_| Err(EventError::Recv));
#[test] // let result = Tui::main(terminal, app, ui, handler, listener);
fn errors() { // assert!(result.is_err());
let io_err: Error = io::Error::new(io::ErrorKind::Interrupted, "error").into(); // assert_eq!(result.unwrap_err(), Error::ListenerPanic);
let event_err: Error = EventError::Recv.into(); // }
let listener_err = Error::ListenerPanic;
assert!(!format!("{:?}", io_err).is_empty()); // #[test]
assert!(!format!("{:?}", event_err).is_empty()); // fn errors() {
assert!(!format!("{:?}", listener_err).is_empty()); // let io_err: Error = io::Error::new(io::ErrorKind::Interrupted, "error").into();
} // 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();
} // }
} // }