Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mutate match arms #485

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open

Conversation

zaneduffield
Copy link
Contributor

Implements #484.

I created just one new genre for the two new kinds of mutations here, but maybe it should be two new genres?

Another thing I wasn't sure about was whether and how to change Mutant::styled_parts to more simply render the new mutations. If the match arm or guard being mutated is long then the output could become quite hard to read.

With this new mutation genre, there are some uncovered mutations in the cargo-mutants source tree that I've added a commit to address. A couple of things about that are worth noting

  1. I removed a match arm in src/fnvalue.rs that I think was redundant. It's possible that it deserves to be there as an optimisation though, which would make it hard to cover.
  2. I wasn't able to add a test to cover the mutation on a match arm relating to Cargo `default-members.
    Ok(default_packages) if default_packages.is_empty() => {
    debug!("manifest has no explicit default packages");
    PackageSelection::All
    }

    I think this might be redundant as well. The docs say that the default value for this property is non-empty, which would mean that for it to be empty would require it to actually be empty:

    When unspecified, the root package will be used. In the case of a virtual workspace, all members will be used (as if --workspace were specified on the command-line).

@sourcefrog sourcefrog self-assigned this Jan 21, 2025
Copy link
Owner

@sourcefrog sourcefrog left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implements #484.

Thanks, this looks good! We should also document the new patterns in https://github.com/sourcefrog/cargo-mutants/blob/main/book/src/mutants.md

I created just one new genre for the two new kinds of mutations here, but maybe it should be two new genres?

I think two distinct new genres is better.

Another thing I wasn't sure about was whether and how to change Mutant::styled_parts to more simply render the new mutations. If the match arm or guard being mutated is long then the output could become quite hard to read.

Probably the rendering code should know to abbreviate long replacements, and just say "replace match arm with xxx". In a sense it has this special coded for functions now, since it doesn't quote the whole of the function body that's being replaced... But, let's not do that in this PR.

With this new mutation genre, there are some uncovered mutations in the cargo-mutants source tree that I've added a commit to address. A couple of things about that are worth noting

  1. I removed a match arm in src/fnvalue.rs that I think was redundant. It's possible that it deserves to be there as an optimisation though, which would make it hard to cover.

That's great that it found this! I think letting it hit the general non-empty case is fine: the iterator will be empty and that should finish quickly. I think the code you removed is just a relic of a time when it didn't know how to expand tuples, only to replace unit types.

  1. I wasn't able to add a test to cover the mutation on a match arm relating to Cargo `default-members.

    Ok(default_packages) if default_packages.is_empty() => {
    debug!("manifest has no explicit default packages");
    PackageSelection::All
    }

    I think this might be redundant as well. The docs say that the default value for this property is non-empty, which would mean that for it to be empty would require it to actually be empty:

    When unspecified, the root package will be used. In the case of a virtual workspace, all members will be used (as if --workspace were specified on the command-line).

Yes I guess we could just delete that arm.

}

impl Foo {
#[mutants::skip]
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this addition to the testdata is harmless but we don't exactly need it... it's just proving again that mutants::skip works...?

Or was there a coverage gap without it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I started adding this when I thought it was possible to add #[mutants::skip] to a statement. I'll delete it.

@@ -23,3 +23,4 @@ mod slices;
mod static_item;
mod struct_with_lifetime;
mod traits;
mod matches;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI I now feel it's probably better to add mutation patterns as unit tests in visit.rs rather than here, because actually testing this tree is contributing to mutants' own test suite becoming slower, and the big Insta snapshots are unwieldy.

However you don't necessarily need to rewrite this now. I'm not settled on converting them to unit tests.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was following the existing testing pattern, but I'm happy to rewrite as unit tests.

}
}

// Don't use the value of the match expression, because we don't expect it to be mutated.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Don't use the value of the match expression, because we don't expect it to be mutated.
// This match expression has no default so won't be mutated.
// This doesn't count as a missed mutant because the function
// return value doesn't depend on the match expression.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

deleted

fn in_workspace_only_default_members_are_mutated_if_present() {
let tmp = copy_of_testdata("workspace_default_members");
run()
.args(["mutants", "--no-shuffle", "-d"])
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this should just run with --list which will be a bit faster: we've probably sufficiently tested elsewhere that running tests from packages works.

fn in_workspace_only_default_members_are_mutated_if_present() {
let tmp = copy_of_testdata("workspace_default_members");
run()
.args(["mutants", "--no-shuffle", "-d"])
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this should just run with --list which will be a bit faster: we've probably sufficiently tested elsewhere that running tests from packages works.

@sourcefrog
Copy link
Owner

I see some mutants come out like this:

src/process.rs:187:17: delete ' ' | '\\' | '\'' | '"' => {
                    r.push('\\');
                    r.push(c);
                } in quote_argv

src/fnvalue.rs:186:9: delete Type::Reference(syn::TypeReference {
            mutability: Some(_),
            elem,
            ..
        }) => match &**elem {
            Type::Slice(TypeSlice { elem, .. }) => iter::once(quote! { Vec::leak(Vec::new()) })
                .chain(
                    type_replacements(elem, error_exprs).map(|r| quote! { Vec::leak(vec![ #r ]) }),
                )
                .collect_vec(),
            _ => {
                // Make &mut with static lifetime by leaking them on the heap.
                type_replacements(elem, error_exprs)
                    .map(|rep| {
                        quote! { Box::leak(Box::new(#rep)) }
                    })
                    .collect_vec()
            }
        }, in type_replacements

and others are even longer.

Maybe we should just change this to "delete match arm" or "replace match guard with true".

@zaneduffield
Copy link
Contributor Author

Thanks, this looks good! We should also document the new patterns in https://github.com/sourcefrog/cargo-mutants/blob/main/book/src/mutants.md

I've addressed all the comments but this. I won't have any time to work on this until next week, though.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants