]> git.lizzy.rs Git - rust.git/blob - clippy_dev/src/bless.rs
Auto merge of #7316 - lengyijun:rc_mutex, r=llogiq
[rust.git] / clippy_dev / src / bless.rs
1 //! `bless` updates the reference files in the repo with changed output files
2 //! from the last test run.
3
4 use std::env;
5 use std::ffi::OsStr;
6 use std::fs;
7 use std::lazy::SyncLazy;
8 use std::path::{Path, PathBuf};
9 use walkdir::WalkDir;
10
11 use crate::clippy_project_root;
12
13 // NOTE: this is duplicated with tests/cargo/mod.rs What to do?
14 pub static CARGO_TARGET_DIR: SyncLazy<PathBuf> = SyncLazy::new(|| match env::var_os("CARGO_TARGET_DIR") {
15     Some(v) => v.into(),
16     None => env::current_dir().unwrap().join("target"),
17 });
18
19 static CLIPPY_BUILD_TIME: SyncLazy<Option<std::time::SystemTime>> = SyncLazy::new(|| {
20     let profile = env::var("PROFILE").unwrap_or_else(|_| "debug".to_string());
21     let mut path = PathBuf::from(&**CARGO_TARGET_DIR);
22     path.push(profile);
23     path.push("cargo-clippy");
24     fs::metadata(path).ok()?.modified().ok()
25 });
26
27 /// # Panics
28 ///
29 /// Panics if the path to a test file is broken
30 pub fn bless(ignore_timestamp: bool) {
31     let test_suite_dirs = [
32         clippy_project_root().join("tests").join("ui"),
33         clippy_project_root().join("tests").join("ui-internal"),
34         clippy_project_root().join("tests").join("ui-toml"),
35         clippy_project_root().join("tests").join("ui-cargo"),
36     ];
37     for test_suite_dir in &test_suite_dirs {
38         WalkDir::new(test_suite_dir)
39             .into_iter()
40             .filter_map(Result::ok)
41             .filter(|f| f.path().extension() == Some(OsStr::new("rs")))
42             .for_each(|f| {
43                 let test_name = f.path().strip_prefix(test_suite_dir).unwrap();
44                 for &ext in &["stdout", "stderr", "fixed"] {
45                     let test_name_ext = format!("stage-id.{}", ext);
46                     update_reference_file(
47                         f.path().with_extension(ext),
48                         test_name.with_extension(test_name_ext),
49                         ignore_timestamp,
50                     );
51                 }
52             });
53     }
54 }
55
56 fn update_reference_file(reference_file_path: PathBuf, test_name: PathBuf, ignore_timestamp: bool) {
57     let test_output_path = build_dir().join(test_name);
58     let relative_reference_file_path = reference_file_path.strip_prefix(clippy_project_root()).unwrap();
59
60     // If compiletest did not write any changes during the test run,
61     // we don't have to update anything
62     if !test_output_path.exists() {
63         return;
64     }
65
66     // If the test output was not updated since the last clippy build, it may be outdated
67     if !ignore_timestamp && !updated_since_clippy_build(&test_output_path).unwrap_or(true) {
68         return;
69     }
70
71     let test_output_file = fs::read(&test_output_path).expect("Unable to read test output file");
72     let reference_file = fs::read(&reference_file_path).unwrap_or_default();
73
74     if test_output_file != reference_file {
75         // If a test run caused an output file to change, update the reference file
76         println!("updating {}", &relative_reference_file_path.display());
77         fs::copy(test_output_path, &reference_file_path).expect("Could not update reference file");
78
79         // We need to re-read the file now because it was potentially updated from copying
80         let reference_file = fs::read(&reference_file_path).unwrap_or_default();
81
82         if reference_file.is_empty() {
83             // If we copied over an empty output file, we remove the now empty reference file
84             println!("removing {}", &relative_reference_file_path.display());
85             fs::remove_file(reference_file_path).expect("Could not remove reference file");
86         }
87     }
88 }
89
90 fn updated_since_clippy_build(path: &Path) -> Option<bool> {
91     let clippy_build_time = (*CLIPPY_BUILD_TIME)?;
92     let modified = fs::metadata(path).ok()?.modified().ok()?;
93     Some(modified >= clippy_build_time)
94 }
95
96 fn build_dir() -> PathBuf {
97     let profile = env::var("PROFILE").unwrap_or_else(|_| "debug".to_string());
98     let mut path = PathBuf::new();
99     path.push(CARGO_TARGET_DIR.clone());
100     path.push(profile);
101     path.push("test_build_base");
102     path
103 }