//! `spectral doctor`, driven through the built binary. //! //! The fixture controls `PATH` and git's configuration, so a missing qemu, a //! missing sender identity and a tree that is not a kernel tree are produced on //! purpose rather than inherited from whichever machine this runs on. That //! extends to git's own send-email support, which the harness cannot control //! through git's configuration: a `git` wrapper answers for it in both //! directions, so a host without it still passes. //! //! Every test name here carries the verb, so the plan's own command //! `cargo test --locked doctor` selects exactly this file's tests. mod common; use common::Fixture; fn stdout_of(output: &std::process::Output) -> String { String::from_utf8_lossy(&output.stdout).into_owned() } fn stderr_of(output: &std::process::Output) -> String { String::from_utf8_lossy(&output.stderr).into_owned() } /// Run `doctor` with a `git` that has send-email support, whatever the host has. fn doctor_with_send_email(fixture: &Fixture) -> std::process::Output { let bin = fixture.path_with_send_email(0); fixture.cli_env( &["doctor"], &[("PATH", bin.to_str().expect("the git bin directory"))], ) } #[test] fn doctor_passes_on_a_complete_fixture_tree() { let fixture = Fixture::kernel_tree(); let output = doctor_with_send_email(&fixture); assert!( output.status.success(), "{}{}", stdout_of(&output), stderr_of(&output) ); let stdout = stdout_of(&output); assert!(!stdout.contains("fail "), "{stdout}"); assert!(stdout.contains("ok kernel tree"), "{stdout}"); assert!( stdout.contains("from $SPECTRAL_KERNEL"), "the report has to say which knob set the path: {stdout}" ); assert!(stdout.contains("ok git send-email"), "{stdout}"); assert!( stdout.contains("nothing failed") || stdout.contains("everything checked out"), "{stdout}" ); } #[test] fn doctor_names_the_first_hard_failure_when_the_tree_is_missing() { let fixture = Fixture::bare(); let output = fixture.cli_env(&["doctor"], &[("SPECTRAL_KERNEL", "/nonexistent/linux")]); assert_eq!(output.status.code(), Some(1)); let stdout = stdout_of(&output); assert!(stdout.contains("fail kernel tree"), "{stdout}"); assert!(stdout.contains("first failure: kernel tree"), "{stdout}"); assert!( stdout.contains("skip checkpatch.pl"), "the checks that need a tree are not answers when there is none: {stdout}" ); assert!(stdout.contains("-> "), "the fix hint is printed: {stdout}"); } #[test] fn doctor_names_a_directory_that_is_not_a_kernel_tree() { let fixture = Fixture::bare(); let tree = fixture.join("not-a-tree"); std::fs::create_dir_all(&tree).expect("create the directory"); let output = fixture.cli_env( &["doctor"], &[("SPECTRAL_KERNEL", tree.to_str().expect("the tree path"))], ); assert_eq!(output.status.code(), Some(1)); let stdout = stdout_of(&output); assert!(stdout.contains("fail kernel tree shape"), "{stdout}"); assert!(stdout.contains("not a kernel source tree"), "{stdout}"); assert!( stdout.contains("first failure: kernel tree shape"), "{stdout}" ); } #[test] fn doctor_warns_about_qemu_off_the_path_rather_than_failing() { let fixture = Fixture::kernel_tree(); // A `PATH` holding the git wrapper and nothing else: qemu is gone, the // rest is not. let bin = fixture.path_with_send_email(0); let output = fixture.cli_env( &["doctor"], &[("PATH", bin.to_str().expect("the git bin directory"))], ); assert!( output.status.success(), "{}{}", stdout_of(&output), stderr_of(&output) ); let stdout = stdout_of(&output); assert!(stdout.contains("warn qemu"), "{stdout}"); assert!( !stdout.contains("fail "), "a missing qemu is not something the loop cannot work around: {stdout}" ); } #[test] fn doctor_fails_on_no_sender_identity_and_says_how_to_fix_it() { let fixture = Fixture::kernel_tree(); // Both places git would find one: the tree's own config and the fixture's // global one. fixture.git(&["config", "--unset", "user.email"]); std::fs::write( fixture.home().join("gitconfig"), "[core]\n\trepositoryformatversion = 0\n", ) .expect("write a global config with no identity"); let output = doctor_with_send_email(&fixture); assert_eq!(output.status.code(), Some(1)); let stdout = stdout_of(&output); assert!(stdout.contains("fail sender identity"), "{stdout}"); assert!( stdout.contains("user.email"), "the failure says how to fix it: {stdout}" ); assert!( stdout.contains("first failure: sender identity"), "{stdout}" ); } #[test] fn doctor_warns_when_git_has_no_send_email_support() { // The direction the host cannot be trusted for: this machine has it, and a // machine without it must fail the same way rather than pass by accident. let fixture = Fixture::kernel_tree(); let bin = fixture.path_with_send_email(1); let output = fixture.cli_env( &["doctor"], &[("PATH", bin.to_str().expect("the git bin directory"))], ); assert_eq!(output.status.code(), Some(1)); let stdout = stdout_of(&output); assert!(stdout.contains("fail git send-email"), "{stdout}"); assert!(stdout.contains("first failure: git send-email"), "{stdout}"); } #[test] fn doctor_warns_about_no_mail_route_because_dry_runs_are_what_matter() { // The fixture's git config has no sendemail.* at all, which is the state // this machine's own config was in when --dry-run was verified. let fixture = Fixture::kernel_tree(); let output = doctor_with_send_email(&fixture); assert!(output.status.success()); let stdout = stdout_of(&output); assert!(stdout.contains("warn mail route"), "{stdout}"); assert!( stdout.contains("--dry-run works"), "the warning says what still works: {stdout}" ); } #[test] fn doctor_reports_where_the_paths_came_from() { let fixture = Fixture::kernel_tree(); let patches = fixture.home().join("patches"); let output = fixture.cli_env( &["doctor"], &[ ( "PATH", fixture.path_with_send_email(0).to_str().expect("the bin"), ), ( "SPECTRAL_PATCH_DIR", patches.to_str().expect("the patch dir"), ), ], ); let stdout = stdout_of(&output); assert!(stdout.contains("from $SPECTRAL_PATCH_DIR"), "{stdout}"); assert!( stdout.contains(patches.to_str().expect("the patch dir")), "{stdout}" ); assert!( patches.is_dir(), "doctor is the one check with a side effect: it makes the directory it was pointed at" ); }