Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(serialize): remove surrial (#2877)
Replace [surrial](https://www.npmjs.com/package/surrial) with `JSON.stringify` and `JSON.parse` to serialize and deserialize the messages between `Checker` and `TestRunner` worker processes. This has a couple of advantages: ✅ Improves performance (slightly). ✅ Removes a (small) security risk where an [`eval`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval)~ish way was used to deserialize. ✅ Reduces weird behavior in edge cases where you provide functions in Stryker options. ✅ Reduces maintenance load by removing a fringe dependency (that I maintain, but still 😅). However, this does mean that an undocumented feature disappears (a small, but breaking change). ```js // stryker.conf.js module.exports = { karma: { webpack: { some: /regex/ // 👈 this will be send as `null` to the worker process. } } }; ``` This is why Stryker will provide an optional warning: ![image](https://user-images.githubusercontent.com/1828233/117358371-5f3c4b80-aeb6-11eb-972b-059d4a8ff7b9.png) BREAKING CHANGE: Having a non-JSON-serializable value in your configuration won't be sent to the child process anymore. If you really need them in your test runner configuration, you should isolate those values and put them in test runner-specific config files, loaded by the test runner plugin itself, for example, jest.config.js, karma.conf.js, webpack.config.js.
- Loading branch information
Showing
33 changed files
with
450 additions
and
174 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
{ | ||
"name": "options-validation", | ||
"scripts": { | ||
"pretest": "rimraf stryker.log .stryker-tmp", | ||
"test:mutation:plugin": "stryker run --fileLogLevel info stryker-error-in-plugin-options.conf.js || exit 0", | ||
"test": "mocha --timeout 60000 --require \"../../tasks/ts-node-register.js\" verify/verify.ts" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,5 +8,8 @@ | |
}, | ||
"karma": { | ||
"projectType": "Project type not supported" | ||
}, | ||
"commandRunner": { | ||
"command": "echo 'no-test'" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
module.exports = { | ||
myCustomReporter: { | ||
filter() { | ||
} | ||
}, | ||
commandRunner: { | ||
command: 'echo "no-test"' | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import { expect } from 'chai'; | ||
import { execStryker, ExecStrykerResult } from '../../../helpers'; | ||
|
||
describe('Options validation', () => { | ||
let result: ExecStrykerResult; | ||
|
||
describe('Errors in plugin options', () => { | ||
|
||
|
||
before(() => { | ||
result = execStryker('stryker run stryker-error-in-plugin-options.conf.json'); | ||
}); | ||
|
||
it('should exit with 1', () => { | ||
expect(result.exitCode).eq(1); | ||
}); | ||
|
||
it('should report mochaOptions.spec error', () => { | ||
expect(result.stdout).includes('Config option "mochaOptions.spec" has the wrong type'); | ||
}); | ||
|
||
it('should report jasmineConfigFile error', () => { | ||
expect(result.stdout).includes('Config option "jasmineConfigFile" has the wrong type'); | ||
}); | ||
|
||
it('should report karma.projectType error', () => { | ||
expect(result.stdout).not.includes('Config option "karma.projectType" has the wrong type'); | ||
expect(result.stdout).includes('Config option "karma.projectType" should be one of the allowed values'); | ||
}); | ||
}); | ||
|
||
describe('Warnings ', () => { | ||
|
||
before(() => { | ||
result = execStryker('stryker run stryker-warnings.conf.js'); | ||
}); | ||
|
||
it('should exit with 0', () => { | ||
expect(result.exitCode).eq(0); | ||
}); | ||
|
||
it('should report about unknown options', () => { | ||
expect(result.stdout).includes('Unknown stryker config option "myCustomReporter"'); | ||
}); | ||
|
||
it('should report about unserializable options', () => { | ||
expect(result.stdout).includes(' Config option "myCustomReporter.filter" is not (fully) serializable. Primitive type "function" has no JSON representation. Any test runner or checker worker processes might not receive this value as intended.'); | ||
}); | ||
}); | ||
}); |
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
export * from './read-config'; | ||
export * from './options-validator'; | ||
export * from './options-marker'; | ||
export * from './build-schema-with-plugin-contributions'; | ||
export * from './file-matcher'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import type { JSONSchema7 } from 'json-schema'; | ||
import { tokens } from 'typed-inject'; | ||
import { StrykerOptions } from '@stryker-mutator/api/core'; | ||
import { commonTokens } from '@stryker-mutator/api/plugin'; | ||
import { Logger } from '@stryker-mutator/api/logging'; | ||
import { PropertyPathBuilder, findUnserializables } from '@stryker-mutator/util'; | ||
|
||
import { coreTokens } from '../di'; | ||
import { isWarningEnabled } from '../utils/object-utils'; | ||
|
||
markOptions.inject = tokens(commonTokens.options, coreTokens.validationSchema, commonTokens.logger); | ||
|
||
/** | ||
* Performs additional validation on the Stryker options to mark unusual behavior with a warning. | ||
* Namely when a value isn't serializable or when unknown options are passed in. | ||
*/ | ||
export function markOptions(options: StrykerOptions, schema: JSONSchema7, log: Logger): StrykerOptions { | ||
markUnknownOptions(options, schema, log); | ||
markUnserializableOptions(options, log); | ||
return options; | ||
} | ||
|
||
function markUnknownOptions(options: StrykerOptions, schema: JSONSchema7, log: Logger) { | ||
const OPTIONS_ADDED_BY_STRYKER = ['set', 'configFile', '$schema']; | ||
|
||
if (isWarningEnabled('unknownOptions', options.warnings)) { | ||
const schemaKeys = Object.keys(schema.properties!); | ||
const unknownPropertyNames = Object.keys(options) | ||
.filter((key) => !key.endsWith('_comment')) | ||
.filter((key) => !OPTIONS_ADDED_BY_STRYKER.includes(key)) | ||
.filter((key) => !schemaKeys.includes(key)); | ||
|
||
if (unknownPropertyNames.length) { | ||
unknownPropertyNames.forEach((unknownPropertyName) => { | ||
log.warn(`Unknown stryker config option "${unknownPropertyName}".`); | ||
}); | ||
|
||
const p = PropertyPathBuilder.create<StrykerOptions>().prop('warnings').prop('unknownOptions').build(); | ||
|
||
log.warn(`Possible causes: | ||
* Is it a typo on your end? | ||
* Did you only write this property as a comment? If so, please postfix it with "_comment". | ||
* You might be missing a plugin that is supposed to use it. Stryker loaded plugins from: ${JSON.stringify(options.plugins)} | ||
* The plugin that is using it did not contribute explicit validation. | ||
(disable "${p}" to ignore this warning)`); | ||
} | ||
} | ||
} | ||
|
||
function markUnserializableOptions(options: StrykerOptions, log: Logger) { | ||
if (isWarningEnabled('unserializableOptions', options.warnings)) { | ||
const unserializables = findUnserializables(options); | ||
if (unserializables) { | ||
unserializables.forEach(({ reason, path }) => | ||
log.warn( | ||
`Config option "${path.join( | ||
'.' | ||
)}" is not (fully) serializable. ${reason}. Any test runner or checker worker processes might not receive this value as intended.` | ||
) | ||
); | ||
const p = PropertyPathBuilder.create<StrykerOptions>().prop('warnings').prop('unserializableOptions').build(); | ||
log.warn(`(disable ${p} to ignore this warning)`); | ||
} | ||
} | ||
} |
Oops, something went wrong.