Explicit test data for crate code

This commit is contained in:
Wojciech Kozlowski 2024-01-21 14:00:01 +01:00
parent 267f4a5461
commit 0ea6cb578c
13 changed files with 749 additions and 523 deletions

View File

@ -65,114 +65,14 @@ mod tests {
use super::*; use super::*;
use crate::{tests::COLLECTION, Artist, ArtistId, Collection, Format}; use crate::{
database::testlib::DATABASE_JSON, testlib::FULL_COLLECTION, Artist, ArtistId, Collection,
fn opt_to_str<S: AsRef<str>>(opt: &Option<S>) -> String { };
match opt {
Some(val) => format!("\"{}\"", val.as_ref()),
None => String::from("null"),
}
}
fn vec_to_str<S: AsRef<str>>(vec: &[S]) -> String {
let mut urls: Vec<String> = vec![];
for item in vec.iter() {
urls.push(format!("\"{}\"", item.as_ref()));
}
format!("[{}]", urls.join(","))
}
fn artist_id_to_str(id: &ArtistId) -> String {
format!("{{\"name\":\"{}\"}}", id.name)
}
fn artist_to_json(artist: &Artist) -> String {
let mut albums: Vec<String> = vec![];
for album in artist.albums.iter() {
let album_year = album.id.year;
let album_title = &album.id.title;
let mut tracks: Vec<String> = vec![];
for track in album.tracks.iter() {
let track_number = track.id.number;
let track_title = &track.id.title;
let mut track_artist: Vec<String> = vec![];
for artist in track.artist.iter() {
track_artist.push(format!("\"{artist}\""))
}
let track_artist = track_artist.join(",");
let track_format = match track.quality.format {
Format::Flac => stringify!(Flac),
Format::Mp3 => stringify!(Mp3),
};
let track_bitrate = track.quality.bitrate;
tracks.push(format!(
"{{\
\"id\":{{\"number\":{track_number},\"title\":\"{track_title}\"}},\
\"artist\":[{track_artist}],\
\"quality\":{{\"format\":\"{track_format}\",\"bitrate\":{track_bitrate}}}\
}}"
));
}
let tracks = tracks.join(",");
albums.push(format!(
"{{\
\"id\":{{\
\"year\":{album_year},\
\"title\":\"{album_title}\"\
}},\"tracks\":[{tracks}]\
}}"
));
}
let albums = albums.join(",");
let musicbrainz = opt_to_str(&artist.properties.musicbrainz);
let musicbutler = vec_to_str(&artist.properties.musicbutler);
let bandcamp = vec_to_str(&artist.properties.bandcamp);
let qobuz = opt_to_str(&artist.properties.qobuz);
let properties = format!(
"{{\
\"musicbrainz\":{musicbrainz},\
\"musicbutler\":{musicbutler},\
\"bandcamp\":{bandcamp},\
\"qobuz\":{qobuz}\
}}"
);
let album_artist = artist_id_to_str(&artist.id);
let album_artist_sort = artist
.sort
.as_ref()
.map(artist_id_to_str)
.unwrap_or_else(|| "null".to_string());
format!(
"{{\
\"id\":{album_artist},\
\"sort\":{album_artist_sort},\
\"properties\":{properties},\
\"albums\":[{albums}]\
}}"
)
}
fn artists_to_json(artists: &[Artist]) -> String {
let mut artists_strings: Vec<String> = vec![];
for artist in artists.iter() {
artists_strings.push(artist_to_json(artist));
}
let artists_json = artists_strings.join(",");
format!("[{artists_json}]")
}
#[test] #[test]
fn save() { fn save() {
let write_data = COLLECTION.to_owned(); let write_data = FULL_COLLECTION.to_owned();
let input = artists_to_json(&write_data); let input = DATABASE_JSON.to_owned();
let mut backend = MockIJsonDatabaseBackend::new(); let mut backend = MockIJsonDatabaseBackend::new();
backend backend
@ -186,8 +86,8 @@ mod tests {
#[test] #[test]
fn load() { fn load() {
let expected = COLLECTION.to_owned(); let expected = FULL_COLLECTION.to_owned();
let result = Ok(artists_to_json(&expected)); let result = Ok(DATABASE_JSON.to_owned());
let mut backend = MockIJsonDatabaseBackend::new(); let mut backend = MockIJsonDatabaseBackend::new();
backend.expect_read().times(1).return_once(|| result); backend.expect_read().times(1).return_once(|| result);
@ -199,8 +99,7 @@ mod tests {
#[test] #[test]
fn reverse() { fn reverse() {
let expected = COLLECTION.to_owned(); let input = DATABASE_JSON.to_owned();
let input = artists_to_json(&expected);
let result = Ok(input.clone()); let result = Ok(input.clone());
let mut backend = MockIJsonDatabaseBackend::new(); let mut backend = MockIJsonDatabaseBackend::new();
@ -213,7 +112,7 @@ mod tests {
let mut database = JsonDatabase::new(backend); let mut database = JsonDatabase::new(backend);
let write_data = COLLECTION.to_owned(); let write_data = FULL_COLLECTION.to_owned();
database.save(&write_data).unwrap(); database.save(&write_data).unwrap();
let read_data: Vec<Artist> = database.load().unwrap(); let read_data: Vec<Artist> = database.load().unwrap();

View File

@ -85,11 +85,14 @@ impl From<std::io::Error> for SaveError {
} }
} }
#[cfg(test)]
pub mod testlib;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::io; use std::io;
use super::{IDatabase, LoadError, NullDatabase, SaveError}; use super::*;
#[test] #[test]
fn no_database_load() { fn no_database_load() {

144
src/database/testlib.rs Normal file
View File

@ -0,0 +1,144 @@
use once_cell::sync::Lazy;
macro_rules! database_json {
() => {
"[\
{\
\"id\":{\"name\":\"album_artist a\"},\
\"sort\":null,\
\"properties\":{\
\"musicbrainz\":\"https://musicbrainz.org/artist/00000000-0000-0000-0000-000000000000\",\
\"musicbutler\":[\"https://www.musicbutler.io/artist-page/000000000\"],\
\"bandcamp\":[],\
\"qobuz\":\"https://www.qobuz.com/nl-nl/interpreter/artist-a/download-streaming-albums\"\
},\
\"albums\":[\
{\
\"id\":{\"year\":1998,\"title\":\"album_title a.a\"},\
\"tracks\":[\
{\
\"id\":{\"number\":1,\"title\":\"track a.a.1\"},\
\"artist\":[\"artist a.a.1\"],\
\"quality\":{\"format\":\"Flac\",\"bitrate\":992}\
},\
{\
\"id\":{\"number\":2,\"title\":\"track a.a.2\"},\
\"artist\":[\"artist a.a.2.1\",\"artist a.a.2.2\"],\
\"quality\":{\"format\":\"Mp3\",\"bitrate\":320}\
},\
{\
\"id\":{\"number\":3,\"title\":\"track a.a.3\"},\
\"artist\":[\"artist a.a.3\"],\
\"quality\":{\"format\":\"Flac\",\"bitrate\":1061}\
}\
]\
},\
{\
\"id\":{\"year\":2015,\"title\":\"album_title a.b\"},\
\"tracks\":[\
{\
\"id\":{\"number\":1,\"title\":\"track a.b.1\"},\
\"artist\":[\"artist a.b.1\"],\
\"quality\":{\"format\":\"Flac\",\"bitrate\":1004}\
},\
{\
\"id\":{\"number\":2,\"title\":\"track a.b.2\"},\
\"artist\":[\"artist a.b.2\"],\
\"quality\":{\"format\":\"Flac\",\"bitrate\":1077}\
}\
]\
}\
]\
},\
{\
\"id\":{\"name\":\"album_artist b\"},\
\"sort\":null,\
\"properties\":{\
\"musicbrainz\":\"https://musicbrainz.org/artist/11111111-1111-1111-1111-111111111111\",\
\"musicbutler\":[\
\"https://www.musicbutler.io/artist-page/111111111\",\
\"https://www.musicbutler.io/artist-page/111111112\"\
],\
\"bandcamp\":[\"https://artist-b.bandcamp.com/\"],\
\"qobuz\":\"https://www.qobuz.com/nl-nl/interpreter/artist-b/download-streaming-albums\"\
},\
\"albums\":[\
{\
\"id\":{\"year\":2003,\"title\":\"album_title b.a\"},\
\"tracks\":[\
{\
\"id\":{\"number\":1,\"title\":\"track b.a.1\"},\
\"artist\":[\"artist b.a.1\"],\
\"quality\":{\"format\":\"Mp3\",\"bitrate\":190}\
},\
{\
\"id\":{\"number\":2,\"title\":\"track b.a.2\"},\
\"artist\":[\"artist b.a.2.1\",\"artist b.a.2.2\"],\
\"quality\":{\"format\":\"Mp3\",\"bitrate\":120}\
}\
]\
},\
{\
\"id\":{\"year\":2008,\"title\":\"album_title b.b\"},\
\"tracks\":[\
{\
\"id\":{\"number\":1,\"title\":\"track b.b.1\"},\
\"artist\":[\"artist b.b.1\"],\
\"quality\":{\"format\":\"Flac\",\"bitrate\":1077}\
},\
{\
\"id\":{\"number\":2,\"title\":\"track b.b.2\"},\
\"artist\":[\"artist b.b.2.1\",\"artist b.b.2.2\"],\
\"quality\":{\"format\":\"Mp3\",\"bitrate\":320}\
}\
]\
}\
]\
},\
{\
\"id\":{\"name\":\"album_artist c\"},\
\"sort\":null,\
\"properties\":{\
\"musicbrainz\":\"https://musicbrainz.org/artist/11111111-1111-1111-1111-111111111111\",\
\"musicbutler\":[],\
\"bandcamp\":[],\
\"qobuz\":null\
},\
\"albums\":[\
{\
\"id\":{\"year\":1985,\"title\":\"album_title c.a\"},\
\"tracks\":[\
{\
\"id\":{\"number\":1,\"title\":\"track c.a.1\"},\
\"artist\":[\"artist c.a.1\"],\
\"quality\":{\"format\":\"Mp3\",\"bitrate\":320}\
},\
{\
\"id\":{\"number\":2,\"title\":\"track c.a.2\"},\
\"artist\":[\"artist c.a.2.1\",\"artist c.a.2.2\"],\
\"quality\":{\"format\":\"Mp3\",\"bitrate\":120}\
}\
]\
},\
{\
\"id\":{\"year\":2018,\"title\":\"album_title c.b\"},\
\"tracks\":[\
{\
\"id\":{\"number\":1,\"title\":\"track c.b.1\"},\
\"artist\":[\"artist c.b.1\"],\
\"quality\":{\"format\":\"Flac\",\"bitrate\":1041}\
},\
{\
\"id\":{\"number\":2,\"title\":\"track c.b.2\"},\
\"artist\":[\"artist c.b.2.1\",\"artist c.b.2.2\"],\
\"quality\":{\"format\":\"Flac\",\"bitrate\":756}\
}\
]\
}\
]\
}\
]"
};
}
pub static DATABASE_JSON: Lazy<&str> = Lazy::new(|| database_json!());

View File

@ -1064,13 +1064,17 @@ impl<LIB, DB> MusicHoardBuilder<LIB, DB> {
#[cfg(test)] #[cfg(test)]
#[macro_use] #[macro_use]
mod testmacros;
#[cfg(test)]
mod testlib; mod testlib;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use mockall::predicate; use mockall::predicate;
use once_cell::sync::Lazy;
use crate::library::testlib::LIBRARY_ITEMS;
use crate::testlib::{FULL_COLLECTION, LIBRARY_COLLECTION};
use crate::{database::MockIDatabase, library::MockILibrary}; use crate::{database::MockIDatabase, library::MockILibrary};
use super::*; use super::*;
@ -1086,45 +1090,6 @@ mod tests {
static QOBUZ: &str = "https://www.qobuz.com/nl-nl/interpreter/the-last-hangmen/1244413"; static QOBUZ: &str = "https://www.qobuz.com/nl-nl/interpreter/the-last-hangmen/1244413";
static QOBUZ_2: &str = "https://www.qobuz.com/nl-nl/interpreter/vicious-crusade/7522386"; static QOBUZ_2: &str = "https://www.qobuz.com/nl-nl/interpreter/vicious-crusade/7522386";
pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| collection!());
pub fn artist_to_items(artist: &Artist) -> Vec<Item> {
let mut items = vec![];
for album in artist.albums.iter() {
for track in album.tracks.iter() {
items.push(Item {
album_artist: artist.id.name.clone(),
album_artist_sort: artist.sort.as_ref().map(|s| s.name.clone()),
album_year: album.id.year,
album_title: album.id.title.clone(),
track_number: track.id.number,
track_title: track.id.title.clone(),
track_artist: track.artist.clone(),
track_format: track.quality.format,
track_bitrate: track.quality.bitrate,
});
}
}
items
}
pub fn artists_to_items(artists: &[Artist]) -> Vec<Item> {
let mut items = vec![];
for artist in artists.iter() {
items.append(&mut artist_to_items(artist));
}
items
}
fn clean_collection(mut collection: Collection) -> Collection {
for artist in collection.iter_mut() {
artist.properties = ArtistProperties::default();
}
collection
}
#[test] #[test]
fn musicbrainz() { fn musicbrainz() {
let uuid = "d368baa8-21ca-4759-9731-0b2753071ad8"; let uuid = "d368baa8-21ca-4759-9731-0b2753071ad8";
@ -1897,8 +1862,8 @@ mod tests {
#[test] #[test]
fn merge_album_no_overlap() { fn merge_album_no_overlap() {
let left = COLLECTION[0].albums[0].to_owned(); let left = FULL_COLLECTION[0].albums[0].to_owned();
let mut right = COLLECTION[0].albums[1].to_owned(); let mut right = FULL_COLLECTION[0].albums[1].to_owned();
right.id = left.id.clone(); right.id = left.id.clone();
let mut expected = left.clone(); let mut expected = left.clone();
@ -1915,8 +1880,8 @@ mod tests {
#[test] #[test]
fn merge_album_overlap() { fn merge_album_overlap() {
let mut left = COLLECTION[0].albums[0].to_owned(); let mut left = FULL_COLLECTION[0].albums[0].to_owned();
let mut right = COLLECTION[0].albums[1].to_owned(); let mut right = FULL_COLLECTION[0].albums[1].to_owned();
right.id = left.id.clone(); right.id = left.id.clone();
left.tracks.push(right.tracks[0].clone()); left.tracks.push(right.tracks[0].clone());
left.tracks.sort_unstable(); left.tracks.sort_unstable();
@ -1932,8 +1897,8 @@ mod tests {
#[test] #[test]
fn merge_artist_no_overlap() { fn merge_artist_no_overlap() {
let left = COLLECTION[0].to_owned(); let left = FULL_COLLECTION[0].to_owned();
let mut right = COLLECTION[1].to_owned(); let mut right = FULL_COLLECTION[1].to_owned();
right.id = left.id.clone(); right.id = left.id.clone();
right.properties = ArtistProperties::default(); right.properties = ArtistProperties::default();
@ -1952,8 +1917,8 @@ mod tests {
#[test] #[test]
fn merge_artist_overlap() { fn merge_artist_overlap() {
let mut left = COLLECTION[0].to_owned(); let mut left = FULL_COLLECTION[0].to_owned();
let mut right = COLLECTION[1].to_owned(); let mut right = FULL_COLLECTION[1].to_owned();
right.id = left.id.clone(); right.id = left.id.clone();
left.albums.push(right.albums[0].clone()); left.albums.push(right.albums[0].clone());
left.albums.sort_unstable(); left.albums.sort_unstable();
@ -1970,12 +1935,12 @@ mod tests {
#[test] #[test]
fn merge_collection_no_overlap() { fn merge_collection_no_overlap() {
let half: usize = COLLECTION.len() / 2; let half: usize = FULL_COLLECTION.len() / 2;
let left = COLLECTION[..half].to_owned(); let left = FULL_COLLECTION[..half].to_owned();
let right = COLLECTION[half..].to_owned(); let right = FULL_COLLECTION[half..].to_owned();
let mut expected = COLLECTION.to_owned(); let mut expected = FULL_COLLECTION.to_owned();
expected.sort_unstable(); expected.sort_unstable();
let merged = MusicHoard::<NoLibrary, NoDatabase>::merge_collections( let merged = MusicHoard::<NoLibrary, NoDatabase>::merge_collections(
@ -2001,12 +1966,12 @@ mod tests {
#[test] #[test]
fn merge_collection_overlap() { fn merge_collection_overlap() {
let half: usize = COLLECTION.len() / 2; let half: usize = FULL_COLLECTION.len() / 2;
let left = COLLECTION[..(half + 1)].to_owned(); let left = FULL_COLLECTION[..(half + 1)].to_owned();
let right = COLLECTION[half..].to_owned(); let right = FULL_COLLECTION[half..].to_owned();
let mut expected = COLLECTION.to_owned(); let mut expected = FULL_COLLECTION.to_owned();
expected.sort_unstable(); expected.sort_unstable();
let merged = MusicHoard::<NoLibrary, NoDatabase>::merge_collections( let merged = MusicHoard::<NoLibrary, NoDatabase>::merge_collections(
@ -2037,9 +2002,9 @@ mod tests {
// sorted consistently. If the merge assumes they are sorted consistently this will lead to // sorted consistently. If the merge assumes they are sorted consistently this will lead to
// the same artist appearing twice in the final list. This should not be the case. // the same artist appearing twice in the final list. This should not be the case.
// We will mimic this situation by taking the last artist from COLLECTION and giving it a // We will mimic this situation by taking the last artist from FULL_COLLECTION and giving it a
// sorting name that would place it in the beginning. // sorting name that would place it in the beginning.
let left = COLLECTION.to_owned(); let left = FULL_COLLECTION.to_owned();
let mut right: Vec<Artist> = vec![left.last().unwrap().clone()]; let mut right: Vec<Artist> = vec![left.last().unwrap().clone()];
assert!(right.first().unwrap() > left.first().unwrap()); assert!(right.first().unwrap() > left.first().unwrap());
@ -2082,7 +2047,7 @@ mod tests {
let database = MockIDatabase::new(); let database = MockIDatabase::new();
let library_input = Query::new(); let library_input = Query::new();
let library_result = Ok(artists_to_items(&COLLECTION)); let library_result = Ok(LIBRARY_ITEMS.to_owned());
library library
.expect_list() .expect_list()
@ -2096,10 +2061,7 @@ mod tests {
.build(); .build();
music_hoard.rescan_library().unwrap(); music_hoard.rescan_library().unwrap();
assert_eq!( assert_eq!(music_hoard.get_collection(), &*LIBRARY_COLLECTION);
music_hoard.get_collection(),
&clean_collection(COLLECTION.to_owned())
);
} }
#[test] #[test]
@ -2108,7 +2070,7 @@ mod tests {
let database = MockIDatabase::new(); let database = MockIDatabase::new();
let library_input = Query::new(); let library_input = Query::new();
let mut library_result = Ok(artists_to_items(&COLLECTION)); let mut library_result = Ok(LIBRARY_ITEMS.to_owned());
// Swap the last item with the first. // Swap the last item with the first.
let last = library_result.as_ref().unwrap().len() - 1; let last = library_result.as_ref().unwrap().len() - 1;
@ -2126,10 +2088,7 @@ mod tests {
.build(); .build();
music_hoard.rescan_library().unwrap(); music_hoard.rescan_library().unwrap();
assert_eq!( assert_eq!(music_hoard.get_collection(), &*LIBRARY_COLLECTION);
music_hoard.get_collection(),
&clean_collection(COLLECTION.to_owned())
);
} }
#[test] #[test]
@ -2137,12 +2096,22 @@ mod tests {
let mut library = MockILibrary::new(); let mut library = MockILibrary::new();
let database = MockIDatabase::new(); let database = MockIDatabase::new();
let mut expected = clean_collection(COLLECTION.to_owned()); let mut expected = LIBRARY_COLLECTION.to_owned();
expected[0].albums[0].id.year = expected[1].albums[0].id.year; let removed_album_id = expected[0].albums[0].id.clone();
expected[0].albums[0].id.title = expected[1].albums[0].id.title.clone(); let clashed_album_id = &expected[1].albums[0].id;
let mut items = LIBRARY_ITEMS.to_owned();
for item in items.iter_mut().filter(|it| {
(it.album_year == removed_album_id.year) && (it.album_title == removed_album_id.title)
}) {
item.album_year = clashed_album_id.year;
item.album_title = clashed_album_id.title.clone();
}
expected[0].albums[0].id = clashed_album_id.clone();
let library_input = Query::new(); let library_input = Query::new();
let library_result = Ok(artists_to_items(&expected)); let library_result = Ok(items);
library library
.expect_list() .expect_list()
@ -2164,9 +2133,8 @@ mod tests {
let mut library = MockILibrary::new(); let mut library = MockILibrary::new();
let database = MockIDatabase::new(); let database = MockIDatabase::new();
let expected = clean_collection(COLLECTION.to_owned());
let library_input = Query::new(); let library_input = Query::new();
let mut library_items = artists_to_items(&expected); let mut library_items = LIBRARY_ITEMS.to_owned();
assert_eq!(library_items[0].album_artist, library_items[1].album_artist); assert_eq!(library_items[0].album_artist, library_items[1].album_artist);
library_items[0].album_artist_sort = Some(library_items[0].album_artist.clone()); library_items[0].album_artist_sort = Some(library_items[0].album_artist.clone());
@ -2203,7 +2171,7 @@ mod tests {
database database
.expect_load() .expect_load()
.times(1) .times(1)
.return_once(|| Ok(COLLECTION.to_owned())); .return_once(|| Ok(FULL_COLLECTION.to_owned()));
let mut music_hoard = MusicHoardBuilder::default() let mut music_hoard = MusicHoardBuilder::default()
.set_library(library) .set_library(library)
@ -2211,7 +2179,7 @@ mod tests {
.build(); .build();
music_hoard.load_from_database().unwrap(); music_hoard.load_from_database().unwrap();
assert_eq!(music_hoard.get_collection(), &*COLLECTION); assert_eq!(music_hoard.get_collection(), &*FULL_COLLECTION);
} }
#[test] #[test]
@ -2220,9 +2188,9 @@ mod tests {
let mut database = MockIDatabase::new(); let mut database = MockIDatabase::new();
let library_input = Query::new(); let library_input = Query::new();
let library_result = Ok(artists_to_items(&COLLECTION)); let library_result = Ok(LIBRARY_ITEMS.to_owned());
let database_input = clean_collection(COLLECTION.to_owned()); let database_input = LIBRARY_COLLECTION.to_owned();
let database_result = Ok(()); let database_result = Ok(());
library library
@ -2243,10 +2211,7 @@ mod tests {
.build(); .build();
music_hoard.rescan_library().unwrap(); music_hoard.rescan_library().unwrap();
assert_eq!( assert_eq!(music_hoard.get_collection(), &*LIBRARY_COLLECTION);
music_hoard.get_collection(),
&clean_collection(COLLECTION.to_owned())
);
music_hoard.save_to_database().unwrap(); music_hoard.save_to_database().unwrap();
} }

View File

@ -173,43 +173,10 @@ impl<BLE: IBeetsLibraryExecutor> ILibraryPrivate for BeetsLibrary<BLE> {
mod tests { mod tests {
use mockall::predicate; use mockall::predicate;
use crate::tests::{artists_to_items, COLLECTION}; use crate::library::testlib::{BEETS_OUTPUT, LIBRARY_ITEMS};
use super::*; use super::*;
fn item_to_beets_string(item: &Item) -> String {
format!(
"{album_artist}{sep}{album_artist_sort}{sep}\
{album_year}{sep}{album_title}{sep}\
{track_number}{sep}{track_title}{sep}\
{track_artist}{sep}{track_format}{sep}{track_bitrate}kbps",
album_artist = item.album_artist,
album_artist_sort = match item.album_artist_sort {
Some(ref album_artist_sort) => album_artist_sort,
None => "",
},
album_year = item.album_year,
album_title = item.album_title,
track_number = item.track_number,
track_title = item.track_title,
track_artist = item.track_artist.join("; "),
track_format = match item.track_format {
Format::Flac => TRACK_FORMAT_FLAC,
Format::Mp3 => TRACK_FORMAT_MP3,
},
track_bitrate = item.track_bitrate,
sep = LIST_FORMAT_SEPARATOR,
)
}
fn items_to_beets_strings(items: &[Item]) -> Vec<String> {
let mut strings = vec![];
for item in items.iter() {
strings.push(item_to_beets_string(item));
}
strings
}
#[test] #[test]
fn test_query() { fn test_query() {
let mut query = Query::new() let mut query = Query::new()
@ -279,8 +246,8 @@ mod tests {
#[test] #[test]
fn test_list() { fn test_list() {
let arguments = vec!["ls".to_string(), LIST_FORMAT_ARG.to_string()]; let arguments = vec!["ls".to_string(), LIST_FORMAT_ARG.to_string()];
let expected = artists_to_items(&COLLECTION); let expected: &Vec<Item> = LIBRARY_ITEMS.as_ref();
let result = Ok(items_to_beets_strings(&expected)); let result = Ok(BEETS_OUTPUT.to_owned());
let mut executor = MockIBeetsLibraryExecutor::new(); let mut executor = MockIBeetsLibraryExecutor::new();
executor executor
@ -292,7 +259,7 @@ mod tests {
let mut beets = BeetsLibrary::new(executor); let mut beets = BeetsLibrary::new(executor);
let output = beets.list(&Query::new()).unwrap(); let output = beets.list(&Query::new()).unwrap();
assert_eq!(output, expected); assert_eq!(&output, expected);
} }
#[test] #[test]
@ -333,8 +300,7 @@ mod tests {
#[test] #[test]
fn invalid_data_split() { fn invalid_data_split() {
let arguments = vec!["ls".to_string(), LIST_FORMAT_ARG.to_string()]; let arguments = vec!["ls".to_string(), LIST_FORMAT_ARG.to_string()];
let expected = artists_to_items(&COLLECTION); let mut output: Vec<String> = BEETS_OUTPUT.to_owned();
let mut output = items_to_beets_strings(&expected);
let invalid_string = output[2] let invalid_string = output[2]
.split(LIST_FORMAT_SEPARATOR) .split(LIST_FORMAT_SEPARATOR)
.map(|s| s.to_owned()) .map(|s| s.to_owned())
@ -359,8 +325,7 @@ mod tests {
#[test] #[test]
fn invalid_data_format() { fn invalid_data_format() {
let arguments = vec!["ls".to_string(), LIST_FORMAT_ARG.to_string()]; let arguments = vec!["ls".to_string(), LIST_FORMAT_ARG.to_string()];
let expected = artists_to_items(&COLLECTION); let mut output: Vec<String> = BEETS_OUTPUT.to_owned();
let mut output = items_to_beets_strings(&expected);
let mut invalid_string = output[2] let mut invalid_string = output[2]
.split(LIST_FORMAT_SEPARATOR) .split(LIST_FORMAT_SEPARATOR)
.map(|s| s.to_owned()) .map(|s| s.to_owned())

View File

@ -27,7 +27,7 @@ impl ILibrary for NullLibrary {
} }
/// An item from the library. An item corresponds to an individual file (usually a single track). /// An item from the library. An item corresponds to an individual file (usually a single track).
#[derive(Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Item { pub struct Item {
pub album_artist: String, pub album_artist: String,
pub album_artist_sort: Option<String>, pub album_artist_sort: Option<String>,
@ -134,11 +134,14 @@ impl From<Utf8Error> for Error {
} }
} }
#[cfg(test)]
pub mod testlib;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::io; use std::io;
use super::{Error, Field, ILibrary, NullLibrary, Query}; use super::*;
#[test] #[test]
fn no_library_list() { fn no_library_list() {

192
src/library/testlib.rs Normal file
View File

@ -0,0 +1,192 @@
use once_cell::sync::Lazy;
use crate::library::Item;
use crate::Format;
macro_rules! beets_output {
() => {
vec![
String::from("album_artist a -*^- -*^- 1998 -*^- album_title a.a -*^- 1 -*^- track a.a.1 -*^- artist a.a.1 -*^- FLAC -*^- 992"),
String::from("album_artist a -*^- -*^- 1998 -*^- album_title a.a -*^- 2 -*^- track a.a.2 -*^- artist a.a.2.1; artist a.a.2.2 -*^- MP3 -*^- 320"),
String::from("album_artist a -*^- -*^- 1998 -*^- album_title a.a -*^- 3 -*^- track a.a.3 -*^- artist a.a.3 -*^- FLAC -*^- 1061"),
String::from("album_artist a -*^- -*^- 2015 -*^- album_title a.b -*^- 1 -*^- track a.b.1 -*^- artist a.b.1 -*^- FLAC -*^- 1004"),
String::from("album_artist a -*^- -*^- 2015 -*^- album_title a.b -*^- 2 -*^- track a.b.2 -*^- artist a.b.2 -*^- FLAC -*^- 1077"),
String::from("album_artist b -*^- -*^- 2003 -*^- album_title b.a -*^- 1 -*^- track b.a.1 -*^- artist b.a.1 -*^- MP3 -*^- 190"),
String::from("album_artist b -*^- -*^- 2003 -*^- album_title b.a -*^- 2 -*^- track b.a.2 -*^- artist b.a.2.1; artist b.a.2.2 -*^- MP3 -*^- 120"),
String::from("album_artist b -*^- -*^- 2008 -*^- album_title b.b -*^- 1 -*^- track b.b.1 -*^- artist b.b.1 -*^- FLAC -*^- 1077"),
String::from("album_artist b -*^- -*^- 2008 -*^- album_title b.b -*^- 2 -*^- track b.b.2 -*^- artist b.b.2.1; artist b.b.2.2 -*^- MP3 -*^- 320"),
String::from("album_artist c -*^- -*^- 1985 -*^- album_title c.a -*^- 1 -*^- track c.a.1 -*^- artist c.a.1 -*^- MP3 -*^- 320"),
String::from("album_artist c -*^- -*^- 1985 -*^- album_title c.a -*^- 2 -*^- track c.a.2 -*^- artist c.a.2.1; artist c.a.2.2 -*^- MP3 -*^- 120"),
String::from("album_artist c -*^- -*^- 2018 -*^- album_title c.b -*^- 1 -*^- track c.b.1 -*^- artist c.b.1 -*^- FLAC -*^- 1041"),
String::from("album_artist c -*^- -*^- 2018 -*^- album_title c.b -*^- 2 -*^- track c.b.2 -*^- artist c.b.2.1; artist c.b.2.2 -*^- FLAC -*^- 756")
]
};
}
macro_rules! library_items {
() => {
vec![
Item {
album_artist: String::from("album_artist a"),
album_artist_sort: None,
album_year: 1998,
album_title: String::from("album_title a.a"),
track_number: 1,
track_title: String::from("track a.a.1"),
track_artist: vec![String::from("artist a.a.1")],
track_format: Format::Flac,
track_bitrate: 992,
},
Item {
album_artist: String::from("album_artist a"),
album_artist_sort: None,
album_year: 1998,
album_title: String::from("album_title a.a"),
track_number: 2,
track_title: String::from("track a.a.2"),
track_artist: vec![
String::from("artist a.a.2.1"),
String::from("artist a.a.2.2"),
],
track_format: Format::Mp3,
track_bitrate: 320,
},
Item {
album_artist: String::from("album_artist a"),
album_artist_sort: None,
album_year: 1998,
album_title: String::from("album_title a.a"),
track_number: 3,
track_title: String::from("track a.a.3"),
track_artist: vec![String::from("artist a.a.3")],
track_format: Format::Flac,
track_bitrate: 1061,
},
Item {
album_artist: String::from("album_artist a"),
album_artist_sort: None,
album_year: 2015,
album_title: String::from("album_title a.b"),
track_number: 1,
track_title: String::from("track a.b.1"),
track_artist: vec![String::from("artist a.b.1")],
track_format: Format::Flac,
track_bitrate: 1004,
},
Item {
album_artist: String::from("album_artist a"),
album_artist_sort: None,
album_year: 2015,
album_title: String::from("album_title a.b"),
track_number: 2,
track_title: String::from("track a.b.2"),
track_artist: vec![String::from("artist a.b.2")],
track_format: Format::Flac,
track_bitrate: 1077,
},
Item {
album_artist: String::from("album_artist b"),
album_artist_sort: None,
album_year: 2003,
album_title: String::from("album_title b.a"),
track_number: 1,
track_title: String::from("track b.a.1"),
track_artist: vec![String::from("artist b.a.1")],
track_format: Format::Mp3,
track_bitrate: 190,
},
Item {
album_artist: String::from("album_artist b"),
album_artist_sort: None,
album_year: 2003,
album_title: String::from("album_title b.a"),
track_number: 2,
track_title: String::from("track b.a.2"),
track_artist: vec![
String::from("artist b.a.2.1"),
String::from("artist b.a.2.2"),
],
track_format: Format::Mp3,
track_bitrate: 120,
},
Item {
album_artist: String::from("album_artist b"),
album_artist_sort: None,
album_year: 2008,
album_title: String::from("album_title b.b"),
track_number: 1,
track_title: String::from("track b.b.1"),
track_artist: vec![String::from("artist b.b.1")],
track_format: Format::Flac,
track_bitrate: 1077,
},
Item {
album_artist: String::from("album_artist b"),
album_artist_sort: None,
album_year: 2008,
album_title: String::from("album_title b.b"),
track_number: 2,
track_title: String::from("track b.b.2"),
track_artist: vec![
String::from("artist b.b.2.1"),
String::from("artist b.b.2.2"),
],
track_format: Format::Mp3,
track_bitrate: 320,
},
Item {
album_artist: String::from("album_artist c"),
album_artist_sort: None,
album_year: 1985,
album_title: String::from("album_title c.a"),
track_number: 1,
track_title: String::from("track c.a.1"),
track_artist: vec![String::from("artist c.a.1")],
track_format: Format::Mp3,
track_bitrate: 320,
},
Item {
album_artist: String::from("album_artist c"),
album_artist_sort: None,
album_year: 1985,
album_title: String::from("album_title c.a"),
track_number: 2,
track_title: String::from("track c.a.2"),
track_artist: vec![
String::from("artist c.a.2.1"),
String::from("artist c.a.2.2"),
],
track_format: Format::Mp3,
track_bitrate: 120,
},
Item {
album_artist: String::from("album_artist c"),
album_artist_sort: None,
album_year: 2018,
album_title: String::from("album_title c.b"),
track_number: 1,
track_title: String::from("track c.b.1"),
track_artist: vec![String::from("artist c.b.1")],
track_format: Format::Flac,
track_bitrate: 1041,
},
Item {
album_artist: String::from("album_artist c"),
album_artist_sort: None,
album_year: 2018,
album_title: String::from("album_title c.b"),
track_number: 2,
track_title: String::from("track c.b.2"),
track_artist: vec![
String::from("artist c.b.2.1"),
String::from("artist c.b.2.2"),
],
track_format: Format::Flac,
track_bitrate: 756,
},
]
};
}
pub static BEETS_OUTPUT: Lazy<Vec<String>> = Lazy::new(|| beets_output!());
pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| library_items!());

View File

@ -128,13 +128,7 @@ fn main() {
#[cfg(test)] #[cfg(test)]
#[macro_use] #[macro_use]
mod testlib; mod testmacros;
#[cfg(test)] #[cfg(test)]
mod tests { mod testbin;
use once_cell::sync::Lazy;
use musichoard::*;
pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| collection!());
}

7
src/testbin.rs Normal file
View File

@ -0,0 +1,7 @@
use musichoard::{
Album, AlbumId, Artist, ArtistId, ArtistProperties, Bandcamp, Format, MusicBrainz, MusicButler,
Qobuz, Quality, Track, TrackId,
};
use once_cell::sync::Lazy;
pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| full_collection!());

View File

@ -1,277 +1,8 @@
macro_rules! collection { use crate::{
() => { Album, AlbumId, Artist, ArtistId, ArtistProperties, Bandcamp, Format, MusicBrainz, MusicButler,
vec![ Qobuz, Quality, Track, TrackId,
Artist { };
id: ArtistId { use once_cell::sync::Lazy;
name: "album_artist a".to_string(),
}, pub static LIBRARY_COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| library_collection!());
sort: None, pub static FULL_COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| full_collection!());
properties: ArtistProperties {
musicbrainz: Some(MusicBrainz::new(
"https://musicbrainz.org/artist/00000000-0000-0000-0000-000000000000",
).unwrap()),
musicbutler: vec![
MusicButler::new(
"https://www.musicbutler.io/artist-page/000000000"
).unwrap(),
],
bandcamp: vec![],
qobuz: Some(Qobuz::new(
"https://www.qobuz.com/nl-nl/interpreter/artist-a/download-streaming-albums",
).unwrap()),
},
albums: vec![
Album {
id: AlbumId {
year: 1998,
title: "album_title a.a".to_string(),
},
tracks: vec![
Track {
id: TrackId {
number: 1,
title: "track a.a.1".to_string(),
},
artist: vec!["artist a.a.1".to_string()],
quality: Quality {
format: Format::Flac,
bitrate: 992,
},
},
Track {
id: TrackId {
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(),
],
quality: Quality {
format: Format::Mp3,
bitrate: 320,
},
},
Track {
id: TrackId {
number: 3,
title: "track a.a.3".to_string(),
},
artist: vec!["artist a.a.3".to_string()],
quality: Quality {
format: Format::Flac,
bitrate: 1061,
},
},
],
},
Album {
id: AlbumId {
year: 2015,
title: "album_title a.b".to_string(),
},
tracks: vec![
Track {
id: TrackId {
number: 1,
title: "track a.b.1".to_string(),
},
artist: vec!["artist a.b.1".to_string()],
quality: Quality {
format: Format::Flac,
bitrate: 1004,
},
},
Track {
id: TrackId {
number: 2,
title: "track a.b.2".to_string(),
},
artist: vec!["artist a.b.2".to_string()],
quality: Quality {
format: Format::Flac,
bitrate: 1077,
},
},
],
},
],
},
Artist {
id: ArtistId {
name: "album_artist b".to_string(),
},
sort: None,
properties: ArtistProperties {
musicbrainz: Some(MusicBrainz::new(
"https://musicbrainz.org/artist/11111111-1111-1111-1111-111111111111",
).unwrap()),
musicbutler: vec![
MusicButler::new(
"https://www.musicbutler.io/artist-page/111111111"
).unwrap(),
MusicButler::new(
"https://www.musicbutler.io/artist-page/111111112"
).unwrap(),
],
bandcamp: vec![
Bandcamp::new("https://artist-b.bandcamp.com/").unwrap(),
],
qobuz: Some(Qobuz::new(
"https://www.qobuz.com/nl-nl/interpreter/artist-b/download-streaming-albums",
).unwrap()),
},
albums: vec![
Album {
id: AlbumId {
year: 2003,
title: "album_title b.a".to_string(),
},
tracks: vec![
Track {
id: TrackId {
number: 1,
title: "track b.a.1".to_string(),
},
artist: vec!["artist b.a.1".to_string()],
quality: Quality {
format: Format::Mp3,
bitrate: 190,
},
},
Track {
id: TrackId {
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(),
],
quality: Quality {
format: Format::Mp3,
bitrate: 120,
},
},
],
},
Album {
id: AlbumId {
year: 2008,
title: "album_title b.b".to_string(),
},
tracks: vec![
Track {
id: TrackId {
number: 1,
title: "track b.b.1".to_string(),
},
artist: vec!["artist b.b.1".to_string()],
quality: Quality {
format: Format::Flac,
bitrate: 1077,
},
},
Track {
id: TrackId {
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(),
],
quality: Quality {
format: Format::Mp3,
bitrate: 320,
},
},
],
},
],
},
Artist {
id: ArtistId {
name: "album_artist c".to_string(),
},
sort: None,
properties: ArtistProperties {
musicbrainz: Some(MusicBrainz::new(
"https://musicbrainz.org/artist/11111111-1111-1111-1111-111111111111",
).unwrap()),
musicbutler: vec![],
bandcamp: vec![],
qobuz: None,
},
albums: vec![
Album {
id: AlbumId {
year: 1985,
title: "album_title c.a".to_string(),
},
tracks: vec![
Track {
id: TrackId {
number: 1,
title: "track c.a.1".to_string(),
},
artist: vec!["artist c.a.1".to_string()],
quality: Quality {
format: Format::Mp3,
bitrate: 320,
},
},
Track {
id: TrackId {
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(),
],
quality: Quality {
format: Format::Mp3,
bitrate: 120,
},
},
],
},
Album {
id: AlbumId {
year: 2018,
title: "album_title c.b".to_string(),
},
tracks: vec![
Track {
id: TrackId {
number: 1,
title: "track c.b.1".to_string(),
},
artist: vec!["artist c.b.1".to_string()],
quality: Quality {
format: Format::Flac,
bitrate: 1041,
},
},
Track {
id: TrackId {
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(),
],
quality: Quality {
format: Format::Flac,
bitrate: 756,
},
},
],
},
],
},
]
};
}

323
src/testmacros.rs Normal file
View File

@ -0,0 +1,323 @@
macro_rules! library_collection {
() => {
vec![
Artist {
id: ArtistId {
name: "album_artist a".to_string(),
},
sort: None,
properties: ArtistProperties {
musicbrainz: None,
musicbutler: vec![],
bandcamp: vec![],
qobuz: None,
},
albums: vec![
Album {
id: AlbumId {
year: 1998,
title: "album_title a.a".to_string(),
},
tracks: vec![
Track {
id: TrackId {
number: 1,
title: "track a.a.1".to_string(),
},
artist: vec!["artist a.a.1".to_string()],
quality: Quality {
format: Format::Flac,
bitrate: 992,
},
},
Track {
id: TrackId {
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(),
],
quality: Quality {
format: Format::Mp3,
bitrate: 320,
},
},
Track {
id: TrackId {
number: 3,
title: "track a.a.3".to_string(),
},
artist: vec!["artist a.a.3".to_string()],
quality: Quality {
format: Format::Flac,
bitrate: 1061,
},
},
],
},
Album {
id: AlbumId {
year: 2015,
title: "album_title a.b".to_string(),
},
tracks: vec![
Track {
id: TrackId {
number: 1,
title: "track a.b.1".to_string(),
},
artist: vec!["artist a.b.1".to_string()],
quality: Quality {
format: Format::Flac,
bitrate: 1004,
},
},
Track {
id: TrackId {
number: 2,
title: "track a.b.2".to_string(),
},
artist: vec!["artist a.b.2".to_string()],
quality: Quality {
format: Format::Flac,
bitrate: 1077,
},
},
],
},
],
},
Artist {
id: ArtistId {
name: "album_artist b".to_string(),
},
sort: None,
properties: ArtistProperties {
musicbrainz: None,
musicbutler: vec![],
bandcamp: vec![],
qobuz: None,
},
albums: vec![
Album {
id: AlbumId {
year: 2003,
title: "album_title b.a".to_string(),
},
tracks: vec![
Track {
id: TrackId {
number: 1,
title: "track b.a.1".to_string(),
},
artist: vec!["artist b.a.1".to_string()],
quality: Quality {
format: Format::Mp3,
bitrate: 190,
},
},
Track {
id: TrackId {
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(),
],
quality: Quality {
format: Format::Mp3,
bitrate: 120,
},
},
],
},
Album {
id: AlbumId {
year: 2008,
title: "album_title b.b".to_string(),
},
tracks: vec![
Track {
id: TrackId {
number: 1,
title: "track b.b.1".to_string(),
},
artist: vec!["artist b.b.1".to_string()],
quality: Quality {
format: Format::Flac,
bitrate: 1077,
},
},
Track {
id: TrackId {
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(),
],
quality: Quality {
format: Format::Mp3,
bitrate: 320,
},
},
],
},
],
},
Artist {
id: ArtistId {
name: "album_artist c".to_string(),
},
sort: None,
properties: ArtistProperties {
musicbrainz: None,
musicbutler: vec![],
bandcamp: vec![],
qobuz: None,
},
albums: vec![
Album {
id: AlbumId {
year: 1985,
title: "album_title c.a".to_string(),
},
tracks: vec![
Track {
id: TrackId {
number: 1,
title: "track c.a.1".to_string(),
},
artist: vec!["artist c.a.1".to_string()],
quality: Quality {
format: Format::Mp3,
bitrate: 320,
},
},
Track {
id: TrackId {
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(),
],
quality: Quality {
format: Format::Mp3,
bitrate: 120,
},
},
],
},
Album {
id: AlbumId {
year: 2018,
title: "album_title c.b".to_string(),
},
tracks: vec![
Track {
id: TrackId {
number: 1,
title: "track c.b.1".to_string(),
},
artist: vec!["artist c.b.1".to_string()],
quality: Quality {
format: Format::Flac,
bitrate: 1041,
},
},
Track {
id: TrackId {
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(),
],
quality: Quality {
format: Format::Flac,
bitrate: 756,
},
},
],
},
],
},
]
};
}
macro_rules! full_collection {
() => {{
let mut collection = library_collection!();
let mut iter = collection.iter_mut();
let artist_a = iter.next().unwrap();
assert_eq!(artist_a.id.name, "album_artist a");
artist_a.properties = ArtistProperties {
musicbrainz: Some(
MusicBrainz::new(
"https://musicbrainz.org/artist/00000000-0000-0000-0000-000000000000",
)
.unwrap(),
),
musicbutler: vec![
MusicButler::new("https://www.musicbutler.io/artist-page/000000000").unwrap(),
],
bandcamp: vec![],
qobuz: Some(
Qobuz::new(
"https://www.qobuz.com/nl-nl/interpreter/artist-a/download-streaming-albums",
)
.unwrap(),
),
};
let artist_b = iter.next().unwrap();
assert_eq!(artist_b.id.name, "album_artist b");
artist_b.properties = ArtistProperties {
musicbrainz: Some(
MusicBrainz::new(
"https://musicbrainz.org/artist/11111111-1111-1111-1111-111111111111",
)
.unwrap(),
),
musicbutler: vec![
MusicButler::new("https://www.musicbutler.io/artist-page/111111111").unwrap(),
MusicButler::new("https://www.musicbutler.io/artist-page/111111112").unwrap(),
],
bandcamp: vec![Bandcamp::new("https://artist-b.bandcamp.com/").unwrap()],
qobuz: Some(
Qobuz::new(
"https://www.qobuz.com/nl-nl/interpreter/artist-b/download-streaming-albums",
)
.unwrap(),
),
};
let artist_c = iter.next().unwrap();
assert_eq!(artist_c.id.name, "album_artist c");
artist_c.properties = ArtistProperties {
musicbrainz: Some(
MusicBrainz::new(
"https://musicbrainz.org/artist/11111111-1111-1111-1111-111111111111",
)
.unwrap(),
),
musicbutler: vec![],
bandcamp: vec![],
qobuz: None,
};
collection
}};
}

View File

@ -164,7 +164,7 @@ mod tests {
use musichoard::Collection; use musichoard::Collection;
use ratatui::{backend::TestBackend, Terminal}; use ratatui::{backend::TestBackend, Terminal};
use crate::tests::COLLECTION; use crate::testbin::COLLECTION;
use super::{ use super::{
event::EventError, event::EventError,

View File

@ -733,7 +733,7 @@ impl<MH: IMusicHoard> IUi for Ui<MH> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::tests::COLLECTION; use crate::testbin::COLLECTION;
use crate::tui::lib::MockIMusicHoard; use crate::tui::lib::MockIMusicHoard;
use crate::tui::tests::{terminal, ui}; use crate::tui::tests::{terminal, ui};