Daemonize the musicbrainz thread #217

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

View File

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

View File

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

View File

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

View File

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

View File

@ -55,45 +55,45 @@ impl IAppInput for AppInputMode {
}
}
// #[cfg(test)]
// mod tests {
// use crate::tui::app::{
// machine::tests::{events, mb_api, music_hoard_init},
// IApp,
// };
#[cfg(test)]
mod tests {
use crate::tui::app::{
machine::tests::{mb_job_sender, music_hoard_init},
IApp,
};
// use super::*;
use super::*;
// fn input_event(c: char) -> InputEvent {
// crossterm::event::KeyEvent::new(
// crossterm::event::KeyCode::Char(c),
// crossterm::event::KeyModifiers::empty(),
// )
// .into()
// }
fn input_event(c: char) -> InputEvent {
crossterm::event::KeyEvent::new(
crossterm::event::KeyCode::Char(c),
crossterm::event::KeyModifiers::empty(),
)
.into()
}
// #[test]
// fn handle_input() {
// let mut app = App::new(music_hoard_init(vec![]), mb_api(), events());
// app.input_mut().replace(Input::default());
#[test]
fn handle_input() {
let mut app = App::new(music_hoard_init(vec![]), mb_job_sender());
app.input_mut().replace(Input::default());
// let input = app.mode().unwrap_input();
// let app = input.input(input_event('H'));
let input = app.mode().unwrap_input();
let app = input.input(input_event('H'));
// let input = app.mode().unwrap_input();
// let app = input.input(input_event('e'));
let input = app.mode().unwrap_input();
let app = input.input(input_event('e'));
// let input = app.mode().unwrap_input();
// let app = input.input(input_event('l'));
let input = app.mode().unwrap_input();
let app = input.input(input_event('l'));
// let input = app.mode().unwrap_input();
// let app = input.input(input_event('l'));
let input = app.mode().unwrap_input();
let app = input.input(input_event('l'));
// let input = app.mode().unwrap_input();
// let app = input.input(input_event('o'));
let input = app.mode().unwrap_input();
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)]
// mod tests {
// use std::sync::mpsc;
#[cfg(test)]
mod tests {
use std::sync::mpsc;
// use musichoard::collection::{
// album::{AlbumDate, AlbumId, AlbumMeta, AlbumPrimaryType, AlbumSecondaryType},
// artist::{ArtistId, ArtistMeta},
// };
use musichoard::collection::{
album::{AlbumDate, AlbumId, AlbumMeta, AlbumPrimaryType, AlbumSecondaryType},
artist::{ArtistId, ArtistMeta},
};
// use crate::tui::{
// app::{
// machine::tests::{inner, music_hoard},
// IApp, IAppAccess, IAppInput,
// },
// lib::interface::musicbrainz::api::Match,
// };
use crate::tui::{
app::{
machine::tests::{inner, music_hoard},
IApp, IAppAccess, IAppInput,
},
lib::interface::musicbrainz::api::Match,
};
// use super::*;
use super::*;
// impl<T> Match<T> {
// pub fn new(score: u8, item: T) -> Self {
// Match {
// score,
// item,
// disambiguation: None,
// }
// }
// }
impl<T> Match<T> {
pub fn new(score: u8, item: T) -> Self {
Match {
score,
item,
disambiguation: None,
}
}
}
// fn artist_match() -> MatchStateInfo {
// let artist = ArtistMeta::new(ArtistId::new("Artist"));
fn artist_match() -> MatchStateInfo {
let artist = ArtistMeta::new(ArtistId::new("Artist"));
// let artist_1 = artist.clone();
// let artist_match_1 = Match::new(100, artist_1);
let artist_1 = artist.clone();
let artist_match_1 = Match::new(100, artist_1);
// let artist_2 = artist.clone();
// let mut artist_match_2 = Match::new(100, artist_2);
// artist_match_2.disambiguation = Some(String::from("some disambiguation"));
let artist_2 = artist.clone();
let mut artist_match_2 = Match::new(100, artist_2);
artist_match_2.disambiguation = Some(String::from("some disambiguation"));
// let list = vec![artist_match_1.clone(), artist_match_2.clone()];
// MatchStateInfo::artist(artist, list)
// }
let list = vec![artist_match_1.clone(), artist_match_2.clone()];
MatchStateInfo::artist(artist, list)
}
// fn album_match() -> MatchStateInfo {
// let album = AlbumMeta::new(
// AlbumId::new("Album"),
// AlbumDate::new(Some(1990), Some(5), None),
// Some(AlbumPrimaryType::Album),
// vec![AlbumSecondaryType::Live, AlbumSecondaryType::Compilation],
// );
fn album_match() -> MatchStateInfo {
let album = AlbumMeta::new(
AlbumId::new("Album"),
AlbumDate::new(Some(1990), Some(5), None),
Some(AlbumPrimaryType::Album),
vec![AlbumSecondaryType::Live, AlbumSecondaryType::Compilation],
);
// let album_1 = album.clone();
// let album_match_1 = Match::new(100, album_1);
let album_1 = album.clone();
let album_match_1 = Match::new(100, album_1);
// let mut album_2 = album.clone();
// album_2.id.title.push_str(" extra title part");
// album_2.secondary_types.pop();
// let album_match_2 = Match::new(100, album_2);
let mut album_2 = album.clone();
album_2.id.title.push_str(" extra title part");
album_2.secondary_types.pop();
let album_match_2 = Match::new(100, album_2);
// let list = vec![album_match_1.clone(), album_match_2.clone()];
// MatchStateInfo::album(album, list)
// }
let list = vec![album_match_1.clone(), album_match_2.clone()];
MatchStateInfo::album(album, list)
}
// fn fetch_state() -> FetchState {
// let (_, rx) = mpsc::channel();
// FetchState::new(rx)
// }
fn fetch_state() -> FetchState {
let (_, rx) = mpsc::channel();
FetchState::new(rx)
}
// fn match_state(matches_info: Option<MatchStateInfo>) -> MatchState {
// MatchState::new(matches_info, fetch_state())
// }
fn match_state(matches_info: Option<MatchStateInfo>) -> MatchState {
MatchState::new(matches_info, fetch_state())
}
// #[test]
// fn create_empty() {
// let matches = AppMachine::match_state(inner(music_hoard(vec![])), match_state(None));
#[test]
fn create_empty() {
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.state, widget_state);
assert_eq!(matches.state.current, None);
assert_eq!(matches.state.state, widget_state);
// let mut app: App = matches.into();
// let public = app.get();
// let public_matches = public.state.unwrap_match();
let mut app: App = matches.into();
let public = app.get();
let public_matches = public.state.unwrap_match();
// assert_eq!(public_matches.info, None);
// assert_eq!(public_matches.state, &widget_state);
// }
assert_eq!(public_matches.info, None);
assert_eq!(public_matches.state, &widget_state);
}
// #[test]
// fn create_nonempty() {
// let mut album_match = album_match();
// let matches = AppMachine::match_state(
// inner(music_hoard(vec![])),
// match_state(Some(album_match.clone())),
// );
// album_match.push_cannot_have_mbid();
// album_match.push_manual_input_mbid();
#[test]
fn create_nonempty() {
let mut album_match = album_match();
let matches = AppMachine::match_state(
inner(music_hoard(vec![])),
match_state(Some(album_match.clone())),
);
album_match.push_cannot_have_mbid();
album_match.push_manual_input_mbid();
// let mut widget_state = WidgetState::default();
// widget_state.list.select(Some(0));
let mut widget_state = WidgetState::default();
widget_state.list.select(Some(0));
// assert_eq!(matches.state.current.as_ref(), Some(&album_match));
// assert_eq!(matches.state.state, widget_state);
assert_eq!(matches.state.current.as_ref(), Some(&album_match));
assert_eq!(matches.state.state, widget_state);
// let mut app: App = matches.into();
// let public = app.get();
// let public_matches = public.state.unwrap_match();
let mut app: App = matches.into();
let public = app.get();
let public_matches = public.state.unwrap_match();
// assert_eq!(public_matches.info, Some(&album_match));
// assert_eq!(public_matches.state, &widget_state);
// }
assert_eq!(public_matches.info, Some(&album_match));
assert_eq!(public_matches.state, &widget_state);
}
// fn match_state_flow(mut matches_info: MatchStateInfo) {
// // tx must exist for rx to return Empty rather than Disconnected.
// #[allow(unused_variables)]
// let (tx, rx) = mpsc::channel();
// let app_matches = MatchState::new(Some(matches_info.clone()), FetchState::new(rx));
fn match_state_flow(mut matches_info: MatchStateInfo) {
// tx must exist for rx to return Empty rather than Disconnected.
#[allow(unused_variables)]
let (tx, rx) = mpsc::channel();
let app_matches = MatchState::new(Some(matches_info.clone()), FetchState::new(rx));
// let matches = AppMachine::match_state(inner(music_hoard(vec![])), app_matches);
// matches_info.push_cannot_have_mbid();
// matches_info.push_manual_input_mbid();
let matches = AppMachine::match_state(inner(music_hoard(vec![])), app_matches);
matches_info.push_cannot_have_mbid();
matches_info.push_manual_input_mbid();
// let mut widget_state = WidgetState::default();
// widget_state.list.select(Some(0));
let mut widget_state = WidgetState::default();
widget_state.list.select(Some(0));
// assert_eq!(matches.state.current.as_ref(), Some(&matches_info));
// assert_eq!(matches.state.state, widget_state);
assert_eq!(matches.state.current.as_ref(), Some(&matches_info));
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.state.list.selected(), Some(0));
assert_eq!(matches.state.current.as_ref(), Some(&matches_info));
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.state.list.selected(), Some(1));
assert_eq!(matches.state.current.as_ref(), Some(&matches_info));
assert_eq!(matches.state.state.list.selected(), Some(1));
// // Next is CannotHaveMBID
// let matches = matches.next_match().unwrap_match();
// Next is CannotHaveMBID
let matches = matches.next_match().unwrap_match();
// assert_eq!(matches.state.current.as_ref(), Some(&matches_info));
// assert_eq!(matches.state.state.list.selected(), Some(2));
assert_eq!(matches.state.current.as_ref(), Some(&matches_info));
assert_eq!(matches.state.state.list.selected(), Some(2));
// // Next is ManualInputMbid
// let matches = matches.next_match().unwrap_match();
// Next is ManualInputMbid
let matches = matches.next_match().unwrap_match();
// assert_eq!(matches.state.current.as_ref(), Some(&matches_info));
// assert_eq!(matches.state.state.list.selected(), Some(3));
assert_eq!(matches.state.current.as_ref(), Some(&matches_info));
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.state.list.selected(), Some(3));
assert_eq!(matches.state.current.as_ref(), Some(&matches_info));
assert_eq!(matches.state.state.list.selected(), Some(3));
// // Go prev_match first as selecting on manual input does not go back to fetch.
// let matches = matches.prev_match().unwrap_match();
// matches.select().unwrap_fetch();
// }
// Go prev_match first as selecting on manual input does not go back to fetch.
let matches = matches.prev_match().unwrap_match();
matches.select().unwrap_fetch();
}
// #[test]
// fn artist_matches_flow() {
// match_state_flow(artist_match());
// }
#[test]
fn artist_matches_flow() {
match_state_flow(artist_match());
}
// #[test]
// fn album_matches_flow() {
// match_state_flow(album_match());
// }
#[test]
fn album_matches_flow() {
match_state_flow(album_match());
}
// #[test]
// fn abort() {
// let mut album_match = album_match();
// let matches = AppMachine::match_state(
// inner(music_hoard(vec![])),
// match_state(Some(album_match.clone())),
// );
// album_match.push_cannot_have_mbid();
// album_match.push_manual_input_mbid();
#[test]
fn abort() {
let mut album_match = album_match();
let matches = AppMachine::match_state(
inner(music_hoard(vec![])),
match_state(Some(album_match.clone())),
);
album_match.push_cannot_have_mbid();
album_match.push_manual_input_mbid();
// let mut widget_state = WidgetState::default();
// widget_state.list.select(Some(0));
let mut widget_state = WidgetState::default();
widget_state.list.select(Some(0));
// assert_eq!(matches.state.current.as_ref(), Some(&album_match));
// assert_eq!(matches.state.state, widget_state);
assert_eq!(matches.state.current.as_ref(), Some(&album_match));
assert_eq!(matches.state.state, widget_state);
// matches.abort().unwrap_browse();
// }
matches.abort().unwrap_browse();
}
// #[test]
// fn select_empty() {
// // 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.
// let matches = AppMachine::match_state(inner(music_hoard(vec![])), match_state(None));
// matches.select().unwrap_browse();
// }
#[test]
fn select_empty() {
// 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.
let matches = AppMachine::match_state(inner(music_hoard(vec![])), match_state(None));
matches.select().unwrap_browse();
}
// #[test]
// fn select_manual_input() {
// let matches =
// AppMachine::match_state(inner(music_hoard(vec![])), match_state(Some(album_match())));
#[test]
fn select_manual_input() {
let matches =
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.
// 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();
// 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 app = matches.select();
let app = matches.select();
// let input = app.mode().unwrap_input();
// input.confirm().unwrap_match();
// }
// }
let input = app.mode().unwrap_input();
input.confirm().unwrap_match();
}
}

