use super::time::Instant;
use super::*;
fn ok(src: &str) -> Gantt {
parse(src).unwrap_or_else(|e| panic!("{e}\n{src}"))
}
fn day(t: Instant) -> (i64, u32, u32) {
let (y, m, d, ..) = t.civil();
(y, m, d)
}
#[test]
fn the_documented_example_reads_as_dated_tasks_in_sections() {
let g = ok("gantt\n title A Gantt Diagram\n dateFormat YYYY-MM-DD\n section Section\n A task :a1, 2014-01-01, 30d\n Another task :after a1, 20d\n section Another\n Task in Another :2014-01-12, 12d\n another task :24d\n");
assert_eq!(g.preamble.title.as_deref(), Some("A Gantt Diagram"));
assert_eq!(g.date_format, "YYYY-MM-DD");
assert_eq!(g.sections, ["Section", "Another"]);
assert_eq!(g.tasks.len(), 4);
assert_eq!(g.tasks[0].id, "a1");
assert_eq!(day(g.tasks[0].start), (2014, 1, 1));
assert_eq!(day(g.tasks[0].end), (2014, 1, 31));
assert_eq!(day(g.tasks[1].start), (2014, 1, 31));
assert_eq!(day(g.tasks[1].end), (2014, 2, 20));
assert_eq!(g.tasks[2].section, "Another");
assert_eq!(day(g.tasks[2].start), (2014, 1, 12));
assert_eq!(day(g.tasks[3].start), day(g.tasks[2].end));
}
#[test]
fn the_field_count_decides_whether_the_first_field_is_an_id() {
let g = ok("gantt\n dateFormat YYYY-MM-DD\n three :des1, 2014-01-06, 2014-01-08\n two :2014-01-09, 2d\n one :3d\n");
assert_eq!(g.tasks[0].id, "des1");
assert_eq!(day(g.tasks[0].start), (2014, 1, 6));
assert_eq!(day(g.tasks[0].end), (2014, 1, 8));
assert_ne!(g.tasks[1].id, "2014-01-09", "two fields means no id");
assert_eq!(day(g.tasks[1].start), (2014, 1, 9));
assert_eq!(day(g.tasks[1].end), (2014, 1, 11));
assert_eq!(day(g.tasks[2].start), (2014, 1, 11));
assert_eq!(day(g.tasks[2].end), (2014, 1, 14));
}
#[test]
fn every_documented_tag_is_read_off_the_front_of_the_data() {
let g = ok("gantt\n dateFormat YYYY-MM-DD\n a :done, des1, 2014-01-06, 2014-01-08\n b :active, des2, 2014-01-09, 3d\n c :crit, done, des3, 2014-01-06, 24h\n d :milestone, m1, 2014-01-25, 0d\n e :vert, v1, 2014-01-26, 0d\n");
assert!(g.tasks[0].tags.done);
assert!(g.tasks[1].tags.active);
assert!(g.tasks[2].tags.crit && g.tasks[2].tags.done);
assert!(g.tasks[3].tags.milestone);
assert!(g.tasks[4].tags.vert);
assert_eq!(g.tasks[3].start, g.tasks[3].end);
}
#[test]
fn after_takes_the_latest_and_until_takes_the_earliest() {
let g = ok("gantt\n dateFormat YYYY-MM-DD\n a :a, 2014-01-01, 5d\n b :b, 2014-01-01, 10d\n c :after a b, 1d\n d :d, 2014-02-01, 1d\n e :e, 2014-03-01, 1d\n f :2014-01-20, until d e\n");
assert_eq!(
day(g.tasks[2].start),
(2014, 1, 11),
"the later of the two ends"
);
assert_eq!(
day(g.tasks[5].end),
(2014, 2, 1),
"the earlier of the two starts"
);
}
#[test]
fn after_can_name_a_task_that_has_not_been_written_yet() {
let g = ok(
"gantt\n dateFormat YYYY-MM-DD\n late :after early, 2d\n early :early, 2014-05-01, 3d\n",
);
assert_eq!(day(g.tasks[0].start), (2014, 5, 4));
}
#[test]
fn inclusive_end_dates_pushes_an_explicit_end_out_by_a_day() {
let plain = ok("gantt\n dateFormat YYYY-MM-DD\n a :2014-01-01, 2014-01-03\n");
let inclusive =
ok("gantt\n dateFormat YYYY-MM-DD\n inclusiveEndDates\n a :2014-01-01, 2014-01-03\n");
assert_eq!(day(plain.tasks[0].end), (2014, 1, 3));
assert_eq!(day(inclusive.tasks[0].end), (2014, 1, 4));
}
#[test]
fn excludes_weekends_lengthens_a_task_that_spans_one() {
let g = ok("gantt\n dateFormat YYYY-MM-DD\n excludes weekends\n a :2024-01-01, 7d\n");
assert_eq!(
day(g.tasks[0].end),
(2024, 1, 10),
"the Saturday and Sunday inside the span push the end out by two"
);
}
#[test]
fn an_included_date_wins_over_excluded_weekends() {
let g = ok("gantt\n dateFormat YYYY-MM-DD\n excludes weekends\n includes 2024-01-06 2024-01-07\n a :2024-01-01, 7d\n");
assert_eq!(day(g.tasks[0].end), (2024, 1, 8));
}
#[test]
fn the_settings_are_read_verbatim_where_they_are_drawn_and_lower_cased_where_they_are_matched() {
let g = ok("gantt\n dateFormat YYYY-MM-DD\n axisFormat %d/%m\n tickInterval 1week\n todayMarker off\n weekday monday\n weekend friday\n excludes Weekends, 2024-01-01\n topAxis\n a :2024-02-01, 1d\n");
assert_eq!(g.axis_format, "%d/%m");
assert_eq!(g.tick_interval, "1week");
assert_eq!(g.today_marker, "off");
assert_eq!(g.weekday, "monday");
assert_eq!(g.weekend, "friday");
assert_eq!(g.excludes, ["weekends", "2024-01-01"]);
assert!(g.top_axis);
}
#[test]
fn a_section_name_may_contain_a_colon() {
let g = ok("gantt\n dateFormat YYYY-MM-DD\n section Phase 1: design\n a :2024-01-01, 1d\n");
assert_eq!(g.sections, ["Phase 1: design"]);
assert_eq!(g.tasks[0].section, "Phase 1: design");
}
#[test]
fn a_click_line_is_read_and_is_not_a_task() {
let g = ok("gantt\n dateFormat YYYY-MM-DD\n a :des1, 2024-01-01, 1d\n click des1 href \"https://x.test/\"\n");
assert_eq!(g.tasks.len(), 1);
}
#[test]
fn a_date_that_cannot_be_read_is_refused_rather_than_drawn_somewhere_wrong() {
assert!(matches!(
parse("gantt\n dateFormat YYYY-MM-DD\n a :not-a-date, 3d\n"),
Err(ParseError::Invalid { .. })
));
assert!(matches!(
parse("gantt\n dateFormat YYYY-MM-DD\n a :2024-01-01, three days\n"),
Err(ParseError::Invalid { .. })
));
}
#[test]
fn a_header_with_no_tasks_is_refused() {
assert!(matches!(
parse("gantt\n title G\n"),
Err(ParseError::NoData { .. })
));
}
#[test]
fn a_task_line_with_no_colon_is_refused() {
assert!(matches!(
parse("gantt\n dateFormat YYYY-MM-DD\n just words\n"),
Err(ParseError::Unexpected { .. })
));
}
#[test]
fn the_routing_predicate_and_the_parser_agree() {
for src in [
"gantt\n a :2024-01-01, 1d",
"GANTT\n a :2024-01-01, 1d",
"gantt",
"gantts\n a",
"journey\n a: 1",
"",
"%% only a comment\n",
] {
let refused = matches!(parse(src), Err(ParseError::NotThisChart { .. }));
assert_eq!(is_gantt(src), !refused, "{src:?}");
}
}
#[test]
fn the_extent_spans_every_task() {
let g = ok("gantt\n dateFormat YYYY-MM-DD\n a :2024-03-01, 2d\n b :2024-01-01, 2d\n c :2024-06-01, 2d\n");
let (lo, hi) = g.extent().expect("tasks");
assert_eq!(day(lo), (2024, 1, 1));
assert_eq!(day(hi), (2024, 6, 3));
}
#[test]
fn a_milestone_ends_when_it_starts_even_when_a_duration_was_written() {
let g = ok("gantt\n dateFormat YYYY-MM-DD\n m :milestone, m1, 2024-01-01, 5d\n");
assert_eq!(g.tasks[0].start, g.tasks[0].end, "a milestone took time");
assert_eq!(day(g.tasks[0].end), (2024, 1, 1));
let (_, hi) = g.extent().expect("tasks");
assert_eq!(day(hi), (2024, 1, 1));
}