-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Error on stray .stderr/.stdout files for (un-)revisioned tests
- Loading branch information
Showing
6 changed files
with
202 additions
and
24 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
const COMMENT: &str = "//@"; | ||
|
||
/// A header line, like `//@name: value` consists of the prefix `//@` and the directive | ||
/// `name: value`. It is also possibly revisioned, e.g. `//@[revision] name: value`. | ||
pub(crate) struct HeaderLine<'ln> { | ||
pub(crate) revision: Option<&'ln str>, | ||
pub(crate) directive: &'ln str, | ||
} | ||
|
||
/// Iterate through compiletest headers in a test contents. | ||
/// | ||
/// Adjusted from compiletest/src/header.rs. | ||
pub(crate) fn iter_header<'ln>(contents: &'ln str, it: &mut dyn FnMut(HeaderLine<'ln>)) { | ||
for ln in contents.lines() { | ||
let ln = ln.trim(); | ||
|
||
// We're left with potentially `[rev]name: value`. | ||
let Some(remainder) = ln.strip_prefix(COMMENT) else { | ||
continue; | ||
}; | ||
|
||
if let Some(remainder) = remainder.trim_start().strip_prefix('[') { | ||
let Some((revision, remainder)) = remainder.split_once(']') else { | ||
panic!("malformed revision directive: expected `//@[rev]`, found `{ln}`"); | ||
}; | ||
// We trimmed off the `[rev]` portion, left with `name: value`. | ||
it(HeaderLine { revision: Some(revision), directive: remainder.trim() }); | ||
} else { | ||
it(HeaderLine { revision: None, directive: remainder.trim() }); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
146 changes: 146 additions & 0 deletions
146
src/tools/tidy/src/tests_revision_unpaired_stdout_stderr.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
//! Checks that there are no unpaired `.stderr` or `.stdout` for a test with and without revisions. | ||
use std::collections::{BTreeMap, BTreeSet}; | ||
use std::ffi::OsStr; | ||
use std::path::Path; | ||
|
||
use crate::iter_header::*; | ||
use crate::walk::*; | ||
|
||
// Should be kept in sync with `CompareMode` in `src/tools/compiletest/src/common.rs`, | ||
// as well as `run`. | ||
const IGNORES: &[&str] = &[ | ||
"polonius", | ||
"chalk", | ||
"split-dwarf", | ||
"split-dwarf-single", | ||
"next-solver-coherence", | ||
"next-solver", | ||
"run", | ||
]; | ||
const EXTENSIONS: &[&str] = &["stdout", "stderr"]; | ||
const SPECIAL_TEST: &str = "tests/ui/command/need-crate-arg-ignore-tidy.x.rs"; | ||
|
||
pub fn check(tests_path: impl AsRef<Path>, bad: &mut bool) { | ||
// Recurse over subdirectories under `tests/` | ||
walk_dir(tests_path.as_ref(), filter, &mut |entry| { | ||
// We are inspecting a folder. Collect the paths to interesting files `.rs`, `.stderr`, | ||
// `.stdout` under the current folder (shallow). | ||
let mut files_under_inspection = BTreeSet::new(); | ||
for sibling in std::fs::read_dir(entry.path()).unwrap() { | ||
let Ok(sibling) = sibling else { | ||
continue; | ||
}; | ||
|
||
if sibling.path().is_dir() { | ||
continue; | ||
} | ||
|
||
let sibling_path = sibling.path(); | ||
|
||
let Some(ext) = sibling_path.extension().map(OsStr::to_str).flatten() else { | ||
continue; | ||
}; | ||
|
||
if ext == "rs" || EXTENSIONS.contains(&ext) { | ||
files_under_inspection.insert(sibling_path); | ||
} | ||
} | ||
|
||
let mut test_info = BTreeMap::new(); | ||
|
||
for test in | ||
files_under_inspection.iter().filter(|f| f.extension().is_some_and(|ext| ext == "rs")) | ||
{ | ||
if test.ends_with(SPECIAL_TEST) { | ||
continue; | ||
} | ||
|
||
let mut expected_revisions = BTreeSet::new(); | ||
|
||
let contents = std::fs::read_to_string(test).unwrap(); | ||
|
||
// Collect directives. | ||
iter_header(&contents, &mut |HeaderLine { revision, directive }| { | ||
// We're trying to *find* `//@ revision: xxx` directives themselves, not revisioned | ||
// directives. | ||
if revision.is_some() { | ||
return; | ||
} | ||
|
||
let directive = directive.trim(); | ||
|
||
if directive.starts_with("revisions") { | ||
let Some((name, value)) = directive.split_once([':', ' ']) else { | ||
return; | ||
}; | ||
|
||
if name == "revisions" { | ||
let revs = value.split(' '); | ||
for rev in revs { | ||
expected_revisions.insert(rev.to_owned()); | ||
} | ||
} | ||
} | ||
}); | ||
|
||
let Some((test_name, _)) = test.to_str().map(|s| s.split_once('.')).flatten() else { | ||
continue; | ||
}; | ||
|
||
test_info.insert(test_name.to_string(), (test, expected_revisions)); | ||
} | ||
|
||
// Our test file `foo.rs` has specified no revisions. There should not be any | ||
// `foo.rev{.stderr,.stdout}` files. rustc-dev-guide says test output files can have names | ||
// of the form: `test-name.revision.compare_mode.extension`, but our only concern is | ||
// `test-name.revision` and `extension`. | ||
for sibling in files_under_inspection.iter().filter(|f| { | ||
f.extension().map(OsStr::to_str).flatten().is_some_and(|ext| EXTENSIONS.contains(&ext)) | ||
}) { | ||
let filename_components = sibling.to_str().unwrap().split('.').collect::<Vec<_>>(); | ||
let file_prefix = filename_components[0]; | ||
|
||
let Some((test_path, expected_revisions)) = test_info.get(file_prefix) else { | ||
continue; | ||
}; | ||
|
||
match filename_components[..] { | ||
// Cannot have a revision component, skip. | ||
[] | [_] => return, | ||
[_, _] if !expected_revisions.is_empty() => { | ||
// Found unrevisioned output files for a revisioned test. | ||
tidy_error!( | ||
bad, | ||
"found unrevisioned output file `{}` for a revisioned test `{}`", | ||
sibling.display(), | ||
test_path.display(), | ||
); | ||
} | ||
[_, _] => return, | ||
[_, found_revision, .., extension] => { | ||
if !IGNORES.contains(&found_revision) | ||
&& !expected_revisions.contains(found_revision) | ||
// This is from `//@ stderr-per-bitwidth` | ||
&& !(extension == "stderr" && ["32bit", "64bit"].contains(&found_revision)) | ||
{ | ||
// Found some unexpected revision-esque component that is not a known | ||
// compare-mode or expected revision. | ||
tidy_error!( | ||
bad, | ||
"found output file `{}` for unexpected revision `{}` of test `{}`", | ||
sibling.display(), | ||
found_revision, | ||
test_path.display() | ||
); | ||
} | ||
} | ||
} | ||
} | ||
}); | ||
} | ||
|
||
fn filter(path: &Path) -> bool { | ||
filter_dirs(path) // ignore certain dirs | ||
|| (path.file_name().is_some_and(|name| name == "auxiliary")) // ignore auxiliary folder | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters