1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
use crate::string_diff::colored_diff;
use colored::*;
use std::path::{Path, PathBuf};
const SNAPSHOT_DIR: &str = "__k9_snapshots__";
fn get_project_root_path() -> PathBuf {
let buck_build_id_present = std::env::var("BUCK_BUILD_ID").is_ok();
if buck_build_id_present {
let pwd = std::env::var("PWD").expect(
"
`BUCK_BUILD_ID` enviroment variable was present, which means this project is being built with
buck and relies on `PWD` env variable to contain the project root, but `PWD` wasn't there",
);
return PathBuf::from(pwd);
}
let project_root =
std::env::var("CARGO_MANIFEST_DIR").expect("Can't get project root directory");
PathBuf::from(project_root)
}
pub fn get_snapshot_dir(source_file: &str) -> PathBuf {
let mut c = Path::new(source_file).components();
let source_file_name = c.next_back().unwrap().as_os_str().to_string_lossy();
let mut p: PathBuf = c.collect();
p.push(SNAPSHOT_DIR);
p.push(source_file_name.replace(".rs", ""));
p
}
pub fn get_test_name() -> String {
let t = std::thread::current();
t.name()
.expect("Can't extract the test name")
.to_string()
.replace("::", "_")
}
pub fn get_test_snap_path(snapshot_dir: &Path, test_name: &str) -> PathBuf {
let mut p = snapshot_dir.to_owned();
p.push(format!("{}.snap", test_name));
p
}
pub fn ensure_snap_dir_exists(snapshot_path: &Path) {
std::fs::create_dir_all(snapshot_path.parent().unwrap()).unwrap();
}
fn is_update_mode() -> bool {
let runtime_var = std::env::var("K9_UPDATE_SNAPSHOTS").map_or(false, |_| true);
if !runtime_var {
if option_env!("K9_UPDATE_SNAPSHOTS").is_some() {
return true;
}
}
runtime_var
}
pub fn snap_internal<T: std::fmt::Display>(
thing: T,
_line: u32,
_column: u32,
file: &str,
) -> Option<String> {
let thing_str = thing.to_string();
let snapshot_dir = get_snapshot_dir(file);
let test_name = get_test_name();
let relative_snap_path = get_test_snap_path(&snapshot_dir, &test_name);
let mut absolute_snap_path = get_project_root_path();
absolute_snap_path.push(&relative_snap_path);
let string_desc = "string".red();
let snapshot_desc = "snapshot".green();
let update_instructions =
"run with `K9_UPDATE_SNAPSHOTS=1` to update/create snapshots".yellow();
if is_update_mode() {
ensure_snap_dir_exists(&absolute_snap_path);
std::fs::write(&absolute_snap_path, thing_str).unwrap();
None
} else if absolute_snap_path.exists() {
let snapshot_content = std::fs::read_to_string(&absolute_snap_path.display().to_string())
.expect("can't read snapshot file");
let diff = colored_diff(&snapshot_content, &thing_str);
diff.map(|diff| {
format!(
"Expected {string_desc} to match {snapshot_desc} stored in
{file}
Difference:
{diff}
{update_instructions}
",
string_desc = string_desc,
snapshot_desc = snapshot_desc,
file = relative_snap_path.display().to_string().green(),
diff = diff,
update_instructions = update_instructions,
)
})
} else {
Some(format!(
"Expected {string_desc} to match {snapshot_desc} stored in
{file}
but that snapshot file does not exist.
{update_instructions}
",
string_desc = string_desc,
snapshot_desc = snapshot_desc,
file = relative_snap_path.display().to_string().green(),
update_instructions = update_instructions,
))
}
}