From 42993ae680fb2fc9b68c218abd1bde262c32f5b5 Mon Sep 17 00:00:00 2001 From: aiongg Date: Mon, 26 Oct 2026 22:10:53 +0000 Subject: [PATCH] =?UTF-8?q?test(engine):=20read=20only=20test=20cfgs=20and?= =?UTF-8?q?=20any=20pub(=E2=80=A6)=20mod=20as=20test=20modules=20in=20the?= =?UTF-8?q?=20file=20guard?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Split files share test helpers through pub(super)/pub(crate) test modules. The guard read only `mod` and `pub mod` as a test module, so it scanned their fixtures as production code. `attr.contains("test")` also matched `#[cfg(not(test))]`, so such a module was skipped; it is scanned now. --- crates/engine/src/tests/file_guard.rs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/crates/engine/src/tests/file_guard.rs b/crates/engine/src/tests/file_guard.rs index 326968329..d27a13b79 100644 --- a/crates/engine/src/tests/file_guard.rs +++ b/crates/engine/src/tests/file_guard.rs @@ -68,8 +68,8 @@ fn code_outside_tests(text: &str) -> Vec<(usize, &str)> { let mut i = 0; while i < lines.len() { let attr = lines[i].trim(); - let module = lines.get(i + 1).map(|l| l.trim_start().trim_start_matches("pub ")); - if attr.starts_with("#[cfg(") && attr.contains("test") && module.is_some_and(|m| m.starts_with("mod ")) { + let module = lines.get(i + 1).map(|l| without_visibility(l.trim_start())); + if attr.starts_with("#[cfg(") && attr.replace("not(test)", "").contains("test") && module.is_some_and(|m| m.starts_with("mod ")) { let header = lines[i + 1]; i += 2; if header.trim_end().ends_with('{') { @@ -88,6 +88,14 @@ fn code_outside_tests(text: &str) -> Vec<(usize, &str)> { code } +/// `item` without a leading `pub`, `pub(crate)`, `pub(super)` or `pub(in …)`: a test module +/// that sibling modules share is still a test module. +fn without_visibility(item: &str) -> &str { + let Some(rest) = item.strip_prefix("pub") else { return item }; + let rest = if rest.starts_with('(') { rest.find(')').map_or(rest, |close| &rest[close + 1..]) } else { rest }; + if rest.starts_with(char::is_whitespace) { rest.trim_start() } else { item } +} + /// `line` without its `//` comment (a `//` inside a string or char is rare enough in this code /// to leave alone: it only hides the rest of that line from the scan). fn without_comment(line: &str) -> &str { @@ -131,7 +139,7 @@ fn imports_fs(text: &str) -> Vec { for (n, line) in code_outside_tests(text) { let line = without_comment(line); if statement.is_empty() { - if !line.trim_start().starts_with("use ") && !line.trim_start().starts_with("pub use ") { + if !without_visibility(line.trim_start()).starts_with("use ") { continue; } start = n; @@ -247,4 +255,6 @@ fn the_scan_sees_every_spelling() { } assert!(imports_fs("use std::io::Read;\nuse std::path::Path;").is_empty()); assert!(imports_fs("#[cfg(test)]\nmod tests {\n use std::fs;\n}\n").is_empty(), "test modules are skipped"); + assert!(imports_fs("#[cfg(test)]\npub(super) mod shared {\n use std::fs;\n}\n").is_empty(), "shared test modules are skipped"); + assert!(!imports_fs("#[cfg(not(test))]\npub(crate) mod real {\n use std::fs;\n}\n").is_empty(), "cfg(not(test)) modules are scanned"); }