From 8678edb61e81f5b82201ac6563688a16901f2d54 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Tue, 18 Jul 2017 21:29:04 +0200 Subject: [PATCH 1/6] Add dev-dependency --- imag-tag/Cargo.toml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/imag-tag/Cargo.toml b/imag-tag/Cargo.toml index 2cdc7823..9de2b894 100644 --- a/imag-tag/Cargo.toml +++ b/imag-tag/Cargo.toml @@ -35,3 +35,8 @@ path = "../libimagentrytag" [dependencies.libimagutil] path = "../libimagutil" +[dev-dependencies.libimagutil] +path = "../libimagutil" +default-features = false +features = ["testing"] + From d163ac810932e62e20a2035e1c63d41c7c81d8d3 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Tue, 18 Jul 2017 21:43:49 +0200 Subject: [PATCH 2/6] Add simple test for tagging an entry --- imag-tag/Cargo.toml | 3 ++ imag-tag/src/main.rs | 71 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) diff --git a/imag-tag/Cargo.toml b/imag-tag/Cargo.toml index 9de2b894..cee124dd 100644 --- a/imag-tag/Cargo.toml +++ b/imag-tag/Cargo.toml @@ -40,3 +40,6 @@ path = "../libimagutil" default-features = false features = ["testing"] +[dev-dependencies] +toml-query = "0.3.0" + diff --git a/imag-tag/src/main.rs b/imag-tag/src/main.rs index d0a83725..34565098 100644 --- a/imag-tag/src/main.rs +++ b/imag-tag/src/main.rs @@ -27,8 +27,13 @@ extern crate libimagstore; extern crate libimagrt; extern crate libimagentrytag; extern crate libimagerror; + +#[macro_use] extern crate libimagutil; +#[cfg(test)] +extern crate toml_query; + use std::path::PathBuf; use libimagrt::runtime::Runtime; @@ -169,3 +174,69 @@ fn list(id: PathBuf, rt: &Runtime) { } } +#[cfg(test)] +mod tests { + use std::path::PathBuf; + use std::ffi::OsStr; + + use toml::value::Value; + use toml_query::read::TomlValueReadExt; + use toml_query::error::Result as TomlQueryResult; + + use libimagentrytag::ui::{get_add_tags, get_remove_tags}; + use libimagrt::runtime::Runtime; + use libimagstore::storeid::StoreId; + use libimagstore::store::{Result as StoreResult, FileLockEntry}; + + use super::alter; + + make_mock_app! { + app "imag-tag"; + modulename mock; + version "0.3.0"; + with help "imag-tag mocking app"; + } + use self::mock::generate_test_runtime; + use libimagutil::testing::DEFAULT_ENTRY; + + fn create_test_default_entry<'a, S: AsRef>(rt: &'a Runtime, name: S) -> StoreResult { + let mut path = PathBuf::new(); + path.set_file_name(name); + + let id = StoreId::new_baseless(path)?; + let mut entry = rt.store().create(id.clone())?; + entry.get_content_mut().push_str(DEFAULT_ENTRY); + + Ok(id) + } + + fn get_entry_tags<'a>(entry: &'a FileLockEntry<'a>) -> TomlQueryResult> { + entry.get_header().read(&"imag.tags".to_owned()) + } + + fn tags_toml_value<'a, I: IntoIterator>(tags: I) -> Value { + Value::Array(tags.into_iter().map(|s| Value::String(s.to_owned())).collect()) + } + + + #[test] + fn test_tag_add_adds_tag() { + let rt = generate_test_runtime(vec!["--id", "test", "--add", "foo"]).unwrap(); + + create_test_default_entry(&rt, "test").unwrap(); + let id = PathBuf::from(String::from("test")); + + let add = get_add_tags(rt.cli()); + let rem = get_remove_tags(rt.cli()); + alter(&rt, id.clone(), add, rem); + + let test_entry = rt.store().get(id).unwrap().unwrap(); + let test_tags = get_entry_tags(&test_entry).unwrap().unwrap(); + + assert_ne!(*test_tags, tags_toml_value(vec![])); + assert_eq!(*test_tags, tags_toml_value(vec!["foo"])); + } + + +} + From 211b93650ba6f1922e87b6283ba863fa99d4fc4f Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Wed, 19 Jul 2017 00:07:46 +0200 Subject: [PATCH 3/6] Add test logging --- imag-tag/Cargo.toml | 1 + imag-tag/src/main.rs | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/imag-tag/Cargo.toml b/imag-tag/Cargo.toml index cee124dd..0388270e 100644 --- a/imag-tag/Cargo.toml +++ b/imag-tag/Cargo.toml @@ -42,4 +42,5 @@ features = ["testing"] [dev-dependencies] toml-query = "0.3.0" +env_logger = "0.3" diff --git a/imag-tag/src/main.rs b/imag-tag/src/main.rs index 34565098..408f1aca 100644 --- a/imag-tag/src/main.rs +++ b/imag-tag/src/main.rs @@ -34,6 +34,9 @@ extern crate libimagutil; #[cfg(test)] extern crate toml_query; +#[cfg(test)] +extern crate env_logger; + use std::path::PathBuf; use libimagrt::runtime::Runtime; @@ -218,17 +221,32 @@ mod tests { Value::Array(tags.into_iter().map(|s| Value::String(s.to_owned())).collect()) } + fn setup_logging() { + use env_logger; + let _ = env_logger::init().unwrap(); + } #[test] fn test_tag_add_adds_tag() { + setup_logging(); + debug!("Generating runtime"); let rt = generate_test_runtime(vec!["--id", "test", "--add", "foo"]).unwrap(); + debug!("Creating default entry"); create_test_default_entry(&rt, "test").unwrap(); let id = PathBuf::from(String::from("test")); + debug!("Getting 'add' tags"); let add = get_add_tags(rt.cli()); + debug!("Add-tags: {:?}", add); + + debug!("Getting 'remove' tags"); let rem = get_remove_tags(rt.cli()); + debug!("Rem-tags: {:?}", rem); + + debug!("Altering things"); alter(&rt, id.clone(), add, rem); + debug!("Altered"); let test_entry = rt.store().get(id).unwrap().unwrap(); let test_tags = get_entry_tags(&test_entry).unwrap().unwrap(); From 798b95785a990f817bf2543a63ea470e18ea9a60 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Wed, 19 Jul 2017 00:26:20 +0200 Subject: [PATCH 4/6] Remove outdated source file --- libimagentrytag/src/lib.rs | 1 - libimagentrytag/src/util.rs | 24 ------------------------ 2 files changed, 25 deletions(-) delete mode 100644 libimagentrytag/src/util.rs diff --git a/libimagentrytag/src/lib.rs b/libimagentrytag/src/lib.rs index 6cac9690..bfc4d461 100644 --- a/libimagentrytag/src/lib.rs +++ b/libimagentrytag/src/lib.rs @@ -48,6 +48,5 @@ pub mod exec; pub mod result; pub mod tag; pub mod tagable; -pub mod util; pub mod ui; diff --git a/libimagentrytag/src/util.rs b/libimagentrytag/src/util.rs deleted file mode 100644 index d0d31db2..00000000 --- a/libimagentrytag/src/util.rs +++ /dev/null @@ -1,24 +0,0 @@ -// -// imag - the personal information management suite for the commandline -// Copyright (C) 2015, 2016 Matthias Beyer and contributors -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; version -// 2.1 of the License. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -// - -use regex::Regex; - -pub fn is_tag(s: &str) -> bool { - Regex::new("^[a-zA-Z]([a-zA-Z0-9_-]*)$").unwrap().captures(s).is_some() -} From e7bd43718d6bf68142150ed1d86dd20594f677e3 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Wed, 19 Jul 2017 00:26:40 +0200 Subject: [PATCH 5/6] Remove duplicated is_tag() function implementation --- libimagentrytag/src/tag.rs | 5 ++++- libimagentrytag/src/tagable.rs | 12 ++++++------ libimagentrytag/src/ui.rs | 3 +-- libimagutil/src/cli_validators.rs | 9 --------- 4 files changed, 11 insertions(+), 18 deletions(-) diff --git a/libimagentrytag/src/tag.rs b/libimagentrytag/src/tag.rs index 6cfcf1c8..be968da7 100644 --- a/libimagentrytag/src/tag.rs +++ b/libimagentrytag/src/tag.rs @@ -17,6 +17,8 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // +use regex::Regex; + pub type Tag = String; pub type TagSlice<'a> = &'a str; @@ -31,8 +33,9 @@ pub fn is_tag_str(s: &String) -> Result<(), String> { let is_lower = |s: &String| s.chars().all(|c| c.is_lowercase()); let no_whitespace = |s: &String| s.chars().all(|c| !c.is_whitespace()); let is_alphanum = |s: &String| s.chars().all(|c| c.is_alphanumeric()); + let matches_regex = |s: &String| Regex::new("^[a-zA-Z]([a-zA-Z0-9_-]*)$").unwrap().captures(s).is_some(); - if is_lower.and(no_whitespace).and(is_alphanum).filter(s) { + if is_lower.and(no_whitespace).and(is_alphanum).and(matches_regex).filter(s) { Ok(()) } else { Err(format!("The string '{}' is not a valid tag", s)) diff --git a/libimagentrytag/src/tagable.rs b/libimagentrytag/src/tagable.rs index 9e0fa2fd..bd53a122 100644 --- a/libimagentrytag/src/tagable.rs +++ b/libimagentrytag/src/tagable.rs @@ -27,7 +27,7 @@ use error::TagErrorKind; use error::MapErrInto; use result::Result; use tag::{Tag, TagSlice}; -use util::is_tag; +use tag::is_tag_str; use toml::Value; @@ -55,7 +55,7 @@ impl Tagable for Value { return Err(TagErrorKind::TagTypeError.into()); } if tags.iter().any(|t| match *t { - Value::String(ref s) => !is_tag(s), + Value::String(ref s) => !is_tag_str(s).is_ok(), _ => unreachable!()}) { return Err(TagErrorKind::NotATag.into()); @@ -77,8 +77,8 @@ impl Tagable for Value { } fn set_tags(&mut self, ts: &[Tag]) -> Result<()> { - if ts.iter().any(|tag| !is_tag(tag)) { - debug!("Not a tag: '{}'", ts.iter().filter(|t| !is_tag(t)).next().unwrap()); + if ts.iter().any(|tag| !is_tag_str(tag).is_ok()) { + debug!("Not a tag: '{}'", ts.iter().filter(|t| !is_tag_str(t).is_ok()).next().unwrap()); return Err(TagErrorKind::NotATag.into()); } @@ -90,7 +90,7 @@ impl Tagable for Value { } fn add_tag(&mut self, t: Tag) -> Result<()> { - if !is_tag(&t) { + if !try!(is_tag_str(&t).map(|_| true).map_err(|_| TagErrorKind::NotATag.into_error())) { debug!("Not a tag: '{}'", t); return Err(TagErrorKind::NotATag.into()); } @@ -104,7 +104,7 @@ impl Tagable for Value { } fn remove_tag(&mut self, t: Tag) -> Result<()> { - if !is_tag(&t) { + if !try!(is_tag_str(&t).map(|_| true).map_err(|_| TagErrorKind::NotATag.into_error())) { debug!("Not a tag: '{}'", t); return Err(TagErrorKind::NotATag.into()); } diff --git a/libimagentrytag/src/ui.rs b/libimagentrytag/src/ui.rs index dad4c87e..734708b3 100644 --- a/libimagentrytag/src/ui.rs +++ b/libimagentrytag/src/ui.rs @@ -20,8 +20,7 @@ use clap::{Arg, ArgMatches, App, SubCommand}; use tag::Tag; - -use libimagutil::cli_validators::is_tag; +use tag::is_tag; /// Generates a `clap::SubCommand` to be integrated in the commandline-ui builder for building a /// "tags --add foo --remove bar" subcommand to do tagging action. diff --git a/libimagutil/src/cli_validators.rs b/libimagutil/src/cli_validators.rs index bbd376f2..24340a4d 100644 --- a/libimagutil/src/cli_validators.rs +++ b/libimagutil/src/cli_validators.rs @@ -47,12 +47,3 @@ pub fn is_url(s: String) -> Result<(), String> { Url::parse(&s).map(|_| ()).map_err(|_| format!("Not a URL: {}", s)) } -pub fn is_tag(s: String) -> Result<(), String> { - use regex::Regex; - lazy_static! { static ref TAG_RE : Regex = Regex::new("[:alpha:][:word:]*").unwrap(); } - - TAG_RE - .is_match(&s) - .as_result((), format!("Not a valid Tag: '{}' - Valid is [a-zA-Z][0-9a-zA-Z]*", s)) -} - From 1567fb0d8e637e91362aa35ea2c41e208f81be45 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Wed, 19 Jul 2017 10:48:39 +0200 Subject: [PATCH 6/6] Add more tests --- imag-tag/src/main.rs | 117 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 116 insertions(+), 1 deletion(-) diff --git a/imag-tag/src/main.rs b/imag-tag/src/main.rs index 408f1aca..7e710c9b 100644 --- a/imag-tag/src/main.rs +++ b/imag-tag/src/main.rs @@ -223,7 +223,7 @@ mod tests { fn setup_logging() { use env_logger; - let _ = env_logger::init().unwrap(); + let _ = env_logger::init().unwrap_or(()); } #[test] @@ -255,6 +255,121 @@ mod tests { assert_eq!(*test_tags, tags_toml_value(vec!["foo"])); } + #[test] + fn test_tag_add_more_than_remove_adds_tags() { + setup_logging(); + debug!("Generating runtime"); + let rt = generate_test_runtime(vec!["--id", "test", + "--add", "foo", + "--add", "bar", + "--add", "baz", + "--add", "bub", + "--remove", "foo", + "--remove", "bar", + "--remove", "baz", + ]).unwrap(); + + debug!("Creating default entry"); + create_test_default_entry(&rt, "test").unwrap(); + let id = PathBuf::from(String::from("test")); + + // Manually add tags + let add = get_add_tags(rt.cli()); + + debug!("Getting 'remove' tags"); + let rem = get_remove_tags(rt.cli()); + debug!("Rem-tags: {:?}", rem); + + debug!("Altering things"); + alter(&rt, id.clone(), add, rem); + debug!("Altered"); + + let test_entry = rt.store().get(id).unwrap().unwrap(); + let test_tags = get_entry_tags(&test_entry).unwrap().unwrap(); + + assert_eq!(*test_tags, tags_toml_value(vec!["bub"])); + } + + #[test] + fn test_tag_remove_removes_tag() { + setup_logging(); + debug!("Generating runtime"); + let rt = generate_test_runtime(vec!["--id", "test", "--remove", "foo"]).unwrap(); + + debug!("Creating default entry"); + create_test_default_entry(&rt, "test").unwrap(); + let id = PathBuf::from(String::from("test")); + + // Manually add tags + let add = Some(vec![ "foo".to_owned() ]); + + debug!("Getting 'remove' tags"); + let rem = get_remove_tags(rt.cli()); + debug!("Rem-tags: {:?}", rem); + + debug!("Altering things"); + alter(&rt, id.clone(), add, rem); + debug!("Altered"); + + let test_entry = rt.store().get(id).unwrap().unwrap(); + let test_tags = get_entry_tags(&test_entry).unwrap().unwrap(); + + assert_eq!(*test_tags, tags_toml_value(vec![])); + } + + #[test] + fn test_tag_remove_removes_only_to_remove_tag() { + setup_logging(); + debug!("Generating runtime"); + let rt = generate_test_runtime(vec!["--id", "test", "--remove", "foo"]).unwrap(); + + debug!("Creating default entry"); + create_test_default_entry(&rt, "test").unwrap(); + let id = PathBuf::from(String::from("test")); + + // Manually add tags + let add = Some(vec![ "foo".to_owned(), "bar".to_owned() ]); + + debug!("Getting 'remove' tags"); + let rem = get_remove_tags(rt.cli()); + debug!("Rem-tags: {:?}", rem); + + debug!("Altering things"); + alter(&rt, id.clone(), add, rem); + debug!("Altered"); + + let test_entry = rt.store().get(id).unwrap().unwrap(); + let test_tags = get_entry_tags(&test_entry).unwrap().unwrap(); + + assert_eq!(*test_tags, tags_toml_value(vec!["bar"])); + } + + #[test] + fn test_tag_remove_removes_but_doesnt_crash_on_nonexistent_tag() { + setup_logging(); + debug!("Generating runtime"); + let rt = generate_test_runtime(vec!["--id", "test", "--remove", "foo", "--remove", "bar"]).unwrap(); + + debug!("Creating default entry"); + create_test_default_entry(&rt, "test").unwrap(); + let id = PathBuf::from(String::from("test")); + + // Manually add tags + let add = Some(vec![ "foo".to_owned() ]); + + debug!("Getting 'remove' tags"); + let rem = get_remove_tags(rt.cli()); + debug!("Rem-tags: {:?}", rem); + + debug!("Altering things"); + alter(&rt, id.clone(), add, rem); + debug!("Altered"); + + let test_entry = rt.store().get(id).unwrap().unwrap(); + let test_tags = get_entry_tags(&test_entry).unwrap().unwrap(); + + assert_eq!(*test_tags, tags_toml_value(vec![])); + } }