Unit test collection module
This commit is contained in:
parent
acebc47946
commit
e0da5d20a9
1
.gitignore
vendored
1
.gitignore
vendored
@ -1 +1,2 @@
|
|||||||
/target
|
/target
|
||||||
|
/codecov
|
||||||
|
14
README.md
14
README.md
@ -12,21 +12,23 @@ cargo install grcov
|
|||||||
### Generating Code Coverage
|
### Generating Code Coverage
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
|
env CARGO_TARGET_DIR=codecov \
|
||||||
cargo clean
|
cargo clean
|
||||||
env RUSTFLAGS="-C instrument-coverage" \
|
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
|
cargo test
|
||||||
grcov target/debug/profraw \
|
grcov codecov/debug/profraw \
|
||||||
--binary-path ./target/debug/ \
|
--binary-path ./codecov/debug/ \
|
||||||
--output-types html \
|
--output-types html \
|
||||||
--source-dir . \
|
--source-dir . \
|
||||||
--ignore-not-existing \
|
--ignore-not-existing \
|
||||||
--ignore "tests/*" \
|
--ignore "tests/*" \
|
||||||
--ignore "src/main.rs" \
|
--ignore "src/main.rs" \
|
||||||
--excl-start "mod tests \{" \
|
--excl-start "mod tests \{" \
|
||||||
--output-path ./target/debug/coverage/
|
--output-path ./codecov/debug/coverage/
|
||||||
xdg-open target/debug/coverage/index.html
|
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.
|
command is rerun.
|
||||||
|
@ -12,7 +12,7 @@ use crate::{
|
|||||||
pub type Collection = Vec<Artist>;
|
pub type Collection = Vec<Artist>;
|
||||||
|
|
||||||
/// Error type for collection manager.
|
/// Error type for collection manager.
|
||||||
#[derive(Debug)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// The [`CollectionManager`] failed to read/write from/to the library.
|
/// The [`CollectionManager`] failed to read/write from/to the library.
|
||||||
LibraryError(String),
|
LibraryError(String),
|
||||||
@ -81,3 +81,91 @@ impl CollectionManager {
|
|||||||
&self.collection
|
&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)]
|
#[cfg(test)]
|
||||||
use mockall::automock;
|
use mockall::automock;
|
||||||
|
|
||||||
use super::{Database, DatabaseRead, DatabaseWrite, Error};
|
use super::{Database, Error};
|
||||||
|
|
||||||
impl From<serde_json::Error> for Error {
|
impl From<serde_json::Error> for Error {
|
||||||
fn from(err: serde_json::Error) -> 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> {
|
fn read(&self, collection: &mut Collection) -> Result<(), Error> {
|
||||||
let serialized = self.backend.read()?;
|
let serialized = self.backend.read()?;
|
||||||
*collection = serde_json::from_str(&serialized)?;
|
*collection = serde_json::from_str(&serialized)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl DatabaseWrite for JsonDatabase {
|
|
||||||
fn write(&mut self, collection: &Collection) -> Result<(), Error> {
|
fn write(&mut self, collection: &Collection) -> Result<(), Error> {
|
||||||
let serialized = serde_json::to_string(&collection)?;
|
let serialized = serde_json::to_string(&collection)?;
|
||||||
self.backend.write(&serialized)?;
|
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.
|
/// JSON database backend that uses a local file for persistent storage.
|
||||||
pub struct JsonDatabaseFileBackend {
|
pub struct JsonDatabaseFileBackend {
|
||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
|
@ -2,6 +2,9 @@
|
|||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
use mockall::automock;
|
||||||
|
|
||||||
use crate::collection::Collection;
|
use crate::collection::Collection;
|
||||||
|
|
||||||
pub mod json;
|
pub mod json;
|
||||||
@ -32,17 +35,12 @@ impl From<std::io::Error> for Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Trait for database reads.
|
/// Trait for interacting with the database.
|
||||||
pub trait DatabaseRead {
|
#[cfg_attr(test, automock)]
|
||||||
|
pub trait Database {
|
||||||
/// Read collection from the database.
|
/// Read collection from the database.
|
||||||
fn read(&self, collection: &mut Collection) -> Result<(), Error>;
|
fn read(&self, collection: &mut Collection) -> Result<(), Error>;
|
||||||
}
|
|
||||||
|
|
||||||
/// Trait for database writes.
|
|
||||||
pub trait DatabaseWrite {
|
|
||||||
/// Write collection to the database.
|
/// Write collection to the database.
|
||||||
fn write(&mut self, collection: &Collection) -> Result<(), Error>;
|
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};
|
use std::{num::ParseIntError, str::Utf8Error, fmt};
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
use mockall::automock;
|
||||||
|
|
||||||
use crate::Artist;
|
use crate::Artist;
|
||||||
|
|
||||||
pub mod beets;
|
pub mod beets;
|
||||||
|
|
||||||
/// A single query option.
|
/// A single query option.
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub enum QueryOption<T> {
|
pub enum QueryOption<T> {
|
||||||
/// Inclusive query.
|
/// Inclusive query.
|
||||||
Include(T),
|
Include(T),
|
||||||
@ -36,7 +40,7 @@ impl<T> Default for QueryOption<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Options for refining library queries.
|
/// Options for refining library queries.
|
||||||
#[derive(Default)]
|
#[derive(Debug, Default, PartialEq, Eq)]
|
||||||
pub struct Query {
|
pub struct Query {
|
||||||
album_artist: QueryOption<String>,
|
album_artist: QueryOption<String>,
|
||||||
album_year: QueryOption<u32>,
|
album_year: QueryOption<u32>,
|
||||||
@ -142,6 +146,7 @@ impl From<Utf8Error> for Error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Trait for interacting with the music library.
|
/// Trait for interacting with the music library.
|
||||||
|
#[cfg_attr(test, automock)]
|
||||||
pub trait Library {
|
pub trait Library {
|
||||||
/// List lirbary items that match the a specific query.
|
/// List lirbary items that match the a specific query.
|
||||||
fn list(&mut self, query: &Query) -> Result<Vec<Artist>, Error>;
|
fn list(&mut self, query: &Query) -> Result<Vec<Artist>, Error>;
|
||||||
|
@ -3,7 +3,7 @@ use std::{fs, path::PathBuf};
|
|||||||
use musichoard::{
|
use musichoard::{
|
||||||
database::{
|
database::{
|
||||||
json::{JsonDatabase, JsonDatabaseFileBackend},
|
json::{JsonDatabase, JsonDatabaseFileBackend},
|
||||||
DatabaseRead, DatabaseWrite,
|
Database,
|
||||||
},
|
},
|
||||||
Artist,
|
Artist,
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user