1use std::path::Path;
13
14use crate::diagnostics::{CheckId, DiagCtx};
15use crate::walk::{filter_dirs, walk};
16
17pub fn check(root_path: &Path, stdlib: bool, diag_ctx: DiagCtx) {
18 let mut check = diag_ctx.start_check(CheckId::new("unit_tests").path(root_path));
19
20 let skip = move |path: &Path, is_dir| {
21 let file_name = path.file_name().unwrap_or_default();
22
23 if is_dir {
25 if filter_dirs(path) || path.ends_with("src/doc") {
26 return true;
27 }
28 } else {
29 let extension = path.extension().unwrap_or_default();
30 if extension != "rs" {
31 return true;
32 }
33 }
34
35 if is_dir && file_name != "tests" && file_name.as_encoded_bytes().ends_with(b"tests") {
37 return true;
38 }
39
40 if !stdlib {
41 if is_dir {
43 if file_name == "tests" || file_name == "benches" {
44 return true;
45 }
46 } else {
47 if file_name == "tests.rs" || file_name == "benches.rs" {
48 return true;
49 }
50 }
51 }
52
53 if is_dir {
54 file_name == "std_detect" || file_name == "std" || file_name == "test"
56 } else {
57 path.ends_with("library/alloc/src/collections/btree/borrow/tests.rs")
61 || path.ends_with("library/alloc/src/collections/btree/map/tests.rs")
62 || path.ends_with("library/alloc/src/collections/btree/node/tests.rs")
63 || path.ends_with("library/alloc/src/collections/btree/set/tests.rs")
64 || path.ends_with("library/alloc/src/collections/linked_list/tests.rs")
65 || path.ends_with("library/alloc/src/collections/vec_deque/tests.rs")
66 || path.ends_with("library/alloc/src/raw_vec/tests.rs")
67 || path.ends_with("library/alloc/src/wtf8/tests.rs")
68 }
69 };
70
71 walk(root_path, skip, &mut |entry, contents| {
72 let path = entry.path();
73 let package = path
74 .strip_prefix(root_path)
75 .unwrap()
76 .components()
77 .next()
78 .unwrap()
79 .as_os_str()
80 .to_str()
81 .unwrap();
82 for (i, line) in contents.lines().enumerate() {
83 let line = line.trim();
84 let is_test = || line.contains("#[test]") && !line.contains("`#[test]");
85 let is_bench = || line.contains("#[bench]") && !line.contains("`#[bench]");
86 if !line.starts_with("//") && (is_test() || is_bench()) {
87 let explanation = if stdlib {
88 format!(
89 "`{package}` unit tests and benchmarks must be placed into `{package}tests`"
90 )
91 } else {
92 "unit tests and benchmarks must be placed into \
93 separate files or directories named \
94 `tests.rs`, `benches.rs`, `tests` or `benches`"
95 .to_owned()
96 };
97 let name = if is_test() { "test" } else { "bench" };
98 check.error(format!(
99 "`{}:{}` contains `#[{name}]`; {explanation}",
100 path.display(),
101 i + 1,
102 ));
103 return;
104 }
105 }
106 });
107}