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

'hybrid mutants' can leak to the next run --ignoreStatic #3442

Closed
nicojs opened this issue Feb 19, 2022 · 2 comments
Closed

'hybrid mutants' can leak to the next run --ignoreStatic #3442

nicojs opened this issue Feb 19, 2022 · 2 comments
Labels
🐛 Bug Something isn't working
Milestone

Comments

@nicojs
Copy link
Member

nicojs commented Feb 19, 2022

Summary

Testing hybrid mutants with --ignoreStatic doesn't work as expected with --ignoreStatic.

For an explanation of what "hybrid" mutants are, see
https://github.com/stryker-mutator/mutation-testing-elements/pull/1547/files

The assumption was that we would be able to test hybrid mutants as regular non-static mutants without the state leaking out. This assumption is incorrect. See the next example (grabbed from expressjs):

From test/res.format.js

var app1 = express();

app1.use(function(req, res, next){
  res.format({ /* redacted for readability */ });
});

app1.use(function(err, req, res, next){
/* redacted for readability */ 
})

var app2 = express();

app2.use(function(req, res, next){
/* redacted for readability */ 
});

app2.use(function(err, req, res, next){
/* redacted for readability */ 
})

var app3 = express();

app3.use(function(req, res, next){
/* redacted for readability */ 
});

var app4 = express();

app4.get('/', function(req, res, next){
/* redacted for readability */ 
});

app4.use(function(err, req, res, next){
/* redacted for readability */ 
})

var app5 = express();

app5.use(function (req, res, next) {
/* redacted for readability */ 
});

describe('res', function(){
  /* a lot of tests here, using `app1` through `app5` */ 
});

As you can see, the tests themselves add static coverage in this example (the initialization of app1 through app5 is done in static context (no test was active yet)). Now imagine a test runner first testing hybrid mutants in scope and next running another non-static mutant. The result will be that these app1 through app5 variables will have loaded the hybrid mutants. The only way to clear them would be by restarting the test runner process.

The result is tons of runtime errors. See this report.

express-v6-concurrency-15-ignore-static.zip

I've rerun with this change (ignoring hybrid mutants):

diff --git a/packages/core/src/mutants/mutant-test-planner.ts b/packages/core/src/mutants/mutant-test-planner.ts
index 4bc9e051..dcc1a7c0 100644
--- a/packages/core/src/mutants/mutant-test-planner.ts
+++ b/packages/core/src/mutants/mutant-test-planner.ts
@@ -68,7 +68,7 @@ export class MutantTestPlanner {
       // If there was coverage information (coverageAnalysis not "off")
       const tests = this.testsByMutantId.get(mutant.id) ?? [];
       const coveredBy = toTestIds(tests);
-      if (!isStatic || (this.options.ignoreStatic && coveredBy.length)) {
+      if (!isStatic) {
         // If not static, or it was "hybrid" (both static and perTest coverage) and ignoreStatic is on.
         // Only run covered tests
         const netTime = calculateTotalTime(tests);

And the result seems to be a lot more consistent (about as consistent as StrykerJS v5). But entire (hybrid) functions are ignored now. Not sure that is what we want. See this report:

express-v6-concurrency-15-ignore-static-hybrid.zip

Note: running without --ignoreStatic works as expected.

I can think of 4 possible solutions here:

  1. Fix
    Keep testing hybrid mutants, but make sure that the mutant isn't active yet during the loading of the files. Our test runners will have to know if it is running under --ignoreStatic. since you don't want that behavior when not running with --ignoreStatic. Both mocha and Jasmine support this. note: I've implemented this fix. See 'hybrid mutants' can leak to the next run --ignoreStatic #3442 (comment)
  2. Known issue
    Keep behavior like it is now and try to educate people to initialize their suts in beforeEach. This will make it impossible to make --ignoreStatic the default because this is essentially a bug.
  3. Ignore
    Always ignore hybrid mutants under --ignoreStatic
  4. Configure
    Allow users to configure the behavior: --static run|ignore|hybrid

Stryker config

Using the epic/esm branch.

{
  "$schema": "../../../packages/core/schema/stryker-schema.json",
  "testRunner": "mocha",
  "coverageAnalysis": "perTest",
  "mochaOptions": {
    "require": ["test/support/env"],
    "files": ["test/*.js", "test/acceptance/*.js"]
  },
  "ignoreStatic": true
}

Test runner config

none

Stryker environment

Using the epic/esm branch.

+-- mocha@9.2.0

Test runner environment

mocha --require test/support/env --reporter spec --bail --check-leaks test/ test/acceptance/

Add stryker.log

stryker.log

@nicojs nicojs added the 🐛 Bug Something isn't working label Feb 19, 2022
@nicojs nicojs added this to the 6.0 milestone Feb 19, 2022
nicojs added a commit that referenced this issue Feb 20, 2022
Prevent hybrid mutants to leak out to the next test run. Implement a new mutant run option called "mutantActivation". It can either be "runtime" or "static".

- `'runtime'`. The test environment should first load all tests and sut files before activating the mutant. Mutant is only active during runtime.
- `'static'`. The test enviornment should load _while the mutant is active_. Mutant is may be active during the entire lifetime of the process.

See #3442 for more details
@nicojs
Copy link
Member Author

nicojs commented Feb 20, 2022

I thought about it this weekend and I think we don't really have a choice in the matter. It should be fixed. I went ahead and implemented the fix in #3443.

@simondel can you review this tomorrow?

nicojs added a commit that referenced this issue Feb 22, 2022
Add `"mutantActivation"` to `MutantRunOptions`. Support this in jasmine runner and mocha runner.

See #3442 for more details
@nicojs
Copy link
Member Author

nicojs commented Mar 2, 2022

Closed with #3443

@nicojs nicojs closed this as completed Mar 2, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🐛 Bug Something isn't working
Projects
None yet
Development

No branches or pull requests

1 participant