View File

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

View File

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

View File

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

View File

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

View File

@ -296,3 +296,193 @@ impl JobQueue {
}
}
}
#[cfg(test)]
mod tests {
// fn event_channel() -> (EventSender, EventReceiver) {
// let event_channel = EventChannel::new();
// let events_tx = event_channel.sender();
// let events_rx = event_channel.receiver();
// (events_tx, events_rx)
// }
// fn album_expectations_1() -> (AlbumMeta, Vec<Match<AlbumMeta>>) {
// let album_1 = COLLECTION[1].albums[0].meta.clone();
// let album_4 = COLLECTION[1].albums[3].meta.clone();
// let album_match_1_1 = Match::new(100, album_1.clone());
// let album_match_1_2 = Match::new(50, album_4.clone());
// let matches_1 = vec![album_match_1_1.clone(), album_match_1_2.clone()];
// (album_1, matches_1)
// }
// fn album_expectations_4() -> (AlbumMeta, Vec<Match<AlbumMeta>>) {
// let album_1 = COLLECTION[1].albums[0].meta.clone();
// let album_4 = COLLECTION[1].albums[3].meta.clone();
// let album_match_4_1 = Match::new(100, album_4.clone());
// let album_match_4_2 = Match::new(30, album_1.clone());
// let matches_4 = vec![album_match_4_1.clone(), album_match_4_2.clone()];
// (album_4, matches_4)
// }
// fn search_release_group_expectation(
// api: &mut MockIMusicBrainz,
// seq: &mut Sequence,
// arid: &Mbid,
// album: &AlbumMeta,
// matches: &[Match<AlbumMeta>],
// ) {
// let result = Ok(matches.to_owned());
// api.expect_search_release_group()
// .with(predicate::eq(arid.clone()), predicate::eq(album.clone()))
// .times(1)
// .in_sequence(seq)
// .return_once(|_, _| result);
// }
// #[test]
// fn fetch_albums() {
// let mut mb_api = MockIMusicBrainz::new();
// let arid: Mbid = "11111111-1111-1111-1111-111111111111".try_into().unwrap();
// let (album_1, matches_1) = album_expectations_1();
// let (album_4, matches_4) = album_expectations_4();
// // Other albums have an MBID and so they will be skipped.
// let mut seq = Sequence::new();
// search_release_group_expectation(&mut mb_api, &mut seq, &arid, &album_1, &matches_1);
// search_release_group_expectation(&mut mb_api, &mut seq, &arid, &album_4, &matches_4);
// let music_hoard = music_hoard(COLLECTION.to_owned());
// let (events_tx, events_rx) = event_channel();
// let inner = AppInner::new(music_hoard, mb_api, events_tx);
// let (fetch_tx, fetch_rx) = mpsc::channel();
// // Use the second artist for this test.
// let handle = AppMachine::spawn_fetch_thread(&inner, &COLLECTION[1], fetch_tx);
// handle.join().unwrap();
// assert_eq!(events_rx.try_recv().unwrap(), Event::FetchResultReady);
// let result = fetch_rx.try_recv().unwrap();
// let expected = Ok(MatchStateInfo::Album(AlbumMatches {
// matching: album_1.clone(),
// list: matches_1.iter().cloned().map(Into::into).collect(),
// }));
// assert_eq!(result, expected);
// assert_eq!(events_rx.try_recv().unwrap(), Event::FetchResultReady);
// let result = fetch_rx.try_recv().unwrap();
// let expected = Ok(MatchStateInfo::Album(AlbumMatches {
// matching: album_4.clone(),
// list: matches_4.iter().cloned().map(Into::into).collect(),
// }));
// assert_eq!(result, expected);
// }
// fn artist_expectations() -> (ArtistMeta, Vec<Match<ArtistMeta>>) {
// let artist = COLLECTION[3].meta.clone();
// let artist_match_1 = Match::new(100, artist.clone());
// let artist_match_2 = Match::new(50, artist.clone());
// let matches = vec![artist_match_1.clone(), artist_match_2.clone()];
// (artist, matches)
// }
// fn search_artist_expectation(
// api: &mut MockIMusicBrainz,
// seq: &mut Sequence,
// artist: &ArtistMeta,
// matches: &[Match<ArtistMeta>],
// ) {
// let result = Ok(matches.to_owned());
// api.expect_search_artist()
// .with(predicate::eq(artist.clone()))
// .times(1)
// .in_sequence(seq)
// .return_once(|_| result);
// }
// #[test]
// fn fetch_artist() {
// let mut mb_api = MockIMusicBrainz::new();
// let (artist, matches) = artist_expectations();
// let mut seq = Sequence::new();
// search_artist_expectation(&mut mb_api, &mut seq, &artist, &matches);
// let music_hoard = music_hoard(COLLECTION.to_owned());
// let (events_tx, events_rx) = event_channel();
// let inner = AppInner::new(music_hoard, mb_api, events_tx);
// let (fetch_tx, fetch_rx) = mpsc::channel();
// // Use the fourth artist for this test as they have no MBID.
// let handle = AppMachine::spawn_fetch_thread(&inner, &COLLECTION[3], fetch_tx);
// handle.join().unwrap();
// assert_eq!(events_rx.try_recv().unwrap(), Event::FetchResultReady);
// let result = fetch_rx.try_recv().unwrap();
// let expected = Ok(MatchStateInfo::Artist(ArtistMatches {
// matching: artist.clone(),
// list: matches.iter().cloned().map(Into::into).collect(),
// }));
// assert_eq!(result, expected);
// }
// #[test]
// fn fetch_artist_fetch_disconnect() {
// let mut mb_api = MockIMusicBrainz::new();
// let (artist, matches) = artist_expectations();
// let mut seq = Sequence::new();
// search_artist_expectation(&mut mb_api, &mut seq, &artist, &matches);
// let music_hoard = music_hoard(COLLECTION.to_owned());
// let (events_tx, events_rx) = event_channel();
// let inner = AppInner::new(music_hoard, mb_api, events_tx);
// let (fetch_tx, _) = mpsc::channel();
// // Use the fourth artist for this test as they have no MBID.
// let handle = AppMachine::spawn_fetch_thread(&inner, &COLLECTION[3], fetch_tx);
// handle.join().unwrap();
// assert!(events_rx.try_recv().is_err());
// }
// #[test]
// fn fetch_albums_event_disconnect() {
// let mut mb_api = MockIMusicBrainz::new();
// let arid: Mbid = "11111111-1111-1111-1111-111111111111".try_into().unwrap();
// let (album_1, matches_1) = album_expectations_1();
// let mut seq = Sequence::new();
// search_release_group_expectation(&mut mb_api, &mut seq, &arid, &album_1, &matches_1);
// let music_hoard = music_hoard(COLLECTION.to_owned());
// let (events_tx, _) = event_channel();
// let inner = AppInner::new(music_hoard, mb_api, events_tx);
// let (fetch_tx, fetch_rx) = mpsc::channel();
// // Use the second artist for this test.
// let handle = AppMachine::spawn_fetch_thread(&inner, &COLLECTION[1], fetch_tx);
// handle.join().unwrap();
// let result = fetch_rx.try_recv().unwrap();
// let expected = Ok(MatchStateInfo::Album(AlbumMatches {
// matching: album_1.clone(),
// list: matches_1.iter().cloned().map(Into::into).collect(),
// }));
// assert_eq!(result, expected);
// assert_eq!(fetch_rx.try_recv().unwrap_err(), TryRecvError::Disconnected);
// }
}

