Unit test collection module
This commit is contained in:
parent
acebc47946
commit
e0da5d20a9
1
.gitignore
vendored
1
.gitignore
vendored
@ -1 +1,2 @@
|
||||
/target
|
||||
/codecov
|
||||
|
16
README.md
16
README.md
@ -12,21 +12,23 @@ cargo install grcov
|
||||
### Generating Code Coverage
|
||||
|
||||
```sh
|
||||
cargo clean
|
||||
env CARGO_TARGET_DIR=codecov \
|
||||
cargo clean
|
||||
env RUSTFLAGS="-C instrument-coverage" \
|
||||
LLVM_PROFILE_FILE="target/debug/profraw/musichoard-%p-%m.profraw" \
|
||||
LLVM_PROFILE_FILE="codecov/debug/profraw/musichoard-%p-%m.profraw" \
|
||||
CARGO_TARGET_DIR=codecov \
|
||||
cargo test
|
||||
grcov target/debug/profraw \
|
||||
--binary-path ./target/debug/ \
|
||||
grcov codecov/debug/profraw \
|
||||
--binary-path ./codecov/debug/ \
|
||||
--output-types html \
|
||||
--source-dir . \
|
||||
--ignore-not-existing \
|
||||
--ignore "tests/*" \
|
||||
--ignore "src/main.rs" \
|
||||
--excl-start "mod tests \{" \
|
||||
--output-path ./target/debug/coverage/
|
||||
xdg-open target/debug/coverage/index.html
|
||||
--output-path ./codecov/debug/coverage/
|
||||
xdg-open codecov/debug/coverage/index.html
|
||||
```
|
||||
|
||||
Note that some changes may not be visible until `target/debug/coverage` is removed and the `grcov`
|
||||
Note that some changes may not be visible until `codecov/debug/coverage` is removed and the `grcov`
|
||||
command is rerun.
|
||||
|
@ -12,7 +12,7 @@ use crate::{
|
||||
pub type Collection = Vec<Artist>;
|
||||
|
||||
/// Error type for collection manager.
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum Error {
|
||||
/// The [`CollectionManager`] failed to read/write from/to the library.
|
||||
LibraryError(String),
|
||||
@ -81,3 +81,91 @@ impl CollectionManager {
|
||||
&self.collection
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use mockall::predicate;
|
||||
|
||||
use crate::{
|
||||
database::{self, MockDatabase},
|
||||
library::{self, MockLibrary, Query},
|
||||
tests::COLLECTION,
|
||||
};
|
||||
|
||||
use super::{CollectionManager, Error};
|
||||
|
||||
#[test]
|
||||
fn read_get_write() {
|
||||
let mut library = MockLibrary::new();
|
||||
let mut database = MockDatabase::new();
|
||||
|
||||
let library_input = Query::default();
|
||||
let library_result = Ok(COLLECTION.to_owned());
|
||||
|
||||
let database_input = COLLECTION.to_owned();
|
||||
let database_result = Ok(());
|
||||
|
||||
library
|
||||
.expect_list()
|
||||
.with(predicate::eq(library_input))
|
||||
.times(1)
|
||||
.return_once(|_| library_result);
|
||||
|
||||
database
|
||||
.expect_write()
|
||||
.with(predicate::eq(database_input))
|
||||
.times(1)
|
||||
.return_once(|_| database_result);
|
||||
|
||||
let mut collection_manager = CollectionManager::new(Box::new(library), Box::new(database));
|
||||
|
||||
collection_manager.rescan_library().unwrap();
|
||||
assert_eq!(collection_manager.get_collection(), &*COLLECTION);
|
||||
collection_manager.save_to_database().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn library_error() {
|
||||
let mut library = MockLibrary::new();
|
||||
let database = MockDatabase::new();
|
||||
|
||||
let library_result = Err(library::Error::InvalidData(String::from("invalid data")));
|
||||
|
||||
library
|
||||
.expect_list()
|
||||
.times(1)
|
||||
.return_once(|_| library_result);
|
||||
|
||||
let mut collection_manager = CollectionManager::new(Box::new(library), Box::new(database));
|
||||
|
||||
let actual_err = collection_manager.rescan_library().unwrap_err();
|
||||
let expected_err = Error::LibraryError(
|
||||
library::Error::InvalidData(String::from("invalid data")).to_string(),
|
||||
);
|
||||
|
||||
assert_eq!(actual_err, expected_err);
|
||||
assert_eq!(actual_err.to_string(), expected_err.to_string());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn database_error() {
|
||||
let library = MockLibrary::new();
|
||||
let mut database = MockDatabase::new();
|
||||
|
||||
let database_result = Err(database::Error::IoError(String::from("I/O error")));
|
||||
|
||||
database
|
||||
.expect_write()
|
||||
.times(1)
|
||||
.return_once(|_| database_result);
|
||||
|
||||
let mut collection_manager = CollectionManager::new(Box::new(library), Box::new(database));
|
||||
|
||||
let actual_err = collection_manager.save_to_database().unwrap_err();
|
||||
let expected_err =
|
||||
Error::DatabaseError(database::Error::IoError(String::from("I/O error")).to_string());
|
||||
|
||||
assert_eq!(actual_err, expected_err);
|
||||
assert_eq!(actual_err.to_string(), expected_err.to_string());
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ use crate::collection::Collection;
|
||||
#[cfg(test)]
|
||||
use mockall::automock;
|
||||
|
||||
use super::{Database, DatabaseRead, DatabaseWrite, Error};
|
||||
use super::{Database, Error};
|
||||
|
||||
impl From<serde_json::Error> for Error {
|
||||
fn from(err: serde_json::Error) -> Error {
|
||||
@ -38,15 +38,13 @@ impl JsonDatabase {
|
||||
}
|
||||
}
|
||||
|
||||
impl DatabaseRead for JsonDatabase {
|
||||
impl Database for JsonDatabase {
|
||||
fn read(&self, collection: &mut Collection) -> Result<(), Error> {
|
||||
let serialized = self.backend.read()?;
|
||||
*collection = serde_json::from_str(&serialized)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl DatabaseWrite for JsonDatabase {
|
||||
fn write(&mut self, collection: &Collection) -> Result<(), Error> {
|
||||
let serialized = serde_json::to_string(&collection)?;
|
||||
self.backend.write(&serialized)?;
|
||||
@ -54,8 +52,6 @@ impl DatabaseWrite for JsonDatabase {
|
||||
}
|
||||
}
|
||||
|
||||
impl Database for JsonDatabase {}
|
||||
|
||||
/// JSON database backend that uses a local file for persistent storage.
|
||||
pub struct JsonDatabaseFileBackend {
|
||||
path: PathBuf,
|
||||
|
@ -2,6 +2,9 @@
|
||||
|
||||
use std::fmt;
|
||||
|
||||
#[cfg(test)]
|
||||
use mockall::automock;
|
||||
|
||||
use crate::collection::Collection;
|
||||
|
||||
pub mod json;
|
||||
@ -32,17 +35,12 @@ impl From<std::io::Error> for Error {
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait for database reads.
|
||||
pub trait DatabaseRead {
|
||||
/// Trait for interacting with the database.
|
||||
#[cfg_attr(test, automock)]
|
||||
pub trait Database {
|
||||
/// Read collection from the database.
|
||||
fn read(&self, collection: &mut Collection) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
/// Trait for database writes.
|
||||
pub trait DatabaseWrite {
|
||||
/// Write collection to the database.
|
||||
fn write(&mut self, collection: &Collection) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
/// Trait for database reads and writes.
|
||||
pub trait Database: DatabaseRead + DatabaseWrite {}
|
||||
|
@ -2,11 +2,15 @@
|
||||
|
||||
use std::{num::ParseIntError, str::Utf8Error, fmt};
|
||||
|
||||
#[cfg(test)]
|
||||
use mockall::automock;
|
||||
|
||||
use crate::Artist;
|
||||
|
||||
pub mod beets;
|
||||
|
||||
/// A single query option.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum QueryOption<T> {
|
||||
/// Inclusive query.
|
||||
Include(T),
|
||||
@ -36,7 +40,7 @@ impl<T> Default for QueryOption<T> {
|
||||
}
|
||||
|
||||
/// Options for refining library queries.
|
||||
#[derive(Default)]
|
||||
#[derive(Debug, Default, PartialEq, Eq)]
|
||||
pub struct Query {
|
||||
album_artist: QueryOption<String>,
|
||||
album_year: QueryOption<u32>,
|
||||
@ -142,6 +146,7 @@ impl From<Utf8Error> for Error {
|
||||
}
|
||||
|
||||
/// Trait for interacting with the music library.
|
||||
#[cfg_attr(test, automock)]
|
||||
pub trait Library {
|
||||
/// List lirbary items that match the a specific query.
|
||||
fn list(&mut self, query: &Query) -> Result<Vec<Artist>, Error>;
|
||||
|
@ -3,7 +3,7 @@ use std::{fs, path::PathBuf};
|
||||
use musichoard::{
|
||||
database::{
|
||||
json::{JsonDatabase, JsonDatabaseFileBackend},
|
||||
DatabaseRead, DatabaseWrite,
|
||||
Database,
|
||||
},
|
||||
Artist,
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user