Skip to content

Commit e45b0cb

Browse files
committed
Add and update tests
1 parent 44e632a commit e45b0cb

File tree

3 files changed

+218
-29
lines changed

3 files changed

+218
-29
lines changed

src/merge.rs

Lines changed: 88 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,55 @@ use chrono::{DateTime, Local};
55
mod tests {
66
use crate::merge::{are_compatible, Interval};
77

8+
use crate::merge::merge_strictly_compatible_lifelapses;
9+
use crate::parse::{get_life_chunk, LifeChunk, LifeLapse, TimedLifeChunk};
810
use chrono::{Date, Local, TimeZone};
11+
use time::Duration;
12+
13+
fn create_lifelapse(start_h: u32, duration_h: i64) -> LifeLapse {
14+
let start = Local.ymd(2020, 12, 1).and_hms(start_h, 0, 0);
15+
let mut ll = LifeLapse::new(start);
16+
let lc = get_life_chunk(&format!("{} 0 test", duration_h));
17+
let tlc = TimedLifeChunk {
18+
start,
19+
life_chunk: lc,
20+
};
21+
ll.extend(vec![tlc]);
22+
ll
23+
}
24+
25+
#[test]
26+
fn merge_strictly_compatible_lifelapses_contiguous_inputs_returns_single_merged_lapse() {
27+
let ll1 = create_lifelapse(10, 1); // 10-11
28+
let ll2 = create_lifelapse(11, 1); // 11-12
29+
30+
let merged = merge_strictly_compatible_lifelapses(vec![ll1, ll2]);
31+
assert_eq!(merged.len(), 1);
32+
assert_eq!(merged[0].tokens_as_ref().len(), 2);
33+
}
34+
35+
#[test]
36+
fn merge_strictly_compatible_lifelapses_inputs_with_gap_returns_separate_lapses() {
37+
let ll1 = create_lifelapse(10, 1); // 10-11
38+
let ll2 = create_lifelapse(12, 1); // 12-13
39+
40+
let merged = merge_strictly_compatible_lifelapses(vec![ll1, ll2]);
41+
assert_eq!(merged.len(), 2);
42+
}
943

1044
#[test]
11-
fn are_compatible_1() {
45+
fn merge_strictly_compatible_lifelapses_overlapping_inputs_returns_separate_lapses() {
46+
// This function assumes input is sorted and strictly compatible means end == start
47+
// If they overlap, is_right_before will be false
48+
let ll1 = create_lifelapse(10, 2); // 10-12
49+
let ll2 = create_lifelapse(11, 1); // 11-12
50+
51+
let merged = merge_strictly_compatible_lifelapses(vec![ll1, ll2]);
52+
assert_eq!(merged.len(), 2);
53+
}
54+
55+
#[test]
56+
fn are_compatible_disjoint_intervals_returns_true() {
1257
let i1 = Interval {
1358
start: Local.ymd(2020, 12, 1).and_hms(14, 0, 0),
1459
end: Local.ymd(2020, 12, 1).and_hms(16, 0, 0),
@@ -20,7 +65,7 @@ mod tests {
2065
assert!(are_compatible(vec![&i1, &i2]));
2166
}
2267
#[test]
23-
fn are_compatible_2() {
68+
fn are_compatible_three_intervals_with_overlap_returns_false() {
2469
let i1 = Interval {
2570
start: Local.ymd(2020, 12, 1).and_hms(14, 0, 0),
2671
end: Local.ymd(2020, 12, 1).and_hms(20, 0, 0),
@@ -35,6 +80,47 @@ mod tests {
3580
};
3681
assert!(!are_compatible(vec![&i3, &i1, &i2]));
3782
}
83+
#[test]
84+
fn are_compatible_two_overlapping_intervals_returns_false() {
85+
let i1 = Interval {
86+
start: Local.ymd(2020, 12, 1).and_hms(14, 0, 0),
87+
end: Local.ymd(2020, 12, 1).and_hms(16, 0, 0),
88+
};
89+
let i2 = Interval {
90+
start: Local.ymd(2020, 12, 1).and_hms(15, 0, 0),
91+
end: Local.ymd(2020, 12, 1).and_hms(17, 0, 0),
92+
};
93+
assert!(!are_compatible(vec![&i1, &i2]));
94+
}
95+
96+
#[test]
97+
fn are_compatible_touching_intervals_returns_true() {
98+
let i1 = Interval {
99+
start: Local.ymd(2020, 12, 1).and_hms(14, 0, 0),
100+
end: Local.ymd(2020, 12, 1).and_hms(16, 0, 0),
101+
};
102+
let i2 = Interval {
103+
start: Local.ymd(2020, 12, 1).and_hms(16, 0, 0),
104+
end: Local.ymd(2020, 12, 1).and_hms(18, 0, 0),
105+
};
106+
// Touching intervals are compatible in this logic (end == start is allowed)
107+
// The check is `if i1.end > i2.start { return false; }`
108+
assert!(are_compatible(vec![&i1, &i2]));
109+
}
110+
111+
#[test]
112+
fn are_compatible_empty_input_returns_true() {
113+
assert!(are_compatible(vec![]));
114+
}
115+
116+
#[test]
117+
fn are_compatible_single_interval_returns_true() {
118+
let i1 = Interval {
119+
start: Local.ymd(2020, 12, 1).and_hms(14, 0, 0),
120+
end: Local.ymd(2020, 12, 1).and_hms(16, 0, 0),
121+
};
122+
assert!(are_compatible(vec![&i1]));
123+
}
38124
}
39125

40126
struct Interval {

src/parse.rs

Lines changed: 63 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ mod tests {
3131
use crate::parse::{get_life_chunk, parse_date, process_line, LifeChunk, LineParseResult};
3232

3333
#[test]
34-
fn parsing_1() {
34+
fn parsing_1_valid_datetime_string_returns_datetime() {
3535
let dt = Local.ymd(2014, 11, 28).and_hms(12, 0, 0);
3636
assert_eq!(
3737
Local
@@ -42,24 +42,24 @@ mod tests {
4242
}
4343

4444
#[test]
45-
fn test_parse_date_custom_format() {
45+
fn parse_date_custom_format_returns_datetime() {
4646
let dt = Local.ymd(2023, 10, 26).and_hms(14, 30, 0);
4747
assert_eq!(parse_date("2023-10-26 14h30"), Some(dt));
4848
}
4949

5050
#[test]
51-
fn test_parse_date_invalid_string() {
51+
fn parse_date_invalid_string_returns_none() {
5252
assert_eq!(parse_date("invalid-date-string"), None);
5353
}
5454

5555
#[test]
56-
fn test_parse_date_empty_string() {
56+
fn parse_date_empty_string_returns_none() {
5757
assert_eq!(parse_date(""), None);
5858
}
5959

6060
// Tests for get_life_chunk
6161
#[test]
62-
fn test_get_life_chunk_full_input() {
62+
fn get_life_chunk_full_input_returns_populated_lifechunk() {
6363
let line = "1 30 Meeting with team @Work @Q2";
6464
let lc = get_life_chunk(line);
6565
assert_eq!(lc.description, "Meeting with team");
@@ -71,7 +71,7 @@ mod tests {
7171
}
7272

7373
#[test]
74-
fn test_get_life_chunk_missing_duration() {
74+
fn get_life_chunk_missing_duration_returns_zero_duration() {
7575
let line = "그냥 프로젝트 작업 @Dev"; // "Just working on a project @Dev"
7676
let lc = get_life_chunk(line);
7777
assert_eq!(lc.description, "작업"); // Adjusted for current behavior
@@ -83,7 +83,7 @@ mod tests {
8383
}
8484

8585
#[test]
86-
fn test_get_life_chunk_missing_categories() {
86+
fn get_life_chunk_missing_categories_returns_empty_categories() {
8787
let line = "2 0 Quick break";
8888
let lc = get_life_chunk(line);
8989
assert_eq!(lc.description, "Quick break");
@@ -95,7 +95,7 @@ mod tests {
9595
}
9696

9797
#[test]
98-
fn test_get_life_chunk_user_quadrant() {
98+
fn get_life_chunk_user_quadrant_returns_parsed_quadrant() {
9999
let line = "0 45 Planning session @Q1";
100100
let lc = get_life_chunk(line);
101101
assert_eq!(lc.description, "Planning session");
@@ -107,7 +107,7 @@ mod tests {
107107
}
108108

109109
#[test]
110-
fn test_get_life_chunk_default_quadrant() {
110+
fn get_life_chunk_no_quadrant_returns_default_quadrant() {
111111
let line = "3 0 Reading a book @Leisure";
112112
let lc = get_life_chunk(line);
113113
assert_eq!(lc.description, "Reading a book");
@@ -120,7 +120,7 @@ mod tests {
120120

121121
// Tests for process_line
122122
#[test]
123-
fn test_process_line_date() {
123+
fn process_line_date_string_returns_date_result() {
124124
let line = "2024-03-10 10:00";
125125
let expected_date = Local.ymd(2024, 3, 10).and_hms(10, 0, 0);
126126
match process_line(line) {
@@ -130,7 +130,7 @@ mod tests {
130130
}
131131

132132
#[test]
133-
fn test_process_line_life_chunk() {
133+
fn process_line_life_chunk_string_returns_lc_result() {
134134
let line = "1 0 Coding @Dev";
135135
match process_line(line) {
136136
LineParseResult::Lc { life_chunk: lc } => {
@@ -146,7 +146,7 @@ mod tests {
146146
}
147147

148148
#[test]
149-
fn test_process_line_comment() {
149+
fn process_line_comment_string_returns_lc_result_with_zero_duration() {
150150
let line = "# This is a comment";
151151
match process_line(line) {
152152
LineParseResult::Lc { life_chunk: lc } => {
@@ -164,7 +164,7 @@ mod tests {
164164
}
165165

166166
#[test]
167-
fn test_process_line_empty_string() {
167+
fn process_line_empty_string_returns_empty_lc_result() {
168168
let line = "";
169169
match process_line(line) {
170170
LineParseResult::Lc { life_chunk: lc } => {
@@ -178,6 +178,53 @@ mod tests {
178178
_ => panic!("Expected LineParseResult::Lc for an empty line"),
179179
}
180180
}
181+
#[test]
182+
fn get_all_life_lapses_single_file_returns_parsed_lapses() {
183+
use crate::config::Config;
184+
use crate::parse::get_all_life_lapses;
185+
186+
let lines = vec![vec![
187+
"2020-12-01 10:00".to_string(),
188+
"1 0 Task 1 @work".to_string(),
189+
"2 0 Task 2 @home".to_string(),
190+
]];
191+
let config = Config::default();
192+
let (start, lapses) = get_all_life_lapses(lines, &config);
193+
194+
assert_eq!(start, Local.ymd(2020, 12, 1).and_hms(10, 0, 0));
195+
assert_eq!(lapses.len(), 1);
196+
assert_eq!(lapses[0].tokens_as_ref().len(), 2);
197+
}
198+
199+
#[test]
200+
fn get_all_life_lapses_multiple_files_returns_merged_lapses() {
201+
use crate::config::Config;
202+
use crate::parse::get_all_life_lapses;
203+
204+
let lines = vec![
205+
vec!["2020-12-01 10:00".to_string(), "1 0 Task 1".to_string()],
206+
vec!["2020-12-01 11:00".to_string(), "1 0 Task 2".to_string()],
207+
];
208+
let config = Config::default();
209+
let (start, lapses) = get_all_life_lapses(lines, &config);
210+
211+
// Start time should be the min of all start times
212+
assert_eq!(start, Local.ymd(2020, 12, 1).and_hms(10, 0, 0));
213+
// They should be merged if compatible
214+
// 10:00 + 1h = 11:00. Second file starts at 11:00.
215+
// They are compatible and touching.
216+
assert_eq!(lapses.len(), 1);
217+
assert_eq!(lapses[0].tokens_as_ref().len(), 2);
218+
}
219+
220+
#[test]
221+
fn get_life_chunk_extra_spaces_returns_correct_description() {
222+
let line = "1 0 Task with spaces @work";
223+
let lc = get_life_chunk(line);
224+
assert_eq!(lc.description, "Task with spaces");
225+
assert_eq!(lc.duration, Duration::hours(1));
226+
assert_eq!(lc.categories, vec!["@work".to_string()]);
227+
}
181228
}
182229

183230
/// A continuous series of life chunks.
@@ -400,7 +447,9 @@ fn parse_date(s: &str) -> Option<Timestamp> {
400447

401448
pub(crate) fn get_life_chunk(line: &str) -> LifeChunk {
402449
// Made pub(crate)
403-
let mut tokens = line.split(|c: char| c == ',' || c.is_whitespace());
450+
let mut tokens = line
451+
.split(|c: char| c == ',' || c.is_whitespace())
452+
.filter(|s| !s.is_empty());
404453

405454
let mut parse_token_as_duration = |parse_as: fn(i64) -> Duration| {
406455
tokens

0 commit comments

Comments
 (0)