//! The real tools, when the machine has them. //! //! Everything else in `tests/` drives stubs, because checkpatch's output moves //! with the kernel version and a test cannot assert on that. These tests are the //! other half: they run the host's own scripts so a kernel-version change to a //! summary line, an argument, or an exit code shows up here rather than in //! someone's patch workflow. //! //! They are ignored by default and need both variables: //! //! ```console //! $ SPECTRAL_REAL_CHECKPATCH=1 SPECTRAL_REAL_TREE=/usr/src/linux \ //! cargo test --locked -- --ignored real_ //! ``` mod common; use common::Fixture; /// A change checkpatch has opinions about, whatever kernel it comes from. const BAD: &str = "static int foo(int a) {\n\tint b = a + 1;\n\tif (a>0)\n\t\tb += 2;\n\treturn b;\n}\n"; #[test] #[ignore = "needs SPECTRAL_REAL_CHECKPATCH=1 and SPECTRAL_REAL_TREE=/path/to/linux"] fn real_checkpatch_reads_all_three_kinds_of_target() { let fixture = real_fixture(); fixture.write("drivers/foo/bar.c", BAD); // The working tree goes in on stdin, because `--git` with no revision // dies with "no git commits after extraction". let working_tree = fixture.cli(&["patch", "check"]); let stdout = String::from_utf8_lossy(&working_tree.stdout).into_owned(); assert_eq!(working_tree.status.code(), Some(1), "{stdout}"); assert!(stdout.contains("total:"), "{stdout}"); assert!(stdout.contains("ERROR"), "{stdout}"); // A revision goes through --git, and checkpatch names the commit it read. let by_rev = fixture.cli(&["patch", "check", "--rev", "HEAD"]); let stdout = String::from_utf8_lossy(&by_rev.stdout).into_owned(); assert!(stdout.contains("fixture: add bar"), "{stdout}"); // A patch file goes through --file. let patch = fixture.home().join("bad.patch"); std::fs::write(&patch, format!("{}\n", fixture.git(&["diff"]))).expect("write the patch"); let by_file = fixture.cli(&["patch", "check", patch.to_str().expect("the patch path")]); let stdout = String::from_utf8_lossy(&by_file.stdout).into_owned(); assert_eq!(by_file.status.code(), Some(1), "{stdout}"); assert!(stdout.contains("ERROR"), "{stdout}"); } /// A fixture whose checkpatch is the host's, copied in. fn real_fixture() -> Fixture { assert_eq!( std::env::var("SPECTRAL_REAL_CHECKPATCH").as_deref(), Ok("1"), "run this with SPECTRAL_REAL_CHECKPATCH=1" ); let tree = std::env::var("SPECTRAL_REAL_TREE") .expect("set SPECTRAL_REAL_TREE to a kernel tree holding scripts/checkpatch.pl"); let fixture = Fixture::kernel_tree(); let script = std::path::Path::new(&tree).join("scripts/checkpatch.pl"); let body = std::fs::read_to_string(&script) .unwrap_or_else(|error| panic!("read {}: {error}", script.display())); fixture.write_script("scripts/checkpatch.pl", &body); fixture } #[test] #[ignore = "needs SPECTRAL_REAL_GETMAINTAINER=1 and SPECTRAL_REAL_TREE=/path/to/linux"] fn real_get_maintainer_fills_the_to_and_cc_lines() { assert_eq!( std::env::var("SPECTRAL_REAL_GETMAINTAINER").as_deref(), Ok("1"), "run this with SPECTRAL_REAL_GETMAINTAINER=1" ); let tree = std::env::var("SPECTRAL_REAL_TREE") .expect("set SPECTRAL_REAL_TREE to a kernel tree holding scripts/get_maintainer.pl"); let fixture = Fixture::kernel_tree(); let script = std::path::Path::new(&tree).join("scripts/get_maintainer.pl"); let body = std::fs::read_to_string(&script) .unwrap_or_else(|error| panic!("read {}: {error}", script.display())); fixture.write_script("scripts/get_maintainer.pl", &body); fixture.commit_all("fixture: use the real get_maintainer"); fixture.work_on_a_branch(); fixture.write("drivers/foo/bar.c", common::EDITED); fixture.commit_all("foo: return a + 1"); let created = fixture.cli(&["patch", "create", "000-foo"]); assert!( created.status.success(), "{}", String::from_utf8_lossy(&created.stderr) ); let patch = fixture.home().join(".spectral/patches/000-foo.patch"); let output = fixture.cli(&[ "patch", "submit", patch.to_str().expect("the patch path"), "--dry-run", ]); assert!( output.status.success(), "{}", String::from_utf8_lossy(&output.stderr) ); let stdout = String::from_utf8_lossy(&output.stdout); // The script's own words decide the split, so the assertion is about the // shape rather than about which kernel's MAINTAINERS matched. assert!( stdout.contains("To: Fixture Maintainer "), "{stdout}" ); assert!( stdout.lines().any(|line| line.starts_with("Cc: ")), "a reviewer and a second list belong on the Cc line: {stdout}" ); assert!(stdout.contains("Dry-OK"), "{stdout}"); } #[test] #[ignore = "needs SPECTRAL_LIVE=1 and a network"] fn live_bugzilla_answers_with_one_issue() { // The only test that talks to bugzilla.kernel.org. It asserts the shape of // the answer rather than which bug came back, which is what a live API can // be held to. assert_eq!( std::env::var("SPECTRAL_LIVE").as_deref(), Ok("1"), "run this with SPECTRAL_LIVE=1" ); let fixture = Fixture::bare(); let output = fixture.cli(&["kernel", "quest", "--json"]); assert!( output.status.success(), "{}", String::from_utf8_lossy(&output.stderr) ); let stdout = String::from_utf8_lossy(&output.stdout); for field in ["\"id\"", "\"title\"", "\"component\"", "\"url\""] { assert!(stdout.contains(field), "{field} is missing from {stdout}"); } assert!( stdout.contains("https://bugzilla.kernel.org/show_bug.cgi?id="), "{stdout}" ); } #[test] #[ignore = "needs SPECTRAL_LIVE=1 and a network"] fn live_bugzilla_names_a_filter_that_matches_nothing() { assert_eq!( std::env::var("SPECTRAL_LIVE").as_deref(), Ok("1"), "run this with SPECTRAL_LIVE=1" ); let fixture = Fixture::bare(); let output = fixture.cli(&["kernel", "quest", "--filter", "zzq-no-such-word-zzq"]); assert_eq!(output.status.code(), Some(1)); let stderr = String::from_utf8_lossy(&output.stderr); assert!(stderr.contains("no open bug matched"), "{stderr}"); }