| //! General tests specifically about diagnostics and other messages. |
| //! |
| //! Tests for message caching can be found in `cache_messages`. |
| |
| use cargo_test_support::compare::assert_e2e; |
| use cargo_test_support::prelude::*; |
| use cargo_test_support::{process, project, Project}; |
| use cargo_util::ProcessError; |
| |
| /// Captures the actual diagnostics displayed by rustc. This is done to avoid |
| /// relying on the exact message formatting in rustc. |
| pub fn raw_rustc_output(project: &Project, path: &str, extra: &[&str]) -> String { |
| let mut proc = process("rustc"); |
| if cfg!(windows) { |
| // Sanitize in case the caller wants to do direct string comparison with Cargo's output. |
| proc.arg(path.replace('/', "\\")); |
| } else { |
| proc.arg(path); |
| } |
| let rustc_output = match proc |
| .arg("--crate-type=lib") |
| .args(extra) |
| .cwd(project.root()) |
| .exec_with_output() |
| { |
| Ok(output) => output.stderr, |
| Err(e) => e.downcast::<ProcessError>().unwrap().stderr.unwrap(), |
| }; |
| // Do a little dance to remove rustc's "warnings emitted" message and the subsequent newline. |
| let stderr = std::str::from_utf8(&rustc_output).expect("utf8"); |
| let mut lines = stderr.lines(); |
| let mut result = String::new(); |
| while let Some(line) = lines.next() { |
| if line.contains("warning emitted") |
| || line.contains("warnings emitted") |
| || line.contains("aborting due to") |
| { |
| // Eat blank line. |
| match lines.next() { |
| None | Some("") => continue, |
| Some(s) => panic!("unexpected str {}", s), |
| } |
| } |
| result.push_str(line); |
| result.push('\n'); |
| } |
| result |
| } |
| |
| fn redact_rustc_message(msg: &str) -> impl IntoData { |
| use snapbox::filter::{Filter, FilterPaths}; |
| let assert = assert_e2e(); |
| let redactions = assert.redactions(); |
| let msg = redactions.redact(msg); |
| FilterPaths.filter(msg.into()) |
| } |
| |
| #[cargo_test] |
| fn deduplicate_messages_basic() { |
| let p = project() |
| .file( |
| "src/lib.rs", |
| r#" |
| pub fn foo() { |
| let x = 1; |
| } |
| "#, |
| ) |
| .build(); |
| let rustc_message = raw_rustc_output(&p, "src/lib.rs", &[]); |
| let expected_output = format!( |
| "{}\ |
| [WARNING] `foo` (lib) generated 1 warning[..] |
| [WARNING] `foo` (lib test) generated 1 warning (1 duplicate) |
| [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s |
| [EXECUTABLE] unittests src/lib.rs (target/debug/deps/foo-[HASH][EXE]) |
| ", |
| rustc_message |
| ); |
| p.cargo("test --no-run -j1") |
| .with_stderr_data(redact_rustc_message(&format!( |
| "\ |
| [COMPILING] foo v0.0.1 ([ROOT]/foo) |
| {}", |
| expected_output |
| ))) |
| .run(); |
| // Run again, to check for caching behavior. |
| p.cargo("test --no-run -j1") |
| .with_stderr_data(redact_rustc_message(&expected_output)) |
| .run(); |
| } |
| |
| #[cargo_test] |
| fn deduplicate_messages_mismatched_warnings() { |
| // One execution prints 1 warning, the other prints 2 where there is an overlap. |
| let p = project() |
| .file( |
| "src/lib.rs", |
| r#" |
| pub fn foo() { |
| let x = 1; |
| } |
| |
| #[test] |
| fn t1() { |
| let MY_VALUE = 1; |
| assert_eq!(MY_VALUE, 1); |
| } |
| "#, |
| ) |
| .build(); |
| let lib_output = raw_rustc_output(&p, "src/lib.rs", &[]); |
| let mut lib_test_output = raw_rustc_output(&p, "src/lib.rs", &["--test"]); |
| // Remove the duplicate warning. |
| let start = lib_test_output.find(&lib_output).expect("same warning"); |
| lib_test_output.replace_range(start..start + lib_output.len(), ""); |
| let expected_output = format!( |
| "\ |
| {}\ |
| [WARNING] `foo` (lib) generated 1 warning[..] |
| {}\ |
| [WARNING] `foo` (lib test) generated 2 warnings (1 duplicate) |
| [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s |
| [EXECUTABLE] unittests src/lib.rs (target/debug/deps/foo-[HASH][EXE]) |
| ", |
| lib_output, lib_test_output |
| ); |
| p.cargo("test --no-run -j1") |
| .with_stderr_data(redact_rustc_message(&format!( |
| "\ |
| [COMPILING] foo v0.0.1 ([ROOT]/foo) |
| {}", |
| expected_output |
| ))) |
| .run(); |
| // Run again, to check for caching behavior. |
| p.cargo("test --no-run -j1") |
| .with_stderr_data(redact_rustc_message(&expected_output)) |
| .run(); |
| } |
| |
| #[cargo_test] |
| fn deduplicate_errors() { |
| let p = project() |
| .file( |
| "src/lib.rs", |
| r#" |
| this should not compile |
| "#, |
| ) |
| .build(); |
| let rustc_message = raw_rustc_output(&p, "src/lib.rs", &[]); |
| p.cargo("test -j1") |
| .with_status(101) |
| .with_stderr_data(redact_rustc_message(&format!( |
| "\ |
| [COMPILING] foo v0.0.1 ([ROOT]/foo) |
| {}[ERROR] could not compile `foo` (lib) due to 1 previous error |
| ", |
| rustc_message |
| ))) |
| .run(); |
| } |