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

feat(ignore): support disable directives in source code #3072

Merged
merged 19 commits into from Sep 1, 2021

Conversation

Garethp
Copy link
Contributor

@Garethp Garethp commented Aug 12, 2021

Add support // Stryker disable and // Stryker restore directives in source code.

Some examples:

// Stryker disable all
const foo = 2 + 40; // mutants here are ignored
// Stryker restore all

// Stryker disable BooleanLiteral
const foo = true || false; // => BooleanLiteral mutant is ignored, but other mutants are tested.
// Stryker restore all

// Stryker disable next-line all
const foo = true || false; // => ignored mutants
const bar = true || false; // => mutants 

// Stryker disable next-line all: I don't care about this piece of code
const foo = bar || baz; // ignored mutants with reason "I don't care about this piece of code"

Ignored mutants do end up in your mutation report, but are not placed or tested and they don't count towards your mutation score.

image

Closes #1472

Copy link
Member

@nicojs nicojs left a comment

Choose a reason for hiding this comment

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

Thanks @Garethp . I have a few remarks, mainly that I want ignore mutants to end up the HTML report with status "ignored", for details see remarks.

Note: we shouldn't forget to add an e2e test for this. Probably it would make sense to add tests to the e2e/test/ignore-project

@@ -139,6 +197,7 @@ export const transformBabel: AstTransformer<ScriptFormat> = (
*/
function collectMutants(path: NodePath) {
return [...mutate(path)]
.filter((mutant) => !isMutantInDisabledMap(path.node.loc, mutant))
Copy link
Member

Choose a reason for hiding this comment

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

I don't want the mutants to be filtered out, instead I want them to be collected, but with an ignoreReason set. That way, they will still end up in the report, but won't be placed, tested or influence the mutation score.

comments
.filter((comment) => comment.value.trim().startsWith('Stryker disable-next-line'))
.forEach((comment) => {
const [_, _disableType, mutations] = comment.value.trim().split(' ');
Copy link
Member

Choose a reason for hiding this comment

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

We should also keep track of the ignoreReason. For example:

// Stryker disable-next-line update The update mutator creates an infinite loop here.

The ignore reason we can then report is: "Disabled by user comment. The update mutator creates an infinite loop here."

For this to work, and keep parsing of the comment positional, we would need to force users to use // Stryker disable-next-line all if they want to ignore all, otherwise we cannot tell where the ignore reason starts.

What do you think about this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's not my preference, having to type all seems coutner to how I'd expect it to work (Personally I'd expect it to work the way eslint does since we're in a JS context and that's how I kind of expect these things to work in JS). I'd prefer to have have us take the positional argument there and check it against the list of enabled mutations to determine if it's a specific mutant or the start of a comment. I think we'd have to do this anwyay if we want to force the positional argument in order to check if the user accidently left it off.

Copy link
Member

@nicojs nicojs Aug 20, 2021

Choose a reason for hiding this comment

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

Hmm good point. But still don't want to leave it up to "chance".

Maybe we can force the mutator names to be in square brackets? Some examples:

// Stryker disable-next-line [update] The update mutator creates an infinite loop here.
// Stryker disable-next-line We don't care about log statements
// Stryker disable-next-line [update] 
// Stryker disable-next-line

(the last 2 are without ignore reasons)

If there are no ignore reasons given, we should still ignore them with "Disabled by user comment", or something like that.

@Garethp
Copy link
Contributor Author

Garethp commented Aug 22, 2021

@nicojs I've made it so that those mutants are marked with an ignore reason when disabled and added some tests to the ignore-project. I've also added the // Stryker disable [mutators] [reason] and // Stryker restore [mutators] [reason] commands. The HTML reporter doesn't show the user-defined reasons as far as I can see, but they do make it into the report and in the HTML viewer you see the comment right above the ignored mutation anyway

@nicojs nicojs self-requested a review August 25, 2021 20:29
Copy link
Member

@nicojs nicojs left a comment

Choose a reason for hiding this comment

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

Thanks for your continued work on this @Garethp. I see you've also managed to implement the disable and restore proposal from #1472 . Great stuff 🎉

I've refactored a bit. I've renamed the tests and moved all disable logic to a separate class: DirectiveBookkeeper. I've also managed to merge all directive matching into 1 regex to rule them all (based on your earlier work with the mutation range regex magic 😊).

One last question: I see we've strayed a bit away from the original design:

  • Stryker disable [foo] because reasons instead of Stryker disable foo: because reasons
  • Stryker disable-next-line because reasons instead of Stryker disable next-line all: because reaons
  • Stryker disable because reasons instead of Stryker disable all: because reasons.

It personally feel like we should keep it more in line with Stryker.NET. We can discus it tomorrow in our community meetup.

@@ -4,7 +4,7 @@ describe('After running stryker on jest-react project', () => {
it('should report expected scores', async () => {
await expectMetrics({
killed: 8,
ignored: 13,
ignored: 23,
Copy link
Member

Choose a reason for hiding this comment

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

I think we should also verify the ignore reasons now. 🤷‍♀️

@nicojs nicojs mentioned this pull request Aug 26, 2021
`,
});
act(ast);
expect(notIgnoredMutants()).lengthOf(0);
Copy link
Member

Choose a reason for hiding this comment

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

Test for the reason with a custom reason

@nicojs
Copy link
Member

nicojs commented Aug 26, 2021

We've chosen to migrate it to the Stryker.NET syntax

@nicojs nicojs changed the title feature(instrumenter): Allow adding a "disable-next-line [mutator] comment" feat(ignore): support ignore directives in source code Aug 27, 2021
@nicojs nicojs changed the title feat(ignore): support ignore directives in source code feat(ignore): support disable directives in source code Sep 1, 2021
@nicojs nicojs enabled auto-merge (squash) September 1, 2021 11:28
@nicojs nicojs self-requested a review September 1, 2021 11:31
@nicojs
Copy link
Member

nicojs commented Sep 1, 2021

@Garethp I actually found some more bugs and decided to change the implementation because of it. Much better now! See fd5e978

@nicojs nicojs merged commit 701d8b3 into stryker-mutator:master Sep 1, 2021
@Garethp Garethp deleted the disable-mutation-comment branch September 1, 2021 16:21
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.

Ignore specific mutations
2 participants