Unit test tui/app

This commit is contained in:
Wojciech Kozlowski 2023-04-13 09:09:27 +02:00
parent e0da5d20a9
commit 230f3cb893
6 changed files with 749 additions and 144 deletions

View File

@ -43,41 +43,51 @@ impl From<database::Error> for Error {
} }
} }
pub trait CollectionManager {
/// Rescan the library and integrate any updates into the collection.
fn rescan_library(&mut self) -> Result<(), Error>;
/// Save the collection state to the database.
fn save_to_database(&mut self) -> Result<(), Error>;
/// Get the current collection.
fn get_collection(&self) -> &Collection;
}
/// The collection manager. It is responsible for pulling information from both the library and the /// The collection manager. It is responsible for pulling information from both the library and the
/// database, ensuring its consistent and writing back any changes. /// database, ensuring its consistent and writing back any changes.
pub struct CollectionManager { pub struct MhCollectionManager {
library: Box<dyn Library + Send + Sync>, library: Box<dyn Library + Send + Sync>,
database: Box<dyn Database + Send + Sync>, database: Box<dyn Database + Send + Sync>,
collection: Collection, collection: Collection,
} }
impl CollectionManager { impl MhCollectionManager {
/// Create a new [`CollectionManager`] with the provided [`Library`] and [`Database`]. /// Create a new [`CollectionManager`] with the provided [`Library`] and [`Database`].
pub fn new( pub fn new(
library: Box<dyn Library + Send + Sync>, library: Box<dyn Library + Send + Sync>,
database: Box<dyn Database + Send + Sync>, database: Box<dyn Database + Send + Sync>,
) -> Self { ) -> Self {
CollectionManager { MhCollectionManager {
library, library,
database, database,
collection: vec![], collection: vec![],
} }
} }
}
/// Rescan the library and integrate any updates into the collection. impl CollectionManager for MhCollectionManager {
pub fn rescan_library(&mut self) -> Result<(), Error> { fn rescan_library(&mut self) -> Result<(), Error> {
self.collection = self.library.list(&Query::default())?; self.collection = self.library.list(&Query::default())?;
Ok(()) Ok(())
} }
/// Save the collection state to the database. fn save_to_database(&mut self) -> Result<(), Error> {
pub fn save_to_database(&mut self) -> Result<(), Error> {
self.database.write(&self.collection)?; self.database.write(&self.collection)?;
Ok(()) Ok(())
} }
/// Get the current collection. fn get_collection(&self) -> &Collection {
pub fn get_collection(&self) -> &Collection {
&self.collection &self.collection
} }
} }
@ -92,7 +102,7 @@ mod tests {
tests::COLLECTION, tests::COLLECTION,
}; };
use super::{CollectionManager, Error}; use super::{CollectionManager, Error, MhCollectionManager};
#[test] #[test]
fn read_get_write() { fn read_get_write() {
@ -117,7 +127,8 @@ mod tests {
.times(1) .times(1)
.return_once(|_| database_result); .return_once(|_| database_result);
let mut collection_manager = CollectionManager::new(Box::new(library), Box::new(database)); let mut collection_manager =
MhCollectionManager::new(Box::new(library), Box::new(database));
collection_manager.rescan_library().unwrap(); collection_manager.rescan_library().unwrap();
assert_eq!(collection_manager.get_collection(), &*COLLECTION); assert_eq!(collection_manager.get_collection(), &*COLLECTION);
@ -136,7 +147,8 @@ mod tests {
.times(1) .times(1)
.return_once(|_| library_result); .return_once(|_| library_result);
let mut collection_manager = CollectionManager::new(Box::new(library), Box::new(database)); let mut collection_manager =
MhCollectionManager::new(Box::new(library), Box::new(database));
let actual_err = collection_manager.rescan_library().unwrap_err(); let actual_err = collection_manager.rescan_library().unwrap_err();
let expected_err = Error::LibraryError( let expected_err = Error::LibraryError(
@ -159,7 +171,8 @@ mod tests {
.times(1) .times(1)
.return_once(|_| database_result); .return_once(|_| database_result);
let mut collection_manager = CollectionManager::new(Box::new(library), Box::new(database)); let mut collection_manager =
MhCollectionManager::new(Box::new(library), Box::new(database));
let actual_err = collection_manager.save_to_database().unwrap_err(); let actual_err = collection_manager.save_to_database().unwrap_err();
let expected_err = let expected_err =

View File

@ -54,97 +54,14 @@ pub struct Artist {
} }
#[cfg(test)] #[cfg(test)]
mod tests { #[macro_use]
use super::*; mod testing;
#[cfg(test)]
mod tests {
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| { use super::*;
vec![
Artist { pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| collection!());
id: ArtistId {
name: "album_artist a".to_string(),
},
albums: vec![
Album {
id: AlbumId {
year: 1998,
title: "album_title a.a".to_string(),
},
tracks: vec![
Track {
number: 1,
title: "track a.a.1".to_string(),
artist: vec!["artist a.a.1".to_string()],
format: TrackFormat::Flac,
},
Track {
number: 2,
title: "track a.a.2".to_string(),
artist: vec![
"artist a.a.2.1".to_string(),
"artist a.a.2.2".to_string(),
],
format: TrackFormat::Flac,
},
Track {
number: 3,
title: "track a.a.3".to_string(),
artist: vec!["artist a.a.3".to_string()],
format: TrackFormat::Flac,
},
],
},
Album {
id: AlbumId {
year: 2015,
title: "album_title a.b".to_string(),
},
tracks: vec![
Track {
number: 1,
title: "track a.b.1".to_string(),
artist: vec!["artist a.b.1".to_string()],
format: TrackFormat::Mp3,
},
Track {
number: 2,
title: "track a.b.2".to_string(),
artist: vec!["artist a.b.2".to_string()],
format: TrackFormat::Flac,
},
],
},
],
},
Artist {
id: ArtistId {
name: "album_artist b.a".to_string(),
},
albums: vec![Album {
id: AlbumId {
year: 2003,
title: "album_title b.a".to_string(),
},
tracks: vec![
Track {
number: 1,
title: "track b.a.1".to_string(),
artist: vec!["artist b.a.1".to_string()],
format: TrackFormat::Mp3,
},
Track {
number: 2,
title: "track b.a.2".to_string(),
artist: vec![
"artist b.a.2.1".to_string(),
"artist b.a.2.2".to_string(),
],
format: TrackFormat::Mp3,
},
],
}],
},
]
});
} }

View File

@ -404,13 +404,14 @@ mod tests {
// Putting the last track first will make the entire artist come first in the output. // Putting the last track first will make the entire artist come first in the output.
expected.rotate_right(1); expected.rotate_right(1);
// Same applies to that artists' albums, but here the artist has only one album. // Same applies to that artists' albums.
assert_eq!(expected[0].albums.len(), 1); expected[0].albums.rotate_right(1);
// Same applies to that album's tracks. // Same applies to that album's tracks.
expected[0].albums[0].tracks.rotate_right(1); expected[0].albums[0].tracks.rotate_right(1);
// And the (now) second album's tracks first track comes last. // And the original first album's (now the first album of the second artist) tracks first
// track comes last.
expected[1].albums[0].tracks.rotate_left(1); expected[1].albums[0].tracks.rotate_left(1);
let mut executor = MockBeetsLibraryExecutor::new(); let mut executor = MockBeetsLibraryExecutor::new();

View File

@ -6,7 +6,7 @@ use std::path::PathBuf;
use structopt::StructOpt; use structopt::StructOpt;
use musichoard::{ use musichoard::{
collection::CollectionManager, collection::MhCollectionManager,
database::json::{JsonDatabase, JsonDatabaseFileBackend}, database::json::{JsonDatabase, JsonDatabaseFileBackend},
library::beets::{BeetsLibrary, BeetsLibraryCommandExecutor}, library::beets::{BeetsLibrary, BeetsLibraryCommandExecutor},
}; };
@ -52,7 +52,7 @@ fn main() {
&opt.database_file_path, &opt.database_file_path,
))); )));
let collection_manager = CollectionManager::new(Box::new(beets), Box::new(database)); let collection_manager = MhCollectionManager::new(Box::new(beets), Box::new(database));
// Initialize the terminal user interface. // Initialize the terminal user interface.
let backend = CrosstermBackend::new(io::stdout()); let backend = CrosstermBackend::new(io::stdout());
@ -64,10 +64,37 @@ fn main() {
let ui = Ui::new(); let ui = Ui::new();
let app = App::new(collection_manager).expect("failed to initialise app"); let app = App::new(Box::new(collection_manager)).expect("failed to initialise app");
let tui = Tui::new(terminal, listener, handler, ui, app); let tui = Tui::new(terminal, listener, handler, ui, app);
// Run the TUI application. // Run the TUI application.
tui.run().expect("failed to run tui"); tui.run().expect("failed to run tui");
} }
#[cfg(test)]
#[macro_use]
mod testing;
#[cfg(test)]
mod tests {
use mockall::mock;
use once_cell::sync::Lazy;
use musichoard::*;
use musichoard::collection::{self, Collection, CollectionManager};
pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| {
collection!()
});
mock! {
pub CollectionManager {}
impl CollectionManager for CollectionManager {
fn rescan_library(&mut self) -> Result<(), collection::Error>;
fn save_to_database(&mut self) -> Result<(), collection::Error>;
fn get_collection(&self) -> &Collection;
}
}
}

