Skip to content

Commit 0eee945

Browse files
author
Lisa Ugray
authored
Merge pull request #102 from Shopify/add-tests-to-diff
Add tests to diff.rs
2 parents e54b01b + 9d0b771 commit 0eee945

File tree

1 file changed

+111
-42
lines changed

1 file changed

+111
-42
lines changed

src/diff.rs

Lines changed: 111 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,105 +1,174 @@
11
use crate::undo;
22

3-
use std::collections::HashMap;
3+
use std::collections::BTreeMap;
44
use std::env;
55

6+
trait Logger {
7+
fn print(&mut self, value: String);
8+
}
9+
struct StdoutLogger;
10+
11+
impl Logger for StdoutLogger {
12+
fn print(&mut self, value: String) {
13+
println!("{}", value);
14+
}
15+
}
16+
617
/// print a diff of the env
718
pub fn run(verbose: bool, color: bool, shadowenv_data: String) -> i32 {
8-
let mut parts = shadowenv_data.splitn(2, ":");
19+
run_with_logger(
20+
&mut StdoutLogger {},
21+
env::vars().collect(),
22+
verbose,
23+
color,
24+
shadowenv_data,
25+
)
26+
}
27+
28+
fn run_with_logger(
29+
logger: &mut dyn Logger,
30+
env_vars: Vec<(String, String)>,
31+
verbose: bool,
32+
color: bool,
33+
shadowenv_data: String,
34+
) -> i32 {
35+
let mut parts = shadowenv_data.splitn(2, ':');
936
let _prev_hash = parts.next();
1037
let json_data = parts.next().unwrap_or("{}");
1138
let shadowenv_data = undo::Data::from_str(json_data).unwrap();
12-
let mut scalars = HashMap::new();
13-
for scalar in shadowenv_data.scalars {
14-
scalars.insert(scalar.name.clone(), scalar);
15-
}
16-
let mut lists = HashMap::new();
17-
for list in shadowenv_data.lists {
18-
lists.insert(list.name.clone(), list);
19-
}
20-
for (name, value) in env::vars() {
39+
let mut scalars = shadowenv_data
40+
.scalars
41+
.iter()
42+
.map(|s| (s.name.clone(), s))
43+
.collect::<BTreeMap<_, _>>();
44+
let mut lists = shadowenv_data
45+
.lists
46+
.iter()
47+
.map(|s| (s.name.clone(), s))
48+
.collect::<BTreeMap<_, _>>();
49+
50+
for (name, value) in env_vars {
2151
if let Some(scalar) = scalars.remove(&name) {
22-
diff_scalar(&scalar, color)
52+
diff_scalar(logger, &scalar, color)
2353
} else if let Some(list) = lists.remove(&name) {
24-
diff_list(&list, &value, color)
54+
diff_list(logger, &list, &value, color)
2555
} else if verbose {
26-
print_verbose(&name, &value)
56+
print_verbose(logger, &name, &value)
2757
}
2858
}
29-
for (_name, scalar) in &scalars {
30-
diff_scalar(&scalar, color)
31-
}
32-
for (_name, list) in &lists {
33-
diff_list(&list, &"".to_string(), color)
34-
}
59+
scalars
60+
.iter()
61+
.for_each(|(_name, scalar)| diff_scalar(logger, scalar, color));
62+
lists
63+
.iter()
64+
.for_each(|(_name, list)| diff_list(logger, list, "", color));
3565
0
3666
}
3767

38-
fn diff_list(list: &undo::List, current: &str, color: bool) {
68+
fn diff_list(logger: &mut dyn Logger, list: &undo::List, current: &str, color: bool) {
3969
let formatted_deletions: Vec<String> = if color {
4070
list.deletions
4171
.iter()
42-
.map(|x| "\x1b[48;5;52m".to_string() + &x + &"\x1b[0;91m".to_string())
72+
.map(|x| "\x1b[48;5;52m".to_string() + x + "\x1b[0;91m")
4373
.collect()
4474
} else {
4575
list.deletions.clone()
4676
};
4777
let mut prefix = formatted_deletions.join(":");
48-
let items = current.split(":").collect::<Vec<&str>>();
49-
let items = items
50-
.into_iter()
78+
79+
let items = current
80+
.split(':')
5181
.skip_while(|x| list.additions.contains(&x.to_string()));
5282
let items: Vec<&str> = items.collect();
5383
let suffix = items.join(":");
54-
if suffix != "" && prefix != "" {
84+
if !suffix.is_empty() && !prefix.is_empty() {
5585
prefix += ":";
5686
}
57-
diff_remove(&list.name, &(prefix + &suffix), color);
87+
diff_remove(logger, &list.name, &(prefix + &suffix), color);
5888

59-
let items = current.split(":").collect::<Vec<&str>>();
60-
let items = items.into_iter().map(|x| {
89+
let items = current.split(':').map(|x| {
6190
if list.additions.contains(&x.to_string()) && color {
62-
"\x1b[48;5;22m".to_string() + &x + &("\x1b[0;92m".to_string())
91+
"\x1b[48;5;22m".to_string() + x + "\x1b[0;92m"
6392
} else {
6493
x.to_string()
6594
}
6695
});
6796
let items: Vec<String> = items.collect();
6897
let newline = items.join(":");
69-
diff_add(&list.name, &newline, color);
98+
diff_add(logger, &list.name, &newline, color);
7099
}
71100

72-
fn diff_scalar(scalar: &undo::Scalar, color: bool) {
101+
fn diff_scalar(logger: &mut dyn Logger, scalar: &undo::Scalar, color: bool) {
73102
if let Some(value) = &scalar.original {
74-
diff_remove(&scalar.name, &value, color);
103+
diff_remove(logger, &scalar.name, value, color);
75104
}
76105
if let Some(value) = &scalar.current {
77-
diff_add(&scalar.name, &value, color);
106+
diff_add(logger, &scalar.name, value, color);
78107
}
79108
}
80109

81-
fn diff_add(name: &str, value: &str, color: bool) {
110+
fn diff_add(logger: &mut dyn Logger, name: &str, value: &str, color: bool) {
82111
if color {
83112
// Clearing to EOL with \x1b[K prevents a weird issue where a wrapped line uses the last
84113
// non-null background color for the newline character, filling the rest of the space in the
85114
// line.
86-
println!("\x1b[92m+ {}={}\x1b[0m\x1b[K", name, value);
115+
logger.print(format!("\x1b[92m+ {}={}\x1b[0m\x1b[K", name, value));
87116
} else {
88-
println!("+ {}={}", name, value);
117+
logger.print(format!("+ {}={}", name, value));
89118
}
90119
}
91120

92-
fn diff_remove(name: &str, value: &str, color: bool) {
121+
fn diff_remove(logger: &mut dyn Logger, name: &str, value: &str, color: bool) {
93122
if color {
94123
// Clearing to EOL with \x1b[K prevents a weird issue where a wrapped line uses the last
95124
// non-null background colour for the newline character, filling the rest of the space in the
96125
// line.
97-
println!("\x1b[91m- {}={}\x1b[0m\x1b[K", name, value);
126+
logger.print(format!("\x1b[91m- {}={}\x1b[0m\x1b[K", name, value));
98127
} else {
99-
println!("- {}={}", name, value);
128+
logger.print(format!("- {}={}", name, value));
100129
}
101130
}
102131

103-
fn print_verbose(name: &str, value: &str) {
104-
println!(" {}={}", name, value)
132+
fn print_verbose(logger: &mut dyn Logger, name: &str, value: &str) {
133+
logger.print(format!(" {}={}", name, value))
134+
}
135+
136+
#[cfg(test)]
137+
mod tests {
138+
use super::*;
139+
#[derive(Default)]
140+
struct DummyLogger(Vec<String>);
141+
impl Logger for DummyLogger {
142+
fn print(&mut self, value: String) {
143+
self.0.push(value);
144+
}
145+
}
146+
147+
#[test]
148+
fn nominal_test() {
149+
let mut logger = DummyLogger::default();
150+
151+
let env_vars = vec![
152+
("VAR_A".to_string(), "/added:/existent".to_string()),
153+
("VAR_B".to_string(), "/added".to_string()),
154+
("VAR_C".to_string(), "/added:/existent".to_string()),
155+
];
156+
157+
let data = r#"62b0b9f86cda84d4:{"scalars":[],"lists":[{"name":"VAR_C","additions":["/added"],"deletions":["/removed"]},{"name":"VAR_B","additions":["/added"],"deletions":[]},{"name":"VAR_A","additions":["/added"],"deletions":[]}]}"#;
158+
let result = run_with_logger(&mut logger, env_vars, false, false, data.to_string());
159+
160+
let expected: Vec<_> = vec![
161+
"- VAR_A=/existent",
162+
"+ VAR_A=/added:/existent",
163+
"- VAR_B=",
164+
"+ VAR_B=/added",
165+
"- VAR_C=/removed:/existent",
166+
"+ VAR_C=/added:/existent",
167+
]
168+
.iter()
169+
.map(ToString::to_string)
170+
.collect();
171+
assert_eq!(result, 0);
172+
assert_eq!(logger.0, expected);
173+
}
105174
}

0 commit comments

Comments
 (0)