Increase code coverage to a sensible amount #33
@ -137,7 +137,7 @@ mod tests {
|
||||
let mut library = MockLibrary::new();
|
||||
let database = MockDatabase::new();
|
||||
|
||||
let library_result = Err(library::Error::InvalidData(String::from("invalid data")));
|
||||
let library_result = Err(library::Error::Invalid(String::from("invalid data")));
|
||||
|
||||
library
|
||||
.expect_list()
|
||||
@ -147,9 +147,8 @@ mod tests {
|
||||
let mut collection_manager = MhCollectionManager::new(library, database);
|
||||
|
||||
let actual_err = collection_manager.rescan_library().unwrap_err();
|
||||
let expected_err = Error::LibraryError(
|
||||
library::Error::InvalidData(String::from("invalid data")).to_string(),
|
||||
);
|
||||
let expected_err =
|
||||
Error::LibraryError(library::Error::Invalid(String::from("invalid data")).to_string());
|
||||
|
||||
assert_eq!(actual_err, expected_err);
|
||||
assert_eq!(actual_err.to_string(), expected_err.to_string());
|
||||
|
@ -81,11 +81,13 @@ impl JsonDatabaseBackend for JsonDatabaseFileBackend {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::collections::HashMap;
|
||||
|
||||
use mockall::predicate;
|
||||
|
||||
use super::*;
|
||||
|
||||
use crate::{tests::COLLECTION, Artist, TrackFormat};
|
||||
use crate::{tests::COLLECTION, Artist, ArtistId, TrackFormat};
|
||||
|
||||
fn artist_to_json(artist: &Artist) -> String {
|
||||
let album_artist = &artist.id.name;
|
||||
@ -203,4 +205,21 @@ mod tests {
|
||||
|
||||
assert_eq!(write_data, read_data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn errors() {
|
||||
let mut object = HashMap::<ArtistId, String>::new();
|
||||
object.insert(
|
||||
ArtistId {
|
||||
name: String::from("artist"),
|
||||
},
|
||||
String::from("string"),
|
||||
);
|
||||
let serde_err = serde_json::to_string(&object);
|
||||
assert!(serde_err.is_err());
|
||||
|
||||
let serde_err: Error = serde_err.unwrap_err().into();
|
||||
assert!(!serde_err.to_string().is_empty());
|
||||
assert!(!format!("{:?}", serde_err).is_empty());
|
||||
}
|
||||
}
|
||||
|
@ -44,3 +44,17 @@ pub trait Database {
|
||||
/// Write collection to the database.
|
||||
fn write<S: Serialize + 'static>(&mut self, collection: &S) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::io;
|
||||
|
||||
use super::Error;
|
||||
|
||||
#[test]
|
||||
fn errors() {
|
||||
let io_err: Error = io::Error::new(io::ErrorKind::Interrupted, "error").into();
|
||||
assert!(!io_err.to_string().is_empty());
|
||||
assert!(!format!("{:?}", io_err).is_empty());
|
||||
}
|
||||
}
|
||||
|
@ -164,7 +164,7 @@ impl<BLE: BeetsLibraryExecutor> LibraryPrivate for BeetsLibrary<BLE> {
|
||||
|
||||
let split: Vec<&str> = line.split(LIST_FORMAT_SEPARATOR).collect();
|
||||
if split.len() != 7 {
|
||||
return Err(Error::InvalidData(line.to_string()));
|
||||
return Err(Error::Invalid(line.to_string()));
|
||||
}
|
||||
|
||||
let album_artist = split[0].to_string();
|
||||
@ -189,7 +189,7 @@ impl<BLE: BeetsLibraryExecutor> LibraryPrivate for BeetsLibrary<BLE> {
|
||||
format: match track_format.as_ref() {
|
||||
TRACK_FORMAT_FLAC => TrackFormat::Flac,
|
||||
TRACK_FORMAT_MP3 => TrackFormat::Mp3,
|
||||
_ => return Err(Error::InvalidData(track_format)),
|
||||
_ => return Err(Error::Invalid(line.to_string())),
|
||||
},
|
||||
};
|
||||
|
||||
@ -272,7 +272,7 @@ impl BeetsLibraryExecutor for BeetsLibraryCommandExecutor {
|
||||
}
|
||||
let output = cmd.args(arguments).output()?;
|
||||
if !output.status.success() {
|
||||
return Err(Error::CmdExecError(
|
||||
return Err(Error::CmdExec(
|
||||
String::from_utf8_lossy(&output.stderr).to_string(),
|
||||
));
|
||||
}
|
||||
@ -346,6 +346,25 @@ mod tests {
|
||||
String::from("^some.all"),
|
||||
]
|
||||
);
|
||||
|
||||
let query = Query::new()
|
||||
.album_artist(QueryOption::Exclude(String::from("some.albumartist")))
|
||||
.album_year(QueryOption::Include(3030))
|
||||
.track_title(QueryOption::Include(String::from("some.track")))
|
||||
.track_artist(QueryOption::Exclude(vec![
|
||||
String::from("some.artist.1"),
|
||||
String::from("some.artist.2"),
|
||||
]));
|
||||
|
||||
assert_eq!(
|
||||
query.to_args(),
|
||||
vec![
|
||||
String::from("^albumartist:some.albumartist"),
|
||||
String::from("year:3030"),
|
||||
String::from("title:some.track"),
|
||||
String::from("^artist:some.artist.1; some.artist.2"),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -472,4 +491,61 @@ mod tests {
|
||||
let expected: Vec<Artist> = vec![];
|
||||
assert_eq!(output, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_data_split() {
|
||||
let arguments = vec!["ls".to_string(), LIST_FORMAT_ARG.to_string()];
|
||||
let expected = COLLECTION.to_owned();
|
||||
let mut output = artists_to_beets_string(&expected);
|
||||
let invalid_string = output[2]
|
||||
.split(LIST_FORMAT_SEPARATOR)
|
||||
.map(|s| s.to_owned())
|
||||
.collect::<Vec<String>>()[..4]
|
||||
.join(LIST_FORMAT_SEPARATOR);
|
||||
output[2] = invalid_string.clone();
|
||||
let result = Ok(output);
|
||||
|
||||
let mut executor = MockBeetsLibraryExecutor::new();
|
||||
executor
|
||||
.expect_exec()
|
||||
.with(predicate::eq(arguments))
|
||||
.times(1)
|
||||
.return_once(|_| result);
|
||||
|
||||
let mut beets = BeetsLibrary::new(executor);
|
||||
let err = beets.list(&Query::default()).unwrap_err();
|
||||
|
||||
assert_eq!(err, Error::Invalid(invalid_string));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_data_format() {
|
||||
let arguments = vec!["ls".to_string(), LIST_FORMAT_ARG.to_string()];
|
||||
let expected = COLLECTION.to_owned();
|
||||
let mut output = artists_to_beets_string(&expected);
|
||||
let mut invalid_string = output[2]
|
||||
.split(LIST_FORMAT_SEPARATOR)
|
||||
.map(|s| s.to_owned())
|
||||
.collect::<Vec<String>>();
|
||||
invalid_string.last_mut().unwrap().clear();
|
||||
invalid_string
|
||||
.last_mut()
|
||||
.unwrap()
|
||||
.push_str("invalid format");
|
||||
let invalid_string = invalid_string.join(LIST_FORMAT_SEPARATOR);
|
||||
output[2] = invalid_string.clone();
|
||||
let result = Ok(output);
|
||||
|
||||
let mut executor = MockBeetsLibraryExecutor::new();
|
||||
executor
|
||||
.expect_exec()
|
||||
.with(predicate::eq(arguments))
|
||||
.times(1)
|
||||
.return_once(|_| result);
|
||||
|
||||
let mut beets = BeetsLibrary::new(executor);
|
||||
let err = beets.list(&Query::default()).unwrap_err();
|
||||
|
||||
assert_eq!(err, Error::Invalid(invalid_string));
|
||||
}
|
||||
}
|
||||
|
@ -20,18 +20,6 @@ pub enum QueryOption<T> {
|
||||
None,
|
||||
}
|
||||
|
||||
impl<T> QueryOption<T> {
|
||||
/// Return `true` if [`QueryOption`] is not [`QueryOption::None`].
|
||||
pub fn is_some(&self) -> bool {
|
||||
!matches!(self, QueryOption::None)
|
||||
}
|
||||
|
||||
/// Return `true` if [`QueryOption`] is [`QueryOption::None`].
|
||||
pub fn is_none(&self) -> bool {
|
||||
matches!(self, QueryOption::None)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Default for QueryOption<T> {
|
||||
/// Create a [`QueryOption::None`] for type `T`.
|
||||
fn default() -> Self {
|
||||
@ -101,47 +89,47 @@ impl Query {
|
||||
}
|
||||
|
||||
/// Error type for library calls.
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum Error {
|
||||
/// The underlying library failed to execute a command.
|
||||
CmdExecError(String),
|
||||
CmdExec(String),
|
||||
/// The underlying library returned invalid data.
|
||||
InvalidData(String),
|
||||
Invalid(String),
|
||||
/// The underlying library experienced an I/O error.
|
||||
IoError(String),
|
||||
Io(String),
|
||||
/// The underlying library failed to parse an integer.
|
||||
ParseIntError(String),
|
||||
ParseInt(String),
|
||||
/// The underlying library failed to parse a UTF-8 string.
|
||||
Utf8Error(String),
|
||||
Utf8(String),
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
Self::CmdExecError(ref s) => write!(f, "the library failed to execute a command: {s}"),
|
||||
Self::InvalidData(ref s) => write!(f, "the library received invalid data: {s}"),
|
||||
Self::IoError(ref s) => write!(f, "the library experienced an I/O error: {s}"),
|
||||
Self::ParseIntError(ref s) => write!(f, "the library received an invalid integer: {s}"),
|
||||
Self::Utf8Error(ref s) => write!(f, "the library received invalid UTF-8: {s}"),
|
||||
Self::CmdExec(ref s) => write!(f, "the library failed to execute a command: {s}"),
|
||||
Self::Invalid(ref s) => write!(f, "the library received invalid data: {s}"),
|
||||
Self::Io(ref s) => write!(f, "the library experienced an I/O error: {s}"),
|
||||
Self::ParseInt(ref s) => write!(f, "the library received an invalid integer: {s}"),
|
||||
Self::Utf8(ref s) => write!(f, "the library received invalid UTF-8: {s}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::io::Error> for Error {
|
||||
fn from(err: std::io::Error) -> Error {
|
||||
Error::IoError(err.to_string())
|
||||
Error::Io(err.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ParseIntError> for Error {
|
||||
fn from(err: ParseIntError) -> Error {
|
||||
Error::ParseIntError(err.to_string())
|
||||
Error::ParseInt(err.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Utf8Error> for Error {
|
||||
fn from(err: Utf8Error) -> Error {
|
||||
Error::Utf8Error(err.to_string())
|
||||
Error::Utf8(err.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
@ -151,3 +139,31 @@ pub trait Library {
|
||||
/// List lirbary items that match the a specific query.
|
||||
fn list(&mut self, query: &Query) -> Result<Vec<Artist>, Error>;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::io;
|
||||
|
||||
use super::Error;
|
||||
|
||||
#[test]
|
||||
fn errors() {
|
||||
let cmd_err = Error::CmdExec(String::from("CmdExec"));
|
||||
let inv_err = Error::Invalid(String::from("Invalid"));
|
||||
let io_err: Error = io::Error::new(io::ErrorKind::Interrupted, "Interrupted").into();
|
||||
let int_err: Error = "five".parse::<u32>().unwrap_err().into();
|
||||
let utf_err: Error = std::str::from_utf8(b"\xe2\x28\xa1").unwrap_err().into();
|
||||
|
||||
assert!(!cmd_err.to_string().is_empty());
|
||||
assert!(!inv_err.to_string().is_empty());
|
||||
assert!(!io_err.to_string().is_empty());
|
||||
assert!(!int_err.to_string().is_empty());
|
||||
assert!(!utf_err.to_string().is_empty());
|
||||
|
||||
assert!(!format!("{:?}", cmd_err).is_empty());
|
||||
assert!(!format!("{:?}", inv_err).is_empty());
|
||||
assert!(!format!("{:?}", io_err).is_empty());
|
||||
assert!(!format!("{:?}", int_err).is_empty());
|
||||
assert!(!format!("{:?}", utf_err).is_empty());
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
use std::{
|
||||
fs,
|
||||
path::PathBuf,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
@ -42,6 +43,17 @@ fn test_no_config_list() {
|
||||
assert_eq!(output, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_invalid_config() {
|
||||
let mut beets = BeetsLibrary::new(BeetsLibraryCommandExecutor::default().config(Some(
|
||||
&PathBuf::from("./tests/files/library/config-does-not-exist.yml"),
|
||||
)));
|
||||
|
||||
let result = beets.list(&Query::default());
|
||||
assert!(result.is_err());
|
||||
assert!(!result.unwrap_err().to_string().is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_full_list() {
|
||||
let beets_arc = BEETS_TEST_CONFIG.clone();
|
||||
|
Loading…
Reference in New Issue
Block a user