use std::collections::HashMap;
use todo_lib::{
todo,
todotxt::{self, CompletionConfig},
};
fn init_tasks() -> todo::TaskVec {
let mut t = Vec::new();
let now = chrono::Local::now().date_naive();
t.push(todotxt::Task::parse("call mother +family @parents", now));
t.push(todotxt::Task::parse(
"x (C) 2018-10-05 2018-10-01 call to car service and schedule repair +car @repair",
now,
));
t.push(todotxt::Task::parse("(B) 2018-10-15 repair family car +Car @repair due:2018-12-01", now));
t.push(todotxt::Task::parse("(A) Kid's art school lesson +Family @Kids due:2018-11-10 rec:1w", now));
t.push(todotxt::Task::parse("take kid to hockey game +Family @kids due:2018-11-18", now));
t.push(todotxt::Task::parse("xmas vacations +FamilyHoliday due:2018-12-24", now));
t
}
fn init_task_lists() -> todo::TaskVec {
let mut t = Vec::new();
let now = chrono::Local::now().date_naive();
t.push(todotxt::Task::parse("call mother +Family @parents", now));
t.push(todotxt::Task::parse("call @Parents @father +family", now));
t.push(todotxt::Task::parse("+car from service @car", now));
t.push(todotxt::Task::parse("@CAR from service +CAR", now));
t.push(todotxt::Task::parse("my +bday @me rec:2y", now));
t.push(todotxt::Task::parse("rec:1m my wife +bday @wife +family", now));
t
}
#[test]
fn clones() {
let t = init_tasks();
let ids: todo::IDVec = vec![2, 4];
let t2 = todo::clone_tasks(&t, &ids);
assert_eq!(t2.len(), ids.len());
assert_eq!(t[2], t2[0]);
assert_eq!(t[4], t2[1]);
let ids: todo::IDVec = vec![15, 4];
let t2 = todo::clone_tasks(&t, &ids);
assert_eq!(t2.len(), 1);
assert_eq!(t[4], t2[0]);
}
#[test]
fn add() {
let mut t = init_tasks();
let mut c: todo::Conf = todo::Conf::default();
let orig_len = t.len();
c.subject = Some("new task".to_owned());
let n = todo::add(&mut t, &c);
assert_eq!(t.len(), orig_len + 1);
assert_eq!(n, orig_len);
}
#[test]
fn done_test() {
let mut t: Vec<todotxt::Task> = init_tasks();
let orig_len = t.len();
let ids: todo::IDVec = vec![0, 1, 3, 4, 10];
let mut must_change = 0;
for i in &ids {
if t.len() < *i {
continue;
}
if t[*i].finished {
continue;
}
if t[*i].recurrence.is_some() && (t[*i].due_date.is_some() || t[*i].threshold_date.is_some()) {
must_change += 1;
}
}
let old_date = t[3].due_date;
let completion_config = CompletionConfig {
completion_mode: todotxt::CompletionMode::JustMark,
completion_date_mode: todotxt::CompletionDateMode::AlwaysSet,
};
let changed = todo::done(&mut t, Some(&ids), completion_config);
assert_eq!(changed, vec![true, false, true, true, false]);
assert!(!t[2].finished);
assert!(t[3].due_date == old_date);
for i in 0..5 {
assert!(i == 2 || t[i].finished);
assert!(i == 2 || t[i].finish_date.is_some())
}
assert_eq!(t.len(), orig_len + must_change);
for idx in orig_len..orig_len + must_change {
assert!(!t[idx].finished);
}
}
#[test]
fn undone() {
let mut t = init_tasks();
let ids: todo::IDVec = vec![0, 2, 3];
let changed = todo::undone(&mut t, Some(&ids), todotxt::CompletionMode::JustMark);
assert_eq!(changed, vec![false, false, false]);
assert!(t[1].finished);
assert!(!t[0].finished && !t[3].finished && !t[3].finished);
let ids: todo::IDVec = vec![0, 1, 3, 4, 10];
let changed = todo::undone(&mut t, Some(&ids), todotxt::CompletionMode::JustMark);
assert_eq!(changed, vec![false, true, false, false, false]);
assert!(!t[1].finished);
assert!(!t[0].finished && !t[3].finished && !t[4].finished);
}
#[test]
fn remove() {
let mut t = init_tasks();
let ids: todo::IDVec = vec![1, 20];
let old_len = t.len();
let changed = todo::remove(&mut t, Some(&ids));
assert_eq!(changed, vec![true, false]);
assert_eq!(t.len(), old_len - 1);
let changed = todo::remove(&mut t, None);
assert_eq!(changed, vec![true, true, true, true, true,]);
assert_eq!(t.len(), 0);
}
#[test]
fn recs() {
let mut t = init_task_lists();
let mut c: todo::Conf = Default::default();
let ids: todo::IDVec = vec![0, 1, 2, 3, 4, 5];
c.projects = todo::ListTagChange { action: todo::Action::Delete, value: vec!["noproj".to_string()] };
let changed = todo::edit(&mut t, Some(&ids), &c);
assert_eq!(changed, vec![false, false, false, false, false, false]);
c.projects = todo::ListTagChange { action: todo::Action::Delete, value: vec!["CAR".to_string()] };
let changed = todo::edit(&mut t, Some(&ids), &c);
assert_eq!(changed, vec![false, false, false, true, false, false]);
c.projects = todo::ListTagChange { action: todo::Action::Replace, value: vec!["Family+People".to_string()] };
let changed = todo::edit(&mut t, Some(&ids), &c);
assert_eq!(changed, vec![true, false, false, false, false, false]);
c.recurrence = todo::RecurrencyTagChange { action: todo::Action::Delete, value: None };
let changed = todo::edit(&mut t, Some(&ids), &c);
assert_eq!(changed, vec![false, false, false, false, true, true]);
}
#[test]
fn tag_update_test() {
struct Test {
subj: &'static str,
tags: Vec<&'static str>,
res: &'static str,
delete: bool,
changes: bool,
}
let data: Vec<Test> = vec![
Test {
subj: "item:ball take to who:me game game:there",
tags: vec!["game:here", "item:puck"],
res: "item:puck take to who:me game game:here",
delete: false,
changes: true,
},
Test {
subj: "item:ball take to who:me game game:there",
tags: vec!["gam", "item"],
res: "take to who:me game game:there",
delete: true,
changes: true,
},
Test {
subj: "item:ball take to who:me why:because game game:there",
tags: vec!["game:new", "item:some", "who:that", "wh:some"],
res: "take to why:because game",
delete: true,
changes: true,
},
Test {
subj: "item:ball take to who:me game game:there",
tags: vec!["game", "item"],
res: "take to who:me game",
delete: false,
changes: true,
},
Test {
subj: "item:ball take to who:me game game:there",
tags: vec!["who:they", "ite"],
res: "item:ball take to who:they game game:there",
delete: false,
changes: true,
},
Test {
subj: "item:ball take to who:me game game:there",
tags: vec!["who:they", "item:puck", "date:tomorrow", "game:somewhere"],
res: "item:puck take to who:they game game:somewhere date:tomorrow",
delete: false,
changes: true,
},
Test {
subj: "item:ball take to who:me game game:there",
tags: vec!["who:me", "item:ball"],
res: "item:ball take to who:me game game:there",
delete: false,
changes: false,
},
Test {
subj: "item:ball take to who:me game game:there",
tags: vec!["wh:me", "ite"],
res: "item:ball take to who:me game game:there",
delete: true,
changes: false,
},
];
let now = chrono::Local::now().date_naive();
for (idx, test) in data.iter().enumerate() {
let mut t = Vec::new();
t.push(todotxt::Task::parse(test.subj, now));
let mut c: todo::Conf = todo::Conf::default();
let tags_act = if test.delete { todo::Action::Delete } else { todo::Action::Set };
let mut hm = HashMap::<String, String>::new();
for tag in &test.tags {
if let Some(pos) = tag.find(':') {
hm.insert(tag[..pos].to_string(), tag[pos + 1..].to_string());
} else {
hm.insert(tag.to_string(), String::new());
}
}
c.tags = todo::TagValuesChange { action: tags_act, value: Some(hm) };
let changed = todo::edit(&mut t, None, &c);
if test.changes {
assert!(changed.len() > 0 && changed[0]);
} else {
assert!(changed.len() == 0 || !changed[0]);
}
assert_eq!(test.res, &t[0].subject, "\n{}. {} != {}", idx, t[0].subject, test.res);
}
}
#[test]
fn hashtags_test() {
struct Test {
subj: &'static str,
hashtags: Vec<&'static str>,
res: &'static str,
act: todo::Action,
changed: bool,
}
let data: Vec<Test> = vec![
Test {
subj: "test #about some #hashtags",
hashtags: vec!["about", "hashtags"],
res: "",
act: todo::Action::None,
changed: false,
},
Test {
subj: "test #about some #hashtags",
hashtags: vec!["about", "tags"],
res: "test some #hashtags",
act: todo::Action::Delete,
changed: true,
},
Test {
subj: "test #about some #hashtags",
hashtags: vec!["about", "tags"],
res: "test #about some #hashtags #tags",
act: todo::Action::Set,
changed: true,
},
Test {
subj: "test #about some #hashtags and #some",
hashtags: vec!["about:this", "hashtags:tags", "no:yes"],
res: "test #this some #tags and #some",
act: todo::Action::Replace,
changed: true,
},
Test {
subj: "test #about some #hashtags and #some",
hashtags: vec!["about:about"],
res: "test #about some #hashtags and #some",
act: todo::Action::Replace,
changed: false,
},
];
let now = chrono::Local::now().date_naive();
for (idx, test) in data.iter().enumerate() {
let mut t = Vec::new();
t.push(todotxt::Task::parse(test.subj, now));
if let todo::Action::None = test.act {
for (i, h) in test.hashtags.iter().enumerate() {
assert_eq!(t[0].hashtags[i], h.to_string(), "{}. {:?} != {:?}", idx, test.hashtags, t[0].hashtags);
}
continue;
}
let mut c: todo::Conf = todo::Conf::default();
let mut hvec = Vec::new();
for h in test.hashtags.iter() {
hvec.push(h.to_string());
}
c.hashtags = todo::ListTagChange { value: hvec, action: test.act };
let changed = todo::edit(&mut t, None, &c);
assert!(changed.len() > 0, "{}. {}", idx, t[0].subject);
assert_eq!(changed[0], test.changed, "{}. {}", idx, t[0].subject);
assert_eq!(test.res, &t[0].subject, "\n{}. {} != {}", idx, t[0].subject, test.res);
}
}