mirror of
https://github.com/martinvonz/jj.git
synced 2025-05-05 23:42:50 +00:00
tests: allow multiple integration tests in check for forgotton test files
The previous implementation of `assert_no_forgotten_test_files` hard-coded the name of the `runner` integration test and required all other source files to appear in matching `mod` declarations. Thus, this approach cannot handle multiple integration tests. However, additional integration tests may be desirable - to support tests using a custom test harness (see upcoming commits) - to balance the trade-off between test run time and compile time as the test suite grows in the future. The new implementation first uses `taplo` to parse the `[[test]]` sections of the manifest to identify integration test main modules, and then searches in those for `mod` declarations. This is then compared to the list of source files in the tests directory. Like the previous implementation, the new one does not attempt to recurse into submodules or to handle directory-style modules; just like before it only treats source files without a module declaration as an error and relies on the compiler to complain about the other way around. When `taplo` is not installed, the check is skipped unless it is running in CI where we require `taplo` to be available.
This commit is contained in:
parent
69cf7b38fc
commit
8882f0016d
@ -13,6 +13,7 @@
|
||||
// limitations under the License.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::collections::HashSet;
|
||||
use std::env;
|
||||
use std::fs;
|
||||
use std::fs::OpenOptions;
|
||||
@ -20,8 +21,11 @@ use std::io::Read as _;
|
||||
use std::io::Write as _;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
use std::process::Stdio;
|
||||
use std::sync::Arc;
|
||||
|
||||
use bstr::ByteSlice as _;
|
||||
use itertools::Itertools as _;
|
||||
use jj_lib::backend;
|
||||
use jj_lib::backend::Backend;
|
||||
@ -621,21 +625,80 @@ pub fn assert_abandoned_with_parent(
|
||||
}
|
||||
|
||||
pub fn assert_no_forgotten_test_files(test_dir: &Path) {
|
||||
let runner_path = test_dir.join("runner.rs");
|
||||
let runner = fs::read_to_string(&runner_path).unwrap();
|
||||
let entries = fs::read_dir(test_dir).unwrap();
|
||||
for entry in entries {
|
||||
let path = entry.unwrap().path();
|
||||
if let Some(ext) = path.extension() {
|
||||
let name = path.file_stem().unwrap();
|
||||
if ext == "rs" && name != "runner" {
|
||||
let search = format!("mod {};", name.to_str().unwrap());
|
||||
assert!(
|
||||
runner.contains(&search),
|
||||
"missing `{search}` declaration in {}",
|
||||
runner_path.display()
|
||||
);
|
||||
}
|
||||
}
|
||||
// We require `taplo` for this check; if it's not installed, that's ok unless
|
||||
// we're running in CI.
|
||||
if Command::new("taplo")
|
||||
.arg("--version")
|
||||
.stdout(Stdio::null())
|
||||
.status()
|
||||
.is_err()
|
||||
{
|
||||
ensure_running_outside_ci("`taplo` must be in the PATH");
|
||||
eprintln!(
|
||||
"Skipping check for forgotten test files because taplo is not installed on the system"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Use taplo to find all the test executable main modules listed in `[[test]]`.
|
||||
let taplo_output = Command::new("taplo")
|
||||
.args(["get", "test[*].name", "-f", "Cargo.toml"])
|
||||
.current_dir(test_dir.parent().unwrap())
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(
|
||||
taplo_output.status.success(),
|
||||
"taplo returned with status {}: {}",
|
||||
taplo_output.status,
|
||||
taplo_output.stderr.to_str_lossy(),
|
||||
);
|
||||
let test_bin_mods = taplo_output
|
||||
.stdout
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.trim()
|
||||
.lines()
|
||||
.map(ToString::to_string)
|
||||
.collect_vec();
|
||||
|
||||
// Add to that all submodules which are declared in the main test modules via
|
||||
// `mod`.
|
||||
let mut test_mods: HashSet<_> = test_bin_mods
|
||||
.iter()
|
||||
.flat_map(|test_mod| {
|
||||
let test_mod_path = test_dir.join(test_mod).with_extension("rs");
|
||||
let test_mod_contents = fs::read_to_string(&test_mod_path).unwrap();
|
||||
test_mod_contents
|
||||
.lines()
|
||||
.map(|line| line.trim_start_matches("pub "))
|
||||
.filter_map(|line| line.strip_prefix("mod"))
|
||||
.filter_map(|line| line.strip_suffix(";"))
|
||||
.map(|line| line.trim().to_string())
|
||||
.collect_vec()
|
||||
})
|
||||
.collect();
|
||||
test_mods.extend(test_bin_mods);
|
||||
|
||||
// Gather list of Rust source files in test directory for comparison.
|
||||
let test_mod_files: HashSet<_> = fs::read_dir(test_dir)
|
||||
.unwrap()
|
||||
.map(|entry| entry.unwrap().path())
|
||||
.filter(|path| path.extension().is_some_and(|ext| ext == "rs"))
|
||||
.filter_map(|path| {
|
||||
path.file_stem()
|
||||
.and_then(|stem| stem.to_os_string().into_string().ok())
|
||||
})
|
||||
.collect();
|
||||
|
||||
assert!(
|
||||
test_mod_files.is_subset(&test_mods),
|
||||
"the following test source files are not declared as integration tests nor included as \
|
||||
submodules of one: {}",
|
||||
test_mod_files
|
||||
.difference(&test_mods)
|
||||
.map(|mod_stem| format!("{mod_stem}.rs"))
|
||||
.join(", "),
|
||||
);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user