168
src/testing.rs Normal file
View File

@ -0,0 +1,168 @@
macro_rules! collection {
() => {
vec![
Artist {
id: ArtistId {
name: "album_artist a".to_string(),
},
albums: vec![
Album {
id: AlbumId {
year: 1998,
title: "album_title a.a".to_string(),
},
tracks: vec![
Track {
number: 1,
title: "track a.a.1".to_string(),
artist: vec!["artist a.a.1".to_string()],
format: TrackFormat::Flac,
},
Track {
number: 2,
title: "track a.a.2".to_string(),
artist: vec![
"artist a.a.2.1".to_string(),
"artist a.a.2.2".to_string(),
],
format: TrackFormat::Flac,
},
Track {
number: 3,
title: "track a.a.3".to_string(),
artist: vec!["artist a.a.3".to_string()],
format: TrackFormat::Flac,
},
],
},
Album {
id: AlbumId {
year: 2015,
title: "album_title a.b".to_string(),
},
tracks: vec![
Track {
number: 1,
title: "track a.b.1".to_string(),
artist: vec!["artist a.b.1".to_string()],
format: TrackFormat::Mp3,
},
Track {
number: 2,
title: "track a.b.2".to_string(),
artist: vec!["artist a.b.2".to_string()],
format: TrackFormat::Flac,
},
],
},
],
},
Artist {
id: ArtistId {
name: "album_artist b".to_string(),
},
albums: vec![
Album {
id: AlbumId {
year: 2003,
title: "album_title b.a".to_string(),
},
tracks: vec![
Track {
number: 1,
title: "track b.a.1".to_string(),
artist: vec!["artist b.a.1".to_string()],
format: TrackFormat::Mp3,
},
Track {
number: 2,
title: "track b.a.2".to_string(),
artist: vec![
"artist b.a.2.1".to_string(),
"artist b.a.2.2".to_string(),
],
format: TrackFormat::Mp3,
},
],
},
Album {
id: AlbumId {
year: 2008,
title: "album_title b.b".to_string(),
},
tracks: vec![
Track {
number: 1,
title: "track b.b.1".to_string(),
artist: vec!["artist b.b.1".to_string()],
format: TrackFormat::Flac,
},
Track {
number: 2,
title: "track b.b.2".to_string(),
artist: vec![
"artist b.b.2.1".to_string(),
"artist b.b.2.2".to_string(),
],
format: TrackFormat::Mp3,
},
],
},
],
},
Artist {
id: ArtistId {
name: "album_artist c".to_string(),
},
albums: vec![
Album {
id: AlbumId {
year: 1985,
title: "album_title c.a".to_string(),
},
tracks: vec![
Track {
number: 1,
title: "track c.a.1".to_string(),
artist: vec!["artist c.a.1".to_string()],
format: TrackFormat::Mp3,
},
Track {
number: 2,
title: "track c.a.2".to_string(),
artist: vec![
"artist c.a.2.1".to_string(),
"artist c.a.2.2".to_string(),
],
format: TrackFormat::Mp3,
},
],
},
Album {
id: AlbumId {
year: 2018,
title: "album_title c.b".to_string(),
},
tracks: vec![
Track {
number: 1,
title: "track c.b.1".to_string(),
artist: vec!["artist c.b.1".to_string()],
format: TrackFormat::Mp3,
},
Track {
number: 2,
title: "track c.b.2".to_string(),
artist: vec![
"artist c.b.2.1".to_string(),
"artist c.b.2.2".to_string(),
],
format: TrackFormat::Mp3,
},
],
},
],
},
]
};
}

