diff --git a/Cargo.toml b/Cargo.toml index 53d0208..15ba454 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,3 +2,6 @@ name = "schematics" version = "0.1.0" authors = ["Adrian Heine "] + +[dev-dependencies] +tempdir = "0.3" diff --git a/src/symbols/file.rs b/src/symbols/file.rs index 59a1b2f..f586591 100644 --- a/src/symbols/file.rs +++ b/src/symbols/file.rs @@ -1,45 +1,13 @@ -use std::error::Error; +use std::borrow::Cow; use std::fmt; +use std::fs::File as FsFile; use std::io; +use std::io::{Read, Write}; use std::ops::Deref; +use std::path::Path; use symbols::Symbol; -#[derive(Debug)] -pub enum FileError { - ExecError(E), - GenericError -} - -impl From for FileError { - fn from(err: io::Error) -> FileError { - FileError::ExecError(err) - } -} - -impl Error for FileError { - fn description(&self) -> &str { - match self { - &FileError::ExecError(ref e) => e.description(), - &FileError::GenericError => "Generic error" - } - } - fn cause(&self) -> Option<&Error> { - match self { - &FileError::ExecError(ref e) => Some(e), - _ => None - } - } -} - -impl fmt::Display for FileError { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - write!(f, "{}", self.description()) - } -} - -use std::convert::AsRef; - pub struct File where C: Deref, D: AsRef + fmt::Display { path: D, content: C @@ -54,11 +22,9 @@ impl File where C: Deref, D: AsRef + fmt::Display { } } -use std::fs::File as FsFile; -use std::io::{Read, Write}; - impl Symbol for File where C: Deref, D: AsRef + fmt::Display { - type Error = FileError; + type Error = io::Error; + fn target_reached(&self) -> Result { let file = FsFile::open(self.path.as_ref()); // Check if file exists @@ -66,16 +32,20 @@ impl Symbol for File where C: Deref, D: AsRef + fmt return if e.kind() == io::ErrorKind::NotFound { Ok(false) } else { - Err(e.into()) + Err(e) }; } // Check if content is the same - let file_content = file.unwrap().bytes(); - let content_equal = try!(self.content.bytes().zip(file_content).fold( - Ok(true), - |state, (target_byte, file_byte_option)| state.and_then(|s| file_byte_option.map(|file_byte| s && file_byte == target_byte)) - )); - return Ok(content_equal) + let mut file_content = file.unwrap().bytes(); + let mut target_content = self.content.bytes(); + loop { + match (file_content.next(), target_content.next()) { + (None, None) => return Ok(true), + (Some(Ok(a)), Some(b)) if a == b => {}, + (Some(Err(e)), _) => return Err(e), + (_, _) => return Ok(false) + } + } } fn execute(&self) -> Result<(), Self::Error> { @@ -86,7 +56,7 @@ impl Symbol for File where C: Deref, D: AsRef + fmt } impl fmt::Display for File where C: Deref, D: AsRef + fmt::Display { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(),fmt::Error>{ + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error>{ write!(f, "File {}", self.path) } } diff --git a/src/symbols/nginx/server.rs b/src/symbols/nginx/server.rs index b5f4db5..703e3f4 100644 --- a/src/symbols/nginx/server.rs +++ b/src/symbols/nginx/server.rs @@ -6,7 +6,6 @@ use std::ops::Deref; use command_runner::CommandRunner; use symbols::Symbol; use symbols::file::File as FileSymbol; -use symbols::file::FileError; #[derive(Debug)] pub enum NginxServerError { @@ -14,18 +13,12 @@ pub enum NginxServerError { GenericError } -impl From> for NginxServerError> { - fn from(err: FileError) -> NginxServerError> { +impl From for NginxServerError { + fn from(err: io::Error) -> NginxServerError { NginxServerError::ExecError(err) } } -impl From for NginxServerError> { - fn from(err: io::Error) -> NginxServerError> { - NginxServerError::GenericError - } -} - impl Error for NginxServerError { fn description(&self) -> &str { match self { diff --git a/tests/file.rs b/tests/file.rs new file mode 100644 index 0000000..2329ed6 --- /dev/null +++ b/tests/file.rs @@ -0,0 +1,103 @@ +extern crate schematics; +extern crate tempdir; + +use std::fs::File; +use std::io::Write; +use std::path::Path; +use tempdir::TempDir; +use schematics::symbols::Symbol; +use schematics::symbols::file::File as FileSymbol; + +fn get_dir(content: Option<&str>) -> TempDir { + let tmp_dir = TempDir::new("unittest").expect("create temp dir"); + if content.is_none() { + return tmp_dir; + } + let file_path = tmp_dir.path().join("filename"); + let mut tmp_file = File::create(file_path).expect("create temp file"); + tmp_file.write_all(content.unwrap().as_bytes()).expect("write temp file"); + tmp_dir +} + +fn get_symbol(path: &Path) -> FileSymbol<&str, String> { + FileSymbol::new(String::from(path.join("filename").to_str().unwrap()), "target content") +} + +// Normal cases + +// Good +#[test] +fn already_reached() { + let dir = get_dir(Some("target content")); + let symbol = get_symbol(dir.path()); + + assert_eq!(symbol.target_reached().unwrap(), true); +} + +// Bad +#[test] +fn wrong_prefix() { + let dir = get_dir(Some("not target content")); + let symbol = get_symbol(dir.path()); + + assert_eq!(symbol.target_reached().unwrap(), false); + symbol.execute().unwrap(); + assert_eq!(symbol.target_reached().unwrap(), true); +} + +#[test] +fn wrong_postfix() { + let dir = get_dir(Some("target content not")); + let symbol = get_symbol(dir.path()); + + assert_eq!(symbol.target_reached().unwrap(), false); + symbol.execute().unwrap(); + assert_eq!(symbol.target_reached().unwrap(), true); +} + +#[test] +fn empty_file() { + let dir = get_dir(Some("")); + let symbol = get_symbol(dir.path()); + + assert_eq!(symbol.target_reached().unwrap(), false); + symbol.execute().unwrap(); + assert_eq!(symbol.target_reached().unwrap(), true); +} + +#[test] +fn no_file() { + let dir = get_dir(None); + let symbol = get_symbol(dir.path()); + + assert_eq!(symbol.target_reached().unwrap(), false); + symbol.execute().unwrap(); + assert_eq!(symbol.target_reached().unwrap(), true); +} + +// Exceptional cases + +#[test] +fn may_not_read_file() { + let symbol = get_symbol(&Path::new("/etc/passwd")); + + assert_eq!(symbol.target_reached().is_err(), true); +} + +#[test] +fn may_not_create_file() { + let symbol = get_symbol(&Path::new("/proc/somefile")); + + // Could also return an error + assert_eq!(symbol.target_reached().unwrap(), false); + assert!(symbol.execute().is_err()); +} + +#[test] +fn directory_missing() { + let symbol = get_symbol(&Path::new("/nonexisting")); + + // Could also return an error + assert_eq!(symbol.target_reached().unwrap(), false); + assert!(symbol.execute().is_err()); +}