tidy/
unit_tests.rs

1//! Tidy check to ensure `#[test]` and `#[bench]` are not used directly inside `core`.
2//!
3//! `#![no_core]` libraries cannot be tested directly due to duplicating lang
4//! items. All tests and benchmarks must be written externally in `core/{tests,benches}`.
5//!
6//! Outside of core tests and benchmarks should be outlined into separate files
7//! named `tests.rs` or `benches.rs`, or directories named `tests` or `benches` unconfigured
8//! during normal build.
9
10use std::path::Path;
11
12use crate::walk::{filter_dirs, walk};
13
14pub fn check(root_path: &Path, bad: &mut bool) {
15    let core = root_path.join("core");
16    let core_copy = core.clone();
17    let core_tests = core.join("tests");
18    let core_benches = core.join("benches");
19    let is_core = move |path: &Path| {
20        path.starts_with(&core)
21            && !(path.starts_with(&core_tests) || path.starts_with(&core_benches))
22    };
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") && !is_core(path)
30        } else {
31            let extension = path.extension().unwrap_or_default();
32            extension != "rs"
33                || (file_name == "tests.rs" || file_name == "benches.rs") && !is_core(path)
34                // UI tests with different names
35                || path.ends_with("src/thread/local/dynamic_tests.rs")
36                || path.ends_with("src/sync/mpsc/sync_tests.rs")
37        }
38    };
39
40    walk(root_path, skip, &mut |entry, contents| {
41        let path = entry.path();
42        let is_core = path.starts_with(&core_copy);
43        for (i, line) in contents.lines().enumerate() {
44            let line = line.trim();
45            let is_test = || line.contains("#[test]") && !line.contains("`#[test]");
46            let is_bench = || line.contains("#[bench]") && !line.contains("`#[bench]");
47            if !line.starts_with("//") && (is_test() || is_bench()) {
48                let explanation = if is_core {
49                    "core unit tests and benchmarks must be placed into \
50                         `core/tests` or `core/benches`"
51                } else {
52                    "unit tests and benchmarks must be placed into \
53                         separate files or directories named \
54                         `tests.rs`, `benches.rs`, `tests` or `benches`"
55                };
56                let name = if is_test() { "test" } else { "bench" };
57                tidy_error!(
58                    bad,
59                    "`{}:{}` contains `#[{}]`; {}",
60                    path.display(),
61                    i + 1,
62                    name,
63                    explanation,
64                );
65                return;
66            }
67        }
68    });
69}