]> git.lizzy.rs Git - rust.git/blob - tests/dogfood.rs
Handle 'implementation safety' headers as well
[rust.git] / tests / dogfood.rs
1 //! This test is a part of quality control and makes clippy eat what it produces. Awesome lints and
2 //! long error messages
3 //!
4 //! See [Eating your own dog food](https://en.wikipedia.org/wiki/Eating_your_own_dog_food) for context
5
6 // Dogfood cannot run on Windows
7 #![cfg(not(windows))]
8 #![feature(once_cell)]
9 #![cfg_attr(feature = "deny-warnings", deny(warnings))]
10 #![warn(rust_2018_idioms, unused_lifetimes)]
11
12 use std::lazy::SyncLazy;
13 use std::path::PathBuf;
14 use std::process::Command;
15
16 mod cargo;
17
18 static CLIPPY_PATH: SyncLazy<PathBuf> = SyncLazy::new(|| {
19     let mut path = std::env::current_exe().unwrap();
20     assert!(path.pop()); // deps
21     path.set_file_name("cargo-clippy");
22     path
23 });
24
25 #[test]
26 fn dogfood_clippy() {
27     // run clippy on itself and fail the test if lint warnings are reported
28     if cargo::is_rustc_test_suite() {
29         return;
30     }
31     let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
32
33     let mut command = Command::new(&*CLIPPY_PATH);
34     command
35         .current_dir(root_dir)
36         .env("CARGO_INCREMENTAL", "0")
37         .arg("clippy")
38         .arg("--all-targets")
39         .arg("--all-features")
40         .arg("--")
41         .args(&["-D", "clippy::all"])
42         .args(&["-D", "clippy::pedantic"])
43         .arg("-Cdebuginfo=0"); // disable debuginfo to generate less data in the target dir
44
45     // internal lints only exist if we build with the internal-lints feature
46     if cfg!(feature = "internal-lints") {
47         command.args(&["-D", "clippy::internal"]);
48     }
49
50     let output = command.output().unwrap();
51
52     println!("status: {}", output.status);
53     println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
54     println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
55
56     assert!(output.status.success());
57 }
58
59 fn test_no_deps_ignores_path_deps_in_workspaces() {
60     if cargo::is_rustc_test_suite() {
61         return;
62     }
63     let root = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
64     let target_dir = root.join("target").join("dogfood");
65     let cwd = root.join("clippy_workspace_tests");
66
67     // Make sure we start with a clean state
68     Command::new("cargo")
69         .current_dir(&cwd)
70         .env("CARGO_TARGET_DIR", &target_dir)
71         .arg("clean")
72         .args(&["-p", "subcrate"])
73         .args(&["-p", "path_dep"])
74         .output()
75         .unwrap();
76
77     // `path_dep` is a path dependency of `subcrate` that would trigger a denied lint.
78     // Make sure that with the `--no-deps` argument Clippy does not run on `path_dep`.
79     let output = Command::new(&*CLIPPY_PATH)
80         .current_dir(&cwd)
81         .env("CARGO_INCREMENTAL", "0")
82         .arg("clippy")
83         .args(&["-p", "subcrate"])
84         .arg("--no-deps")
85         .arg("--")
86         .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir
87         .args(&["--cfg", r#"feature="primary_package_test""#])
88         .output()
89         .unwrap();
90     println!("status: {}", output.status);
91     println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
92     println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
93
94     assert!(output.status.success());
95
96     let lint_path_dep = || {
97         // Test that without the `--no-deps` argument, `path_dep` is linted.
98         let output = Command::new(&*CLIPPY_PATH)
99             .current_dir(&cwd)
100             .env("CARGO_INCREMENTAL", "0")
101             .arg("clippy")
102             .args(&["-p", "subcrate"])
103             .arg("--")
104             .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir
105             .args(&["--cfg", r#"feature="primary_package_test""#])
106             .output()
107             .unwrap();
108         println!("status: {}", output.status);
109         println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
110         println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
111
112         assert!(!output.status.success());
113         assert!(
114             String::from_utf8(output.stderr)
115                 .unwrap()
116                 .contains("error: empty `loop {}` wastes CPU cycles")
117         );
118     };
119
120     // Make sure Cargo is aware of the removal of `--no-deps`.
121     lint_path_dep();
122
123     let successful_build = || {
124         let output = Command::new(&*CLIPPY_PATH)
125             .current_dir(&cwd)
126             .env("CARGO_INCREMENTAL", "0")
127             .arg("clippy")
128             .args(&["-p", "subcrate"])
129             .arg("--")
130             .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir
131             .output()
132             .unwrap();
133         println!("status: {}", output.status);
134         println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
135         println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
136
137         assert!(output.status.success());
138
139         output
140     };
141
142     // Trigger a sucessful build, so Cargo would like to cache the build result.
143     successful_build();
144
145     // Make sure there's no spurious rebuild when nothing changes.
146     let stderr = String::from_utf8(successful_build().stderr).unwrap();
147     assert!(!stderr.contains("Compiling"));
148     assert!(!stderr.contains("Checking"));
149     assert!(stderr.contains("Finished"));
150
151     // Make sure Cargo is aware of the new `--cfg` flag.
152     lint_path_dep();
153 }
154
155 #[test]
156 fn dogfood_subprojects() {
157     // run clippy on remaining subprojects and fail the test if lint warnings are reported
158     if cargo::is_rustc_test_suite() {
159         return;
160     }
161
162     // NOTE: `path_dep` crate is omitted on purpose here
163     for project in &[
164         "clippy_workspace_tests",
165         "clippy_workspace_tests/src",
166         "clippy_workspace_tests/subcrate",
167         "clippy_workspace_tests/subcrate/src",
168         "clippy_dev",
169         "clippy_lints",
170         "clippy_utils",
171         "rustc_tools_util",
172     ] {
173         run_clippy_for_project(project);
174     }
175
176     // NOTE: Since tests run in parallel we can't run cargo commands on the same workspace at the
177     // same time, so we test this immediately after the dogfood for workspaces.
178     test_no_deps_ignores_path_deps_in_workspaces();
179 }
180
181 #[test]
182 #[ignore]
183 #[cfg(feature = "metadata-collector-lint")]
184 fn run_metadata_collection_lint() {
185     use std::fs::File;
186     use std::time::SystemTime;
187
188     // Setup for validation
189     let metadata_output_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("util/gh-pages/lints.json");
190     let start_time = SystemTime::now();
191
192     // Run collection as is
193     std::env::set_var("ENABLE_METADATA_COLLECTION", "1");
194     run_clippy_for_project("clippy_lints");
195
196     // Check if cargo caching got in the way
197     if let Ok(file) = File::open(metadata_output_path) {
198         if let Ok(metadata) = file.metadata() {
199             if let Ok(last_modification) = metadata.modified() {
200                 if last_modification > start_time {
201                     // The output file has been modified. Most likely by a hungry
202                     // metadata collection monster. So We'll return.
203                     return;
204                 }
205             }
206         }
207     }
208
209     // Force cargo to invalidate the caches
210     filetime::set_file_mtime(
211         PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("clippy_lints/src/lib.rs"),
212         filetime::FileTime::now(),
213     )
214     .unwrap();
215
216     // Running the collection again
217     run_clippy_for_project("clippy_lints");
218 }
219
220 fn run_clippy_for_project(project: &str) {
221     let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
222
223     let mut command = Command::new(&*CLIPPY_PATH);
224
225     command
226         .current_dir(root_dir.join(project))
227         .env("CARGO_INCREMENTAL", "0")
228         .arg("clippy")
229         .arg("--all-targets")
230         .arg("--all-features")
231         .arg("--")
232         .args(&["-D", "clippy::all"])
233         .args(&["-D", "clippy::pedantic"])
234         .arg("-Cdebuginfo=0"); // disable debuginfo to generate less data in the target dir
235
236     // internal lints only exist if we build with the internal-lints feature
237     if cfg!(feature = "internal-lints") {
238         command.args(&["-D", "clippy::internal"]);
239     }
240
241     let output = command.output().unwrap();
242
243     println!("status: {}", output.status);
244     println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
245     println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
246
247     assert!(output.status.success());
248 }