testing_conventions/
tiers.rs1use std::path::{Path, PathBuf};
12
13pub fn package_root(scan_root: &Path, manifest: &str) -> Option<PathBuf> {
19 for dir in scan_root.ancestors() {
20 if dir.join(manifest).is_file() {
21 return Some(dir.to_path_buf());
22 }
23 if dir.join(".git").exists() {
24 break;
25 }
26 }
27 None
28}
29
30pub fn suite_tests_dir(scan_root: &Path, manifest: &str) -> Option<PathBuf> {
34 package_root(scan_root, manifest).map(|root| root.join("tests"))
35}
36
37#[cfg(test)]
38mod tests {
39 use std::path::PathBuf;
40 use std::sync::atomic::{AtomicU64, Ordering};
41
42 use super::{package_root, suite_tests_dir};
43
44 struct TempTree(PathBuf);
46
47 impl TempTree {
48 fn new() -> Self {
49 static COUNTER: AtomicU64 = AtomicU64::new(0);
50 let dir = std::env::temp_dir().join(format!(
51 "tc-tiers-{}-{}",
52 std::process::id(),
53 COUNTER.fetch_add(1, Ordering::Relaxed),
54 ));
55 std::fs::create_dir_all(&dir).unwrap();
56 TempTree(dir)
57 }
58
59 fn touch(&self, name: &str) {
60 let path = self.0.join(name);
61 std::fs::create_dir_all(path.parent().unwrap()).unwrap();
62 std::fs::write(path, "").unwrap();
63 }
64 }
65
66 impl Drop for TempTree {
67 fn drop(&mut self) {
68 let _ = std::fs::remove_dir_all(&self.0);
69 }
70 }
71
72 #[test]
73 fn finds_the_nearest_manifest_above_the_scan_root() {
74 let tree = TempTree::new();
75 tree.touch("pkg/pyproject.toml");
76 tree.touch("pkg/src/widget.py");
77 assert_eq!(
78 package_root(&tree.0.join("pkg/src"), "pyproject.toml"),
79 Some(tree.0.join("pkg")),
80 );
81 }
82
83 #[test]
84 fn the_scan_root_itself_can_be_the_package_root() {
85 let tree = TempTree::new();
86 tree.touch("pkg/package.json");
87 assert_eq!(
88 package_root(&tree.0.join("pkg"), "package.json"),
89 Some(tree.0.join("pkg")),
90 );
91 }
92
93 #[test]
94 fn the_walk_stops_at_a_git_boundary() {
95 let tree = TempTree::new();
96 tree.touch("Cargo.toml");
97 tree.touch("repo/.git/HEAD");
98 tree.touch("repo/src/lib.rs");
99 assert_eq!(package_root(&tree.0.join("repo/src"), "Cargo.toml"), None);
100 }
101
102 #[test]
103 fn a_manifest_at_the_git_boundary_is_still_found() {
104 let tree = TempTree::new();
105 tree.touch("repo/.git/HEAD");
106 tree.touch("repo/pyproject.toml");
107 assert_eq!(
108 package_root(&tree.0.join("repo"), "pyproject.toml"),
109 Some(tree.0.join("repo")),
110 );
111 }
112
113 #[test]
114 fn suite_tests_dir_is_the_package_roots_tests() {
115 let tree = TempTree::new();
116 tree.touch(".git/HEAD");
117 tree.touch("pkg/pyproject.toml");
118 assert_eq!(
119 suite_tests_dir(&tree.0.join("pkg"), "pyproject.toml"),
120 Some(tree.0.join("pkg/tests")),
121 );
122 assert_eq!(suite_tests_dir(&tree.0, "pyproject.toml"), None);
123 }
124}