tidy/
unit_tests.rs

1//! Tidy check to ensure `#[test]` and `#[bench]` are not used directly inside
2//! `core` or `alloc`.
3//!
4//! `core` and `alloc` cannot be tested directly due to duplicating lang items.
5//! All tests and benchmarks must be written externally in
6//! `{coretests,alloctests}/{tests,benches}`.
7//!
8//! Outside of `core` and `alloc`, tests and benchmarks should be outlined into
9//! separate files named `tests.rs` or `benches.rs`, or directories named
10//! `tests` or `benches` unconfigured during normal build.
11
12use std::path::Path;
13
14use crate::walk::{filter_dirs, walk};
15
16pub fn check(root_path: &Path, bad: &mut bool) {
17    let core = root_path.join("core");
18    let core_copy = core.clone();
19    let is_core = move |path: &Path| path.starts_with(&core);
20    let alloc = root_path.join("alloc");
21    let alloc_copy = alloc.clone();
22    let is_alloc = move |path: &Path| path.starts_with(&alloc);
23
24    let skip = move |path: &Path, is_dir| {
25        let file_name = path.file_name().unwrap_or_default();
26        if is_dir {
27            filter_dirs(path)
28                || path.ends_with("src/doc")
29                || (file_name == "tests" || file_name == "benches")
30                    && !is_core(path)
31                    && !is_alloc(path)
32        } else {
33            let extension = path.extension().unwrap_or_default();
34            extension != "rs"
35                || (file_name == "tests.rs" || file_name == "benches.rs")
36                    && !is_core(path)
37                    && !is_alloc(path)
38                // Tests which use non-public internals and, as such, need to
39                // have the types in the same crate as the tests themselves. See
40                // the comment in alloctests/lib.rs.
41                || path.ends_with("library/alloc/src/collections/btree/borrow/tests.rs")
42                || path.ends_with("library/alloc/src/collections/btree/map/tests.rs")
43                || path.ends_with("library/alloc/src/collections/btree/node/tests.rs")
44                || path.ends_with("library/alloc/src/collections/btree/set/tests.rs")
45                || path.ends_with("library/alloc/src/collections/linked_list/tests.rs")
46                || path.ends_with("library/alloc/src/collections/vec_deque/tests.rs")
47                || path.ends_with("library/alloc/src/raw_vec/tests.rs")
48        }
49    };
50
51    walk(root_path, skip, &mut |entry, contents| {
52        let path = entry.path();
53        let is_core = path.starts_with(&core_copy);
54        let is_alloc = path.starts_with(&alloc_copy);
55        for (i, line) in contents.lines().enumerate() {
56            let line = line.trim();
57            let is_test = || line.contains("#[test]") && !line.contains("`#[test]");
58            let is_bench = || line.contains("#[bench]") && !line.contains("`#[bench]");
59            if !line.starts_with("//") && (is_test() || is_bench()) {
60                let explanation = if is_core {
61                    "`core` unit tests and benchmarks must be placed into `coretests`"
62                } else if is_alloc {
63                    "`alloc` unit tests and benchmarks must be placed into `alloctests`"
64                } else {
65                    "unit tests and benchmarks must be placed into \
66                         separate files or directories named \
67                         `tests.rs`, `benches.rs`, `tests` or `benches`"
68                };
69                let name = if is_test() { "test" } else { "bench" };
70                tidy_error!(
71                    bad,
72                    "`{}:{}` contains `#[{}]`; {}",
73                    path.display(),
74                    i + 1,
75                    name,
76                    explanation,
77                );
78                return;
79            }
80        }
81    });
82}