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