View File

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

View File

@ -173,154 +173,149 @@ impl<B: Backend, UI: IUi, APP: IApp + IAppAccess> Tui<B, UI, APP> {
#[cfg(test)]
mod testmod;
// #[cfg(test)]
// mod tests {
// use std::{io, thread};
#[cfg(test)]
mod tests {
use std::{io, thread};
// use event::EventSender;
// use lib::interface::musicbrainz::api::MockIMusicBrainz;
// use ratatui::{backend::TestBackend, Terminal};
use lib::interface::musicbrainz::daemon::MockIMbJobSender;
use ratatui::{backend::TestBackend, Terminal};
// use musichoard::collection::Collection;
use musichoard::collection::Collection;
// use crate::tui::{
// app::App, handler::MockIEventHandler, lib::MockIMusicHoard, listener::MockIEventListener,
// ui::Ui,
// };
use crate::tui::{
app::App, handler::MockIEventHandler, lib::MockIMusicHoard, listener::MockIEventListener,
ui::Ui,
};
// use super::*;
// use testmod::COLLECTION;
use super::*;
use testmod::COLLECTION;
// pub fn terminal() -> Terminal<TestBackend> {
// let backend = TestBackend::new(150, 30);
// Terminal::new(backend).unwrap()
// }
pub fn terminal() -> Terminal<TestBackend> {
let backend = TestBackend::new(150, 30);
Terminal::new(backend).unwrap()
}
// fn music_hoard(collection: Collection) -> MockIMusicHoard {
// let mut music_hoard = MockIMusicHoard::new();
fn music_hoard(collection: Collection) -> MockIMusicHoard {
let mut music_hoard = MockIMusicHoard::new();
// music_hoard.expect_reload_database().returning(|| Ok(()));
// music_hoard.expect_rescan_library().returning(|| Ok(()));
// music_hoard.expect_get_collection().return_const(collection);
music_hoard.expect_reload_database().returning(|| Ok(()));
music_hoard.expect_rescan_library().returning(|| Ok(()));
music_hoard.expect_get_collection().return_const(collection);
// music_hoard
// }
music_hoard
}
// fn events() -> EventSender {
// EventChannel::new().sender()
// }
fn app(collection: Collection) -> App {
App::new(music_hoard(collection), MockIMbJobSender::new())
}
// fn app(collection: Collection) -> App {
// App::new(music_hoard(collection), MockIMusicBrainz::new(), events())
// }
fn listener() -> MockIEventListener {
let mut listener = MockIEventListener::new();
listener.expect_spawn().return_once(|| {
thread::spawn(|| {
thread::park();
EventError::Io(io::Error::new(io::ErrorKind::Interrupted, "unparked"))
})
});
listener
}
// fn listener() -> MockIEventListener {
// let mut listener = MockIEventListener::new();
// listener.expect_spawn().return_once(|| {
// thread::spawn(|| {
// thread::park();
// EventError::Io(io::Error::new(io::ErrorKind::Interrupted, "unparked"))
// })
// });
// listener
// }
fn handler() -> MockIEventHandler<App> {
let mut handler = MockIEventHandler::new();
handler
.expect_handle_next_event()
.return_once(|app: App| Ok(app.force_quit()));
handler
}
// fn handler() -> MockIEventHandler<App> {
// let mut handler = MockIEventHandler::new();
// handler
// .expect_handle_next_event()
// .return_once(|app: App| Ok(app.force_quit()));
// handler
// }
#[test]
fn run() {
let terminal = terminal();
let app = app(COLLECTION.to_owned());
let ui = Ui;
// #[test]
// fn run() {
// let terminal = terminal();
// let app = app(COLLECTION.to_owned());
// let ui = Ui;
let listener = listener();
let handler = handler();
// let listener = listener();
// let handler = handler();
let result = Tui::main(terminal, app, ui, handler, listener);
assert!(result.is_ok());
}
// let result = Tui::main(terminal, app, ui, handler, listener);
// assert!(result.is_ok());
// }
#[test]
fn event_error() {
let terminal = terminal();
let app = app(COLLECTION.to_owned());
let ui = Ui;
// #[test]
// fn event_error() {
// let terminal = terminal();
// let app = app(COLLECTION.to_owned());
// let ui = Ui;
let listener = listener();
// let listener = listener();
let mut handler = MockIEventHandler::new();
handler
.expect_handle_next_event()
.return_once(|_| Err(EventError::Recv));
// let mut handler = MockIEventHandler::new();
// handler
// .expect_handle_next_event()
// .return_once(|_| Err(EventError::Recv));
let result = Tui::main(terminal, app, ui, handler, listener);
assert!(result.is_err());
// let result = Tui::main(terminal, app, ui, handler, listener);
// assert!(result.is_err());
let error = EventError::Recv;
assert_eq!(result.unwrap_err(), Error::Event(error.to_string()));
}
// let error = EventError::Recv;
// assert_eq!(result.unwrap_err(), Error::Event(error.to_string()));
// }
#[test]
fn listener_error() {
let terminal = terminal();
let app = app(COLLECTION.to_owned());
let ui = Ui;
// #[test]
// fn listener_error() {
// let terminal = terminal();
// let app = app(COLLECTION.to_owned());
// let ui = Ui;
let error = EventError::Io(io::Error::new(io::ErrorKind::Interrupted, "error"));
let listener_handle: thread::JoinHandle<EventError> = thread::spawn(|| error);
while !listener_handle.is_finished() {}
// let error = EventError::Io(io::Error::new(io::ErrorKind::Interrupted, "error"));
// let listener_handle: thread::JoinHandle<EventError> = thread::spawn(|| error);
// while !listener_handle.is_finished() {}
let mut listener = MockIEventListener::new();
listener.expect_spawn().return_once(|| listener_handle);
// let mut listener = MockIEventListener::new();
// listener.expect_spawn().return_once(|| listener_handle);
let mut handler = MockIEventHandler::new();
handler
.expect_handle_next_event()
.return_once(|_| Err(EventError::Recv));
// let mut handler = MockIEventHandler::new();
// handler
// .expect_handle_next_event()
// .return_once(|_| Err(EventError::Recv));
let result = Tui::main(terminal, app, ui, handler, listener);
assert!(result.is_err());
// let result = Tui::main(terminal, app, ui, handler, listener);
// assert!(result.is_err());
let error = EventError::Io(io::Error::new(io::ErrorKind::Interrupted, "error"));
assert_eq!(result.unwrap_err(), Error::Event(error.to_string()));
}
// let error = EventError::Io(io::Error::new(io::ErrorKind::Interrupted, "error"));
// assert_eq!(result.unwrap_err(), Error::Event(error.to_string()));
// }
#[test]
fn listener_panic() {
let terminal = terminal();
let app = app(COLLECTION.to_owned());
let ui = Ui;
// #[test]
// fn listener_panic() {
// let terminal = terminal();
// let app = app(COLLECTION.to_owned());
// let ui = Ui;
let listener_handle: thread::JoinHandle<EventError> = thread::spawn(|| panic!());
while !listener_handle.is_finished() {}
// let listener_handle: thread::JoinHandle<EventError> = thread::spawn(|| panic!());
// while !listener_handle.is_finished() {}
let mut listener = MockIEventListener::new();
listener.expect_spawn().return_once(|| listener_handle);
// let mut listener = MockIEventListener::new();
// listener.expect_spawn().return_once(|| listener_handle);
let mut handler = MockIEventHandler::new();
handler
.expect_handle_next_event()
.return_once(|_| Err(EventError::Recv));
// let mut handler = MockIEventHandler::new();
// handler
// .expect_handle_next_event()
// .return_once(|_| Err(EventError::Recv));
let result = Tui::main(terminal, app, ui, handler, listener);
assert!(result.is_err());
assert_eq!(result.unwrap_err(), Error::ListenerPanic);
}
// let result = Tui::main(terminal, app, ui, handler, listener);
// assert!(result.is_err());
// assert_eq!(result.unwrap_err(), Error::ListenerPanic);
// }
#[test]
fn errors() {
let io_err: Error = io::Error::new(io::ErrorKind::Interrupted, "error").into();
let event_err: Error = EventError::Recv.into();
let listener_err = Error::ListenerPanic;
// #[test]
// fn errors() {
// 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());
// }
// }
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)]
// mod tests {
// use musichoard::collection::{
// album::{AlbumDate, AlbumId, AlbumMeta, AlbumPrimaryType, AlbumSecondaryType},
// artist::{Artist, ArtistId, ArtistMeta},
// };
#[cfg(test)]
mod tests {
use musichoard::collection::{
album::{AlbumDate, AlbumId, AlbumMeta, AlbumPrimaryType, AlbumSecondaryType},
artist::{Artist, ArtistId, ArtistMeta},
};
// use crate::tui::{
// app::{AppPublic, AppPublicInner, Delta, MatchOption, MatchStatePublic},
// lib::interface::musicbrainz::api::Match,
// testmod::COLLECTION,
// tests::terminal,
// };
use crate::tui::{
app::{AppPublic, AppPublicInner, Delta, MatchOption, MatchStatePublic},
lib::interface::musicbrainz::api::Match,
testmod::COLLECTION,
tests::terminal,
};
// use super::*;
use super::*;
// // Automock does not support returning types with generic lifetimes.
// impl<'app> IAppAccess for AppPublic<'app> {
// fn get(&mut self) -> AppPublic {
// AppPublic {
// inner: AppPublicInner {
// collection: self.inner.collection,
// selection: self.inner.selection,
// },
// state: match self.state {
// AppState::Browse(()) => AppState::Browse(()),
// AppState::Info(()) => AppState::Info(()),
// AppState::Reload(()) => AppState::Reload(()),
// AppState::Search(s) => AppState::Search(s),
// AppState::Fetch(()) => AppState::Fetch(()),
// AppState::Match(ref mut m) => AppState::Match(MatchStatePublic {
// info: m.info,
// state: m.state,
// }),
// AppState::Error(s) => AppState::Error(s),
// AppState::Critical(s) => AppState::Critical(s),
// },
// input: self.input,
// }
// }
// }
// Automock does not support returning types with generic lifetimes.
impl<'app> IAppAccess for AppPublic<'app> {
fn get(&mut self) -> AppPublic {
AppPublic {
inner: AppPublicInner {
collection: self.inner.collection,
selection: self.inner.selection,
},
state: match self.state {
AppState::Browse(()) => AppState::Browse(()),
AppState::Info(()) => AppState::Info(()),
AppState::Reload(()) => AppState::Reload(()),
AppState::Search(s) => AppState::Search(s),
AppState::Fetch(()) => AppState::Fetch(()),
AppState::Match(ref mut m) => AppState::Match(MatchStatePublic {
info: m.info,
state: m.state,
}),
AppState::Error(s) => AppState::Error(s),
AppState::Critical(s) => AppState::Critical(s),
},
input: self.input,
}
}
}
// fn public_inner<'app>(
// collection: &'app Collection,
// selection: &'app mut Selection,
// ) -> AppPublicInner<'app> {
// AppPublicInner {
// collection,
// selection,
// }
// }
fn public_inner<'app>(
collection: &'app Collection,
selection: &'app mut Selection,
) -> AppPublicInner<'app> {
AppPublicInner {
collection,
selection,
}
}
// fn artist_matches(matching: ArtistMeta, list: Vec<Match<ArtistMeta>>) -> MatchStateInfo {
// let mut list: Vec<MatchOption<ArtistMeta>> = list.into_iter().map(Into::into).collect();
// list.push(MatchOption::CannotHaveMbid);
// list.push(MatchOption::ManualInputMbid);
// MatchStateInfo::artist(matching, list)
// }
fn artist_matches(matching: ArtistMeta, list: Vec<Match<ArtistMeta>>) -> MatchStateInfo {
let mut list: Vec<MatchOption<ArtistMeta>> = list.into_iter().map(Into::into).collect();
list.push(MatchOption::CannotHaveMbid);
list.push(MatchOption::ManualInputMbid);
MatchStateInfo::artist(matching, list)
}
// fn album_matches(matching: AlbumMeta, list: Vec<Match<AlbumMeta>>) -> MatchStateInfo {
// let mut list: Vec<MatchOption<AlbumMeta>> = list.into_iter().map(Into::into).collect();
// list.push(MatchOption::CannotHaveMbid);
// list.push(MatchOption::ManualInputMbid);
// MatchStateInfo::album(matching, list)
// }
fn album_matches(matching: AlbumMeta, list: Vec<Match<AlbumMeta>>) -> MatchStateInfo {
let mut list: Vec<MatchOption<AlbumMeta>> = list.into_iter().map(Into::into).collect();
list.push(MatchOption::CannotHaveMbid);
list.push(MatchOption::ManualInputMbid);
MatchStateInfo::album(matching, list)
}
// fn draw_test_suite(collection: &Collection, selection: &mut Selection) {
// let mut terminal = terminal();
fn draw_test_suite(collection: &Collection, selection: &mut Selection) {
let mut terminal = terminal();
// let mut app = AppPublic {
// inner: public_inner(collection, selection),
// state: AppState::Browse(()),
// input: None,
// };
// terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap();
let mut app = AppPublic {
inner: public_inner(collection, selection),
state: AppState::Browse(()),
input: None,
};
terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap();
// app.state = AppState::Info(());
// terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap();
app.state = AppState::Info(());
terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap();
// app.state = AppState::Reload(());
// terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap();
app.state = AppState::Reload(());
terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap();
// app.state = AppState::Search("");
// terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap();
app.state = AppState::Search("");
terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap();
// app.state = AppState::Fetch(());
// terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap();
app.state = AppState::Fetch(());
terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap();
// app.state = AppState::Error("get rekt scrub");
// terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap();
app.state = AppState::Error("get rekt scrub");
terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap();
// app.state = AppState::Critical("get critically rekt scrub");
// terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap();
// }
app.state = AppState::Critical("get critically rekt scrub");
terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap();
}
// #[test]
// fn empty() {
// let artists: Vec<Artist> = vec![];
// let mut selection = Selection::new(&artists);
#[test]
fn empty() {
let artists: Vec<Artist> = vec![];
let mut selection = Selection::new(&artists);
// draw_test_suite(&artists, &mut selection);
// }
draw_test_suite(&artists, &mut selection);
}
// #[test]
// fn empty_album() {
// let mut artists: Vec<Artist> = vec![Artist::new(ArtistId::new("An artist"))];
// artists[0]
// .albums
// .push(Album::new("An album", AlbumDate::default(), None, vec![]));
// let mut selection = Selection::new(&artists);
#[test]
fn empty_album() {
let mut artists: Vec<Artist> = vec![Artist::new(ArtistId::new("An artist"))];
artists[0]
.albums
.push(Album::new("An album", AlbumDate::default(), None, vec![]));
let mut selection = Selection::new(&artists);
// draw_test_suite(&artists, &mut selection);
// }
draw_test_suite(&artists, &mut selection);
}
// #[test]
// fn collection() {
// let artists = &COLLECTION;
// let mut selection = Selection::new(artists);
#[test]
fn collection() {
let artists = &COLLECTION;
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).
// selection.increment_category();
// selection.increment_category();
// selection.increment_selection(artists, Delta::Line);
// Change the track (which has a different track format).
selection.increment_category();
selection.increment_category();
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).
// selection.decrement_category();
// selection.decrement_category();
// selection.increment_selection(artists, Delta::Line);
// Change the artist (which has a multi-link entry).
selection.decrement_category();
selection.decrement_category();
selection.increment_selection(artists, Delta::Line);
// draw_test_suite(artists, &mut selection);
// }
draw_test_suite(artists, &mut selection);
}
// #[test]
// fn draw_empty_matches() {
// let collection = &COLLECTION;
// let mut selection = Selection::new(collection);
#[test]
fn draw_empty_matches() {
let collection = &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 {
// inner: public_inner(collection, &mut selection),
// state: AppState::Match(MatchStatePublic {
// info: None,
// state: &mut widget_state,
// }),
// input: None,
// };
// terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap();
// }
let mut app = AppPublic {
inner: public_inner(collection, &mut selection),
state: AppState::Match(MatchStatePublic {
info: None,
state: &mut widget_state,
}),
input: None,
};
terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap();
}
// #[test]
// fn draw_artist_matches() {
// let collection = &COLLECTION;
// let mut selection = Selection::new(collection);
#[test]
fn draw_artist_matches() {
let collection = &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_match = Match {
// score: 80,
// item: artist.clone(),
// disambiguation: None,
// };
// let list = vec![artist_match.clone(), artist_match.clone()];
// let artist_matches = artist_matches(artist, list);
let artist = ArtistMeta::new(ArtistId::new("an artist"));
let artist_match = Match {
score: 80,
item: artist.clone(),
disambiguation: None,
};
let list = vec![artist_match.clone(), artist_match.clone()];
let artist_matches = artist_matches(artist, list);
// let mut widget_state = WidgetState::default();
// widget_state.list.select(Some(0));
let mut widget_state = WidgetState::default();
widget_state.list.select(Some(0));
// let mut app = AppPublic {
// inner: public_inner(collection, &mut selection),
// state: AppState::Match(MatchStatePublic {
// info: Some(&artist_matches),
// state: &mut widget_state,
// }),
// input: None,
// };
// terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap();
let mut app = AppPublic {
inner: public_inner(collection, &mut selection),
state: AppState::Match(MatchStatePublic {
info: Some(&artist_matches),
state: &mut widget_state,
}),
input: None,
};
terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap();
// let input = tui_input::Input::default();
// app.input = Some(&input);
// terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap();
// }
let input = tui_input::Input::default();
app.input = Some(&input);
terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap();
}
// #[test]
// fn draw_album_matches() {
// let collection = &COLLECTION;
// let mut selection = Selection::new(collection);
#[test]
fn draw_album_matches() {
let collection = &COLLECTION;
let mut selection = Selection::new(collection);
// let mut terminal = terminal();
let mut terminal = terminal();
// let album = AlbumMeta::new(
// AlbumId::new("An Album"),
// AlbumDate::new(Some(1990), Some(5), None),
// Some(AlbumPrimaryType::Album),
// vec![AlbumSecondaryType::Live, AlbumSecondaryType::Compilation],
// );
// let album_match = Match {
// score: 80,
// item: album.clone(),
// disambiguation: None,
// };
// let list = vec![album_match.clone(), album_match.clone()];
// let album_matches = album_matches(album, list);
let album = AlbumMeta::new(
AlbumId::new("An Album"),
AlbumDate::new(Some(1990), Some(5), None),
Some(AlbumPrimaryType::Album),
vec![AlbumSecondaryType::Live, AlbumSecondaryType::Compilation],
);
let album_match = Match {
score: 80,
item: album.clone(),
disambiguation: None,
};
let list = vec![album_match.clone(), album_match.clone()];
let album_matches = album_matches(album, list);
// let mut widget_state = WidgetState::default();
// widget_state.list.select(Some(0));
let mut widget_state = WidgetState::default();
widget_state.list.select(Some(0));
// let mut app = AppPublic {
// inner: public_inner(collection, &mut selection),
// state: AppState::Match(MatchStatePublic {
// info: Some(&album_matches),
// state: &mut widget_state,
// }),
// input: None,
// };
// terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap();
let mut app = AppPublic {
inner: public_inner(collection, &mut selection),
state: AppState::Match(MatchStatePublic {
info: Some(&album_matches),
state: &mut widget_state,
}),
input: None,
};
terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap();
// let input = tui_input::Input::default();
// app.input = Some(&input);
// terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap();
// }
// }
let input = tui_input::Input::default();
app.input = Some(&input);
terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap();
}
}