View File

@ -2,7 +2,7 @@ use musichoard::{collection::CollectionManager, Album, AlbumId, Artist, ArtistId
use super::Error; use super::Error;
#[derive(Copy, Clone, PartialEq, Eq)] #[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Category { pub enum Category {
Artist, Artist,
Album, Album,
@ -10,7 +10,7 @@ pub enum Category {
} }
struct TrackSelection { struct TrackSelection {
index: u16, index: usize,
} }
impl TrackSelection { impl TrackSelection {
@ -24,7 +24,7 @@ impl TrackSelection {
fn increment(&mut self, tracks: &Vec<Track>) { fn increment(&mut self, tracks: &Vec<Track>) {
if let Some(result) = self.index.checked_add(1) { if let Some(result) = self.index.checked_add(1) {
if (result as usize) < tracks.len() { if result < tracks.len() {
self.index = result; self.index = result;
} }
} }
@ -38,7 +38,7 @@ impl TrackSelection {
} }
struct AlbumSelection { struct AlbumSelection {
index: u16, index: usize,
track: Option<TrackSelection>, track: Option<TrackSelection>,
} }
@ -56,9 +56,9 @@ impl AlbumSelection {
fn increment(&mut self, albums: &Vec<Album>) { fn increment(&mut self, albums: &Vec<Album>) {
if let Some(result) = self.index.checked_add(1) { if let Some(result) = self.index.checked_add(1) {
if (result as usize) < albums.len() { if result < albums.len() {
self.index = result; self.index = result;
self.track = TrackSelection::initialise(&albums[self.index as usize].tracks); self.track = TrackSelection::initialise(&albums[self.index].tracks);
} }
} }
} }
@ -66,13 +66,13 @@ impl AlbumSelection {
fn decrement(&mut self, albums: &Vec<Album>) { fn decrement(&mut self, albums: &Vec<Album>) {
if let Some(result) = self.index.checked_sub(1) { if let Some(result) = self.index.checked_sub(1) {
self.index = result; self.index = result;
self.track = TrackSelection::initialise(&albums[self.index as usize].tracks); self.track = TrackSelection::initialise(&albums[self.index].tracks);
} }
} }
} }
struct ArtistSelection { struct ArtistSelection {
index: u16, index: usize,
album: Option<AlbumSelection>, album: Option<AlbumSelection>,
} }
@ -90,9 +90,9 @@ impl ArtistSelection {
fn increment(&mut self, artists: &Vec<Artist>) { fn increment(&mut self, artists: &Vec<Artist>) {
if let Some(result) = self.index.checked_add(1) { if let Some(result) = self.index.checked_add(1) {
if (result as usize) < artists.len() { if result < artists.len() {
self.index = result; self.index = result;
self.album = AlbumSelection::initialise(&artists[self.index as usize].albums); self.album = AlbumSelection::initialise(&artists[self.index].albums);
} }
} }
} }
@ -100,7 +100,7 @@ impl ArtistSelection {
fn decrement(&mut self, artists: &Vec<Artist>) { fn decrement(&mut self, artists: &Vec<Artist>) {
if let Some(result) = self.index.checked_sub(1) { if let Some(result) = self.index.checked_sub(1) {
self.index = result; self.index = result;
self.album = AlbumSelection::initialise(&artists[self.index as usize].albums); self.album = AlbumSelection::initialise(&artists[self.index].albums);
} }
} }
} }
@ -111,13 +111,13 @@ struct Selection {
} }
pub struct App { pub struct App {
collection_manager: CollectionManager, collection_manager: Box<dyn CollectionManager>,
selection: Selection, selection: Selection,
running: bool, running: bool,
} }
impl App { impl App {
pub fn new(mut collection_manager: CollectionManager) -> Result<Self, Error> { pub fn new(mut collection_manager: Box<dyn CollectionManager>) -> Result<Self, Error> {
collection_manager.rescan_library()?; collection_manager.rescan_library()?;
let selection = Selection { let selection = Selection {
active: Category::Artist, active: Category::Artist,
@ -184,7 +184,7 @@ impl App {
if let Some(ref mut artist_selection) = self.selection.artist { if let Some(ref mut artist_selection) = self.selection.artist {
if let Some(ref mut album_selection) = artist_selection.album { if let Some(ref mut album_selection) = artist_selection.album {
let artists = &self.collection_manager.get_collection(); let artists = &self.collection_manager.get_collection();
let albums = &artists[artist_selection.index as usize].albums; let albums = &artists[artist_selection.index].albums;
album_selection.increment(albums); album_selection.increment(albums);
} }
} }
@ -194,7 +194,7 @@ impl App {
if let Some(ref mut artist_selection) = self.selection.artist { if let Some(ref mut artist_selection) = self.selection.artist {
if let Some(ref mut album_selection) = artist_selection.album { if let Some(ref mut album_selection) = artist_selection.album {
let artists = &self.collection_manager.get_collection(); let artists = &self.collection_manager.get_collection();
let albums = &artists[artist_selection.index as usize].albums; let albums = &artists[artist_selection.index].albums;
album_selection.decrement(albums); album_selection.decrement(albums);
} }
} }
@ -205,8 +205,8 @@ impl App {
if let Some(ref mut album_selection) = artist_selection.album { if let Some(ref mut album_selection) = artist_selection.album {
if let Some(ref mut track_selection) = album_selection.track { if let Some(ref mut track_selection) = album_selection.track {
let artists = &self.collection_manager.get_collection(); let artists = &self.collection_manager.get_collection();
let albums = &artists[artist_selection.index as usize].albums; let albums = &artists[artist_selection.index].albums;
let tracks = &albums[album_selection.index as usize].tracks; let tracks = &albums[album_selection.index].tracks;
track_selection.increment(tracks); track_selection.increment(tracks);
} }
} }
@ -218,8 +218,8 @@ impl App {
if let Some(ref mut album_selection) = artist_selection.album { if let Some(ref mut album_selection) = artist_selection.album {
if let Some(ref mut track_selection) = album_selection.track { if let Some(ref mut track_selection) = album_selection.track {
let artists = &self.collection_manager.get_collection(); let artists = &self.collection_manager.get_collection();
let albums = &artists[artist_selection.index as usize].albums; let albums = &artists[artist_selection.index].albums;
let tracks = &albums[album_selection.index as usize].tracks; let tracks = &albums[album_selection.index].tracks;
track_selection.decrement(tracks); track_selection.decrement(tracks);
} }
} }
@ -230,20 +230,17 @@ impl App {
self.selection.active self.selection.active
} }
fn get_artists(&self) -> Option<&Vec<Artist>> { fn get_artists(&self) -> &Vec<Artist> {
Some(&self.collection_manager.get_collection()) &self.collection_manager.get_collection()
} }
fn get_albums(&self) -> Option<&Vec<Album>> { fn get_albums(&self) -> Option<&Vec<Album>> {
if let Some(artists) = self.get_artists() { let artists = self.get_artists();
if let Some(artist_index) = self.selected_artist() { if let Some(artist_index) = self.selected_artist() {
Some(&artists[artist_index].albums) Some(&artists[artist_index].albums)
} else { } else {
None None
} }
} else {
None
}
} }
fn get_tracks(&self) -> Option<&Vec<Track>> { fn get_tracks(&self) -> Option<&Vec<Track>> {
@ -259,11 +256,8 @@ impl App {
} }
pub fn get_artist_ids(&self) -> Vec<&ArtistId> { pub fn get_artist_ids(&self) -> Vec<&ArtistId> {
if let Some(artists) = self.get_artists() { let artists = self.get_artists();
artists.iter().map(|a| &a.id).collect() artists.iter().map(|a| &a.id).collect()
} else {
vec![]
}
} }
pub fn get_album_ids(&self) -> Vec<&AlbumId> { pub fn get_album_ids(&self) -> Vec<&AlbumId> {
@ -284,7 +278,7 @@ impl App {
pub fn selected_artist(&self) -> Option<usize> { pub fn selected_artist(&self) -> Option<usize> {
if let Some(ref artist_selection) = self.selection.artist { if let Some(ref artist_selection) = self.selection.artist {
Some(artist_selection.index as usize) Some(artist_selection.index)
} else { } else {
None None
} }
@ -293,7 +287,7 @@ impl App {
pub fn selected_album(&self) -> Option<usize> { pub fn selected_album(&self) -> Option<usize> {
if let Some(ref artist_selection) = self.selection.artist { if let Some(ref artist_selection) = self.selection.artist {
if let Some(ref album_selection) = artist_selection.album { if let Some(ref album_selection) = artist_selection.album {
Some(album_selection.index as usize) Some(album_selection.index)
} else { } else {
None None
} }
@ -306,7 +300,7 @@ impl App {
if let Some(ref artist_selection) = self.selection.artist { if let Some(ref artist_selection) = self.selection.artist {
if let Some(ref album_selection) = artist_selection.album { if let Some(ref album_selection) = artist_selection.album {
if let Some(ref track_selection) = album_selection.track { if let Some(ref track_selection) = album_selection.track {
Some(track_selection.index as usize) Some(track_selection.index)
} else { } else {
None None
} }
@ -322,3 +316,488 @@ impl App {
self.running = false; self.running = false;
} }
} }
#[cfg(test)]
mod tests {
use crate::tests::{MockCollectionManager, COLLECTION};
use super::*;
#[test]
fn test_track_selection() {
let tracks = &COLLECTION[0].albums[0].tracks;
assert!(tracks.len() > 1);
let empty = TrackSelection::initialise(&vec![]);
assert!(empty.is_none());
let sel = TrackSelection::initialise(tracks);
assert!(sel.is_some());
let mut sel = sel.unwrap();
assert_eq!(sel.index, 0);
sel.decrement(tracks);
assert_eq!(sel.index, 0);
sel.increment(tracks);
assert_eq!(sel.index, 1);
sel.decrement(tracks);
assert_eq!(sel.index, 0);
for _ in 0..(tracks.len() + 5) {
sel.increment(tracks);
}
assert_eq!(sel.index, tracks.len() - 1);
// Artifical test case to verify upper limit.
let mut sel = TrackSelection {
index: std::usize::MAX,
};
assert_eq!(sel.index, std::usize::MAX);
sel.increment(&vec![]);
assert_eq!(sel.index, std::usize::MAX);
}
#[test]
fn test_album_selection() {
let albums = &COLLECTION[0].albums;
assert!(albums.len() > 1);
let empty = AlbumSelection::initialise(&vec![]);
assert!(empty.is_none());
let sel = AlbumSelection::initialise(albums);
assert!(sel.is_some());
let mut sel = sel.unwrap();
assert_eq!(sel.index, 0);
assert!(sel.track.is_some());
assert_eq!(sel.track.as_ref().unwrap().index, 0);
sel.track
.as_mut()
.unwrap()
.increment(&albums[sel.index].tracks);
assert_eq!(sel.index, 0);
assert!(sel.track.is_some());
assert_eq!(sel.track.as_ref().unwrap().index, 1);
// Verify that decrement that doesn't change index does not reset track.
sel.decrement(albums);
assert_eq!(sel.index, 0);
assert!(sel.track.is_some());
assert_eq!(sel.track.as_ref().unwrap().index, 1);
sel.increment(albums);
assert_eq!(sel.index, 1);
assert!(sel.track.is_some());
assert_eq!(sel.track.as_ref().unwrap().index, 0);
sel.decrement(albums);
assert_eq!(sel.index, 0);
assert!(sel.track.is_some());
assert_eq!(sel.track.as_ref().unwrap().index, 0);
for _ in 0..(albums.len() + 5) {
sel.increment(albums);
}
assert_eq!(sel.index, albums.len() - 1);
assert!(sel.track.is_some());
assert_eq!(sel.track.as_ref().unwrap().index, 0);
sel.track
.as_mut()
.unwrap()
.increment(&albums[sel.index].tracks);
assert_eq!(sel.index, albums.len() - 1);
assert!(sel.track.is_some());
assert_eq!(sel.track.as_ref().unwrap().index, 1);
// Verify that increment that doesn't change index does not reset track.
sel.increment(albums);
assert_eq!(sel.index, albums.len() - 1);
assert!(sel.track.is_some());
assert_eq!(sel.track.as_ref().unwrap().index, 1);
// Artifical test case to verify upper limit.
let mut sel = AlbumSelection {
index: std::usize::MAX,
track: Some(TrackSelection { index: 1 }),
};
assert_eq!(sel.index, std::usize::MAX);
assert!(sel.track.is_some());
assert_eq!(sel.track.as_ref().unwrap().index, 1);
sel.increment(&vec![]);
assert_eq!(sel.index, std::usize::MAX);
assert!(sel.track.is_some());
assert_eq!(sel.track.as_ref().unwrap().index, 1);
}
#[test]
fn test_artist_selection() {
let artists = &COLLECTION;
assert!(artists.len() > 1);
let empty = ArtistSelection::initialise(&vec![]);
assert!(empty.is_none());
let sel = ArtistSelection::initialise(artists);
assert!(sel.is_some());
let mut sel = sel.unwrap();
assert_eq!(sel.index, 0);
assert!(sel.album.is_some());
assert_eq!(sel.album.as_ref().unwrap().index, 0);
sel.album
.as_mut()
.unwrap()
.increment(&artists[sel.index].albums);
assert_eq!(sel.index, 0);
assert!(sel.album.is_some());
assert_eq!(sel.album.as_ref().unwrap().index, 1);
// Verify that decrement that doesn't change index does not reset album.
sel.decrement(artists);
assert_eq!(sel.index, 0);
assert!(sel.album.is_some());
assert_eq!(sel.album.as_ref().unwrap().index, 1);
sel.increment(artists);
assert_eq!(sel.index, 1);
assert!(sel.album.is_some());
assert_eq!(sel.album.as_ref().unwrap().index, 0);
sel.decrement(artists);
assert_eq!(sel.index, 0);
assert!(sel.album.is_some());
assert_eq!(sel.album.as_ref().unwrap().index, 0);
for _ in 0..(artists.len() + 5) {
sel.increment(artists);
}
assert_eq!(sel.index, artists.len() - 1);
assert!(sel.album.is_some());
assert_eq!(sel.album.as_ref().unwrap().index, 0);
sel.album
.as_mut()
.unwrap()
.increment(&artists[sel.index].albums);
assert_eq!(sel.index, artists.len() - 1);
assert!(sel.album.is_some());
assert_eq!(sel.album.as_ref().unwrap().index, 1);
// Verify that increment that doesn't change index does not reset album.
sel.increment(artists);
assert_eq!(sel.index, artists.len() - 1);
assert!(sel.album.is_some());
assert_eq!(sel.album.as_ref().unwrap().index, 1);
// Artifical test case to verify upper limit.
let mut sel = ArtistSelection {
index: std::usize::MAX,
album: Some(AlbumSelection {
index: 1,
track: None,
}),
};
assert_eq!(sel.index, std::usize::MAX);
assert!(sel.album.is_some());
assert_eq!(sel.album.as_ref().unwrap().index, 1);
sel.increment(&vec![]);
assert_eq!(sel.index, std::usize::MAX);
assert!(sel.album.is_some());
assert_eq!(sel.album.as_ref().unwrap().index, 1);
}
#[test]
fn app_running() {
let mut collection_manager = MockCollectionManager::new();
collection_manager
.expect_rescan_library()
.times(1)
.return_once(|| Ok(()));
collection_manager
.expect_get_collection()
.return_const(COLLECTION.to_owned());
let mut app = App::new(Box::new(collection_manager)).unwrap();
assert!(app.is_running());
app.quit();
assert!(!app.is_running());
}
#[test]
fn app_modifiers() {
let mut collection_manager = MockCollectionManager::new();
collection_manager
.expect_rescan_library()
.times(1)
.return_once(|| Ok(()));
collection_manager
.expect_get_collection()
.return_const(COLLECTION.to_owned());
let mut app = App::new(Box::new(collection_manager)).unwrap();
assert!(app.is_running());
assert!(!app.get_artist_ids().is_empty());
assert!(!app.get_album_ids().is_empty());
assert!(!app.get_track_ids().is_empty());
assert_eq!(app.get_active_category(), Category::Artist);
assert_eq!(app.selected_artist(), Some(0));
assert_eq!(app.selected_album(), Some(0));
assert_eq!(app.selected_track(), Some(0));
app.increment_selection();
assert_eq!(app.get_active_category(), Category::Artist);
assert_eq!(app.selected_artist(), Some(1));
assert_eq!(app.selected_album(), Some(0));
assert_eq!(app.selected_track(), Some(0));
app.increment_category();
assert_eq!(app.get_active_category(), Category::Album);
assert_eq!(app.selected_artist(), Some(1));
assert_eq!(app.selected_album(), Some(0));
assert_eq!(app.selected_track(), Some(0));
app.increment_selection();
assert_eq!(app.get_active_category(), Category::Album);
assert_eq!(app.selected_artist(), Some(1));
assert_eq!(app.selected_album(), Some(1));
assert_eq!(app.selected_track(), Some(0));
app.increment_category();
assert_eq!(app.get_active_category(), Category::Track);
assert_eq!(app.selected_artist(), Some(1));
assert_eq!(app.selected_album(), Some(1));
assert_eq!(app.selected_track(), Some(0));
app.increment_selection();
assert_eq!(app.get_active_category(), Category::Track);
assert_eq!(app.selected_artist(), Some(1));
assert_eq!(app.selected_album(), Some(1));
assert_eq!(app.selected_track(), Some(1));
app.increment_category();
assert_eq!(app.get_active_category(), Category::Track);
assert_eq!(app.selected_artist(), Some(1));
assert_eq!(app.selected_album(), Some(1));
assert_eq!(app.selected_track(), Some(1));
app.decrement_selection();
assert_eq!(app.get_active_category(), Category::Track);
assert_eq!(app.selected_artist(), Some(1));
assert_eq!(app.selected_album(), Some(1));
assert_eq!(app.selected_track(), Some(0));
app.increment_selection();
app.decrement_category();
assert_eq!(app.get_active_category(), Category::Album);
assert_eq!(app.selected_artist(), Some(1));
assert_eq!(app.selected_album(), Some(1));
assert_eq!(app.selected_track(), Some(1));
app.decrement_selection();
assert_eq!(app.get_active_category(), Category::Album);
assert_eq!(app.selected_artist(), Some(1));
assert_eq!(app.selected_album(), Some(0));
assert_eq!(app.selected_track(), Some(0));
app.increment_selection();
app.decrement_category();
assert_eq!(app.get_active_category(), Category::Artist);
assert_eq!(app.selected_artist(), Some(1));
assert_eq!(app.selected_album(), Some(1));
assert_eq!(app.selected_track(), Some(0));
app.decrement_selection();
assert_eq!(app.get_active_category(), Category::Artist);
assert_eq!(app.selected_artist(), Some(0));
assert_eq!(app.selected_album(), Some(0));
assert_eq!(app.selected_track(), Some(0));
app.increment_category();
app.increment_selection();
app.decrement_category();
app.decrement_selection();
app.decrement_category();
assert_eq!(app.get_active_category(), Category::Artist);
assert_eq!(app.selected_artist(), Some(0));
assert_eq!(app.selected_album(), Some(1));
assert_eq!(app.selected_track(), Some(0));
}
#[test]
fn app_no_tracks() {
let mut collection_manager = MockCollectionManager::new();
let mut collection = COLLECTION.to_owned();
collection[0].albums[0].tracks = vec![];
collection_manager
.expect_rescan_library()
.times(1)
.return_once(|| Ok(()));
collection_manager
.expect_get_collection()
.return_const(collection);
let mut app = App::new(Box::new(collection_manager)).unwrap();
assert!(app.is_running());
assert!(!app.get_artist_ids().is_empty());
assert!(!app.get_album_ids().is_empty());
assert!(app.get_track_ids().is_empty());
assert_eq!(app.get_active_category(), Category::Artist);
assert_eq!(app.selected_artist(), Some(0));
assert_eq!(app.selected_album(), Some(0));
assert_eq!(app.selected_track(), None);
app.increment_category();
app.increment_category();
app.increment_selection();
assert_eq!(app.get_active_category(), Category::Track);
assert_eq!(app.selected_artist(), Some(0));
assert_eq!(app.selected_album(), Some(0));
assert_eq!(app.selected_track(), None);
app.decrement_selection();
assert_eq!(app.get_active_category(), Category::Track);
assert_eq!(app.selected_artist(), Some(0));
assert_eq!(app.selected_album(), Some(0));
assert_eq!(app.selected_track(), None);
}
#[test]
fn app_no_albums() {
let mut collection_manager = MockCollectionManager::new();
let mut collection = COLLECTION.to_owned();
collection[0].albums = vec![];
collection_manager
.expect_rescan_library()
.times(1)
.return_once(|| Ok(()));
collection_manager
.expect_get_collection()
.return_const(collection);
let mut app = App::new(Box::new(collection_manager)).unwrap();
assert!(app.is_running());
assert!(!app.get_artist_ids().is_empty());
assert!(app.get_album_ids().is_empty());
assert!(app.get_track_ids().is_empty());
assert_eq!(app.get_active_category(), Category::Artist);
assert_eq!(app.selected_artist(), Some(0));
assert_eq!(app.selected_album(), None);
assert_eq!(app.selected_track(), None);
app.increment_category();
app.increment_selection();
assert_eq!(app.get_active_category(), Category::Album);
assert_eq!(app.selected_artist(), Some(0));
assert_eq!(app.selected_album(), None);
assert_eq!(app.selected_track(), None);
app.decrement_selection();
assert_eq!(app.get_active_category(), Category::Album);
assert_eq!(app.selected_artist(), Some(0));
assert_eq!(app.selected_album(), None);
assert_eq!(app.selected_track(), None);
app.increment_category();
app.increment_selection();
assert_eq!(app.get_active_category(), Category::Track);
assert_eq!(app.selected_artist(), Some(0));
assert_eq!(app.selected_album(), None);
assert_eq!(app.selected_track(), None);
app.decrement_selection();
assert_eq!(app.get_active_category(), Category::Track);
assert_eq!(app.selected_artist(), Some(0));
assert_eq!(app.selected_album(), None);
assert_eq!(app.selected_track(), None);
}
#[test]
fn app_no_artists() {
let mut collection_manager = MockCollectionManager::new();
let collection = vec![];
collection_manager
.expect_rescan_library()
.times(1)
.return_once(|| Ok(()));
collection_manager
.expect_get_collection()
.return_const(collection);
let mut app = App::new(Box::new(collection_manager)).unwrap();
assert!(app.is_running());
assert!(app.get_artist_ids().is_empty());
assert!(app.get_album_ids().is_empty());
assert!(app.get_track_ids().is_empty());
assert_eq!(app.get_active_category(), Category::Artist);
assert_eq!(app.selected_artist(), None);
assert_eq!(app.selected_album(), None);
assert_eq!(app.selected_track(), None);
app.increment_selection();
assert_eq!(app.get_active_category(), Category::Artist);
assert_eq!(app.selected_artist(), None);
assert_eq!(app.selected_album(), None);
assert_eq!(app.selected_track(), None);
app.decrement_selection();
assert_eq!(app.get_active_category(), Category::Artist);
assert_eq!(app.selected_artist(), None);
assert_eq!(app.selected_album(), None);
assert_eq!(app.selected_track(), None);
app.increment_category();
app.increment_selection();
assert_eq!(app.get_active_category(), Category::Album);
assert_eq!(app.selected_artist(), None);
assert_eq!(app.selected_album(), None);
assert_eq!(app.selected_track(), None);
app.decrement_selection();
assert_eq!(app.get_active_category(), Category::Album);
assert_eq!(app.selected_artist(), None);
assert_eq!(app.selected_album(), None);
assert_eq!(app.selected_track(), None);
app.increment_category();
app.increment_selection();
assert_eq!(app.get_active_category(), Category::Track);
assert_eq!(app.selected_artist(), None);
assert_eq!(app.selected_album(), None);
assert_eq!(app.selected_track(), None);
app.decrement_selection();
assert_eq!(app.get_active_category(), Category::Track);
assert_eq!(app.selected_artist(), None);
assert_eq!(app.selected_album(), None);
assert_eq!(app.selected_track(), None);
}
}