mirror of
https://github.com/rust-lang/rust-clippy
synced 2024-11-10 07:04:18 +00:00
Port clippy away from compiletest to ui_test
This commit is contained in:
parent
78e36d9f53
commit
514b6d04bb
14 changed files with 208 additions and 392 deletions
|
@ -1,5 +1,7 @@
|
|||
[alias]
|
||||
uitest = "test --test compile-test"
|
||||
uibless = "test --test compile-test -- -- --bless"
|
||||
bless = "test -- -- --bless"
|
||||
dev = "run --package clippy_dev --bin clippy_dev --manifest-path clippy_dev/Cargo.toml --"
|
||||
lintcheck = "run --package lintcheck --bin lintcheck --manifest-path lintcheck/Cargo.toml -- "
|
||||
collect-metadata = "test --test dogfood --features internal -- run_metadata_collection_lint --ignored"
|
||||
|
|
19
Cargo.toml
19
Cargo.toml
|
@ -27,27 +27,14 @@ tempfile = { version = "3.2", optional = true }
|
|||
termize = "0.1"
|
||||
|
||||
[dev-dependencies]
|
||||
compiletest_rs = { version = "0.10", features = ["tmp"] }
|
||||
ui_test = "0.11.5"
|
||||
tester = "0.9"
|
||||
regex = "1.5"
|
||||
toml = "0.7.3"
|
||||
walkdir = "2.3"
|
||||
# This is used by the `collect-metadata` alias.
|
||||
filetime = "0.2"
|
||||
|
||||
# UI test dependencies
|
||||
clap = { version = "4.1.4", features = ["derive"] }
|
||||
clippy_utils = { path = "clippy_utils" }
|
||||
derive-new = "0.5"
|
||||
if_chain = "1.0"
|
||||
itertools = "0.10.1"
|
||||
quote = "1.0"
|
||||
serde = { version = "1.0.125", features = ["derive"] }
|
||||
syn = { version = "2.0", features = ["full"] }
|
||||
futures = "0.3"
|
||||
parking_lot = "0.12"
|
||||
tokio = { version = "1", features = ["io-util"] }
|
||||
rustc-semver = "1.1"
|
||||
|
||||
[build-dependencies]
|
||||
rustc_tools_util = "0.3.0"
|
||||
|
@ -60,3 +47,7 @@ internal = ["clippy_lints/internal", "tempfile"]
|
|||
[package.metadata.rust-analyzer]
|
||||
# This package uses #[feature(rustc_private)]
|
||||
rustc_private = true
|
||||
|
||||
[[test]]
|
||||
name = "compile-test"
|
||||
harness = false
|
||||
|
|
|
@ -122,20 +122,17 @@ fn main() {
|
|||
}
|
||||
```
|
||||
|
||||
Now we can run the test with `TESTNAME=foo_functions cargo uitest`, currently
|
||||
Now we can run the test with `TESTNAME=foo_functions cargo uibless`, currently
|
||||
this test is meaningless though.
|
||||
|
||||
While we are working on implementing our lint, we can keep running the UI test.
|
||||
That allows us to check if the output is turning into what we want.
|
||||
That allows us to check if the output is turning into what we want by checking the
|
||||
`.stderr` file that gets updated on every test run.
|
||||
|
||||
Once we are satisfied with the output, we need to run `cargo dev bless` to
|
||||
update the `.stderr` file for our lint. Please note that, we should run
|
||||
`TESTNAME=foo_functions cargo uitest` every time before running `cargo dev
|
||||
bless`. Running `TESTNAME=foo_functions cargo uitest` should pass then. When we
|
||||
Running `TESTNAME=foo_functions cargo uitest` should pass on its own. When we
|
||||
commit our lint, we need to commit the generated `.stderr` files, too. In
|
||||
general, you should only commit files changed by `cargo dev bless` for the
|
||||
specific lint you are creating/editing. Note that if the generated files are
|
||||
empty, they should be removed.
|
||||
general, you should only commit files changed by `cargo bless` for the
|
||||
specific lint you are creating/editing.
|
||||
|
||||
> _Note:_ you can run multiple test files by specifying a comma separated list:
|
||||
> `TESTNAME=foo_functions,test2,test3`.
|
||||
|
@ -169,7 +166,7 @@ additionally run [rustfix] for that test. Rustfix will apply the suggestions
|
|||
from the lint to the code of the test file and compare that to the contents of a
|
||||
`.fixed` file.
|
||||
|
||||
Use `cargo dev bless` to automatically generate the `.fixed` file after running
|
||||
Use `cargo bless` to automatically generate the `.fixed` file while running
|
||||
the tests.
|
||||
|
||||
[rustfix]: https://github.com/rust-lang/rustfix
|
||||
|
|
|
@ -66,7 +66,7 @@ If the output of a [UI test] differs from the expected output, you can update
|
|||
the reference file with:
|
||||
|
||||
```bash
|
||||
cargo dev bless
|
||||
cargo bless
|
||||
```
|
||||
|
||||
For example, this is necessary if you fix a typo in an error message of a lint,
|
||||
|
|
|
@ -1,60 +0,0 @@
|
|||
//! `bless` updates the reference files in the repo with changed output files
|
||||
//! from the last test run.
|
||||
|
||||
use crate::cargo_clippy_path;
|
||||
use std::ffi::OsStr;
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::LazyLock;
|
||||
use walkdir::{DirEntry, WalkDir};
|
||||
|
||||
static CLIPPY_BUILD_TIME: LazyLock<Option<std::time::SystemTime>> =
|
||||
LazyLock::new(|| cargo_clippy_path().metadata().ok()?.modified().ok());
|
||||
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the path to a test file is broken
|
||||
pub fn bless(ignore_timestamp: bool) {
|
||||
let extensions = ["stdout", "stderr", "fixed"].map(OsStr::new);
|
||||
|
||||
WalkDir::new(build_dir())
|
||||
.into_iter()
|
||||
.map(Result::unwrap)
|
||||
.filter(|entry| entry.path().extension().map_or(false, |ext| extensions.contains(&ext)))
|
||||
.for_each(|entry| update_reference_file(&entry, ignore_timestamp));
|
||||
}
|
||||
|
||||
fn update_reference_file(test_output_entry: &DirEntry, ignore_timestamp: bool) {
|
||||
let test_output_path = test_output_entry.path();
|
||||
|
||||
let reference_file_name = test_output_entry.file_name().to_str().unwrap().replace(".stage-id", "");
|
||||
let reference_file_path = Path::new("tests")
|
||||
.join(test_output_path.strip_prefix(build_dir()).unwrap())
|
||||
.with_file_name(reference_file_name);
|
||||
|
||||
// If the test output was not updated since the last clippy build, it may be outdated
|
||||
if !ignore_timestamp && !updated_since_clippy_build(test_output_entry).unwrap_or(true) {
|
||||
return;
|
||||
}
|
||||
|
||||
let test_output_file = fs::read(test_output_path).expect("Unable to read test output file");
|
||||
let reference_file = fs::read(&reference_file_path).unwrap_or_default();
|
||||
|
||||
if test_output_file != reference_file {
|
||||
// If a test run caused an output file to change, update the reference file
|
||||
println!("updating {}", reference_file_path.display());
|
||||
fs::copy(test_output_path, &reference_file_path).expect("Could not update reference file");
|
||||
}
|
||||
}
|
||||
|
||||
fn updated_since_clippy_build(entry: &DirEntry) -> Option<bool> {
|
||||
let clippy_build_time = (*CLIPPY_BUILD_TIME)?;
|
||||
let modified = entry.metadata().ok()?.modified().ok()?;
|
||||
Some(modified >= clippy_build_time)
|
||||
}
|
||||
|
||||
fn build_dir() -> PathBuf {
|
||||
let mut path = std::env::current_exe().unwrap();
|
||||
path.set_file_name("test");
|
||||
path
|
||||
}
|
|
@ -14,7 +14,6 @@ use std::io;
|
|||
use std::path::PathBuf;
|
||||
use std::process::{self, ExitStatus};
|
||||
|
||||
pub mod bless;
|
||||
pub mod dogfood;
|
||||
pub mod fmt;
|
||||
pub mod lint;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#![warn(rust_2018_idioms, unused_lifetimes)]
|
||||
|
||||
use clap::{Arg, ArgAction, ArgMatches, Command};
|
||||
use clippy_dev::{bless, dogfood, fmt, lint, new_lint, serve, setup, update_lints};
|
||||
use clippy_dev::{dogfood, fmt, lint, new_lint, serve, setup, update_lints};
|
||||
use indoc::indoc;
|
||||
use std::convert::Infallible;
|
||||
|
||||
|
@ -11,8 +11,8 @@ fn main() {
|
|||
let matches = get_clap_config();
|
||||
|
||||
match matches.subcommand() {
|
||||
Some(("bless", matches)) => {
|
||||
bless::bless(matches.get_flag("ignore-timestamp"));
|
||||
Some(("bless", _)) => {
|
||||
eprintln!("use `cargo bless` to automatically replace `.stderr` and `.fixed` files as tests are being run");
|
||||
},
|
||||
Some(("dogfood", matches)) => {
|
||||
dogfood::dogfood(
|
||||
|
|
|
@ -96,7 +96,7 @@ fn create_test(lint: &LintData<'_>) -> io::Result<()> {
|
|||
|
||||
path.push("src");
|
||||
fs::create_dir(&path)?;
|
||||
let header = format!("// compile-flags: --crate-name={lint_name}");
|
||||
let header = format!("//@compile-flags: --crate-name={lint_name}");
|
||||
write_file(path.join("main.rs"), get_test_file_contents(lint_name, Some(&header)))?;
|
||||
|
||||
Ok(())
|
||||
|
|
25
clippy_test_deps/Cargo.toml
Normal file
25
clippy_test_deps/Cargo.toml
Normal file
|
@ -0,0 +1,25 @@
|
|||
[package]
|
||||
name = "clippy_test_deps"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "4.1.4", features = ["derive"] }
|
||||
clippy_utils = { path = "../clippy_utils" }
|
||||
derive-new = "0.5"
|
||||
if_chain = "1.0"
|
||||
itertools = "0.10.1"
|
||||
quote = "1.0"
|
||||
serde = { version = "1.0.125", features = ["derive"] }
|
||||
syn = { version = "2.0", features = ["full"] }
|
||||
futures = "0.3"
|
||||
parking_lot = "0.12"
|
||||
tokio = { version = "1", features = ["io-util"] }
|
||||
rustc-semver = "1.1"
|
||||
regex = "1.5"
|
||||
clippy_lints = { path = "../clippy_lints" }
|
||||
|
||||
[features]
|
||||
internal = ["clippy_lints/internal"]
|
14
clippy_test_deps/src/lib.rs
Normal file
14
clippy_test_deps/src/lib.rs
Normal file
|
@ -0,0 +1,14 @@
|
|||
pub fn add(left: usize, right: usize) -> usize {
|
||||
left + right
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn it_works() {
|
||||
let result = add(2, 2);
|
||||
assert_eq!(result, 4);
|
||||
}
|
||||
}
|
|
@ -2397,7 +2397,7 @@ fn with_test_item_names(tcx: TyCtxt<'_>, module: LocalDefId, f: impl Fn(&[Symbol
|
|||
|
||||
/// Checks if the function containing the given `HirId` is a `#[test]` function
|
||||
///
|
||||
/// Note: Add `// compile-flags: --test` to UI tests with a `#[test]` function
|
||||
/// Note: Add `//@compile-flags: --test` to UI tests with a `#[test]` function
|
||||
pub fn is_in_test_function(tcx: TyCtxt<'_>, id: hir::HirId) -> bool {
|
||||
with_test_item_names(tcx, tcx.parent_module(id), |names| {
|
||||
tcx.hir()
|
||||
|
@ -2419,7 +2419,7 @@ pub fn is_in_test_function(tcx: TyCtxt<'_>, id: hir::HirId) -> bool {
|
|||
|
||||
/// Checks if the item containing the given `HirId` has `#[cfg(test)]` attribute applied
|
||||
///
|
||||
/// Note: Add `// compile-flags: --test` to UI tests with a `#[cfg(test)]` function
|
||||
/// Note: Add `//@compile-flags: --test` to UI tests with a `#[cfg(test)]` function
|
||||
pub fn is_in_cfg_test(tcx: TyCtxt<'_>, id: hir::HirId) -> bool {
|
||||
fn is_cfg_test(attr: &Attribute) -> bool {
|
||||
if attr.has_name(sym::cfg)
|
||||
|
@ -2441,7 +2441,7 @@ pub fn is_in_cfg_test(tcx: TyCtxt<'_>, id: hir::HirId) -> bool {
|
|||
/// Checks whether item either has `test` attribute applied, or
|
||||
/// is a module with `test` in its name.
|
||||
///
|
||||
/// Note: Add `// compile-flags: --test` to UI tests with a `#[test]` function
|
||||
/// Note: Add `//@compile-flags: --test` to UI tests with a `#[test]` function
|
||||
pub fn is_test_module_or_function(tcx: TyCtxt<'_>, item: &Item<'_>) -> bool {
|
||||
is_in_test_function(tcx, item.hir_id())
|
||||
|| matches!(item.kind, ItemKind::Mod(..))
|
||||
|
|
|
@ -4,16 +4,14 @@
|
|||
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
|
||||
#![warn(rust_2018_idioms, unused_lifetimes)]
|
||||
|
||||
use compiletest_rs as compiletest;
|
||||
use compiletest_rs::common::Mode as TestMode;
|
||||
use compiletest::{status_emitter, CommandBuilder};
|
||||
use ui_test as compiletest;
|
||||
use ui_test::Mode as TestMode;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::env::{self, remove_var, set_var, var_os};
|
||||
use std::ffi::{OsStr, OsString};
|
||||
use std::fs;
|
||||
use std::io;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::LazyLock;
|
||||
use test_utils::IS_RUSTC_TEST_SUITE;
|
||||
|
||||
mod test_utils;
|
||||
|
@ -21,143 +19,41 @@ mod test_utils;
|
|||
// whether to run internal tests or not
|
||||
const RUN_INTERNAL_TESTS: bool = cfg!(feature = "internal");
|
||||
|
||||
/// All crates used in UI tests are listed here
|
||||
static TEST_DEPENDENCIES: &[&str] = &[
|
||||
"clippy_lints",
|
||||
"clippy_utils",
|
||||
"derive_new",
|
||||
"futures",
|
||||
"if_chain",
|
||||
"itertools",
|
||||
"quote",
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"syn",
|
||||
"tokio",
|
||||
"parking_lot",
|
||||
"rustc_semver",
|
||||
];
|
||||
|
||||
// Test dependencies may need an `extern crate` here to ensure that they show up
|
||||
// in the depinfo file (otherwise cargo thinks they are unused)
|
||||
#[allow(unused_extern_crates)]
|
||||
extern crate clippy_lints;
|
||||
#[allow(unused_extern_crates)]
|
||||
extern crate clippy_utils;
|
||||
#[allow(unused_extern_crates)]
|
||||
extern crate derive_new;
|
||||
#[allow(unused_extern_crates)]
|
||||
extern crate futures;
|
||||
#[allow(unused_extern_crates)]
|
||||
extern crate if_chain;
|
||||
#[allow(unused_extern_crates)]
|
||||
extern crate itertools;
|
||||
#[allow(unused_extern_crates)]
|
||||
extern crate parking_lot;
|
||||
#[allow(unused_extern_crates)]
|
||||
extern crate quote;
|
||||
#[allow(unused_extern_crates)]
|
||||
extern crate rustc_semver;
|
||||
#[allow(unused_extern_crates)]
|
||||
extern crate syn;
|
||||
#[allow(unused_extern_crates)]
|
||||
extern crate tokio;
|
||||
|
||||
/// Produces a string with an `--extern` flag for all UI test crate
|
||||
/// dependencies.
|
||||
///
|
||||
/// The dependency files are located by parsing the depinfo file for this test
|
||||
/// module. This assumes the `-Z binary-dep-depinfo` flag is enabled. All test
|
||||
/// dependencies must be added to Cargo.toml at the project root. Test
|
||||
/// dependencies that are not *directly* used by this test module require an
|
||||
/// `extern crate` declaration.
|
||||
static EXTERN_FLAGS: LazyLock<String> = LazyLock::new(|| {
|
||||
let current_exe_depinfo = {
|
||||
let mut path = env::current_exe().unwrap();
|
||||
path.set_extension("d");
|
||||
fs::read_to_string(path).unwrap()
|
||||
};
|
||||
let mut crates: HashMap<&str, &str> = HashMap::with_capacity(TEST_DEPENDENCIES.len());
|
||||
for line in current_exe_depinfo.lines() {
|
||||
// each dependency is expected to have a Makefile rule like `/path/to/crate-hash.rlib:`
|
||||
let parse_name_path = || {
|
||||
if line.starts_with(char::is_whitespace) {
|
||||
return None;
|
||||
}
|
||||
let path_str = line.strip_suffix(':')?;
|
||||
let path = Path::new(path_str);
|
||||
if !matches!(path.extension()?.to_str()?, "rlib" | "so" | "dylib" | "dll") {
|
||||
return None;
|
||||
}
|
||||
let (name, _hash) = path.file_stem()?.to_str()?.rsplit_once('-')?;
|
||||
// the "lib" prefix is not present for dll files
|
||||
let name = name.strip_prefix("lib").unwrap_or(name);
|
||||
Some((name, path_str))
|
||||
};
|
||||
if let Some((name, path)) = parse_name_path() {
|
||||
if TEST_DEPENDENCIES.contains(&name) {
|
||||
// A dependency may be listed twice if it is available in sysroot,
|
||||
// and the sysroot dependencies are listed first. As of the writing,
|
||||
// this only seems to apply to if_chain.
|
||||
crates.insert(name, path);
|
||||
}
|
||||
}
|
||||
}
|
||||
let not_found: Vec<&str> = TEST_DEPENDENCIES
|
||||
.iter()
|
||||
.copied()
|
||||
.filter(|n| !crates.contains_key(n))
|
||||
.collect();
|
||||
assert!(
|
||||
not_found.is_empty(),
|
||||
"dependencies not found in depinfo: {not_found:?}\n\
|
||||
help: Make sure the `-Z binary-dep-depinfo` rust flag is enabled\n\
|
||||
help: Try adding to dev-dependencies in Cargo.toml\n\
|
||||
help: Be sure to also add `extern crate ...;` to tests/compile-test.rs",
|
||||
);
|
||||
crates
|
||||
.into_iter()
|
||||
.map(|(name, path)| format!(" --extern {name}={path}"))
|
||||
.collect()
|
||||
});
|
||||
|
||||
fn base_config(test_dir: &str) -> compiletest::Config {
|
||||
let mut config = compiletest::Config {
|
||||
edition: Some("2021".into()),
|
||||
mode: TestMode::Ui,
|
||||
strict_headers: true,
|
||||
..Default::default()
|
||||
mode: TestMode::Yolo,
|
||||
stderr_filters: vec![],
|
||||
stdout_filters: vec![],
|
||||
output_conflict_handling: if std::env::args().any(|arg| arg == "--bless") {
|
||||
compiletest::OutputConflictHandling::Bless
|
||||
} else {
|
||||
compiletest::OutputConflictHandling::Error("cargo test -- -- --bless".into())
|
||||
},
|
||||
dependencies_crate_manifest_path: Some("clippy_test_deps/Cargo.toml".into()),
|
||||
target: None,
|
||||
out_dir: "target/ui_test".into(),
|
||||
..compiletest::Config::rustc(Path::new("tests").join(test_dir))
|
||||
};
|
||||
|
||||
if let Ok(filters) = env::var("TESTNAME") {
|
||||
config.filters = filters.split(',').map(ToString::to_string).collect();
|
||||
}
|
||||
|
||||
if let Some(path) = option_env!("RUSTC_LIB_PATH") {
|
||||
let path = PathBuf::from(path);
|
||||
config.run_lib_path = path.clone();
|
||||
config.compile_lib_path = path;
|
||||
if let Some(_path) = option_env!("RUSTC_LIB_PATH") {
|
||||
//let path = PathBuf::from(path);
|
||||
//config.run_lib_path = path.clone();
|
||||
//config.compile_lib_path = path;
|
||||
}
|
||||
let current_exe_path = env::current_exe().unwrap();
|
||||
let deps_path = current_exe_path.parent().unwrap();
|
||||
let profile_path = deps_path.parent().unwrap();
|
||||
|
||||
// Using `-L dependency={}` enforces that external dependencies are added with `--extern`.
|
||||
// This is valuable because a) it allows us to monitor what external dependencies are used
|
||||
// and b) it ensures that conflicting rlibs are resolved properly.
|
||||
let host_libs = option_env!("HOST_LIBS")
|
||||
.map(|p| format!(" -L dependency={}", Path::new(p).join("deps").display()))
|
||||
.unwrap_or_default();
|
||||
config.target_rustcflags = Some(format!(
|
||||
"--emit=metadata -Dwarnings -Zui-testing -L dependency={}{host_libs}{}",
|
||||
deps_path.display(),
|
||||
&*EXTERN_FLAGS,
|
||||
));
|
||||
config.program.args.push("--emit=metadata".into());
|
||||
config.program.args.push("-Aunused".into());
|
||||
config.program.args.push("-Zui-testing".into());
|
||||
config.program.args.push("-Dwarnings".into());
|
||||
|
||||
config.src_base = Path::new("tests").join(test_dir);
|
||||
config.build_base = profile_path.join("test").join(test_dir);
|
||||
config.rustc_path = profile_path.join(if cfg!(windows) {
|
||||
// Normalize away slashes in windows paths.
|
||||
config.stderr_filter(r#"\\"#, "/");
|
||||
|
||||
//config.build_base = profile_path.join("test").join(test_dir);
|
||||
config.program.program = profile_path.join(if cfg!(windows) {
|
||||
"clippy-driver.exe"
|
||||
} else {
|
||||
"clippy-driver"
|
||||
|
@ -165,9 +61,23 @@ fn base_config(test_dir: &str) -> compiletest::Config {
|
|||
config
|
||||
}
|
||||
|
||||
fn test_filter() -> Box<dyn Sync + Fn(&Path) -> bool> {
|
||||
if let Ok(filters) = env::var("TESTNAME") {
|
||||
let filters: Vec<_> = filters.split(',').map(ToString::to_string).collect();
|
||||
Box::new(move |path| {
|
||||
filters.is_empty()
|
||||
|| filters
|
||||
.iter()
|
||||
.any(|f| path.file_stem().map_or(false, |stem| stem == f.as_str()))
|
||||
})
|
||||
} else {
|
||||
Box::new(|_| true)
|
||||
}
|
||||
}
|
||||
|
||||
fn run_ui() {
|
||||
let mut config = base_config("ui");
|
||||
config.rustfix_coverage = true;
|
||||
let config = base_config("ui");
|
||||
//config.rustfix_coverage = true;
|
||||
// use tests/clippy.toml
|
||||
let _g = VarGuard::set("CARGO_MANIFEST_DIR", fs::canonicalize("tests").unwrap());
|
||||
let _threads = VarGuard::set(
|
||||
|
@ -179,7 +89,19 @@ fn run_ui() {
|
|||
.to_string()
|
||||
}),
|
||||
);
|
||||
compiletest::run_tests(&config);
|
||||
eprintln!(" Compiler: {}", config.program.display());
|
||||
|
||||
let name = config.root_dir.display().to_string();
|
||||
|
||||
let test_filter = test_filter();
|
||||
|
||||
compiletest::run_tests_generic(
|
||||
config,
|
||||
move |path| compiletest::default_file_filter(path) && test_filter(path),
|
||||
compiletest::default_per_file_config,
|
||||
(status_emitter::Text, status_emitter::Gha::<true> { name }),
|
||||
)
|
||||
.unwrap();
|
||||
check_rustfix_coverage();
|
||||
}
|
||||
|
||||
|
@ -188,177 +110,114 @@ fn run_internal_tests() {
|
|||
if !RUN_INTERNAL_TESTS {
|
||||
return;
|
||||
}
|
||||
let config = base_config("ui-internal");
|
||||
compiletest::run_tests(&config);
|
||||
let mut config = base_config("ui-internal");
|
||||
config.dependency_builder.args.push("--features".into());
|
||||
config.dependency_builder.args.push("internal".into());
|
||||
compiletest::run_tests(config).unwrap();
|
||||
}
|
||||
|
||||
fn run_ui_toml() {
|
||||
fn run_tests(config: &compiletest::Config, mut tests: Vec<tester::TestDescAndFn>) -> Result<bool, io::Error> {
|
||||
let mut result = true;
|
||||
let opts = compiletest::test_opts(config);
|
||||
for dir in fs::read_dir(&config.src_base)? {
|
||||
let dir = dir?;
|
||||
if !dir.file_type()?.is_dir() {
|
||||
continue;
|
||||
}
|
||||
let dir_path = dir.path();
|
||||
let _g = VarGuard::set("CARGO_MANIFEST_DIR", &dir_path);
|
||||
for file in fs::read_dir(&dir_path)? {
|
||||
let file = file?;
|
||||
let file_path = file.path();
|
||||
if file.file_type()?.is_dir() {
|
||||
continue;
|
||||
}
|
||||
if file_path.extension() != Some(OsStr::new("rs")) {
|
||||
continue;
|
||||
}
|
||||
let paths = compiletest::common::TestPaths {
|
||||
file: file_path,
|
||||
base: config.src_base.clone(),
|
||||
relative_dir: dir_path.file_name().unwrap().into(),
|
||||
};
|
||||
let test_name = compiletest::make_test_name(config, &paths);
|
||||
let index = tests
|
||||
.iter()
|
||||
.position(|test| test.desc.name == test_name)
|
||||
.expect("The test should be in there");
|
||||
result &= tester::run_tests_console(&opts, vec![tests.swap_remove(index)])?;
|
||||
}
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
let mut config = base_config("ui-toml");
|
||||
config.src_base = config.src_base.canonicalize().unwrap();
|
||||
|
||||
let tests = compiletest::make_tests(&config);
|
||||
config.stderr_filter(
|
||||
®ex::escape(
|
||||
&std::path::Path::new(file!())
|
||||
.parent()
|
||||
.unwrap()
|
||||
.canonicalize()
|
||||
.unwrap()
|
||||
.parent()
|
||||
.unwrap()
|
||||
.display()
|
||||
.to_string()
|
||||
.replace('\\', "/"),
|
||||
),
|
||||
"$$DIR",
|
||||
);
|
||||
|
||||
let res = run_tests(&config, tests);
|
||||
match res {
|
||||
Ok(true) => {},
|
||||
Ok(false) => panic!("Some tests failed"),
|
||||
Err(e) => {
|
||||
panic!("I/O failure during tests: {e:?}");
|
||||
let name = config.root_dir.display().to_string();
|
||||
|
||||
let test_filter = test_filter();
|
||||
|
||||
ui_test::run_tests_generic(
|
||||
config,
|
||||
|path| test_filter(path) && path.extension() == Some("rs".as_ref()),
|
||||
|config, path| {
|
||||
let mut config = config.clone();
|
||||
config
|
||||
.program
|
||||
.envs
|
||||
.push(("CLIPPY_CONF_DIR".into(), Some(path.parent().unwrap().into())));
|
||||
Some(config)
|
||||
},
|
||||
}
|
||||
(status_emitter::Text, status_emitter::Gha::<true> { name }),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn run_ui_cargo() {
|
||||
fn run_tests(
|
||||
config: &compiletest::Config,
|
||||
filters: &[String],
|
||||
mut tests: Vec<tester::TestDescAndFn>,
|
||||
) -> Result<bool, io::Error> {
|
||||
let mut result = true;
|
||||
let opts = compiletest::test_opts(config);
|
||||
|
||||
for dir in fs::read_dir(&config.src_base)? {
|
||||
let dir = dir?;
|
||||
if !dir.file_type()?.is_dir() {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Use the filter if provided
|
||||
let dir_path = dir.path();
|
||||
for filter in filters {
|
||||
if !dir_path.ends_with(filter) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
for case in fs::read_dir(&dir_path)? {
|
||||
let case = case?;
|
||||
if !case.file_type()?.is_dir() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let src_path = case.path().join("src");
|
||||
|
||||
// When switching between branches, if the previous branch had a test
|
||||
// that the current branch does not have, the directory is not removed
|
||||
// because an ignored Cargo.lock file exists.
|
||||
if !src_path.exists() {
|
||||
continue;
|
||||
}
|
||||
|
||||
env::set_current_dir(&src_path)?;
|
||||
|
||||
let cargo_toml_path = case.path().join("Cargo.toml");
|
||||
let cargo_content = fs::read(cargo_toml_path)?;
|
||||
let cargo_parsed: toml::Value = toml::from_str(
|
||||
std::str::from_utf8(&cargo_content).expect("`Cargo.toml` is not a valid utf-8 file!"),
|
||||
)
|
||||
.expect("Can't parse `Cargo.toml`");
|
||||
|
||||
let _g = VarGuard::set("CARGO_MANIFEST_DIR", case.path());
|
||||
let _h = VarGuard::set(
|
||||
"CARGO_PKG_RUST_VERSION",
|
||||
cargo_parsed
|
||||
.get("package")
|
||||
.and_then(|p| p.get("rust-version"))
|
||||
.and_then(toml::Value::as_str)
|
||||
.unwrap_or(""),
|
||||
);
|
||||
|
||||
for file in fs::read_dir(&src_path)? {
|
||||
let file = file?;
|
||||
if file.file_type()?.is_dir() {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Search for the main file to avoid running a test for each file in the project
|
||||
let file_path = file.path();
|
||||
match file_path.file_name().and_then(OsStr::to_str) {
|
||||
Some("main.rs") => {},
|
||||
_ => continue,
|
||||
}
|
||||
let _g = VarGuard::set("CLIPPY_CONF_DIR", case.path());
|
||||
let paths = compiletest::common::TestPaths {
|
||||
file: file_path,
|
||||
base: config.src_base.clone(),
|
||||
relative_dir: src_path.strip_prefix(&config.src_base).unwrap().into(),
|
||||
};
|
||||
let test_name = compiletest::make_test_name(config, &paths);
|
||||
let index = tests
|
||||
.iter()
|
||||
.position(|test| test.desc.name == test_name)
|
||||
.expect("The test should be in there");
|
||||
result &= tester::run_tests_console(&opts, vec![tests.swap_remove(index)])?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
if IS_RUSTC_TEST_SUITE {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut config = base_config("ui-cargo");
|
||||
config.src_base = config.src_base.canonicalize().unwrap();
|
||||
config.program = CommandBuilder::cargo();
|
||||
config.program.args = vec!["clippy".into(), "--color".into(), "never".into(), "--quiet".into()];
|
||||
config
|
||||
.program
|
||||
.envs
|
||||
.push(("RUSTFLAGS".into(), Some("-Dwarnings".into())));
|
||||
// We need to do this while we still have a rustc in the `program` field.
|
||||
config.fill_host_and_target().unwrap();
|
||||
config.dependencies_crate_manifest_path = None;
|
||||
config.program.program.set_file_name(if cfg!(windows) {
|
||||
"cargo-clippy.exe"
|
||||
} else {
|
||||
"cargo-clippy"
|
||||
});
|
||||
config.edition = None;
|
||||
|
||||
let tests = compiletest::make_tests(&config);
|
||||
config.stderr_filter(
|
||||
®ex::escape(
|
||||
&std::path::Path::new(file!())
|
||||
.parent()
|
||||
.unwrap()
|
||||
.canonicalize()
|
||||
.unwrap()
|
||||
.parent()
|
||||
.unwrap()
|
||||
.display()
|
||||
.to_string()
|
||||
.replace('\\', "/"),
|
||||
),
|
||||
"$$DIR",
|
||||
);
|
||||
|
||||
let current_dir = env::current_dir().unwrap();
|
||||
let res = run_tests(&config, &config.filters, tests);
|
||||
env::set_current_dir(current_dir).unwrap();
|
||||
let name = config.root_dir.display().to_string();
|
||||
|
||||
match res {
|
||||
Ok(true) => {},
|
||||
Ok(false) => panic!("Some tests failed"),
|
||||
Err(e) => {
|
||||
panic!("I/O failure during tests: {e:?}");
|
||||
let test_filter = test_filter();
|
||||
|
||||
ui_test::run_tests_generic(
|
||||
config,
|
||||
|path| test_filter(path) && path.ends_with("Cargo.toml"),
|
||||
|config, path| {
|
||||
let mut config = config.clone();
|
||||
config.out_dir = PathBuf::from("target/ui_test_cargo/").join(path.parent().unwrap());
|
||||
Some(config)
|
||||
},
|
||||
}
|
||||
(status_emitter::Text, status_emitter::Gha::<true> { name }),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compile_test() {
|
||||
fn main() {
|
||||
set_var("CLIPPY_DISABLE_DOCS_LINKS", "true");
|
||||
run_ui();
|
||||
run_ui_toml();
|
||||
run_ui_cargo();
|
||||
run_internal_tests();
|
||||
rustfix_coverage_known_exceptions_accuracy();
|
||||
ui_cargo_toml_metadata();
|
||||
}
|
||||
|
||||
const RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS: &[&str] = &[
|
||||
|
@ -384,7 +243,6 @@ const RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS: &[&str] = &[
|
|||
"needless_for_each_unfixable.rs",
|
||||
"nonminimal_bool.rs",
|
||||
"print_literal.rs",
|
||||
"print_with_newline.rs",
|
||||
"redundant_static_lifetimes_multiple.rs",
|
||||
"ref_binding_to_reference.rs",
|
||||
"repl_uninit.rs",
|
||||
|
@ -399,7 +257,6 @@ const RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS: &[&str] = &[
|
|||
"unnecessary_lazy_eval_unfixable.rs",
|
||||
"write_literal.rs",
|
||||
"write_literal_2.rs",
|
||||
"write_with_newline.rs",
|
||||
];
|
||||
|
||||
fn check_rustfix_coverage() {
|
||||
|
@ -432,25 +289,16 @@ fn check_rustfix_coverage() {
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rustfix_coverage_known_exceptions_accuracy() {
|
||||
for filename in RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS {
|
||||
let rs_path = Path::new("tests/ui").join(filename);
|
||||
assert!(
|
||||
rs_path.exists(),
|
||||
"`{}` does not exist",
|
||||
rs_path.strip_prefix(env!("CARGO_MANIFEST_DIR")).unwrap().display()
|
||||
);
|
||||
assert!(rs_path.exists(), "`{}` does not exist", rs_path.display());
|
||||
let fixed_path = rs_path.with_extension("fixed");
|
||||
assert!(
|
||||
!fixed_path.exists(),
|
||||
"`{}` exists",
|
||||
fixed_path.strip_prefix(env!("CARGO_MANIFEST_DIR")).unwrap().display()
|
||||
);
|
||||
println!("{}", fixed_path.display());
|
||||
assert!(!fixed_path.exists(), "`{}` exists", fixed_path.display());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ui_cargo_toml_metadata() {
|
||||
let ui_cargo_path = Path::new("tests/ui-cargo");
|
||||
let cargo_common_metadata_path = ui_cargo_path.join("cargo_common_metadata");
|
||||
|
|
|
@ -41,8 +41,8 @@ fn explore_directory(dir: &Path) -> Vec<String> {
|
|||
x.path().extension().and_then(OsStr::to_str),
|
||||
y.path().extension().and_then(OsStr::to_str),
|
||||
) {
|
||||
(Some("rs"), _) => Ordering::Less,
|
||||
(_, Some("rs")) => Ordering::Greater,
|
||||
(Some("rs" | "toml"), _) => Ordering::Less,
|
||||
(_, Some("rs" | "toml")) => Ordering::Greater,
|
||||
_ => Ordering::Equal,
|
||||
}
|
||||
});
|
||||
|
@ -54,7 +54,7 @@ fn explore_directory(dir: &Path) -> Vec<String> {
|
|||
let file_prefix = path.file_prefix().unwrap().to_str().unwrap().to_string();
|
||||
if let Some(ext) = path.extension() {
|
||||
match ext.to_str().unwrap() {
|
||||
"rs" => current_file = file_prefix.clone(),
|
||||
"rs" | "toml" => current_file = file_prefix.clone(),
|
||||
"stderr" | "stdout" => {
|
||||
if file_prefix != current_file {
|
||||
missing_files.push(path.to_str().unwrap().to_string());
|
||||
|
|
|
@ -47,9 +47,9 @@
|
|||
"group": "test"
|
||||
},
|
||||
{
|
||||
"label": "cargo dev bless",
|
||||
"label": "bless ui tests",
|
||||
"type": "shell",
|
||||
"command": "cargo dev bless",
|
||||
"command": "cargo bless",
|
||||
"problemMatcher": [],
|
||||
"group": "none"
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue