From 0c2fde58b3759c4351313e233d9a163b3c64ea7d Mon Sep 17 00:00:00 2001 From: Nico Jansen Date: Tue, 4 Apr 2017 08:29:14 +0200 Subject: [PATCH] feat(multi-package): Migrate to multi-package repo (#257) * refactor(stryker): Add stryker package * refactor(stryker-api): Add stryker-api package * Refactor estree types. Instead of extending every type and adding a `nodeID` property, create a new type `type IdentifiedNode = Node & Identified`. * refactor(travis): Add travis config to lerna root * docs(readme): Add root project readme --- .bithoundrc | 11 +- .gitignore | 14 -- .travis.yml | 4 - README.md | 173 +------------ lerna.json | 7 + package.json | 126 +--------- .../stryker-api/.editorconfig | 0 packages/stryker-api/.gitignore | 14 ++ packages/stryker-api/.npmignore | 9 + packages/stryker-api/.travis.yml | 9 + packages/stryker-api/.vscode/launch.json | 48 ++++ packages/stryker-api/.vscode/settings.json | 17 ++ packages/stryker-api/CHANGELOG.md | 105 ++++++++ packages/stryker-api/Gruntfile.js | 126 ++++++++++ LICENSE => packages/stryker-api/LICENSE | 0 packages/stryker-api/README.md | 24 ++ packages/stryker-api/config.ts | 3 + packages/stryker-api/core.ts | 7 + packages/stryker-api/mutant.ts | 3 + packages/stryker-api/package.json | 64 +++++ packages/stryker-api/report.ts | 7 + packages/stryker-api/src/config/Config.ts | 28 +++ .../stryker-api/src/config/ConfigWriter.ts | 7 + .../src/config/ConfigWriterFactory.ts | 26 ++ packages/stryker-api/src/core/Factory.ts | 56 +++++ packages/stryker-api/src/core/InputFile.ts | 8 + .../src/core/InputFileDescriptor.ts | 8 + packages/stryker-api/src/core/Location.ts | 11 + packages/stryker-api/src/core/Position.ts | 9 + packages/stryker-api/src/core/Range.ts | 7 + .../stryker-api/src/core/StrykerOptions.ts | 93 +++++++ .../stryker-api/src/mutant/IdentifiedNode.ts | 7 + packages/stryker-api/src/mutant/Mutator.ts | 25 ++ .../stryker-api/src/mutant/MutatorFactory.ts | 23 ++ .../stryker-api/src/report/MatchedMutant.ts | 9 + .../stryker-api/src/report/MutantResult.ts | 16 ++ .../stryker-api/src/report/MutantStatus.ts | 29 +++ packages/stryker-api/src/report/Reporter.ts | 49 ++++ .../stryker-api/src/report/ReporterFactory.ts | 34 +++ packages/stryker-api/src/report/SourceFile.ts | 7 + .../src/test_framework/TestFramework.ts | 29 +++ .../test_framework/TestFrameworkFactory.ts | 24 ++ .../test_framework/TestFrameworkSettings.ts | 13 + .../stryker-api/src/test_runner/Coverage.ts | 61 +++++ .../stryker-api/src/test_runner/RunOptions.ts | 11 + .../stryker-api/src/test_runner/RunResult.ts | 30 +++ .../stryker-api/src/test_runner/RunStatus.ts | 17 ++ .../src/test_runner/RunnerOptions.ts | 23 ++ .../stryker-api/src/test_runner/TestResult.ts | 25 ++ .../stryker-api/src/test_runner/TestRunner.ts | 62 +++++ .../src/test_runner/TestRunnerFactory.ts | 35 +++ .../stryker-api/src/test_runner/TestStatus.ts | 19 ++ .../integration/install-module/Executor.ts | 0 .../install-module/install-module.ts | 48 ++++ .../stryker-api/test/unit/core/FactorySpec.ts | 40 +++ .../testResources}/module/.gitignore | 0 .../testResources/module/package.json | 22 ++ .../testResources/module/tsconfig.json | 16 ++ .../testResources/module/useConfig.ts | 17 ++ .../testResources/module/useCore.ts | 29 +++ .../testResources/module/useMutant.ts | 20 ++ .../testResources/module/useReport.ts | 59 +++++ .../testResources/module/useTestFramework.ts | 27 ++ .../testResources/module/useTestRunner.ts | 51 ++++ packages/stryker-api/test_framework.ts | 3 + packages/stryker-api/test_runner.ts | 9 + packages/stryker-api/tsconfig.json | 28 +++ .../stryker-api/tslint.json | 0 packages/stryker/.editorconfig | 4 + .../stryker/.gitattributes | 0 packages/stryker/.gitignore | 15 ++ .npmignore => packages/stryker/.npmignore | 0 packages/stryker/.travis.yml | 13 + .../stryker/.vscode}/launch.json | 0 packages/stryker/.vscode/settings.json | 16 ++ CHANGELOG.md => packages/stryker/CHANGELOG.md | 0 .../stryker/CONTRIBUTING.md | 0 Gruntfile.js => packages/stryker/Gruntfile.js | 0 packages/stryker/LICENSE | 201 +++++++++++++++ packages/stryker/README.md | 231 ++++++++++++++++++ {bin => packages/stryker/bin}/stryker | 0 packages/stryker/package.json | 122 +++++++++ {src => packages/stryker/src}/ConfigReader.ts | 0 .../stryker/src}/FileStatements.ts | 0 .../stryker/src}/InputFileResolver.ts | 0 {src => packages/stryker/src}/Mutant.ts | 0 .../stryker/src}/MutantTestMatcher.ts | 0 .../stryker/src}/MutatorOrchestrator.ts | 4 +- {src => packages/stryker/src}/PluginLoader.ts | 0 .../stryker/src}/ReporterOrchestrator.ts | 0 {src => packages/stryker/src}/Sandbox.ts | 0 .../stryker/src}/SandboxCoordinator.ts | 0 {src => packages/stryker/src}/Stryker.ts | 0 .../stryker/src}/TestFrameworkOrchestrator.ts | 0 .../src}/coverage/CoverageInstrumenter.ts | 0 .../coverage/CoverageInstrumenterStream.ts | 1 - .../isolated-runner/IsolatedRunnerOptions.ts | 0 .../IsolatedTestRunnerAdapter.ts | 0 .../IsolatedTestRunnerAdapterFactory.ts | 0 .../IsolatedTestRunnerAdapterWorker.ts | 0 .../src}/isolated-runner/MessageProtocol.ts | 0 .../ResilientTestRunnerFactory.ts | 0 .../src}/isolated-runner/RetryDecorator.ts | 0 .../isolated-runner/TestRunnerDecorator.ts | 0 .../src}/isolated-runner/TimeoutDecorator.ts | 0 .../src}/mutators/ArrayDeclaratorMutator.ts | 7 +- .../src}/mutators/BinaryOperatorMutator.ts | 8 +- .../src}/mutators/BlockStatementMutator.ts | 7 +- .../src}/mutators/LogicalOperatorMutator.ts | 14 +- .../mutators/RemoveConditionalsMutator.ts | 12 +- .../src}/mutators/UnaryOperatorMutator.ts | 14 +- .../src}/mutators/UpdateOperatorMutator.ts | 12 +- .../src}/reporters/BroadcastReporter.ts | 0 .../src}/reporters/ClearTextReporter.ts | 0 .../stryker/src}/reporters/DotsReporter.ts | 0 .../src}/reporters/EventRecorderReporter.ts | 0 .../reporters/ProgressAppendOnlyReporter.ts | 0 .../stryker/src}/reporters/ProgressBar.ts | 0 .../stryker/src}/reporters/ProgressKeeper.ts | 0 .../src}/reporters/ProgressReporter.ts | 0 .../stryker/src}/reporters/StrictReporter.ts | 0 {src => packages/stryker/src}/stryker-cli.ts | 0 .../stryker/src}/utils/StrykerTempFolder.ts | 0 {src => packages/stryker/src}/utils/Task.ts | 0 {src => packages/stryker/src}/utils/Timer.ts | 0 .../stryker/src}/utils/fileUtils.ts | 0 .../stryker/src}/utils/objectUtils.ts | 0 packages/stryker/src/utils/parserUtils.ts | 98 ++++++++ .../stryker/stryker.conf.js | 0 .../stryker/test}/helpers/TestRunnerMock.ts | 0 .../stryker/test}/helpers/log4jsMock.ts | 0 .../stryker/test}/helpers/producers.ts | 0 .../test}/helpers/registerChaiPlugins.ts | 0 .../test}/helpers/registerSinonPlugins.ts | 0 .../stryker/test}/helpers/streamHelpers.ts | 0 .../config-reader/ConfigReaderSpec.ts | 0 .../integration/install-module/Executor.ts | 56 +++++ .../install-module/install-module.ts | 0 .../isolated-runner/AdditionalTestRunners.ts | 0 .../DiscoverRegexTestRunner.ts | 0 .../isolated-runner/ErroredTestRunner.ts | 0 .../NeverResolvedTestRunner.ts | 0 .../ResilientTestRunnerFactorySpec.ts | 0 .../SlowInitAndDisposeTestRunner.ts | 0 .../test}/integration/utils/fileUtilsSpec.ts | 0 .../test}/unit/InputFileResolverSpec.ts | 0 .../stryker/test}/unit/MutantSpec.ts | 0 .../test}/unit/MutantTestMatcherSpec.ts | 0 .../test}/unit/MutatorOrchestratorSpec.ts | 8 +- .../stryker/test}/unit/PluginLoaderSpec.ts | 0 .../test}/unit/ReporterOrchestratorSpec.ts | 0 .../test}/unit/SandboxCoordinatorSpec.ts | 0 .../stryker/test}/unit/SandboxSpec.ts | 0 .../stryker/test}/unit/StrykerSpec.ts | 0 .../unit/TestFrameworkOrchestratorSpec.ts | 0 .../unit/coverage/CoverageInstrumenterSpec.ts | 0 .../CoverageInstrumenterStreamSpec.ts | 0 .../IsolatedTestRunnerAdapterSpec.ts | 0 .../isolated-runner/RetryDecoratorSpec.ts | 0 .../TestRunnerDecoratorSpec.ts | 0 .../isolated-runner/TimeoutDecoratorSpec.ts | 0 .../mutators/ArrayDeclaratorMutatorSpec.ts | 17 +- .../mutators/BinaryOperatorMutatorSpec.ts | 20 +- .../mutators/BlockStatementMutatorSpec.ts | 17 +- .../mutators/LogicalOperatorMutatorSpec.ts | 13 +- .../mutators/RemoveConditionalsMutatorSpec.ts | 61 ++--- .../mutators/UpdateOperatorMutatorSpec.ts | 19 +- .../unit/reporters/BroadcastReporterSpec.ts | 0 .../unit/reporters/ClearTextReporterSpec.ts | 0 .../test}/unit/reporters/DotsReporterSpec.ts | 0 .../reporters/EventRecorderReporterSpec.ts | 0 .../reporters/ProgressAppendOnlyReporter.ts | 0 .../unit/reporters/ProgressReporterSpec.ts | 0 .../stryker/test}/unit/utils/TimerSpec.ts | 0 .../stryker/test}/unit/utils/fileUtilsSpec.ts | 0 .../test}/unit/utils/parserUtilsSpec.ts | 4 +- .../config-reader/invalid.conf.js | 0 .../config-reader/stryker.conf.js | 0 .../config-reader/syntax-error.conf.js | 0 .../config-reader/valid.conf.js | 0 .../stryker/testResources/module/.gitignore | 2 + .../testResources}/module/package.json | 0 .../testResources}/module/stryker.conf.js | 0 .../testResources}/module/tsconfig.json | 0 .../testResources}/module/usingStryker.d.ts | 0 .../testResources}/module/usingStryker.ts | 0 .../testResources}/sampleProject/src/Add.js | 0 .../testResources}/sampleProject/src/Array.js | 0 .../sampleProject/src/Circle.js | 0 .../testResources}/sampleProject/src/Error.js | 0 .../sampleProject/src/InfiniteAdd.js | 0 .../sampleProject/stryker.conf.js | 0 .../sampleProject/test/AddSpec.js | 0 .../sampleProject/test/CircleSpec.js | 0 .../sampleProject/test/EmptySpec.js | 0 .../sampleProject/test/FailingAddSpec.js | 0 .../vendor/zone.js/zone.file.js | 0 .../stryker/tsconfig.json | 0 packages/stryker/tslint.json | 58 +++++ src/utils/parserUtils.ts | 71 ------ 200 files changed, 2564 insertions(+), 502 deletions(-) create mode 100644 lerna.json rename .editorconfig => packages/stryker-api/.editorconfig (100%) create mode 100644 packages/stryker-api/.gitignore create mode 100644 packages/stryker-api/.npmignore create mode 100644 packages/stryker-api/.travis.yml create mode 100644 packages/stryker-api/.vscode/launch.json create mode 100644 packages/stryker-api/.vscode/settings.json create mode 100644 packages/stryker-api/CHANGELOG.md create mode 100644 packages/stryker-api/Gruntfile.js rename LICENSE => packages/stryker-api/LICENSE (100%) create mode 100644 packages/stryker-api/README.md create mode 100644 packages/stryker-api/config.ts create mode 100644 packages/stryker-api/core.ts create mode 100644 packages/stryker-api/mutant.ts create mode 100644 packages/stryker-api/package.json create mode 100644 packages/stryker-api/report.ts create mode 100644 packages/stryker-api/src/config/Config.ts create mode 100644 packages/stryker-api/src/config/ConfigWriter.ts create mode 100644 packages/stryker-api/src/config/ConfigWriterFactory.ts create mode 100644 packages/stryker-api/src/core/Factory.ts create mode 100644 packages/stryker-api/src/core/InputFile.ts create mode 100644 packages/stryker-api/src/core/InputFileDescriptor.ts create mode 100644 packages/stryker-api/src/core/Location.ts create mode 100644 packages/stryker-api/src/core/Position.ts create mode 100644 packages/stryker-api/src/core/Range.ts create mode 100644 packages/stryker-api/src/core/StrykerOptions.ts create mode 100644 packages/stryker-api/src/mutant/IdentifiedNode.ts create mode 100644 packages/stryker-api/src/mutant/Mutator.ts create mode 100644 packages/stryker-api/src/mutant/MutatorFactory.ts create mode 100644 packages/stryker-api/src/report/MatchedMutant.ts create mode 100644 packages/stryker-api/src/report/MutantResult.ts create mode 100644 packages/stryker-api/src/report/MutantStatus.ts create mode 100644 packages/stryker-api/src/report/Reporter.ts create mode 100644 packages/stryker-api/src/report/ReporterFactory.ts create mode 100644 packages/stryker-api/src/report/SourceFile.ts create mode 100644 packages/stryker-api/src/test_framework/TestFramework.ts create mode 100644 packages/stryker-api/src/test_framework/TestFrameworkFactory.ts create mode 100644 packages/stryker-api/src/test_framework/TestFrameworkSettings.ts create mode 100644 packages/stryker-api/src/test_runner/Coverage.ts create mode 100644 packages/stryker-api/src/test_runner/RunOptions.ts create mode 100644 packages/stryker-api/src/test_runner/RunResult.ts create mode 100644 packages/stryker-api/src/test_runner/RunStatus.ts create mode 100644 packages/stryker-api/src/test_runner/RunnerOptions.ts create mode 100644 packages/stryker-api/src/test_runner/TestResult.ts create mode 100644 packages/stryker-api/src/test_runner/TestRunner.ts create mode 100644 packages/stryker-api/src/test_runner/TestRunnerFactory.ts create mode 100644 packages/stryker-api/src/test_runner/TestStatus.ts rename {test => packages/stryker-api/test}/integration/install-module/Executor.ts (100%) create mode 100644 packages/stryker-api/test/integration/install-module/install-module.ts create mode 100644 packages/stryker-api/test/unit/core/FactorySpec.ts rename {testResources => packages/stryker-api/testResources}/module/.gitignore (100%) create mode 100644 packages/stryker-api/testResources/module/package.json create mode 100644 packages/stryker-api/testResources/module/tsconfig.json create mode 100644 packages/stryker-api/testResources/module/useConfig.ts create mode 100644 packages/stryker-api/testResources/module/useCore.ts create mode 100644 packages/stryker-api/testResources/module/useMutant.ts create mode 100644 packages/stryker-api/testResources/module/useReport.ts create mode 100644 packages/stryker-api/testResources/module/useTestFramework.ts create mode 100644 packages/stryker-api/testResources/module/useTestRunner.ts create mode 100644 packages/stryker-api/test_framework.ts create mode 100644 packages/stryker-api/test_runner.ts create mode 100644 packages/stryker-api/tsconfig.json rename tslint.json => packages/stryker-api/tslint.json (100%) create mode 100644 packages/stryker/.editorconfig rename .gitattributes => packages/stryker/.gitattributes (100%) create mode 100644 packages/stryker/.gitignore rename .npmignore => packages/stryker/.npmignore (100%) create mode 100644 packages/stryker/.travis.yml rename {.vscode => packages/stryker/.vscode}/launch.json (100%) create mode 100644 packages/stryker/.vscode/settings.json rename CHANGELOG.md => packages/stryker/CHANGELOG.md (100%) rename CONTRIBUTING.md => packages/stryker/CONTRIBUTING.md (100%) rename Gruntfile.js => packages/stryker/Gruntfile.js (100%) create mode 100644 packages/stryker/LICENSE create mode 100644 packages/stryker/README.md rename {bin => packages/stryker/bin}/stryker (100%) mode change 100755 => 100644 create mode 100644 packages/stryker/package.json rename {src => packages/stryker/src}/ConfigReader.ts (100%) rename {src => packages/stryker/src}/FileStatements.ts (100%) rename {src => packages/stryker/src}/InputFileResolver.ts (100%) rename {src => packages/stryker/src}/Mutant.ts (100%) rename {src => packages/stryker/src}/MutantTestMatcher.ts (100%) rename {src => packages/stryker/src}/MutatorOrchestrator.ts (97%) rename {src => packages/stryker/src}/PluginLoader.ts (100%) rename {src => packages/stryker/src}/ReporterOrchestrator.ts (100%) rename {src => packages/stryker/src}/Sandbox.ts (100%) rename {src => packages/stryker/src}/SandboxCoordinator.ts (100%) rename {src => packages/stryker/src}/Stryker.ts (100%) rename {src => packages/stryker/src}/TestFrameworkOrchestrator.ts (100%) rename {src => packages/stryker/src}/coverage/CoverageInstrumenter.ts (100%) rename {src => packages/stryker/src}/coverage/CoverageInstrumenterStream.ts (98%) rename {src => packages/stryker/src}/isolated-runner/IsolatedRunnerOptions.ts (100%) rename {src => packages/stryker/src}/isolated-runner/IsolatedTestRunnerAdapter.ts (100%) rename {src => packages/stryker/src}/isolated-runner/IsolatedTestRunnerAdapterFactory.ts (100%) rename {src => packages/stryker/src}/isolated-runner/IsolatedTestRunnerAdapterWorker.ts (100%) rename {src => packages/stryker/src}/isolated-runner/MessageProtocol.ts (100%) rename {src => packages/stryker/src}/isolated-runner/ResilientTestRunnerFactory.ts (100%) rename {src => packages/stryker/src}/isolated-runner/RetryDecorator.ts (100%) rename {src => packages/stryker/src}/isolated-runner/TestRunnerDecorator.ts (100%) rename {src => packages/stryker/src}/isolated-runner/TimeoutDecorator.ts (100%) rename {src => packages/stryker/src}/mutators/ArrayDeclaratorMutator.ts (75%) rename {src => packages/stryker/src}/mutators/BinaryOperatorMutator.ts (83%) rename {src => packages/stryker/src}/mutators/BlockStatementMutator.ts (63%) rename {src => packages/stryker/src}/mutators/LogicalOperatorMutator.ts (56%) rename {src => packages/stryker/src}/mutators/RemoveConditionalsMutator.ts (74%) rename {src => packages/stryker/src}/mutators/UnaryOperatorMutator.ts (56%) rename {src => packages/stryker/src}/mutators/UpdateOperatorMutator.ts (56%) rename {src => packages/stryker/src}/reporters/BroadcastReporter.ts (100%) rename {src => packages/stryker/src}/reporters/ClearTextReporter.ts (100%) rename {src => packages/stryker/src}/reporters/DotsReporter.ts (100%) rename {src => packages/stryker/src}/reporters/EventRecorderReporter.ts (100%) rename {src => packages/stryker/src}/reporters/ProgressAppendOnlyReporter.ts (100%) rename {src => packages/stryker/src}/reporters/ProgressBar.ts (100%) rename {src => packages/stryker/src}/reporters/ProgressKeeper.ts (100%) rename {src => packages/stryker/src}/reporters/ProgressReporter.ts (100%) rename {src => packages/stryker/src}/reporters/StrictReporter.ts (100%) rename {src => packages/stryker/src}/stryker-cli.ts (100%) rename {src => packages/stryker/src}/utils/StrykerTempFolder.ts (100%) rename {src => packages/stryker/src}/utils/Task.ts (100%) rename {src => packages/stryker/src}/utils/Timer.ts (100%) rename {src => packages/stryker/src}/utils/fileUtils.ts (100%) rename {src => packages/stryker/src}/utils/objectUtils.ts (100%) create mode 100644 packages/stryker/src/utils/parserUtils.ts rename stryker.conf.js => packages/stryker/stryker.conf.js (100%) rename {test => packages/stryker/test}/helpers/TestRunnerMock.ts (100%) rename {test => packages/stryker/test}/helpers/log4jsMock.ts (100%) rename {test => packages/stryker/test}/helpers/producers.ts (100%) rename {test => packages/stryker/test}/helpers/registerChaiPlugins.ts (100%) rename {test => packages/stryker/test}/helpers/registerSinonPlugins.ts (100%) rename {test => packages/stryker/test}/helpers/streamHelpers.ts (100%) rename {test => packages/stryker/test}/integration/config-reader/ConfigReaderSpec.ts (100%) create mode 100644 packages/stryker/test/integration/install-module/Executor.ts rename {test => packages/stryker/test}/integration/install-module/install-module.ts (100%) rename {test => packages/stryker/test}/integration/isolated-runner/AdditionalTestRunners.ts (100%) rename {test => packages/stryker/test}/integration/isolated-runner/DiscoverRegexTestRunner.ts (100%) rename {test => packages/stryker/test}/integration/isolated-runner/ErroredTestRunner.ts (100%) rename {test => packages/stryker/test}/integration/isolated-runner/NeverResolvedTestRunner.ts (100%) rename {test => packages/stryker/test}/integration/isolated-runner/ResilientTestRunnerFactorySpec.ts (100%) rename {test => packages/stryker/test}/integration/isolated-runner/SlowInitAndDisposeTestRunner.ts (100%) rename {test => packages/stryker/test}/integration/utils/fileUtilsSpec.ts (100%) rename {test => packages/stryker/test}/unit/InputFileResolverSpec.ts (100%) rename {test => packages/stryker/test}/unit/MutantSpec.ts (100%) rename {test => packages/stryker/test}/unit/MutantTestMatcherSpec.ts (100%) rename {test => packages/stryker/test}/unit/MutatorOrchestratorSpec.ts (92%) rename {test => packages/stryker/test}/unit/PluginLoaderSpec.ts (100%) rename {test => packages/stryker/test}/unit/ReporterOrchestratorSpec.ts (100%) rename {test => packages/stryker/test}/unit/SandboxCoordinatorSpec.ts (100%) rename {test => packages/stryker/test}/unit/SandboxSpec.ts (100%) rename {test => packages/stryker/test}/unit/StrykerSpec.ts (100%) rename {test => packages/stryker/test}/unit/TestFrameworkOrchestratorSpec.ts (100%) rename {test => packages/stryker/test}/unit/coverage/CoverageInstrumenterSpec.ts (100%) rename {test => packages/stryker/test}/unit/coverage/CoverageInstrumenterStreamSpec.ts (100%) rename {test => packages/stryker/test}/unit/isolated-runner/IsolatedTestRunnerAdapterSpec.ts (100%) rename {test => packages/stryker/test}/unit/isolated-runner/RetryDecoratorSpec.ts (100%) rename {test => packages/stryker/test}/unit/isolated-runner/TestRunnerDecoratorSpec.ts (100%) rename {test => packages/stryker/test}/unit/isolated-runner/TimeoutDecoratorSpec.ts (100%) rename {test => packages/stryker/test}/unit/mutators/ArrayDeclaratorMutatorSpec.ts (87%) rename {test => packages/stryker/test}/unit/mutators/BinaryOperatorMutatorSpec.ts (77%) rename {test => packages/stryker/test}/unit/mutators/BlockStatementMutatorSpec.ts (61%) rename {test => packages/stryker/test}/unit/mutators/LogicalOperatorMutatorSpec.ts (73%) rename {test => packages/stryker/test}/unit/mutators/RemoveConditionalsMutatorSpec.ts (69%) rename {test => packages/stryker/test}/unit/mutators/UpdateOperatorMutatorSpec.ts (74%) rename {test => packages/stryker/test}/unit/reporters/BroadcastReporterSpec.ts (100%) rename {test => packages/stryker/test}/unit/reporters/ClearTextReporterSpec.ts (100%) rename {test => packages/stryker/test}/unit/reporters/DotsReporterSpec.ts (100%) rename {test => packages/stryker/test}/unit/reporters/EventRecorderReporterSpec.ts (100%) rename {test => packages/stryker/test}/unit/reporters/ProgressAppendOnlyReporter.ts (100%) rename {test => packages/stryker/test}/unit/reporters/ProgressReporterSpec.ts (100%) rename {test => packages/stryker/test}/unit/utils/TimerSpec.ts (100%) rename {test => packages/stryker/test}/unit/utils/fileUtilsSpec.ts (100%) rename {test => packages/stryker/test}/unit/utils/parserUtilsSpec.ts (92%) rename {testResources => packages/stryker/testResources}/config-reader/invalid.conf.js (100%) rename {testResources => packages/stryker/testResources}/config-reader/stryker.conf.js (100%) rename {testResources => packages/stryker/testResources}/config-reader/syntax-error.conf.js (100%) rename {testResources => packages/stryker/testResources}/config-reader/valid.conf.js (100%) create mode 100644 packages/stryker/testResources/module/.gitignore rename {testResources => packages/stryker/testResources}/module/package.json (100%) rename {testResources => packages/stryker/testResources}/module/stryker.conf.js (100%) rename {testResources => packages/stryker/testResources}/module/tsconfig.json (100%) rename {testResources => packages/stryker/testResources}/module/usingStryker.d.ts (100%) rename {testResources => packages/stryker/testResources}/module/usingStryker.ts (100%) rename {testResources => packages/stryker/testResources}/sampleProject/src/Add.js (100%) rename {testResources => packages/stryker/testResources}/sampleProject/src/Array.js (100%) rename {testResources => packages/stryker/testResources}/sampleProject/src/Circle.js (100%) rename {testResources => packages/stryker/testResources}/sampleProject/src/Error.js (100%) rename {testResources => packages/stryker/testResources}/sampleProject/src/InfiniteAdd.js (100%) rename {testResources => packages/stryker/testResources}/sampleProject/stryker.conf.js (100%) rename {testResources => packages/stryker/testResources}/sampleProject/test/AddSpec.js (100%) rename {testResources => packages/stryker/testResources}/sampleProject/test/CircleSpec.js (100%) rename {testResources => packages/stryker/testResources}/sampleProject/test/EmptySpec.js (100%) rename {testResources => packages/stryker/testResources}/sampleProject/test/FailingAddSpec.js (100%) rename {testResources => packages/stryker/testResources}/vendor/zone.js/zone.file.js (100%) rename tsconfig.json => packages/stryker/tsconfig.json (100%) create mode 100644 packages/stryker/tslint.json delete mode 100644 src/utils/parserUtils.ts diff --git a/.bithoundrc b/.bithoundrc index b77a787673..8e110c4624 100644 --- a/.bithoundrc +++ b/.bithoundrc @@ -4,13 +4,12 @@ "wc": { "limit": 3000 } }, "test": [ - "test/**", - "testResources/**" + "packages/**/test/**", + "packages/**/testResources/**" ], - "ignore": [ - "src/TestFrameworkOrchestrator.ts", - "src/MutantTestMatcher.ts", - "testResources/config-reader/syntax-error.conf.js" + "ignore": [ + "packages/**/Gruntfile.js", + "packages/stryker/testResources/config-reader/syntax-error.conf.js" ], "dependencies": { "unused-ignores": [ diff --git a/.gitignore b/.gitignore index aed04b7a12..3c3629e647 100644 --- a/.gitignore +++ b/.gitignore @@ -1,15 +1 @@ node_modules -/coverage -/testResources/module/coverage -.tscache -.stryker-tmp -reports -typings -*.js.map -src/**/*.js -test/**/*.js -src/**/*.d.ts -test/**/*.d.ts -.idea -*.iml -*.tmp.txt \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 0f5d69f19c..040679b247 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,9 +5,5 @@ node_js: - "6" - "4" before_install: - - npm install -g grunt-cli - if [[ `npm -v` != 3* ]]; then npm i -g npm@3; fi install: npm install -before_script: - - export DISPLAY=:99.0 - - sh -e /etc/init.d/xvfb start diff --git a/README.md b/README.md index 5cd00326a4..35f078a97e 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,9 @@ *Professor X: For someone who hates mutants... you certainly keep some strange company.* *William Stryker: Oh, they serve their purpose... as long as they can be controlled.* +## Introduction +For an introduction to mutation testing and Stryker's features, see [stryker-mutator.github.io](http://stryker-mutator.github.io/). + ## Getting started Stryker is a mutation testing framework for JavaScript. It allows you to test your tests by temporarily inserting bugs. @@ -60,172 +63,4 @@ Make sure you *at least* specify the `files` and the `testRunner` options when m See our website for the [list of currently supported mutators](http://stryker-mutator.github.io/mutators.html). ## Configuration -All configuration options can either be set via the command line or via the `stryker.conf.js` config file. - -`files` and `mutate` both support globbing expressions using [node glob](https://github.com/isaacs/node-glob). -This is the same globbing format you might know from [Grunt](https://github.com/gruntjs/grunt) or [Karma](https://github.com/karma-runner/karma). -You can *ignore* files by adding an exclamation mark (`!`) at the start of an expression. - -#### Files required to run your tests -**Command line:** `[--files|-f] node_modules/a-lib/**/*.js,src/**/*.js,a.js,test/**/*.js` -**Config file:** `files: ['{ pattern: 'src/**/*.js', mutated: true }, '!src/**/index.js', 'test/**/*.js']` -**Default value:** *none* -**Mandatory**: yes -**Description:** -With `files` you specify all files needed to run your tests. If the test runner you use already provides the test framework (Jasmine, Mocha, etc.), -you should *not* include those files here as well. -The files will be loaded in the other in which they are specified. - -When using the command line, the list can only contain a comma separated list of globbing expressions. -When using the config file you can provide an array with `string`s or `InputFileDescriptor` objects, like so: - -* `string`: The globbing expression used for selecting the files needed to run the tests. -* `InputFileDescriptor` object: `{ pattern: 'pattern', included: true, mutated: false }`: - * The `pattern` property is mandatory and contains the globbing expression used for selecting the files. Using `!` to ignore files is *not* supported here. - * The `included` property is optional and determines whether or not this file should be loaded initially by the test-runner (default: true) - * The `mutated` property is optional and determines whether or not this file should be targeted for mutations (default: false) - -*Note*: To include a file/folder which start with an exclamation mark (`!`), use the `InputFileDescriptor` syntax. - -#### Source code files to mutate -**Command line:** `[--mutate|-m] src/**/*.js,a.js` -**Config file:** `mutate: ['src/**/*.js', 'a.js']` -**Default value:** *none* -**Mandatory**: no -**Description:** -With `mutate` you configure the subset of files to use for mutation testing. Generally speaking, these should be your own source files. -This is optional, as you can also use the `mutated` property with the `files` parameter or not mutate any files at all to perform a dry-run (test-run). -We expect a comma separated list of globbing expressions, which will be used to select the files to be mutated. - -#### Test runner -**Command line:** `--testRunner karma` -**Config file:** `testRunner: 'karma'` -**Default value:** *none* -**Mandatory**: yes -**Description:** -With `testRunner` you specify the test runner to run your tests. This option is required. -Make sure the test runner plugin for Stryker is installed. E.g. we need the `stryker-karma-runner` to use `karma` as a test runner. -See the [list of plugins](http://stryker-mutator.github.io/plugins.html) for an up-to-date list of supported test runners and plugins. - -#### Test framework -**Command line:** `--testFramework jasmine` -**Config file:** `testFramework: 'jasmine'` -**Default value:** *none* -**Mandatory**: yes -**Description:** -With `testFramework` you configure which test framework your tests are using. This value is directly consumed by the test runner and therefore -depends what framework that specific test runner supports. By default, this value is also used for `testFramework`. - -#### Type of coverage analysis -**Full notation:** `--coverageAnalysis perTest` -**Config file key:** `coverageAnalysis: 'perTest'` -**Default value:** `perTest` -**Mandatory**: no -**Description:** -With `coverageAnalysis` you specify which coverage analysis strategy you want to use. -Stryker can analyse code coverage results. This can potentially speed up mutation testing a lot, as only the tests covering a -particular mutation are tested for each mutant. -This does *not* influence the resulting mutation testing score. It only improves performance, so we enable it by default. - -The possible values are: -* **off**: Stryker will not determine the code covered by tests during the initial test run phase. All tests will be executed for each mutant -during the mutation testing phase. - -* **all**: Stryker will determine the code covered by all tests during the initial test run phase. Only mutants actually covered by your -test suite are tested during the mutation testing phase. This setting requires your test runner to be able to report the code coverage back to Stryker. -Currently, only the `stryker-mocha-runner` and the `stryker-karma-runner` do this. - -* **perTest**: Stryker will determine the code covered by your test per executed test during the initial test run phase. Only mutants actually covered by your -test suite are tested during the mutation testing phase. -Only the tests that cover a particular mutant are tested for each one. This requires your tests to be able to run independently of each other and in random order. -In addition to requiring your test runner to be able to report the code coverage back to Stryker, your chosen `testFramework` also needs to support running code - before and after each test, as well as test filtering. - Currently, `stryker-mocha-runner` as well as `stryker-karma-runner` support this. However, `stryker-karma-runner` support is limited to using it with `Jasmine` as the test framework - (`Mocha` is not yet supported). - -#### Reporters -**Command line:** `--reporter clear-text,progress,dots` -**Config file:** `reporter: ['clear-text', 'progress', 'dots']` -**Default value:** `['clear-text', 'progress']` -**Mandatory**: no -**Description:** -With `reporter` you can set a reporter or group of reporters for stryker to use. -These reporters can be used out of the box: `clear-text`, `progress` and `event-recorder`. -By default `clear-text` and `progress` are active if no reporter is configured. -You can load additional plugins to get more reporters. See [stryker-mutator.github.io](http://stryker-mutator.github.io) -for an up-to-date list of supported reporter plugins and a description on each reporter. - -The `clear-text` reporter supports an additional config option to show more tests that were executed to kill a mutant. The config for your config file is: `clearTextReporter: { maxTestsToLog: 3 },` - -#### Plugins -**Command line:** `--plugins stryker-html-reporter,stryker-karma-runner` -**Config file:** `plugins: ['stryker-html-reporter', 'stryker-karma-runner']` -**Default value:** `['stryker-*']` -**Mandatory**: no -**Description:** -With `plugins` you can add additional Node modules for Stryker to load (or `require`). -By default, all `node_modules` starting with `stryker-` will be loaded, so you would normally not need to specify this option. -These modules should be installed right next to stryker. For a current list of plugins, -you can consult [npm](https://www.npmjs.com/search?q=%40stryker-plugin) or -[stryker-mutator.github.io](http://stryker-mutator.github.io). - -#### Start of port range for test runners -**Command line:** `--port 9234` -**Config file:** `port: 9234` -**Default value:** `9234` -**Mandatory**: no -**Description:** -With `port` you specify the first port to pass on to the test runner to use. Any additional test runners will be spawned using ports n+1, n+2, etc. -For example, when you set to use port 9234 and Stryker decides to start four test runner processes, ports 9234, 9235, 9236 and 9237 will be passed to the test runner. -If the test runner decides to use the port it should be available for use. - -#### Global timeout in milliseconds -**Command line:** `--timeoutMs 5000` -**Config file:** `timeoutMs: 5000` -**Default value:** `5000` -**Mandatory**: no -**Description:** -When Stryker is mutating code, it cannot determine indefinitely whether or not a code mutation results in an infinite loop (see [Halting problem](https://en.wikipedia.org/wiki/Halting_problem)). -In order to battle infinite loops, a test run gets killed after a certain period. This period is configurable with two settings: `timeoutMs` and `timeoutFactor`. -To calculate the actual timeout in milliseconds the, following formula is used: - -``` -timeoutForTestRunMs = timeOfTheInitialTestRunMs * timeoutFactor + timeoutMs -``` - -With `timeoutFactor` you can configure the allowed deviation relative to the time of a normal test run. Tweak this if you notice that mutants are prone to creating slower code, but not infinite loops. -`timeoutMs` let's you configure an absolute deviation. Use it, if you run Stryker on a busy machine and you need to wait longer to make sure that the code indeed entered an infinite loop. - -#### Timeout factor -**Command line:** `--timeoutFactor 1.5` -**Config file:** `timeoutFactor: 1.5` -**Default value:** `1.5` -**Mandatory**: no -**Description:** -See [Timeout in milliseconds](#Timeout-in-milliseconds). - -#### Number of maximum concurrent test runners -**Command line:** `--maxConcurrentTestRunners 3` -**Config file:** `maxConcurrentTestRunners: 3` -**Default value:** number of CPU cores -**Mandatory**: no -**Description:** -Specifies the maximum number of concurrent test runners to spawn. -Mutation testing is time consuming. By default Stryker tries to make the most of your CPU, by spawning as many test runners as you have CPU cores. -This setting allows you to override this default behavior. - -Reasons you might want to lower this setting: - -* Your test runner starts a browser (another CPU-intensive process) -* You're running on a shared server and/or -* Your hard disk cannot handle the I/O of all test runners - -#### Log level -**Command line:** `--logLevel info` -**Config file:** `logLevel: 'info'` -**Default value:** `info` -**Mandatory**: no -**Description:** - Set the `log4js` log level that Stryker uses (default is `info`). Possible values: `fatal`, `error`, `warn`, `info`, `debug`, `trace`, `all` and `off`. - *Note*: Test runners are run as child processes of the Stryker Node process. All output (stdout) of the `testRunner` is logged as `trace`. - Thus, to see logging output from the test runner set the `logLevel` to `all` or `trace`. +See the [stryker's package readme](https://github.com/stryker-mutator/stryker/packages/stryker/README.md#Configuration) for all config options. \ No newline at end of file diff --git a/lerna.json b/lerna.json new file mode 100644 index 0000000000..8e42d33fb4 --- /dev/null +++ b/lerna.json @@ -0,0 +1,7 @@ +{ + "lerna": "2.0.0-beta.38", + "packages": [ + "packages/*" + ], + "version": "independent" +} diff --git a/package.json b/package.json index 39da84fc9e..30e54387ea 100644 --- a/package.json +++ b/package.json @@ -1,122 +1,12 @@ { - "name": "stryker", - "version": "0.5.9", - "description": "The extendable JavaScript mutation testing framework", - "main": "src/Stryker.js", - "typings": "src/Stryker.d.ts", - "scripts": { - "test": "grunt test", - "start": "tsc -w", - "sample": "node src/stryker-cli.js --configFile testResources/sampleProject/stryker.conf.js", - "preversion": "grunt", - "clean": "grunt clean", - "jshint": "grunt jshint", - "tslint": "grunt tslint", - "coverage": "grunt coverage", - "intergration": "grunt intergration", - "build": "grunt build", - "serve": "grunt serve", - "watch-test": "grunt watch-test", - "release": "grunt release" - }, - "repository": { - "type": "git", - "url": "https://github.com/stryker-mutator/stryker" - }, - "engines": { - "node": ">=4" - }, - "keywords": [ - "mutation testing", - "mutation", - "testing", - "test", - "js", - "stryker" - ], - "author": "Simon de Lang", - "contributors": [ - "Simon de Lang ", - "Nico Jansen ", - "Sander Koenders ", - "global ", - "Philipp Weissenbacher ", - "Jasper Catthoor ", - "Nico Stapelbroek ", - "Alex van Assem ", - "Jelle Peters ", - "Jeremy Nagel ", - "MarktHart ", - "Michael Williamson ", - "Willem Meints " - ], - "license": "Apache-2.0", - "bin": { - "stryker": "./bin/stryker" - }, - "dependencies": { - "chalk": "^1.1.1", - "commander": "^2.9.0", - "es6-promise-pool": "^2.4.4", - "escodegen": "^1.8.0", - "esprima": "^2.7.0", - "glob": "^7.0.3", - "graceful-fs": "^4.1.11", - "istanbul": "^0.4.5", - "lodash": "^4.17.4", - "log4js": "^1.1.0", - "mkdirp": "^0.5.1", - "progress": "^1.1.8", - "serialize-javascript": "^1.3.0", - "tslib": "^1.5.0" - }, + "private": true, "devDependencies": { - "@types/chai-as-promised": "0.0.29", - "@types/chalk": "^0.4.28", - "@types/escodegen": "0.0.6", - "@types/esprima": "^2.1.31", - "@types/estree": "0.0.32", - "@types/glob": "^5.0.29", - "@types/graceful-fs": "^2.0.29", - "@types/istanbul": "^0.4.29", - "@types/karma": "^0.13.32", - "@types/lodash": "^4.14.34", - "@types/log4js": "0.0.32", - "@types/mkdirp": "^0.3.28", - "@types/mocha": "^2.2.34", - "@types/progress": "^1.1.28", - "@types/sinon": "^1.16.33", - "@types/sinon-as-promised": "^4.0.5", - "@types/sinon-chai": "^2.7.27", - "chai": "^3.4.1", - "chai-as-promised": "^6.0.0", - "grunt": "^1.0.1", - "grunt-bump": "^0.8.0", - "grunt-cli": "^1.2.0", - "grunt-contrib-clean": "^1.0.0", - "grunt-contrib-jshint": "^1.0.0", - "grunt-contrib-watch": "^1.0.0", - "grunt-conventional-changelog": "^6.1.0", - "grunt-mocha-istanbul": "^5.0.1", - "grunt-mocha-test": "^0.13.2", - "grunt-npm": "0.0.2", - "grunt-ts": "^6.0.0-beta.3", - "grunt-tslint": "^4.0.0", - "jasmine-core": "^2.4.1", - "karma": "1.0.0", - "karma-coverage": "^1.0.0", - "karma-jasmine": "^1.0.2", - "karma-phantomjs-launcher": "^1.0.1", - "load-grunt-tasks": "^3.5.0", - "mocha": "^3.2.0", - "sinon": "^1.17.2", - "sinon-as-promised": "^4.0.2", - "sinon-chai": "^2.8.0", - "stryker-api": "^0.5.0-rc0", - "tslint": "^4.4.2", - "typescript": "~2.2.1" + "lerna": "^2.0.0-beta.38", + "typescript": "^2.2.1" }, - "peerDependencies": { - "stryker-api": "^0.5.0-rc0" + "scripts": { + "postinstall": "lerna bootstrap", + "build": "lerna run build", + "test": "lerna run test" } -} +} \ No newline at end of file diff --git a/.editorconfig b/packages/stryker-api/.editorconfig similarity index 100% rename from .editorconfig rename to packages/stryker-api/.editorconfig diff --git a/packages/stryker-api/.gitignore b/packages/stryker-api/.gitignore new file mode 100644 index 0000000000..d0c4789819 --- /dev/null +++ b/packages/stryker-api/.gitignore @@ -0,0 +1,14 @@ +node_modules +npm-debug.log +coverage +.tscache +typings +*.js.map +src/**/*.js +test/**/*.js +src/**/*.d.ts +test/**/*.d.ts +*.js +*.d.ts +!Gruntfile.js +!testResources \ No newline at end of file diff --git a/packages/stryker-api/.npmignore b/packages/stryker-api/.npmignore new file mode 100644 index 0000000000..d7a839916b --- /dev/null +++ b/packages/stryker-api/.npmignore @@ -0,0 +1,9 @@ +*.ts +!*.d.ts +*.js.map +Gruntfile.js +test +tsconfig.json +typings.json +testResources +typings \ No newline at end of file diff --git a/packages/stryker-api/.travis.yml b/packages/stryker-api/.travis.yml new file mode 100644 index 0000000000..6eedcb6e0d --- /dev/null +++ b/packages/stryker-api/.travis.yml @@ -0,0 +1,9 @@ +language: node_js +node_js: + - "node" + - "6" + - "4" +install: npm install +before_script: + - export DISPLAY=:99.0 + - sh -e /etc/init.d/xvfb start \ No newline at end of file diff --git a/packages/stryker-api/.vscode/launch.json b/packages/stryker-api/.vscode/launch.json new file mode 100644 index 0000000000..537fdc8230 --- /dev/null +++ b/packages/stryker-api/.vscode/launch.json @@ -0,0 +1,48 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Run unit tests", + "type": "node", + "request": "launch", + "program": "${workspaceRoot}/node_modules/grunt-cli/bin/grunt", + "preLaunchTask": "build", + "stopOnEntry": false, + "args": [ + "mochaTest:unit" + ], + "cwd": "${workspaceRoot}/.", + "runtimeExecutable": null, + "runtimeArgs": [ + "--nolazy" + ], + "env": { + "NODE_ENV": "development" + }, + "externalConsole": false, + "sourceMaps": true, + "outDir": "${workspaceRoot}" + },{ + "name": "Run integration tests", + "type": "node", + "request": "launch", + "program": "${workspaceRoot}/node_modules/grunt-cli/bin/grunt", + // "preLaunchTask": "build", + "stopOnEntry": false, + "args": [ + "mochaTest:integration" + ], + "cwd": "${workspaceRoot}/.", + "runtimeExecutable": null, + "runtimeArgs": [ + "--nolazy" + ], + "env": { + "NODE_ENV": "development" + }, + "externalConsole": false, + "sourceMaps": true, + "outDir": "${workspaceRoot}" + } + ] +} \ No newline at end of file diff --git a/packages/stryker-api/.vscode/settings.json b/packages/stryker-api/.vscode/settings.json new file mode 100644 index 0000000000..6c6cda894b --- /dev/null +++ b/packages/stryker-api/.vscode/settings.json @@ -0,0 +1,17 @@ +{ + "typescript.tsdk": "node_modules/typescript/lib", + "files": { + "exclude": { + ".git": "", + "**/*.js": { + "when": "$(basename).ts" + }, + "**/*.d.ts": { + "when": "$(basename).ts" + }, + "**/*.map": { + "when": "$(basename)" + } + } + } +} \ No newline at end of file diff --git a/packages/stryker-api/CHANGELOG.md b/packages/stryker-api/CHANGELOG.md new file mode 100644 index 0000000000..5bcc2223c7 --- /dev/null +++ b/packages/stryker-api/CHANGELOG.md @@ -0,0 +1,105 @@ + +## [0.4.2](https://github.com/stryker-mutator/stryker-api/compare/v0.4.1...v0.4.2) (2016-12-30) + + +### Bug Fixes + +* **config:** Update `files` array type ([#12](https://github.com/stryker-mutator/stryker-api/issues/12)) ([9874730](https://github.com/stryker-mutator/stryker-api/commit/9874730)) + + +### Features + +* **report:** Report matched mutants ([#13](https://github.com/stryker-mutator/stryker-api/issues/13)) ([b0e2f6a](https://github.com/stryker-mutator/stryker-api/commit/b0e2f6a)) + + + + +## [0.4.1](https://github.com/stryker-mutator/stryker-api/compare/v0.4.0...v0.4.1) (2016-11-30) + + +### Features + +* **es2015-promise:** Remove dep to es6-promise ([#11](https://github.com/stryker-mutator/stryker-api/issues/11)) ([7042381](https://github.com/stryker-mutator/stryker-api/commit/7042381)) + + + + +# [0.4.0](https://github.com/stryker-mutator/stryker-api/compare/v0.2.1...v0.4.0) (2016-11-20) + + +### Features + +* **configurable-concurrency:** Add setting for maxConcurrentTestRunners ([731a05b](https://github.com/stryker-mutator/stryker-api/commit/731a05b)) +* **configurable-concurrency:** Default value ([32abab2](https://github.com/stryker-mutator/stryker-api/commit/32abab2)) +* **mutant-status:** Add `Error` to `MutantStatus` ([#7](https://github.com/stryker-mutator/stryker-api/issues/7)) ([e9df479](https://github.com/stryker-mutator/stryker-api/commit/e9df479)) +* **new-api:** Allow for one-pass coverage/test ([#6](https://github.com/stryker-mutator/stryker-api/issues/6)) ([d42c3c7](https://github.com/stryker-mutator/stryker-api/commit/d42c3c7)) + + + + + +## 0.2.1 (2016-10-03) + +* 0.2.1 ([109d01e](https://github.com/stryker-mutator/stryker-api/commit/109d01e)) +* fix(version): Reset version back to 0.2.0 ([50bceb8](https://github.com/stryker-mutator/stryker-api/commit/50bceb8)) + + + + +# 0.3.0-0 (2016-09-30) + +* 0.3.0-0 ([e73cbba](https://github.com/stryker-mutator/stryker-api/commit/e73cbba)) +* feat(ts-2): Upgrade to typescript 2 (#5) ([88a4254](https://github.com/stryker-mutator/stryker-api/commit/88a4254)) + + + + +# 0.2.0 (2016-07-21) + +* 0.2.0 ([3410831](https://github.com/stryker-mutator/stryker-api/commit/3410831)) +* docs(readme): Add mutator to the list of extensions ([7cef4bd](https://github.com/stryker-mutator/stryker-api/commit/7cef4bd)) + + + + +## 0.1.2 (2016-07-18) + +* 0.1.2 ([52f330c](https://github.com/stryker-mutator/stryker-api/commit/52f330c)) +* feat(include-comments): Include comments in d-ts files (#2) ([0d2279e](https://github.com/stryker-mutator/stryker-api/commit/0d2279e)) +* feat(unincluded-files): Add `include` boolean (#3) ([32d7cdf](https://github.com/stryker-mutator/stryker-api/commit/32d7cdf)) + + + + +## 0.1.1 (2016-07-15) + +* 0.1.1 ([e5f039d](https://github.com/stryker-mutator/stryker-api/commit/e5f039d)) +* feat(testRunner): Add lifecycle events. (#1) ([94e61c7](https://github.com/stryker-mutator/stryker-api/commit/94e61c7)) + + + + +# 0.1.0 (2016-07-01) + +* 0.0.3 ([af5864e](https://github.com/stryker-mutator/stryker-api/commit/af5864e)) +* 0.1.0 ([1530de2](https://github.com/stryker-mutator/stryker-api/commit/1530de2)) +* docs(readme.md) Update markup ([18f4907](https://github.com/stryker-mutator/stryker-api/commit/18f4907)) +* feat(testSelector) Add test selector option to stryker ([79952c3](https://github.com/stryker-mutator/stryker-api/commit/79952c3)) + + + + +## 0.0.2 (2016-06-09) + +* docs(build) Add travis build file ([6a7acdb](https://github.com/stryker-mutator/stryker-api/commit/6a7acdb)) +* docs(readme) Add stryker logo ([66d45db](https://github.com/stryker-mutator/stryker-api/commit/66d45db)) +* fix(TestRunner) Replace TestRunner base class with interface ([8507b89](https://github.com/stryker-mutator/stryker-api/commit/8507b89)) +* Initial commit - Basic copy from stryker-mutator/stryker ([e9818e5](https://github.com/stryker-mutator/stryker-api/commit/e9818e5)) +* Initial version of the stryker-api ([f7bb9c2](https://github.com/stryker-mutator/stryker-api/commit/f7bb9c2)) +* refactor(Factory) Fix typo in error message ([70eec6c](https://github.com/stryker-mutator/stryker-api/commit/70eec6c)) +* refactor(package.json) Remove unused dependencies ([b9ba1a4](https://github.com/stryker-mutator/stryker-api/commit/b9ba1a4)) +* refactor(report) Rename spec to test as it is more logical in the context of a test report. ([9396c98](https://github.com/stryker-mutator/stryker-api/commit/9396c98)) +* test(integration) Add a lot of integration tests ([ed24290](https://github.com/stryker-mutator/stryker-api/commit/ed24290)) + + + diff --git a/packages/stryker-api/Gruntfile.js b/packages/stryker-api/Gruntfile.js new file mode 100644 index 0000000000..6f3028e246 --- /dev/null +++ b/packages/stryker-api/Gruntfile.js @@ -0,0 +1,126 @@ +'use strict'; + +module.exports = function (grunt) { + + require('load-grunt-tasks')(grunt); + + grunt.initConfig({ + + clean: { + build: { + src: ['+(test|src)/**/*+(.d.ts|.js|.map)', '*+(.js|.d.ts|.map)', '!Gruntfile.js'] + }, + coverage: { + src: ['coverage'] + }, + test: { + src: ['testResources/module/node_modules/stryker-api'] + } + }, + + watch: { + testFiles: { + files: ['**/*.js'], + tasks: ['mochaTest:unit'] + } + }, + mochaTest: { + unit: { + options: { + reporter: 'spec' + }, + src: ['test/unit/**/*.js'] + }, + integration: { + options: { + reporter: 'spec', + timeout: 5000 + }, + // Register helpers before, it includes a log4js mock which has to be loaded as early as possible + src: ['test/helpers/**/*.js', 'test/integration/**/*.js'] + } + }, + mocha_istanbul: { + coverage: { + // Register helpers before, it includes a log4js mock which has to be loaded as early as possible + src: ['test/helpers/**/*.js', 'test/unit/**/*.js', 'test/integration/**/*.js'], + } + }, + istanbul_check_coverage: { + default: { + options: { + coverageFolder: 'coverage*', + check: { + lines: 80, + statements: 80 + } + } + } + }, + /* End code coverage */ + + ts: { + options: { + failOnTypeErrors: true + }, + build: { + tsconfig: { + passThrough: true + } + }, + }, + + tslint: { + src: { + src: ['*.ts', 'src/**/*.ts'] + }, + test: { + src: ['test/**/*.ts', 'testResources/module/*.ts'] + } + }, + 'npm-contributors': { + options: { + commitMessage: 'chore: update contributors' + } + }, + conventionalChangelog: { + release: { + options: { + changelogOpts: { + preset: 'angular' + } + }, + src: 'CHANGELOG.md' + } + }, + bump: { + options: { + commitFiles: [ + 'package.json', + 'CHANGELOG.md' + ], + commitMessage: 'chore: release v%VERSION%', + prereleaseName: 'rc' + } + } + }); + + grunt.registerTask('release', 'Build, bump and publish to NPM.', function (type) { + grunt.task.run([ + 'test', + 'npm-contributors', + 'bump:' + (type || 'patch') + ':bump-only', + 'conventionalChangelog', + 'bump-commit', + 'npm-publish' + ]); + }); + + grunt.registerTask('default', ['test']); + grunt.registerTask('watch-test', ['test', 'watch']); + grunt.registerTask('test', ['build', 'coverage']); + grunt.registerTask('build', ['clean', 'tslint', 'ts']); + grunt.registerTask('integration', ['mochaTest:integration']); + grunt.registerTask('coverage', ['mocha_istanbul:coverage']); + grunt.registerTask('serve', ['watch']); +}; diff --git a/LICENSE b/packages/stryker-api/LICENSE similarity index 100% rename from LICENSE rename to packages/stryker-api/LICENSE diff --git a/packages/stryker-api/README.md b/packages/stryker-api/README.md new file mode 100644 index 0000000000..37a1c89b46 --- /dev/null +++ b/packages/stryker-api/README.md @@ -0,0 +1,24 @@ +[![Build Status](https://travis-ci.org/stryker-mutator/stryker.svg?branch=master)](https://travis-ci.org/stryker-mutator/stryker-api) +[![Gitter](https://badges.gitter.im/stryker-mutator/stryker.svg)](https://gitter.im/stryker-mutator/stryker?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) + +![Stryker](https://github.com/stryker-mutator/stryker/raw/master/stryker-80x80.png) + +# Stryker API +This is the repository for maintaining the API of the [Stryker](http://stryker-mutator.github.io) JavaScript mutation testing framework. +Plugin creators should depend on this API rather than on the main Stryker repository directly. + +# Extension use cases +You can extend Stryker in a number of ways. + +1. Create your own `Mutator` +2. Create a custom `Reporter` +3. Create a `TestFramework` for a test framework +4. Create a `TestRunner` to bridge the gap between your test runner and Stryker +5. Create a custom way of configuring Stryker by creating a `ConfigWriter` + +All extension points work in the same basic way. + +1. Create a `constructor function` (or `class`) +2. Register the `constructor function` to the correct `Factory`. + +More info comming soon. In the mean time, take a look at the [Stryker homepage](http://stryker-mutator.github.io). \ No newline at end of file diff --git a/packages/stryker-api/config.ts b/packages/stryker-api/config.ts new file mode 100644 index 0000000000..319e2f8ffd --- /dev/null +++ b/packages/stryker-api/config.ts @@ -0,0 +1,3 @@ +export {default as Config} from './src/config/Config'; +export {default as ConfigWriter} from './src/config/ConfigWriter'; +export {default as ConfigWriterFactory} from './src/config/ConfigWriterFactory'; diff --git a/packages/stryker-api/core.ts b/packages/stryker-api/core.ts new file mode 100644 index 0000000000..bb7c77674a --- /dev/null +++ b/packages/stryker-api/core.ts @@ -0,0 +1,7 @@ +export {default as StrykerOptions} from './src/core/StrykerOptions'; +export {default as Factory} from './src/core/Factory'; +export {default as InputFile} from './src/core/InputFile'; +export {default as Position} from './src/core/Position'; +export {default as Location} from './src/core/Location'; +export {default as Range} from './src/core/Range'; +export {default as InputFileDescriptor} from './src/core/InputFileDescriptor'; \ No newline at end of file diff --git a/packages/stryker-api/mutant.ts b/packages/stryker-api/mutant.ts new file mode 100644 index 0000000000..068d1aaafc --- /dev/null +++ b/packages/stryker-api/mutant.ts @@ -0,0 +1,3 @@ +export { default as Mutator } from './src/mutant/Mutator'; +export { default as MutatorFactory } from './src/mutant/MutatorFactory'; +export { IdentifiedNode, Identified } from './src/mutant/IdentifiedNode'; \ No newline at end of file diff --git a/packages/stryker-api/package.json b/packages/stryker-api/package.json new file mode 100644 index 0000000000..28fd7fb199 --- /dev/null +++ b/packages/stryker-api/package.json @@ -0,0 +1,64 @@ +{ + "name": "stryker-api", + "version": "0.5.0-2", + "description": "The api for the extendable JavaScript mutation testing framework Stryker", + "scripts": { + "test": "grunt test", + "tsc:w": "tsc -w", + "preversion": "grunt", + "build": "grunt build" + }, + "repository": { + "type": "git", + "url": "https://github.com/stryker-mutator/stryker-api" + }, + "keywords": [ + "mutation testing", + "mutation", + "testing", + "test", + "js", + "stryker" + ], + "contributors": [ + "nicojs ", + "Alex van Assem ", + "Jeremy Nagel ", + "Philipp Weissenbacher ", + "Simon de Lang " + ], + "license": "Apache-2.0", + "engines": { + "node": ">=4" + }, + "devDependencies": { + "@types/chai": "^3.4.32", + "@types/estree": "0.0.32", + "@types/mocha": "^2.2.31", + "@types/node": "^6.0.38", + "chai": "^3.4.1", + "chai-as-promised": "^6.0.0", + "grunt": "^1.0.1", + "grunt-bump": "^0.8.0", + "grunt-cli": "^1.2.0", + "grunt-contrib-clean": "^1.0.0", + "grunt-contrib-watch": "^1.0.0", + "grunt-conventional-changelog": "^6.1.0", + "grunt-mocha-istanbul": "^5.0.1", + "grunt-mocha-test": "^0.13.2", + "grunt-npm": "0.0.2", + "grunt-ts": "^6.0.0-beta.3", + "grunt-tslint": "^3.2.1", + "istanbul": "^0.4.0", + "load-grunt-tasks": "^3.5.2", + "mocha": "^3.0.2", + "mocha-sinon": "^1.1.6", + "sinon": "^1.17.2", + "sinon-chai": "^2.8.0", + "tslint": "^3.15.1", + "typescript": "^2.1.4" + }, + "dependencies": { + "tslib": "^1.6.0" + } +} diff --git a/packages/stryker-api/report.ts b/packages/stryker-api/report.ts new file mode 100644 index 0000000000..8454f6de7f --- /dev/null +++ b/packages/stryker-api/report.ts @@ -0,0 +1,7 @@ + +export { default as Reporter } from './src/report/Reporter'; +export { default as MutantResult } from './src/report/MutantResult'; +export { default as MutantStatus } from './src/report/MutantStatus'; +export { default as ReporterFactory } from './src/report/ReporterFactory'; +export { default as SourceFile } from './src/report/SourceFile'; +export { default as MatchedMutant } from './src/report/MatchedMutant'; \ No newline at end of file diff --git a/packages/stryker-api/src/config/Config.ts b/packages/stryker-api/src/config/Config.ts new file mode 100644 index 0000000000..f6cae8e2b0 --- /dev/null +++ b/packages/stryker-api/src/config/Config.ts @@ -0,0 +1,28 @@ +import { StrykerOptions, InputFileDescriptor } from '../../core'; + +export default class Config implements StrykerOptions { + + [customConfig: string]: any; + + files: Array; + mutate: string[]; + + logLevel = 'info'; + timeoutMs = 5000; + timeoutFactor = 1.5; + plugins: string[] = ['stryker-*']; + port = 9234; + reporter = ['progress', 'clear-text']; + coverageAnalysis: 'perTest' | 'all' | 'off' = 'perTest'; + testRunner: string; + testFramework: string; + maxConcurrentTestRunners: number = Infinity; + + public set(newConfig: StrykerOptions) { + if (newConfig) { + Object.keys(newConfig).forEach((key) => { + this[key] = newConfig[key]; + }); + } + } +} \ No newline at end of file diff --git a/packages/stryker-api/src/config/ConfigWriter.ts b/packages/stryker-api/src/config/ConfigWriter.ts new file mode 100644 index 0000000000..42b90428c9 --- /dev/null +++ b/packages/stryker-api/src/config/ConfigWriter.ts @@ -0,0 +1,7 @@ +import Config from './Config'; + +interface ConfigWriter { + write(config: Config): void; +} + +export default ConfigWriter; \ No newline at end of file diff --git a/packages/stryker-api/src/config/ConfigWriterFactory.ts b/packages/stryker-api/src/config/ConfigWriterFactory.ts new file mode 100644 index 0000000000..71e6fea351 --- /dev/null +++ b/packages/stryker-api/src/config/ConfigWriterFactory.ts @@ -0,0 +1,26 @@ +import {Factory} from '../../core'; +import ConfigWriter from './ConfigWriter'; + +namespace ConfigWriterFactory { + + /** + * Represents a Factory for ConfigWriters. + */ + class ConfigWriterFactory extends Factory { + + constructor() { + super('config-reader'); + } + } + + let configWriterFactory = new ConfigWriterFactory(); + + /** + * Returns the current instance of the ConfigWriterFactory. + */ + export function instance() { + return >configWriterFactory; + } +} + +export default ConfigWriterFactory; \ No newline at end of file diff --git a/packages/stryker-api/src/core/Factory.ts b/packages/stryker-api/src/core/Factory.ts new file mode 100644 index 0000000000..7469d637e2 --- /dev/null +++ b/packages/stryker-api/src/core/Factory.ts @@ -0,0 +1,56 @@ +/** + * Represents a Factory to which items can register themselves and which can be used to create instances of said items. + * represents the type of the (one single) constructor argument used to create the instances. + * represents the Type of the items created by this factory. + */ +abstract class Factory { + + /** + * Creates a new Factory. + * @param factoryName The name of the Factory. + */ + constructor(private factoryName: string) { + } + + private classMap: { [name: string]: { new (settings: TSettings): T } } = Object.create(null); + + /** + * Retrieves the known names registered to this factory. + * @returns A list of sorted items which are registered. + */ + knownNames(): string[] { + let keys = Object.keys(this.classMap); + keys.sort(); + return keys; + } + + /** + * Registers a constructor function to this factory. + * @param name The name of the item. + * @param constructor The constructor of the item. + */ + register(name: string, constructor: { new (settings: TSettings): T }): void { + this.classMap[name] = constructor; + } + + /** + * Creates a new instance of a registered item. + * @param name The name of the item. + * @param settings The settings object related to the item. + * @throws Will throw if no item has been registered with the specified name + * @returns A new instance of the requested item. + */ + create(name: string, settings: TSettings): T { + if (Object.keys(this.classMap).indexOf(name) < 0) { + throw new Error(`Could not find a ${this.factoryName} with name ${name}, did you install it correctly (for example: npm install --save-dev ${this.importSuggestion(name)})?`); + } else { + return new this.classMap[name](settings); + } + } + + protected importSuggestion(name: string): string { + return 'stryker-' + name; + } +} + +export default Factory; \ No newline at end of file diff --git a/packages/stryker-api/src/core/InputFile.ts b/packages/stryker-api/src/core/InputFile.ts new file mode 100644 index 0000000000..c2a85fc504 --- /dev/null +++ b/packages/stryker-api/src/core/InputFile.ts @@ -0,0 +1,8 @@ + +interface InputFile { + path: string; + mutated: boolean; + included: boolean; +} + +export default InputFile; \ No newline at end of file diff --git a/packages/stryker-api/src/core/InputFileDescriptor.ts b/packages/stryker-api/src/core/InputFileDescriptor.ts new file mode 100644 index 0000000000..749016cf66 --- /dev/null +++ b/packages/stryker-api/src/core/InputFileDescriptor.ts @@ -0,0 +1,8 @@ + +interface InputFileDescriptor { + pattern: string; + included?: boolean; + mutated?: boolean; +} + +export default InputFileDescriptor; \ No newline at end of file diff --git a/packages/stryker-api/src/core/Location.ts b/packages/stryker-api/src/core/Location.ts new file mode 100644 index 0000000000..98ad64ccef --- /dev/null +++ b/packages/stryker-api/src/core/Location.ts @@ -0,0 +1,11 @@ +import Position from './Position'; + +/** + * A location in the source code which can span multiple lines and/or columns. + */ +interface Location { + start: Position; + end: Position; +} + +export default Location; diff --git a/packages/stryker-api/src/core/Position.ts b/packages/stryker-api/src/core/Position.ts new file mode 100644 index 0000000000..6b9314dba2 --- /dev/null +++ b/packages/stryker-api/src/core/Position.ts @@ -0,0 +1,9 @@ +/** + * A specific spot in the source code. + */ +interface Position { + line: number; + column: number; +} + +export default Position; diff --git a/packages/stryker-api/src/core/Range.ts b/packages/stryker-api/src/core/Range.ts new file mode 100644 index 0000000000..c4cd5f54ff --- /dev/null +++ b/packages/stryker-api/src/core/Range.ts @@ -0,0 +1,7 @@ + +/** + * Represents a location in a file by range [fromInclusive, toExclusive] + */ +type Range = [number, number]; + +export default Range; \ No newline at end of file diff --git a/packages/stryker-api/src/core/StrykerOptions.ts b/packages/stryker-api/src/core/StrykerOptions.ts new file mode 100644 index 0000000000..4af42821c1 --- /dev/null +++ b/packages/stryker-api/src/core/StrykerOptions.ts @@ -0,0 +1,93 @@ +import InputFileDescriptor from './InputFileDescriptor'; + +interface StrykerOptions { + // this ensures that custom config for for example 'karma' can be added under the 'karma' key + [customConfig: string]: any; + + /** + * The files array determines which files are in scope for mutation testing. + * These include library files, test files and files to mutate, but should NOT include test framework files (for example jasmine). + * Each element can be either a string or an object with 2 properties + * * `string`: A globbing expression used for selecting the files needed to run the tests. + * * { pattern: 'pattern', included: true } : + * * The `pattern` property is mandatory and contains the globbing expression used for selecting the files + * * The `included` property is optional and determines whether or not this file should be loaded initially by the test-runner (default: true) + * * The `mutated` property is optional and determines whether or not this file should be targeted for mutations (default: false) + * + * @example + * files: ['test/helpers/**\/*.js', 'test/unit/**\/*.js', { pattern: 'src/**\/*.js', included: false }], + */ + files?: Array; + + /** + * A list of globbing expression used for selecting the files that should be mutated. + */ + mutate?: string[]; + + /** + * Specify the maximum number of concurrent test runners. Useful if you don't want to use + * all the CPU cores of your machine. Default: infinity, Stryker will decide for you and tries to use + * all CPUs in your machine optimally. + */ + maxConcurrentTestRunners?: number; + + /** + * A location to a config file. That file should export a function which accepts a "config" object which it uses to configure stryker + */ + configFile?: string; + + /** + * The name of the test framework to use + */ + testFramework?: string; + + /** + * The name of the test runner to use (default is the same name as the testFramework) + */ + testRunner?: string; + + /** + * Indicates which coverage analysis strategy to use. + * During mutation testion, stryker will try to only run the tests that cover a particular line of code. + * + * 'perTest' (default): Analyse coverage per test. + * 'all': Analyse the coverage for the entire test suite. + * 'off': Don't use coverage analysis + */ + coverageAnalysis?: 'perTest' | 'all' | 'off'; + + /** + * The name (or names) of the reporter to use + * Possible values: 'clear-text', 'progress'. + * Load more plugins to be able to use more plugins + */ + reporter?: string | string[]; + + /** + * The log4js loglevel. Possible values: fatal, error, warn, info, debug, trace, all and off. Default is "info" + */ + logLevel?: string; + + /** + * Amount of additional time, in milliseconds, the mutation test is allowed to run + */ + timeoutMs?: number; + + /** + * The factor is applied on top of the other timeouts when during mutation testing + */ + timeoutFactor?: number; + + /** + * A list of plugins. These plugins will be imported ('required') by Stryker upon loading. + */ + plugins?: string[]; + + /** + * The starting port to used for test frameworks that need to run a server (for example karma). + * If more test runners will run simultaniously, subsequent port numbers will be used (n+1, n+2, etc.) + */ + port?: number; +} + +export default StrykerOptions; \ No newline at end of file diff --git a/packages/stryker-api/src/mutant/IdentifiedNode.ts b/packages/stryker-api/src/mutant/IdentifiedNode.ts new file mode 100644 index 0000000000..bc95195bb6 --- /dev/null +++ b/packages/stryker-api/src/mutant/IdentifiedNode.ts @@ -0,0 +1,7 @@ +import { Node } from 'estree'; + +export interface Identified { + nodeID: number; +} + +export type IdentifiedNode = Node & Identified; \ No newline at end of file diff --git a/packages/stryker-api/src/mutant/Mutator.ts b/packages/stryker-api/src/mutant/Mutator.ts new file mode 100644 index 0000000000..7c41bec07c --- /dev/null +++ b/packages/stryker-api/src/mutant/Mutator.ts @@ -0,0 +1,25 @@ +import { IdentifiedNode } from './IdentifiedNode'; + +/** + * Represents a class which can mutate parts of an Abstract Syntax Tree. + */ +interface Mutator { + /** + * The name of the Mutator which may be used by reporters. + */ + name: string; + + /** + * Applies the Mutator to a Node. This can result in one or more mutated Nodes, or null if no mutation was applied. + * This method will be called on every node of the abstract syntax tree, + * implementing mutators should decide themselves if they want to mutate this specific node. + * If the mutator wants to mutate the node, it should return a clone of the node with mutations, + * otherwise null. + * @param node A FROZEN Node which could be cloned and mutated. + * @param copy A function to create a copy of an object. + * @returns An array of mutated Nodes. + */ + applyMutations(node: IdentifiedNode, copy: (obj: T, deep?: boolean) => T): void | IdentifiedNode | IdentifiedNode[]; +} + +export default Mutator; \ No newline at end of file diff --git a/packages/stryker-api/src/mutant/MutatorFactory.ts b/packages/stryker-api/src/mutant/MutatorFactory.ts new file mode 100644 index 0000000000..19a8a76399 --- /dev/null +++ b/packages/stryker-api/src/mutant/MutatorFactory.ts @@ -0,0 +1,23 @@ +import Mutator from './Mutator'; +import { Factory } from '../../core'; + +namespace MutatorFactory { + /** + * Represents a Factory for TestFrameworks. + */ + class MutatorFactory extends Factory { + constructor() { + super('mutator'); + } + } + let mutatorFactoryInstance = new MutatorFactory(); + + /** + * Returns the current instance of the MutatorFactory. + */ + export function instance() { + return >mutatorFactoryInstance; + } +} + +export default MutatorFactory; \ No newline at end of file diff --git a/packages/stryker-api/src/report/MatchedMutant.ts b/packages/stryker-api/src/report/MatchedMutant.ts new file mode 100644 index 0000000000..962691d631 --- /dev/null +++ b/packages/stryker-api/src/report/MatchedMutant.ts @@ -0,0 +1,9 @@ +interface MatchedMutant { + readonly mutatorName: string; + readonly scopedTestIds: number[]; + readonly timeSpentScopedTests: number; + readonly filename: string; + readonly replacement: string; +} + +export default MatchedMutant; \ No newline at end of file diff --git a/packages/stryker-api/src/report/MutantResult.ts b/packages/stryker-api/src/report/MutantResult.ts new file mode 100644 index 0000000000..be4ae8ed32 --- /dev/null +++ b/packages/stryker-api/src/report/MutantResult.ts @@ -0,0 +1,16 @@ +import MutantStatus from './MutantStatus'; +import {Location, Range} from '../../core'; + +interface MutantResult { + sourceFilePath: string; + mutatorName: string; + status: MutantStatus; + replacement: string; + originalLines: string; + mutatedLines: string; + testsRan: string[]; + location: Location; + range: Range; +} + +export default MutantResult; \ No newline at end of file diff --git a/packages/stryker-api/src/report/MutantStatus.ts b/packages/stryker-api/src/report/MutantStatus.ts new file mode 100644 index 0000000000..3562361156 --- /dev/null +++ b/packages/stryker-api/src/report/MutantStatus.ts @@ -0,0 +1,29 @@ +enum MutantStatus { + + /** + * The status of a survived mutant, because it was not covered by any test. + */ + NoCoverage, + + /** + * The status of a killed mutant. + */ + Killed, + + /** + * The status of a survived mutant. + */ + Survived, + + /** + * The status of a timed out mutant. + */ + TimedOut, + + /** + * The status of a mutant of which the tests resulted in an Error + */ + Error +} + +export default MutantStatus; \ No newline at end of file diff --git a/packages/stryker-api/src/report/Reporter.ts b/packages/stryker-api/src/report/Reporter.ts new file mode 100644 index 0000000000..b1e058cf31 --- /dev/null +++ b/packages/stryker-api/src/report/Reporter.ts @@ -0,0 +1,49 @@ +import SourceFile from './SourceFile'; +import MutantResult from './MutantResult'; +import MatchedMutant from './MatchedMutant'; + +/** + * Represents a reporter which can report during or after a Stryker run + */ +interface Reporter { + + /** + * Called when a source file was loaded + * @param file The immutable source file + */ + onSourceFileRead?(file: SourceFile): void; + + /** + * Called when all source files were loaded + * @param files The immutable source files + */ + onAllSourceFilesRead?(files: SourceFile[]): void; + + /** + * Called when mutants are matched with tests + * @param results The immutable array of mutants + */ + onAllMutantsMatchedWithTests?(results: ReadonlyArray): void; + + /** + * Called when a mutant was tested + * @param result The immutable result + */ + onMutantTested?(result: MutantResult): void; + + /** + * Called when all mutants were tested + * @param results The immutable results + */ + onAllMutantsTested?(results: MutantResult[]): void; + + /** + * Called when stryker wants to quite + * Gives a reporter the ability to finish up any async tasks + * Stryker will not close untill the promise is either resolved or rejected. + * @return a promise which will resolve when the reporter is done reporting + */ + wrapUp?(): void | Promise; +} + +export default Reporter; \ No newline at end of file diff --git a/packages/stryker-api/src/report/ReporterFactory.ts b/packages/stryker-api/src/report/ReporterFactory.ts new file mode 100644 index 0000000000..6595ea55b1 --- /dev/null +++ b/packages/stryker-api/src/report/ReporterFactory.ts @@ -0,0 +1,34 @@ +import {Factory, StrykerOptions} from '../../core'; +import Reporter from './Reporter'; + +namespace ReporterFactory { + + /** + * Represents a Factory for Reporters. + */ + class ReporterFactory extends Factory { + constructor() { + super('reporter'); + } + + /** + * Returns the import suggestion for a Reporter + * @param name The name of the Reporter the user tried to use. + * @returns The name of the package the user may want to install (if it exists). + */ + importSuggestion(name: string) { + return `stryker-${name}-reporter`; + } + } + + let reporterFactoryInstance = new ReporterFactory(); + + /** + * Returns the current instance of the TestRunnerFactory. + */ + export function instance() { + return >reporterFactoryInstance; + } +} + +export default ReporterFactory; \ No newline at end of file diff --git a/packages/stryker-api/src/report/SourceFile.ts b/packages/stryker-api/src/report/SourceFile.ts new file mode 100644 index 0000000000..88c883a5be --- /dev/null +++ b/packages/stryker-api/src/report/SourceFile.ts @@ -0,0 +1,7 @@ + +interface SourceFile { + path: string; + content: string; +} + +export default SourceFile; \ No newline at end of file diff --git a/packages/stryker-api/src/test_framework/TestFramework.ts b/packages/stryker-api/src/test_framework/TestFramework.ts new file mode 100644 index 0000000000..ffb057b8f8 --- /dev/null +++ b/packages/stryker-api/src/test_framework/TestFramework.ts @@ -0,0 +1,29 @@ +/** + * Represents a TestFramework which can select one or more tests to be executed. + */ +interface TestFramework { + + /** + * Creates a code fragment which, if included in a test run, + * is ran before a particular test is run. + */ + beforeEach(codeFragment: string): string; + + /** + * Creates a code fragment which, if included in a test run, + * is ran before a particular test is run. + */ + afterEach(codeFragment: string): string; + + /** + * Creates a code fragment which, if included in a test run, + * will be responsible for filtering out tests with given ids. + * The first test gets id 0, the second id 1, etc. + * + * @param indices A list of testId's to select. + * @returns A script which, if included in the test run, will filter out the correct tests. + */ + filter(ids: number[]): string; +} + +export default TestFramework; diff --git a/packages/stryker-api/src/test_framework/TestFrameworkFactory.ts b/packages/stryker-api/src/test_framework/TestFrameworkFactory.ts new file mode 100644 index 0000000000..bb19164705 --- /dev/null +++ b/packages/stryker-api/src/test_framework/TestFrameworkFactory.ts @@ -0,0 +1,24 @@ +import { Factory } from '../../core'; +import TestFrameworkSettings from './TestFrameworkSettings'; +import TestFramework from './TestFramework'; + +namespace TestFrameworkFactory { + /** + * Represents a Factory for TestFrameworks. + */ + class TestFrameworkFactory extends Factory { + constructor() { + super('test framework'); + } + } + let TestFrameworkFactoryInstance = new TestFrameworkFactory(); + + /** + * Returns the current instance of the TestFrameworkFactory. + */ + export function instance() { + return >TestFrameworkFactoryInstance; + } +} + +export default TestFrameworkFactory; \ No newline at end of file diff --git a/packages/stryker-api/src/test_framework/TestFrameworkSettings.ts b/packages/stryker-api/src/test_framework/TestFrameworkSettings.ts new file mode 100644 index 0000000000..ef5069e269 --- /dev/null +++ b/packages/stryker-api/src/test_framework/TestFrameworkSettings.ts @@ -0,0 +1,13 @@ +import {StrykerOptions} from '../../core'; + +/** + * Represents an settings object for a TestFramework. + */ +interface TestFrameworkSettings { + /** + * The StrykerOptions. + */ + options: StrykerOptions; +} + +export default TestFrameworkSettings; \ No newline at end of file diff --git a/packages/stryker-api/src/test_runner/Coverage.ts b/packages/stryker-api/src/test_runner/Coverage.ts new file mode 100644 index 0000000000..abf3917954 --- /dev/null +++ b/packages/stryker-api/src/test_runner/Coverage.ts @@ -0,0 +1,61 @@ +import {Location} from '../../core'; + +/** + * Represents a collection of code coverage results per test run. + */ +export interface CoveragePerTestResult { + /** + * The baseline coverage which is true for each test run. + * This baseline should be taken when all files all loaded, but before tests are ran. + */ + baseline: CoverageCollection; + /** + * The deviations with respect to the baseline per test. + */ + deviations: CoverageCollectionPerTest; +} + +/** + * Represents a collection of code coverage results per test run. + */ +export interface CoverageCollectionPerTest { + [testId: number]: CoverageCollection; +} + +/** + * Represents a collection of Coverage results for a set of files. + */ +export interface CoverageCollection { + /** + * An array of CoverageResults for files. + */ + [filename: string]: CoverageResult; +} + +/** + * Represents the coverage result for a single file. + */ +export interface CoverageResult { + /** + * Hash of statement counts, where keys are statement IDs. + */ + s: CoverageData; +} + +/** + * Indicates the amount of time a certain type of data was covered. + * The key depends on the context. This can for example be a line number, making the value the amount of times the line was covered. + */ +export interface CoverageData { + [ref: string]: number; +} + + +/** + * Hash where keys are statement IDs, and values are Location objects for each statement. + * The Location for a function definition is really an assignment, and should include the entire function. + */ +export interface StatementMap { + [ref: string]: Location; +} + diff --git a/packages/stryker-api/src/test_runner/RunOptions.ts b/packages/stryker-api/src/test_runner/RunOptions.ts new file mode 100644 index 0000000000..bab8a3e962 --- /dev/null +++ b/packages/stryker-api/src/test_runner/RunOptions.ts @@ -0,0 +1,11 @@ +/** + * Represents an options object for a single run of a TestRunner. + */ +interface RunOptions { + /** + * The amount of time (in milliseconds) the TestRunner has to complete the test run before a timeout occurs. + */ + timeout: number; +} + +export default RunOptions; \ No newline at end of file diff --git a/packages/stryker-api/src/test_runner/RunResult.ts b/packages/stryker-api/src/test_runner/RunResult.ts new file mode 100644 index 0000000000..00db9a771c --- /dev/null +++ b/packages/stryker-api/src/test_runner/RunResult.ts @@ -0,0 +1,30 @@ +import TestResult from './TestResult'; +import RunStatus from './RunStatus'; +import { CoverageCollection, CoveragePerTestResult } from './Coverage'; + +/** + * Represents the result of a test run. + */ +interface RunResult { + /** + * The individual test results. + */ + tests: TestResult[]; + + /** + * If `state` is `error`, this collection should contain the error messages + */ + errorMessages?: string[]; + + /** + * The status of the run + */ + status: RunStatus; + + /** + * Optional: the code coverage result of the run. + */ + coverage?: CoverageCollection | CoveragePerTestResult; +} + +export default RunResult; \ No newline at end of file diff --git a/packages/stryker-api/src/test_runner/RunStatus.ts b/packages/stryker-api/src/test_runner/RunStatus.ts new file mode 100644 index 0000000000..1504e416ab --- /dev/null +++ b/packages/stryker-api/src/test_runner/RunStatus.ts @@ -0,0 +1,17 @@ + +enum RunStatus { + /** + * Indicates that a test run is completed with failed or succeeded tests + */ + Complete, + /** + * Indicates that a test run cut off early with an error + */ + Error, + /** + * Indicates that a test run timed out + */ + Timeout +} + +export default RunStatus; \ No newline at end of file diff --git a/packages/stryker-api/src/test_runner/RunnerOptions.ts b/packages/stryker-api/src/test_runner/RunnerOptions.ts new file mode 100644 index 0000000000..f10a351dfb --- /dev/null +++ b/packages/stryker-api/src/test_runner/RunnerOptions.ts @@ -0,0 +1,23 @@ +import { StrykerOptions, InputFile } from '../../core'; + +/** + * Represents an options object to configure a TestRunner. + */ +interface RunnerOptions { + /** + * The collection of files to load into the test runner in that exact order. + */ + files: InputFile[]; + + /** + * Represents a free port which the test runner can choose to use + */ + port: number; + + /** + * The underlying strykerOptions + */ + strykerOptions: StrykerOptions; +} + +export default RunnerOptions; \ No newline at end of file diff --git a/packages/stryker-api/src/test_runner/TestResult.ts b/packages/stryker-api/src/test_runner/TestResult.ts new file mode 100644 index 0000000000..7ee997e336 --- /dev/null +++ b/packages/stryker-api/src/test_runner/TestResult.ts @@ -0,0 +1,25 @@ +import TestStatus from './TestStatus'; + +/** + * Indicates the result of a single test + */ +interface TestResult { + /** + * The full human readable name of the test + */ + name: string; + /** + * The status of the test + */ + status: TestStatus; + /** + * The time it took to run the test + */ + timeSpentMs: number; + /** + * Optional: messages in case of status: Failed + */ + failureMessages?: string[]; +} + +export default TestResult; diff --git a/packages/stryker-api/src/test_runner/TestRunner.ts b/packages/stryker-api/src/test_runner/TestRunner.ts new file mode 100644 index 0000000000..b481b63ad9 --- /dev/null +++ b/packages/stryker-api/src/test_runner/TestRunner.ts @@ -0,0 +1,62 @@ +import RunResult from './RunResult'; +import RunOptions from './RunOptions'; +import { EventEmitter } from 'events'; + + +/** + * Represents a TestRunner which can execute tests, resulting in a RunResult. + * + * A test runner should: + * - Report per a `testResult` per test. See `TestResult` interface to know what is expected. + * - Emit a `'test_done'` event with a TestResult as an argument every time a test is executed (for reporting purposes). + * - Report on code coverage after the initial test run (maybe, see below). + * + * ## A note on code coverage: + * + * TL;DR + * If the test runner ran the tests in the same process as the test runner is spawned in, it doesn't have to do anything. + * If it runs in a different process (i.e. a worker process) or uses a browser (i.e. karma) it needs to do something, see below. + * + * Collecting code coverage can improve the performance of a mutation test run quite a bit. Instead of running all tests for all mutants, + * it can select which tests to run per mutant. Code coverage is a shared responsibility between Stryker and the test runner depending on the + * `coverageStrategy`: + * + * 1. If `coverageAnalysis: 'off'`: No code coverage should be collected. + * 2. If `coverageStrategy: 'all'`: Coverage should be collected for the entire test run. + * 3. If `coverageStrategy: 'perTest'`: Coverage should be collected per test. + * + * For 2 and 3, Stryker will instrument the code with the istanbul code coverage engine during initial run. + * In case of 3, Stryker will also inject beforeEach and afterEach hooks (specific to each test framework) in distinct between specific tests. + * + * At the end of the test run, the code coverage report is ready in a global variable called `__coverage__`. Node based test runners + * which run their tests in the same process as the test runner is spawned in actually don't have to do any work, Stryker will be able + * to pick up the report globally. However, if running in worker processes or a browser, it is the test runner's responsibility to + * report the `__coverage__` at the end of the test run. + * + * If it doesn't exists globally, you don't have to do anything. In that case it's not an initial test run and there was no code instrumented. + */ +interface TestRunner extends EventEmitter { + + /** + * Optional. When implemented, will be called before runs are done on this test runner. + * @returns A promise if stuff is initialized asyncronously, runs will not start until the promise is resolved. + * Otherwise void + */ + init?(): Promise | void; + + /** + * Executes a test run. + * @param options The options for this test run. + * @returns A promise to eventually complete the test run and deliver a RunResult. + */ + run(options: RunOptions): Promise; + + /** + * Optional. When implemented, will be called before the test runner's process is killed. + * @returns A promise if stuff is destroyed asyncronously, the runners process will not end until the promise is resolved. + * Otherwise void + */ + dispose?(): Promise | void; +} + +export default TestRunner; diff --git a/packages/stryker-api/src/test_runner/TestRunnerFactory.ts b/packages/stryker-api/src/test_runner/TestRunnerFactory.ts new file mode 100644 index 0000000000..50c6360129 --- /dev/null +++ b/packages/stryker-api/src/test_runner/TestRunnerFactory.ts @@ -0,0 +1,35 @@ +import {Factory} from '../../core'; +import TestRunner from './TestRunner'; +import RunnerOptions from './RunnerOptions'; + +namespace TestRunnerFactory { + + /** + * Represents a Factory for TestRunners. + */ + class TestRunnerFactory extends Factory { + constructor() { + super('testrunner'); + } + + /** + * Returns the import suggestion for a TestRunner + * @param name The name of the TestRunner the user tried to use. + * @returns The name of the package the user may want to install (if it exists). + */ + importSuggestion(name: string) { + return `stryker-${name}-runner`; + } + } + + let testRunnerFactoryInstance = new TestRunnerFactory(); + + /** + * Returns the current instance of the TestRunnerFactory. + */ + export function instance() { + return >testRunnerFactoryInstance; + } +} + +export default TestRunnerFactory; \ No newline at end of file diff --git a/packages/stryker-api/src/test_runner/TestStatus.ts b/packages/stryker-api/src/test_runner/TestStatus.ts new file mode 100644 index 0000000000..d620ff02d3 --- /dev/null +++ b/packages/stryker-api/src/test_runner/TestStatus.ts @@ -0,0 +1,19 @@ +/** + * Indicates what the result of a single test was. + */ +enum TestStatus { + /** + * The test succeeded + */ + Success, + /** + * The test failed + */ + Failed, + /** + * The test was skipped (not executed) + */ + Skipped +} + +export default TestStatus; \ No newline at end of file diff --git a/test/integration/install-module/Executor.ts b/packages/stryker-api/test/integration/install-module/Executor.ts similarity index 100% rename from test/integration/install-module/Executor.ts rename to packages/stryker-api/test/integration/install-module/Executor.ts diff --git a/packages/stryker-api/test/integration/install-module/install-module.ts b/packages/stryker-api/test/integration/install-module/install-module.ts new file mode 100644 index 0000000000..1ce59bc734 --- /dev/null +++ b/packages/stryker-api/test/integration/install-module/install-module.ts @@ -0,0 +1,48 @@ +import Executor from './Executor'; +import {expect} from 'chai'; + +describe('we have a module using stryker', function () { + + this.timeout(100000); + + describe('after installing Stryker', () => { + let executor: Executor; + + before((done) => { + executor = new Executor('../../../testResources/module'); + executor.exec('npm install', {}, (errors) => done(errors)); + }); + + describe('when typescript is compiled', () => { + + let arrangeActAndAssertModule = (moduleToRun: string, partsToBeAsserted: string[]) => { + let stdOut: string; + + describe(`and i use the "${moduleToRun}" module`, () => { + before((done) => { + executor.exec(`npm run use:${moduleToRun}`, {}, (errors, out) => { + stdOut = out; + done(errors); + }); + }); + + it(`the output should contain ${partsToBeAsserted}`, () => { + partsToBeAsserted.forEach(part => expect(stdOut).to.contain(part)); + }); + }); + }; + + before((done) => { + executor.exec('npm run tsc', {}, (errors) => done(errors)); + }); + + arrangeActAndAssertModule('core', ['files', 'some', 'file', 'pattern']); + arrangeActAndAssertModule('config', ['plugins: [ \'stryker-*\' ]', 'port: 9234']); + arrangeActAndAssertModule('test_framework', ['framework-1']); + arrangeActAndAssertModule('mutant', ['nodeID: 3', 'type: \'Literal\'']); + arrangeActAndAssertModule('report', ['empty', 'all', 'status: 3', 'originalLines: \'string\'', 'Mutant status error: Error']); + arrangeActAndAssertModule('test_runner', ['MyTestRunner']); + + }); + }); +}); \ No newline at end of file diff --git a/packages/stryker-api/test/unit/core/FactorySpec.ts b/packages/stryker-api/test/unit/core/FactorySpec.ts new file mode 100644 index 0000000000..9551b7691a --- /dev/null +++ b/packages/stryker-api/test/unit/core/FactorySpec.ts @@ -0,0 +1,40 @@ +import { Factory } from '../../../core'; +import { expect } from 'chai'; + +describe('Factory', () => { + + class TestFactory extends Factory<{ settings: string }, { someInstance: string }> { + } + + class TestItem { + constructor(private s: { settings: string }) { } + + get someInstance() { + return this.s.settings; + } + } + + describe('when creating a sub-class', () => { + let sut = new TestFactory('test'); + + it('should have empty known names', () => { + expect(sut.knownNames()).to.be.empty; + }); + + it('should throw an error if it is requested to create a non-existing item', () => { + expect(() => sut.create('not-exist', { settings: 'not exist' })) + .to.throw(Error, 'Could not find a test with name not-exist, did you install it correctly (for example: npm install --save-dev stryker-not-exist)?'); + }); + + describe('when registering a test class "some-item"', () => { + beforeEach(() => { + sut.register('some-item', TestItem); + }); + + it('should retrieve a new item when `create` is called with "some-item"', () => { + expect(sut.create('some-item', { settings: 'some item' })).to.be.instanceof(TestItem); + }); + + }); + }); +}); \ No newline at end of file diff --git a/testResources/module/.gitignore b/packages/stryker-api/testResources/module/.gitignore similarity index 100% rename from testResources/module/.gitignore rename to packages/stryker-api/testResources/module/.gitignore diff --git a/packages/stryker-api/testResources/module/package.json b/packages/stryker-api/testResources/module/package.json new file mode 100644 index 0000000000..641f1e4070 --- /dev/null +++ b/packages/stryker-api/testResources/module/package.json @@ -0,0 +1,22 @@ +{ + "name": "test-module", + "version": "0.0.0", + "description": "A module to perform an integration test", + "main": "index.js", + "scripts": { + "tsc": "tsc", + "useTestFramework": "node useTestFramework.js", + "use:core": "node useCore.js", + "use:config": "node useConfig.js", + "use:test_framework": "node useTestFramework.js", + "use:mutant": "node useMutant.js", + "use:report": "node useReport.js", + "use:test_runner": "node useTestRunner.js" + }, + "author": "", + "license": "ISC", + "devDependencies": { + "stryker-api": "file:../../", + "typescript": "^2.1.4" + } +} diff --git a/packages/stryker-api/testResources/module/tsconfig.json b/packages/stryker-api/testResources/module/tsconfig.json new file mode 100644 index 0000000000..0937bd01df --- /dev/null +++ b/packages/stryker-api/testResources/module/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "module": "commonjs", + "moduleResolution": "node", + "target": "es5", + "noImplicitAny": true, + "sourceMap": false, + "lib": [ + "es5", + "es2015.promise" + ] + }, + "exclude": [ + "node_modules" + ] +} \ No newline at end of file diff --git a/packages/stryker-api/testResources/module/useConfig.ts b/packages/stryker-api/testResources/module/useConfig.ts new file mode 100644 index 0000000000..3c87e7cf3d --- /dev/null +++ b/packages/stryker-api/testResources/module/useConfig.ts @@ -0,0 +1,17 @@ +import {Config, ConfigWriter, ConfigWriterFactory} from 'stryker-api/config'; + +let config: Config = new Config(); + +class MyConfigWriter { + constructor() { + } + + write(config: Config) { + config.set({ 'myConfig': true }); + } +} + +ConfigWriterFactory.instance().register('myConfigWriter', MyConfigWriter); +let myConfigWriter = ConfigWriterFactory.instance().create('myConfigWriter', undefined); +myConfigWriter.write(config); +console.log(config); \ No newline at end of file diff --git a/packages/stryker-api/testResources/module/useCore.ts b/packages/stryker-api/testResources/module/useCore.ts new file mode 100644 index 0000000000..66e97b826a --- /dev/null +++ b/packages/stryker-api/testResources/module/useCore.ts @@ -0,0 +1,29 @@ +import { StrykerOptions, Factory, InputFile, InputFileDescriptor, Position, Location, Range } from 'stryker-api/core'; + +let options: StrykerOptions = {}; +let optionsAllArgs: StrykerOptions = { + files: ['some', { pattern: 'file' }, { included: false, mutated: true, pattern: 'some pattern' }], + mutate: ['some'], + configFile: 'string', + testFramework: 'string', + testRunner: 'string', + reporter: 'string', + logLevel: 'string', + timeoutMs: 1, + timeoutFactor: 2, + plugins: ['string'], + port: 3, +}; + +let inputFile: InputFile = { + path: 'string', + mutated: true, + included: true +}; + +let range: Range = [1, 2]; +let filePatternDescriptor: InputFileDescriptor = { included: true, mutated: false, pattern: '/files/**/*.js' }; +let position: Position = { column: 2, line: 2 }; +let location: Location = { start: position, end: position }; + +console.log(range, position, location, inputFile, optionsAllArgs, options, filePatternDescriptor); \ No newline at end of file diff --git a/packages/stryker-api/testResources/module/useMutant.ts b/packages/stryker-api/testResources/module/useMutant.ts new file mode 100644 index 0000000000..1ec029c106 --- /dev/null +++ b/packages/stryker-api/testResources/module/useMutant.ts @@ -0,0 +1,20 @@ +import { IdentifiedNode } from 'stryker-api/mutant'; +import {Mutator, MutatorFactory} from 'stryker-api/mutant'; + + +class MyMutator implements Mutator { + public name = 'myMutator'; + + applyMutations(node: IdentifiedNode, copy: (obj: any, deep?: boolean) => any): IdentifiedNode[] { + return null; + } +} + +MutatorFactory.instance().register('myMutator', MyMutator); +let myMutator = MutatorFactory.instance().create('myMutator', null); +if (!(myMutator instanceof MyMutator)) { + throw Error('Something wrong with myMutator'); +} + +let node: IdentifiedNode = { nodeID: 3, type: 'Literal', value: null, raw: '' }; +console.log(node); \ No newline at end of file diff --git a/packages/stryker-api/testResources/module/useReport.ts b/packages/stryker-api/testResources/module/useReport.ts new file mode 100644 index 0000000000..5f2c6ac00f --- /dev/null +++ b/packages/stryker-api/testResources/module/useReport.ts @@ -0,0 +1,59 @@ +import { Reporter, MutantResult, MutantStatus, ReporterFactory, SourceFile, MatchedMutant } from 'stryker-api/report'; + +class EmptyReporter { +} + +class AllReporter implements Reporter { + onSourceFileRead(file: SourceFile) { + } + onAllSourceFilesRead(files: SourceFile[]) { + } + onMutantTested(result: MutantResult) { + } + onAllMutantsTested(results: MutantResult[]) { + } + onAllMutantsMatchedWithTests(mutants: ReadonlyArray) { + } + wrapUp() { + return new Promise(r => r()); + } +} + +ReporterFactory.instance().register('empty', EmptyReporter); +ReporterFactory.instance().register('all', AllReporter); +console.log(ReporterFactory.instance().knownNames()); +let emptyReporter = ReporterFactory.instance().create('empty', {}); +let allReporter = ReporterFactory.instance().create('all', {}); +if (!(emptyReporter instanceof EmptyReporter)) { + throw Error('Something wrong with empty reporter'); +} +if (!(allReporter instanceof AllReporter)) { + throw Error('Something wrong with all reporter'); +} + +let result: MutantResult = { + sourceFilePath: 'string', + mutatorName: 'string', + status: MutantStatus.TimedOut, + replacement: 'string', + originalLines: 'string', + mutatedLines: 'string', + testsRan: [''], + location: null, + range: [1, 2] +}; +allReporter.onMutantTested(result); +console.log(result); +console.log(`Mutant status error: ${MutantStatus[MutantStatus.Error]}`); + +const matchedMutant: MatchedMutant = { + mutatorName: '', + scopedTestIds: [52], + timeSpentScopedTests: 52, + filename: 'string', + replacement: 'string' +}; + +allReporter.onAllMutantsMatchedWithTests([Object.freeze(matchedMutant)]); +const allMutants = Object.freeze([matchedMutant]); +allReporter.onAllMutantsMatchedWithTests(allMutants); diff --git a/packages/stryker-api/testResources/module/useTestFramework.ts b/packages/stryker-api/testResources/module/useTestFramework.ts new file mode 100644 index 0000000000..8e705587a8 --- /dev/null +++ b/packages/stryker-api/testResources/module/useTestFramework.ts @@ -0,0 +1,27 @@ +import { TestFramework, TestFrameworkFactory, TestFrameworkSettings } from 'stryker-api/test_framework'; + +class TestFramework1 implements TestFramework { + + constructor(settings: TestFrameworkSettings) { + + } + + beforeEach(codeFragment: string) { + return ''; + } + + afterEach(codeFragment: string) { + return ''; + } + + filter(ids: number[]) { + return ids.toString(); + } +} + +TestFrameworkFactory.instance().register('framework-1', TestFramework1); +const testFramework = TestFrameworkFactory.instance().create('framework-1', null); +if (!(testFramework instanceof TestFramework1)) { + throw Error('Test framework does not seem to be working'); +} +console.log(TestFrameworkFactory.instance().knownNames()); \ No newline at end of file diff --git a/packages/stryker-api/testResources/module/useTestRunner.ts b/packages/stryker-api/testResources/module/useTestRunner.ts new file mode 100644 index 0000000000..d3b3f13242 --- /dev/null +++ b/packages/stryker-api/testResources/module/useTestRunner.ts @@ -0,0 +1,51 @@ +import { + CoverageCollection, CoverageResult, CoverageCollectionPerTest, CoverageData, + StatementMap, TestResult, TestRunner, RunnerOptions, + RunResult, RunOptions, TestRunnerFactory, + TestStatus, RunStatus +} from 'stryker-api/test_runner'; +import { EventEmitter } from 'events'; + +class MyTestRunner extends EventEmitter implements TestRunner { + + run(options: RunOptions) { + const coverage: CoverageCollection | CoverageCollectionPerTest = { + 'a/file': { + s: { + '23': 32 + } + } + }; + return new Promise(r => r({ + tests: [{ + status: TestStatus.Failed, + name: '', + failureMessages: [''], + timeSpentMs: 23 + }], + status: RunStatus.Complete, + coverage + })); + }; +} + +let runnerOptions: RunnerOptions = { + files: [{ path: 'some', mutated: true, included: false }, { path: 'files', mutated: false, included: true }], + port: 1, + strykerOptions: null +}; + +TestRunnerFactory.instance().register('MyTestRunner', MyTestRunner); +let myTestRunner = TestRunnerFactory.instance().create('MyTestRunner', runnerOptions); +if (!(myTestRunner instanceof MyTestRunner)) { + throw Error('Something wrong with myTestRunner'); +} + +console.log(TestRunnerFactory.instance().knownNames()); +let coverageData: CoverageData = {}; +let statementMap: StatementMap = {}; +statementMap['23'] = { start: { line: 23, column: 23 }, end: { line: 42, column: 42 } }; +coverageData['32'] = 24; +let coverageResult: CoverageResult = { + s: coverageData, +}; diff --git a/packages/stryker-api/test_framework.ts b/packages/stryker-api/test_framework.ts new file mode 100644 index 0000000000..adc76a6823 --- /dev/null +++ b/packages/stryker-api/test_framework.ts @@ -0,0 +1,3 @@ +export {default as TestFramework} from './src/test_framework/TestFramework'; +export {default as TestFrameworkFactory} from './src/test_framework/TestFrameworkFactory'; +export {default as TestFrameworkSettings} from './src/test_framework/TestFrameworkSettings'; diff --git a/packages/stryker-api/test_runner.ts b/packages/stryker-api/test_runner.ts new file mode 100644 index 0000000000..fd1c8b762d --- /dev/null +++ b/packages/stryker-api/test_runner.ts @@ -0,0 +1,9 @@ +export * from './src/test_runner/Coverage'; +export {default as TestResult} from './src/test_runner/TestResult'; +export {default as TestRunner} from './src/test_runner/TestRunner'; +export {default as TestStatus} from './src/test_runner/TestStatus'; +export {default as RunnerOptions} from './src/test_runner/RunnerOptions'; +export {default as RunResult} from './src/test_runner/RunResult'; +export {default as RunOptions} from './src/test_runner/RunOptions'; +export {default as RunStatus} from './src/test_runner/RunStatus'; +export {default as TestRunnerFactory} from './src/test_runner/TestRunnerFactory'; \ No newline at end of file diff --git a/packages/stryker-api/tsconfig.json b/packages/stryker-api/tsconfig.json new file mode 100644 index 0000000000..dc249b8ff4 --- /dev/null +++ b/packages/stryker-api/tsconfig.json @@ -0,0 +1,28 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es5", + "noImplicitAny": true, + "moduleResolution": "node", + "rootDir": ".", + "sourceMap": true, + "removeComments": false, + "declaration": true, + "forceConsistentCasingInFileNames": true, + "allowJs": false, + "noUnusedLocals": true, + "noImplicitReturns": true, + "noResolve": true, + "strictNullChecks": true, + "lib": [ + "es5", + "es2015.promise" + ] + }, + "exclude": [ + "node_modules", + "testResources", + "**/*.d.ts", + "*.d.ts" + ] +} \ No newline at end of file diff --git a/tslint.json b/packages/stryker-api/tslint.json similarity index 100% rename from tslint.json rename to packages/stryker-api/tslint.json diff --git a/packages/stryker/.editorconfig b/packages/stryker/.editorconfig new file mode 100644 index 0000000000..8f467bb9a1 --- /dev/null +++ b/packages/stryker/.editorconfig @@ -0,0 +1,4 @@ +# 2 space indentation +[{*.ts,*.js,*.json}] +indent_style = space +indent_size = 2 \ No newline at end of file diff --git a/.gitattributes b/packages/stryker/.gitattributes similarity index 100% rename from .gitattributes rename to packages/stryker/.gitattributes diff --git a/packages/stryker/.gitignore b/packages/stryker/.gitignore new file mode 100644 index 0000000000..aed04b7a12 --- /dev/null +++ b/packages/stryker/.gitignore @@ -0,0 +1,15 @@ +node_modules +/coverage +/testResources/module/coverage +.tscache +.stryker-tmp +reports +typings +*.js.map +src/**/*.js +test/**/*.js +src/**/*.d.ts +test/**/*.d.ts +.idea +*.iml +*.tmp.txt \ No newline at end of file diff --git a/.npmignore b/packages/stryker/.npmignore similarity index 100% rename from .npmignore rename to packages/stryker/.npmignore diff --git a/packages/stryker/.travis.yml b/packages/stryker/.travis.yml new file mode 100644 index 0000000000..0f5d69f19c --- /dev/null +++ b/packages/stryker/.travis.yml @@ -0,0 +1,13 @@ +language: node_js +node_js: + - "node" + - "7" + - "6" + - "4" +before_install: + - npm install -g grunt-cli + - if [[ `npm -v` != 3* ]]; then npm i -g npm@3; fi +install: npm install +before_script: + - export DISPLAY=:99.0 + - sh -e /etc/init.d/xvfb start diff --git a/.vscode/launch.json b/packages/stryker/.vscode/launch.json similarity index 100% rename from .vscode/launch.json rename to packages/stryker/.vscode/launch.json diff --git a/packages/stryker/.vscode/settings.json b/packages/stryker/.vscode/settings.json new file mode 100644 index 0000000000..fcc94dfe1a --- /dev/null +++ b/packages/stryker/.vscode/settings.json @@ -0,0 +1,16 @@ +{ + "typescript.tsdk": "node_modules/typescript/lib", + "files": { + "exclude": { + ".git": "", + ".tscache": "", + "**/*.js": { + "when": "$(basename).ts" + }, + "**/*.d.ts": true, + "**/*.map": { + "when": "$(basename)" + } + } + } +} \ No newline at end of file diff --git a/CHANGELOG.md b/packages/stryker/CHANGELOG.md similarity index 100% rename from CHANGELOG.md rename to packages/stryker/CHANGELOG.md diff --git a/CONTRIBUTING.md b/packages/stryker/CONTRIBUTING.md similarity index 100% rename from CONTRIBUTING.md rename to packages/stryker/CONTRIBUTING.md diff --git a/Gruntfile.js b/packages/stryker/Gruntfile.js similarity index 100% rename from Gruntfile.js rename to packages/stryker/Gruntfile.js diff --git a/packages/stryker/LICENSE b/packages/stryker/LICENSE new file mode 100644 index 0000000000..8dada3edaf --- /dev/null +++ b/packages/stryker/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/packages/stryker/README.md b/packages/stryker/README.md new file mode 100644 index 0000000000..cb270df278 --- /dev/null +++ b/packages/stryker/README.md @@ -0,0 +1,231 @@ +[![Build Status](https://travis-ci.org/stryker-mutator/stryker.svg?branch=master)](https://travis-ci.org/stryker-mutator/stryker) +[![NPM](https://img.shields.io/npm/dm/stryker.svg)](https://www.npmjs.com/package/stryker) +[![Node version](https://img.shields.io/node/v/stryker.svg)](https://img.shields.io/node/v/stryker.svg) +[![Gitter](https://badges.gitter.im/stryker-mutator/stryker.svg)](https://gitter.im/stryker-mutator/stryker?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) + +![Stryker](https://github.com/stryker-mutator/stryker/raw/master/stryker-80x80.png) + +# Stryker +*Professor X: For someone who hates mutants... you certainly keep some strange company.* +*William Stryker: Oh, they serve their purpose... as long as they can be controlled.* + +## Getting started +Stryker is a mutation testing framework for JavaScript. It allows you to test your tests by temporarily inserting bugs. + +To install Stryker, execute the command: +```sh +$ npm install stryker stryker-api --save-dev +``` + +***Note:*** *During installation you may run into errors caused by [node-gyp](https://github.com/nodejs/node-gyp). It is safe to ignore them.* + +To test if Stryker is installed correctly, execute the command: +```sh +$ node_modules/.bin/stryker --version +``` + +This should print the latest version of Stryker. + +## Usage + +```sh +$ node_modules/.bin/stryker [options] [stryker.conf.js] +``` + +The only `command` currently available is `run`, which kicks off mutation testing. + +By default, we expect a `stryker.conf.js` file in the current working directory. This can be overridden by specifying a different file as the last parameter. + +The following is an example `stryker.conf.js` file: + +```javascript +module.exports = function(config){ + config.set({ + files: ['test/helpers/**/*.js', 'test/unit/**/*.js', { pattern: 'src/**/*.js', included: false, mutated: true }], + testFramework: 'mocha', + testRunner: 'mocha', + reporter: ['progress', 'clear-text', 'dots', 'html', 'event-recorder'], + coverageAnalysis: 'perTest', + plugins: ['stryker-mocha-runner', 'stryker-html-reporter'] + }); +} +``` + +As you can see, the config file is *not* a simple JSON file, it should be a common js (a.k.a. npm) module. +You might recognize this way of working from the karma test runner. + +Make sure you *at least* specify the `files` and the `testRunner` options when mixing the config file and/or command line options. + +## Supported mutators +See our website for the [list of currently supported mutators](http://stryker-mutator.github.io/mutators.html). + +## Configuration +All configuration options can either be set via the command line or via the `stryker.conf.js` config file. + +`files` and `mutate` both support globbing expressions using [node glob](https://github.com/isaacs/node-glob). +This is the same globbing format you might know from [Grunt](https://github.com/gruntjs/grunt) or [Karma](https://github.com/karma-runner/karma). +You can *ignore* files by adding an exclamation mark (`!`) at the start of an expression. + +#### Files required to run your tests +**Command line:** `[--files|-f] node_modules/a-lib/**/*.js,src/**/*.js,a.js,test/**/*.js` +**Config file:** `files: ['{ pattern: 'src/**/*.js', mutated: true }, '!src/**/index.js', 'test/**/*.js']` +**Default value:** *none* +**Mandatory**: yes +**Description:** +With `files` you specify all files needed to run your tests. If the test runner you use already provides the test framework (Jasmine, Mocha, etc.), +you should *not* include those files here as well. +The files will be loaded in the other in which they are specified. + +When using the command line, the list can only contain a comma separated list of globbing expressions. +When using the config file you can provide an array with `string`s or `InputFileDescriptor` objects, like so: + +* `string`: The globbing expression used for selecting the files needed to run the tests. +* `InputFileDescriptor` object: `{ pattern: 'pattern', included: true, mutated: false }`: + * The `pattern` property is mandatory and contains the globbing expression used for selecting the files. Using `!` to ignore files is *not* supported here. + * The `included` property is optional and determines whether or not this file should be loaded initially by the test-runner (default: true) + * The `mutated` property is optional and determines whether or not this file should be targeted for mutations (default: false) + +*Note*: To include a file/folder which start with an exclamation mark (`!`), use the `InputFileDescriptor` syntax. + +#### Source code files to mutate +**Command line:** `[--mutate|-m] src/**/*.js,a.js` +**Config file:** `mutate: ['src/**/*.js', 'a.js']` +**Default value:** *none* +**Mandatory**: no +**Description:** +With `mutate` you configure the subset of files to use for mutation testing. Generally speaking, these should be your own source files. +This is optional, as you can also use the `mutated` property with the `files` parameter or not mutate any files at all to perform a dry-run (test-run). +We expect a comma separated list of globbing expressions, which will be used to select the files to be mutated. + +#### Test runner +**Command line:** `--testRunner karma` +**Config file:** `testRunner: 'karma'` +**Default value:** *none* +**Mandatory**: yes +**Description:** +With `testRunner` you specify the test runner to run your tests. This option is required. +Make sure the test runner plugin for Stryker is installed. E.g. we need the `stryker-karma-runner` to use `karma` as a test runner. +See the [list of plugins](http://stryker-mutator.github.io/plugins.html) for an up-to-date list of supported test runners and plugins. + +#### Test framework +**Command line:** `--testFramework jasmine` +**Config file:** `testFramework: 'jasmine'` +**Default value:** *none* +**Mandatory**: yes +**Description:** +With `testFramework` you configure which test framework your tests are using. This value is directly consumed by the test runner and therefore +depends what framework that specific test runner supports. By default, this value is also used for `testFramework`. + +#### Type of coverage analysis +**Full notation:** `--coverageAnalysis perTest` +**Config file key:** `coverageAnalysis: 'perTest'` +**Default value:** `perTest` +**Mandatory**: no +**Description:** +With `coverageAnalysis` you specify which coverage analysis strategy you want to use. +Stryker can analyse code coverage results. This can potentially speed up mutation testing a lot, as only the tests covering a +particular mutation are tested for each mutant. +This does *not* influence the resulting mutation testing score. It only improves performance, so we enable it by default. + +The possible values are: +* **off**: Stryker will not determine the code covered by tests during the initial test run phase. All tests will be executed for each mutant +during the mutation testing phase. + +* **all**: Stryker will determine the code covered by all tests during the initial test run phase. Only mutants actually covered by your +test suite are tested during the mutation testing phase. This setting requires your test runner to be able to report the code coverage back to Stryker. +Currently, only the `stryker-mocha-runner` and the `stryker-karma-runner` do this. + +* **perTest**: Stryker will determine the code covered by your test per executed test during the initial test run phase. Only mutants actually covered by your +test suite are tested during the mutation testing phase. +Only the tests that cover a particular mutant are tested for each one. This requires your tests to be able to run independently of each other and in random order. +In addition to requiring your test runner to be able to report the code coverage back to Stryker, your chosen `testFramework` also needs to support running code + before and after each test, as well as test filtering. + Currently, `stryker-mocha-runner` as well as `stryker-karma-runner` support this. However, `stryker-karma-runner` support is limited to using it with `Jasmine` as the test framework + (`Mocha` is not yet supported). + +#### Reporters +**Command line:** `--reporter clear-text,progress,dots` +**Config file:** `reporter: ['clear-text', 'progress', 'dots']` +**Default value:** `['clear-text', 'progress']` +**Mandatory**: no +**Description:** +With `reporter` you can set a reporter or group of reporters for stryker to use. +These reporters can be used out of the box: `clear-text`, `progress` and `event-recorder`. +By default `clear-text` and `progress` are active if no reporter is configured. +You can load additional plugins to get more reporters. See [stryker-mutator.github.io](http://stryker-mutator.github.io) +for an up-to-date list of supported reporter plugins and a description on each reporter. + +The `clear-text` reporter supports an additional config option to show more tests that were executed to kill a mutant. The config for your config file is: `clearTextReporter: { maxTestsToLog: 3 },` + +#### Plugins +**Command line:** `--plugins stryker-html-reporter,stryker-karma-runner` +**Config file:** `plugins: ['stryker-html-reporter', 'stryker-karma-runner']` +**Default value:** `['stryker-*']` +**Mandatory**: no +**Description:** +With `plugins` you can add additional Node modules for Stryker to load (or `require`). +By default, all `node_modules` starting with `stryker-` will be loaded, so you would normally not need to specify this option. +These modules should be installed right next to stryker. For a current list of plugins, +you can consult [npm](https://www.npmjs.com/search?q=%40stryker-plugin) or +[stryker-mutator.github.io](http://stryker-mutator.github.io). + +#### Start of port range for test runners +**Command line:** `--port 9234` +**Config file:** `port: 9234` +**Default value:** `9234` +**Mandatory**: no +**Description:** +With `port` you specify the first port to pass on to the test runner to use. Any additional test runners will be spawned using ports n+1, n+2, etc. +For example, when you set to use port 9234 and Stryker decides to start four test runner processes, ports 9234, 9235, 9236 and 9237 will be passed to the test runner. +If the test runner decides to use the port it should be available for use. + +#### Global timeout in milliseconds +**Command line:** `--timeoutMs 5000` +**Config file:** `timeoutMs: 5000` +**Default value:** `5000` +**Mandatory**: no +**Description:** +When Stryker is mutating code, it cannot determine indefinitely whether or not a code mutation results in an infinite loop (see [Halting problem](https://en.wikipedia.org/wiki/Halting_problem)). +In order to battle infinite loops, a test run gets killed after a certain period. This period is configurable with two settings: `timeoutMs` and `timeoutFactor`. +To calculate the actual timeout in milliseconds the, following formula is used: + +``` +timeoutForTestRunMs = timeOfTheInitialTestRunMs * timeoutFactor + timeoutMs +``` + +With `timeoutFactor` you can configure the allowed deviation relative to the time of a normal test run. Tweak this if you notice that mutants are prone to creating slower code, but not infinite loops. +`timeoutMs` let's you configure an absolute deviation. Use it, if you run Stryker on a busy machine and you need to wait longer to make sure that the code indeed entered an infinite loop. + +#### Timeout factor +**Command line:** `--timeoutFactor 1.5` +**Config file:** `timeoutFactor: 1.5` +**Default value:** `1.5` +**Mandatory**: no +**Description:** +See [Timeout in milliseconds](#Timeout-in-milliseconds). + +#### Number of maximum concurrent test runners +**Command line:** `--maxConcurrentTestRunners 3` +**Config file:** `maxConcurrentTestRunners: 3` +**Default value:** number of CPU cores +**Mandatory**: no +**Description:** +Specifies the maximum number of concurrent test runners to spawn. +Mutation testing is time consuming. By default Stryker tries to make the most of your CPU, by spawning as many test runners as you have CPU cores. +This setting allows you to override this default behavior. + +Reasons you might want to lower this setting: + +* Your test runner starts a browser (another CPU-intensive process) +* You're running on a shared server and/or +* Your hard disk cannot handle the I/O of all test runners + +#### Log level +**Command line:** `--logLevel info` +**Config file:** `logLevel: 'info'` +**Default value:** `info` +**Mandatory**: no +**Description:** + Set the `log4js` log level that Stryker uses (default is `info`). Possible values: `fatal`, `error`, `warn`, `info`, `debug`, `trace`, `all` and `off`. + *Note*: Test runners are run as child processes of the Stryker Node process. All output (stdout) of the `testRunner` is logged as `trace`. + Thus, to see logging output from the test runner set the `logLevel` to `all` or `trace`. diff --git a/bin/stryker b/packages/stryker/bin/stryker old mode 100755 new mode 100644 similarity index 100% rename from bin/stryker rename to packages/stryker/bin/stryker diff --git a/packages/stryker/package.json b/packages/stryker/package.json new file mode 100644 index 0000000000..ab0f7fc73d --- /dev/null +++ b/packages/stryker/package.json @@ -0,0 +1,122 @@ +{ + "name": "stryker", + "version": "0.6.0-2", + "description": "The extendable JavaScript mutation testing framework", + "main": "src/Stryker.js", + "typings": "src/Stryker.d.ts", + "scripts": { + "test": "grunt test", + "start": "tsc -w", + "sample": "node src/stryker-cli.js run testResources/sampleProject/stryker.conf.js", + "preversion": "grunt", + "clean": "grunt clean", + "jshint": "grunt jshint", + "tslint": "grunt tslint", + "coverage": "grunt coverage", + "intergration": "grunt intergration", + "build": "grunt build", + "serve": "grunt serve", + "watch-test": "grunt watch-test", + "release": "grunt release" + }, + "repository": { + "type": "git", + "url": "https://github.com/stryker-mutator/stryker" + }, + "engines": { + "node": ">=4" + }, + "keywords": [ + "mutation testing", + "mutation", + "testing", + "test", + "js", + "stryker" + ], + "author": "Simon de Lang", + "contributors": [ + "Simon de Lang ", + "Nico Jansen ", + "Sander Koenders ", + "global ", + "Philipp Weissenbacher ", + "Jasper Catthoor ", + "Nico Stapelbroek ", + "Alex van Assem ", + "Jelle Peters ", + "Jeremy Nagel ", + "MarktHart ", + "Michael Williamson ", + "Willem Meints " + ], + "license": "Apache-2.0", + "bin": { + "stryker": "./bin/stryker" + }, + "dependencies": { + "chalk": "^1.1.1", + "commander": "^2.9.0", + "es6-promise-pool": "^2.4.4", + "escodegen": "^1.8.0", + "esprima": "^2.7.0", + "glob": "^7.0.3", + "graceful-fs": "^4.1.11", + "istanbul": "^0.4.5", + "lodash": "^4.17.4", + "log4js": "^1.1.0", + "mkdirp": "^0.5.1", + "progress": "^1.1.8", + "serialize-javascript": "^1.3.0", + "tslib": "^1.5.0" + }, + "devDependencies": { + "@types/chai-as-promised": "0.0.29", + "@types/chalk": "^0.4.28", + "@types/escodegen": "0.0.6", + "@types/esprima": "^2.1.31", + "@types/estree": "0.0.32", + "@types/glob": "^5.0.29", + "@types/graceful-fs": "^2.0.29", + "@types/istanbul": "^0.4.29", + "@types/karma": "^0.13.32", + "@types/lodash": "^4.14.34", + "@types/log4js": "0.0.32", + "@types/mkdirp": "^0.3.28", + "@types/mocha": "^2.2.34", + "@types/progress": "^1.1.28", + "@types/sinon": "^1.16.33", + "@types/sinon-as-promised": "^4.0.5", + "@types/sinon-chai": "^2.7.27", + "chai": "^3.4.1", + "chai-as-promised": "^6.0.0", + "grunt": "^1.0.1", + "grunt-bump": "^0.8.0", + "grunt-cli": "^1.2.0", + "grunt-contrib-clean": "^1.0.0", + "grunt-contrib-jshint": "^1.0.0", + "grunt-contrib-watch": "^1.0.0", + "grunt-conventional-changelog": "^6.1.0", + "grunt-mocha-istanbul": "^5.0.1", + "grunt-mocha-test": "^0.13.2", + "grunt-npm": "0.0.2", + "grunt-ts": "^6.0.0-beta.3", + "grunt-tslint": "^4.0.0", + "jasmine-core": "^2.4.1", + "karma": "1.0.0", + "karma-coverage": "^1.0.0", + "karma-jasmine": "^1.0.2", + "karma-phantomjs-launcher": "^1.0.1", + "load-grunt-tasks": "^3.5.0", + "mocha": "^3.2.0", + "sinon": "^1.17.2", + "sinon-as-promised": "^4.0.2", + "sinon-chai": "^2.8.0", + "stryker-api": "^0.5.0-2", + "tslint": "^4.4.2", + "typescript": "~2.2.1" + }, + "peerDependencies": { + "stryker-api": "^0.5.0-2" + } +} diff --git a/src/ConfigReader.ts b/packages/stryker/src/ConfigReader.ts similarity index 100% rename from src/ConfigReader.ts rename to packages/stryker/src/ConfigReader.ts diff --git a/src/FileStatements.ts b/packages/stryker/src/FileStatements.ts similarity index 100% rename from src/FileStatements.ts rename to packages/stryker/src/FileStatements.ts diff --git a/src/InputFileResolver.ts b/packages/stryker/src/InputFileResolver.ts similarity index 100% rename from src/InputFileResolver.ts rename to packages/stryker/src/InputFileResolver.ts diff --git a/src/Mutant.ts b/packages/stryker/src/Mutant.ts similarity index 100% rename from src/Mutant.ts rename to packages/stryker/src/Mutant.ts diff --git a/src/MutantTestMatcher.ts b/packages/stryker/src/MutantTestMatcher.ts similarity index 100% rename from src/MutantTestMatcher.ts rename to packages/stryker/src/MutantTestMatcher.ts diff --git a/src/MutatorOrchestrator.ts b/packages/stryker/src/MutatorOrchestrator.ts similarity index 97% rename from src/MutatorOrchestrator.ts rename to packages/stryker/src/MutatorOrchestrator.ts index f0f6029027..d365dac63c 100644 --- a/src/MutatorOrchestrator.ts +++ b/packages/stryker/src/MutatorOrchestrator.ts @@ -46,7 +46,7 @@ export default class MutatorOrchestrator { let fileContent = fileUtils.readFile(sourceFile); this.reportFileRead(sourceFile, fileContent); let abstractSyntaxTree = parserUtils.parse(fileContent); - let nodes = parserUtils.collectFrozenNodes(abstractSyntaxTree); + let nodes = new parserUtils.NodeIdentifier().identifyAndFreeze(abstractSyntaxTree); let newMutants = this.findMutants(sourceFile, fileContent, abstractSyntaxTree, nodes); mutants = mutants.concat(newMutants); } catch (err) { @@ -115,7 +115,7 @@ export default class MutatorOrchestrator { log.debug(`The mutator '${mutator.name}' mutated ${mutatedNodes.length} node${mutatedNodes.length > 1 ? 's' : ''} between (Ln ${astnode.loc.start.line}, Col ${astnode.loc.start.column}) and (Ln ${astnode.loc.end.line}, Col ${astnode.loc.end.column}) in file ${sourceFile}`); } - mutatedNodes.forEach((mutatedNode: estree.Node) => { + mutatedNodes.forEach(mutatedNode => { let mutatedCode = parserUtils.generate(mutatedNode); let originalNode = nodes[mutatedNode.nodeID]; mutants.push(new Mutant(mutator.name, sourceFile, originalCode, mutatedCode, originalNode.loc, originalNode.range)); diff --git a/src/PluginLoader.ts b/packages/stryker/src/PluginLoader.ts similarity index 100% rename from src/PluginLoader.ts rename to packages/stryker/src/PluginLoader.ts diff --git a/src/ReporterOrchestrator.ts b/packages/stryker/src/ReporterOrchestrator.ts similarity index 100% rename from src/ReporterOrchestrator.ts rename to packages/stryker/src/ReporterOrchestrator.ts diff --git a/src/Sandbox.ts b/packages/stryker/src/Sandbox.ts similarity index 100% rename from src/Sandbox.ts rename to packages/stryker/src/Sandbox.ts diff --git a/src/SandboxCoordinator.ts b/packages/stryker/src/SandboxCoordinator.ts similarity index 100% rename from src/SandboxCoordinator.ts rename to packages/stryker/src/SandboxCoordinator.ts diff --git a/src/Stryker.ts b/packages/stryker/src/Stryker.ts similarity index 100% rename from src/Stryker.ts rename to packages/stryker/src/Stryker.ts diff --git a/src/TestFrameworkOrchestrator.ts b/packages/stryker/src/TestFrameworkOrchestrator.ts similarity index 100% rename from src/TestFrameworkOrchestrator.ts rename to packages/stryker/src/TestFrameworkOrchestrator.ts diff --git a/src/coverage/CoverageInstrumenter.ts b/packages/stryker/src/coverage/CoverageInstrumenter.ts similarity index 100% rename from src/coverage/CoverageInstrumenter.ts rename to packages/stryker/src/coverage/CoverageInstrumenter.ts diff --git a/src/coverage/CoverageInstrumenterStream.ts b/packages/stryker/src/coverage/CoverageInstrumenterStream.ts similarity index 98% rename from src/coverage/CoverageInstrumenterStream.ts rename to packages/stryker/src/coverage/CoverageInstrumenterStream.ts index d31b49c2af..94c7fce545 100644 --- a/src/coverage/CoverageInstrumenterStream.ts +++ b/packages/stryker/src/coverage/CoverageInstrumenterStream.ts @@ -2,7 +2,6 @@ import { StatementMap } from 'stryker-api/test_runner'; import { Transform, TransformOptions } from 'stream'; import { Instrumenter } from 'istanbul'; import * as log4js from 'log4js'; -import 'stryker-api/estree'; const coverageObjRegex = /\{.*"path".*"fnMap".*"statementMap".*"branchMap".*\}/g; const log = log4js.getLogger('CoverageInstrumenterStream'); diff --git a/src/isolated-runner/IsolatedRunnerOptions.ts b/packages/stryker/src/isolated-runner/IsolatedRunnerOptions.ts similarity index 100% rename from src/isolated-runner/IsolatedRunnerOptions.ts rename to packages/stryker/src/isolated-runner/IsolatedRunnerOptions.ts diff --git a/src/isolated-runner/IsolatedTestRunnerAdapter.ts b/packages/stryker/src/isolated-runner/IsolatedTestRunnerAdapter.ts similarity index 100% rename from src/isolated-runner/IsolatedTestRunnerAdapter.ts rename to packages/stryker/src/isolated-runner/IsolatedTestRunnerAdapter.ts diff --git a/src/isolated-runner/IsolatedTestRunnerAdapterFactory.ts b/packages/stryker/src/isolated-runner/IsolatedTestRunnerAdapterFactory.ts similarity index 100% rename from src/isolated-runner/IsolatedTestRunnerAdapterFactory.ts rename to packages/stryker/src/isolated-runner/IsolatedTestRunnerAdapterFactory.ts diff --git a/src/isolated-runner/IsolatedTestRunnerAdapterWorker.ts b/packages/stryker/src/isolated-runner/IsolatedTestRunnerAdapterWorker.ts similarity index 100% rename from src/isolated-runner/IsolatedTestRunnerAdapterWorker.ts rename to packages/stryker/src/isolated-runner/IsolatedTestRunnerAdapterWorker.ts diff --git a/src/isolated-runner/MessageProtocol.ts b/packages/stryker/src/isolated-runner/MessageProtocol.ts similarity index 100% rename from src/isolated-runner/MessageProtocol.ts rename to packages/stryker/src/isolated-runner/MessageProtocol.ts diff --git a/src/isolated-runner/ResilientTestRunnerFactory.ts b/packages/stryker/src/isolated-runner/ResilientTestRunnerFactory.ts similarity index 100% rename from src/isolated-runner/ResilientTestRunnerFactory.ts rename to packages/stryker/src/isolated-runner/ResilientTestRunnerFactory.ts diff --git a/src/isolated-runner/RetryDecorator.ts b/packages/stryker/src/isolated-runner/RetryDecorator.ts similarity index 100% rename from src/isolated-runner/RetryDecorator.ts rename to packages/stryker/src/isolated-runner/RetryDecorator.ts diff --git a/src/isolated-runner/TestRunnerDecorator.ts b/packages/stryker/src/isolated-runner/TestRunnerDecorator.ts similarity index 100% rename from src/isolated-runner/TestRunnerDecorator.ts rename to packages/stryker/src/isolated-runner/TestRunnerDecorator.ts diff --git a/src/isolated-runner/TimeoutDecorator.ts b/packages/stryker/src/isolated-runner/TimeoutDecorator.ts similarity index 100% rename from src/isolated-runner/TimeoutDecorator.ts rename to packages/stryker/src/isolated-runner/TimeoutDecorator.ts diff --git a/src/mutators/ArrayDeclaratorMutator.ts b/packages/stryker/src/mutators/ArrayDeclaratorMutator.ts similarity index 75% rename from src/mutators/ArrayDeclaratorMutator.ts rename to packages/stryker/src/mutators/ArrayDeclaratorMutator.ts index 5d0422613a..c5ae6f3079 100644 --- a/src/mutators/ArrayDeclaratorMutator.ts +++ b/packages/stryker/src/mutators/ArrayDeclaratorMutator.ts @@ -1,6 +1,5 @@ -import {Syntax} from 'esprima'; -import {Mutator} from 'stryker-api/mutant'; -import * as estree from 'estree'; +import { Syntax } from 'esprima'; +import { Mutator, IdentifiedNode } from 'stryker-api/mutant'; /** * Represents a mutator which can remove the content of an array's elements. @@ -10,7 +9,7 @@ export default class ArrayDeclaratorMutator implements Mutator { constructor() { } - applyMutations(node: estree.Node, copy: (obj: T, deep?: boolean) => T): void | estree.Node | estree.Node[] { + applyMutations(node: IdentifiedNode, copy: (obj: T, deep?: boolean) => T): void | IdentifiedNode { if ((node.type === Syntax.CallExpression || node.type === Syntax.NewExpression) && node.callee.type === Syntax.Identifier && node.callee.name === 'Array' && node.arguments.length > 0) { let mutatedNode = copy(node); mutatedNode.arguments = []; diff --git a/src/mutators/BinaryOperatorMutator.ts b/packages/stryker/src/mutators/BinaryOperatorMutator.ts similarity index 83% rename from src/mutators/BinaryOperatorMutator.ts rename to packages/stryker/src/mutators/BinaryOperatorMutator.ts index f3cac47bd2..81f8d6f222 100644 --- a/src/mutators/BinaryOperatorMutator.ts +++ b/packages/stryker/src/mutators/BinaryOperatorMutator.ts @@ -1,4 +1,4 @@ -import { Mutator } from 'stryker-api/mutant'; +import { Mutator, IdentifiedNode } from 'stryker-api/mutant'; import { Syntax } from 'esprima'; import * as estree from 'estree'; @@ -21,8 +21,8 @@ export default class BinaryOperatorMutator implements Mutator { '!==': '===' }; - applyMutations(node: estree.Node, copy: (obj: T, deep?: boolean) => T): estree.Node[] { - let nodes: estree.Node[] = []; + applyMutations(node: IdentifiedNode, copy: (obj: T, deep?: boolean) => T): IdentifiedNode[] { + let nodes: IdentifiedNode[] = []; if (node.type === this.type && this.operators[node.operator]) { let binaryNode = node; @@ -36,8 +36,6 @@ export default class BinaryOperatorMutator implements Mutator { nodes.push(mutatedNode); }); } - return nodes; } - } \ No newline at end of file diff --git a/src/mutators/BlockStatementMutator.ts b/packages/stryker/src/mutators/BlockStatementMutator.ts similarity index 63% rename from src/mutators/BlockStatementMutator.ts rename to packages/stryker/src/mutators/BlockStatementMutator.ts index 60a17903dd..a8eb11f18b 100644 --- a/src/mutators/BlockStatementMutator.ts +++ b/packages/stryker/src/mutators/BlockStatementMutator.ts @@ -1,6 +1,5 @@ -import {Syntax} from 'esprima'; -import {Mutator} from 'stryker-api/mutant'; -import * as estree from 'estree'; +import { Syntax } from 'esprima'; +import { Mutator, IdentifiedNode } from 'stryker-api/mutant'; /** * Represents a mutator which can remove the content of a BlockStatement. @@ -11,7 +10,7 @@ export default class BlockStatementMutator implements Mutator { constructor() { } - applyMutations(node: estree.Node, copy: (obj: T, deep?: boolean) => T): void | estree.Node | estree.Node[] { + applyMutations(node: IdentifiedNode, copy: (obj: T, deep?: boolean) => T): void | IdentifiedNode { if (node.type === this.type && node.body.length > 0) { let mutatedNode = copy(node); mutatedNode.body = []; diff --git a/src/mutators/LogicalOperatorMutator.ts b/packages/stryker/src/mutators/LogicalOperatorMutator.ts similarity index 56% rename from src/mutators/LogicalOperatorMutator.ts rename to packages/stryker/src/mutators/LogicalOperatorMutator.ts index c275280481..573723137f 100644 --- a/src/mutators/LogicalOperatorMutator.ts +++ b/packages/stryker/src/mutators/LogicalOperatorMutator.ts @@ -1,17 +1,17 @@ -import {Mutator} from 'stryker-api/mutant'; -import {Syntax} from 'esprima'; +import { Mutator, IdentifiedNode } from 'stryker-api/mutant'; +import { Syntax } from 'esprima'; import * as estree from 'estree'; -export default class LogicalOperatorMutator implements Mutator { +export default class LogicalOperatorMutator implements Mutator { name = 'LogicalOperator'; private type = Syntax.LogicalExpression; private operators: { [targetedOperator: string]: estree.LogicalOperator } = { - '&&': '||', - '||': '&&' + '&&': '||', + '||': '&&' }; - applyMutations(node: estree.Node, copy: (obj: T, deep?: boolean) => T): estree.Node[] { - let nodes: estree.Node[] = []; + applyMutations(node: IdentifiedNode, copy: (obj: T, deep?: boolean) => T): IdentifiedNode[] { + let nodes: IdentifiedNode[] = []; if (node.type === this.type && this.operators[node.operator]) { let mutatedNode = copy(node); diff --git a/src/mutators/RemoveConditionalsMutator.ts b/packages/stryker/src/mutators/RemoveConditionalsMutator.ts similarity index 74% rename from src/mutators/RemoveConditionalsMutator.ts rename to packages/stryker/src/mutators/RemoveConditionalsMutator.ts index 087010a65f..6b6882acee 100644 --- a/src/mutators/RemoveConditionalsMutator.ts +++ b/packages/stryker/src/mutators/RemoveConditionalsMutator.ts @@ -1,5 +1,5 @@ import { Syntax } from 'esprima'; -import { Mutator } from 'stryker-api/mutant'; +import { Mutator, IdentifiedNode, Identified } from 'stryker-api/mutant'; import * as estree from 'estree'; type ConditionExpression = estree.DoWhileStatement | estree.IfStatement | estree.ForStatement | estree.WhileStatement | estree.ConditionalExpression; @@ -13,12 +13,12 @@ export default class RemoveConditionalsMutator implements Mutator { constructor() { } - applyMutations(node: estree.Node, copy: (obj: T, deep?: boolean) => T): estree.Node[] | void { + applyMutations(node: IdentifiedNode, copy: (obj: T, deep?: boolean) => T): IdentifiedNode[] | void { if (this.canMutate(node)) { - let nodes: estree.Node[] = []; + let nodes: IdentifiedNode[] = []; if (node.test) { - nodes.push(this.booleanLiteralNode(node.test.nodeID, false)); + nodes.push(this.booleanLiteralNode((node.test as IdentifiedNode).nodeID, false)); } else { let mutatedNode = copy(node); mutatedNode.test = this.booleanLiteralNode(-1, false); @@ -26,13 +26,13 @@ export default class RemoveConditionalsMutator implements Mutator { } if (node.type === Syntax.IfStatement || node.type === Syntax.ConditionalExpression) { - nodes.push(this.booleanLiteralNode(node.test.nodeID, true)); + nodes.push(this.booleanLiteralNode((node.test as IdentifiedNode).nodeID, true)); } return nodes; } } - private booleanLiteralNode(nodeID: number, value: boolean): estree.SimpleLiteral { + private booleanLiteralNode(nodeID: number, value: boolean): estree.SimpleLiteral & Identified { return { nodeID: nodeID, type: Syntax.Literal, diff --git a/src/mutators/UnaryOperatorMutator.ts b/packages/stryker/src/mutators/UnaryOperatorMutator.ts similarity index 56% rename from src/mutators/UnaryOperatorMutator.ts rename to packages/stryker/src/mutators/UnaryOperatorMutator.ts index af978ba928..a0e3720d52 100644 --- a/src/mutators/UnaryOperatorMutator.ts +++ b/packages/stryker/src/mutators/UnaryOperatorMutator.ts @@ -1,17 +1,17 @@ -import {Mutator} from 'stryker-api/mutant'; -import {Syntax} from 'esprima'; +import { Mutator, IdentifiedNode } from 'stryker-api/mutant'; +import { Syntax } from 'esprima'; import * as estree from 'estree'; -export default class UnaryOperatorMutator implements Mutator { +export default class UnaryOperatorMutator implements Mutator { name = 'UnaryOperator'; private type = Syntax.UnaryExpression; private operators: { [targetedOperator: string]: estree.UnaryOperator } = { - '+': '-', - '-': '+' + '+': '-', + '-': '+' }; - applyMutations(node: estree.Node, copy: (obj: T, deep?: boolean) => T): estree.Node[] { - let nodes: estree.Node[] = []; + applyMutations(node: IdentifiedNode, copy: (obj: T, deep?: boolean) => T): IdentifiedNode[] { + let nodes: IdentifiedNode[] = []; if (node.type === this.type && this.operators[node.operator]) { let mutatedNode = copy(node); diff --git a/src/mutators/UpdateOperatorMutator.ts b/packages/stryker/src/mutators/UpdateOperatorMutator.ts similarity index 56% rename from src/mutators/UpdateOperatorMutator.ts rename to packages/stryker/src/mutators/UpdateOperatorMutator.ts index 77163e9bee..1de6438cac 100644 --- a/src/mutators/UpdateOperatorMutator.ts +++ b/packages/stryker/src/mutators/UpdateOperatorMutator.ts @@ -1,16 +1,16 @@ -import {Mutator} from 'stryker-api/mutant'; -import {Syntax} from 'esprima'; +import { Mutator, IdentifiedNode } from 'stryker-api/mutant'; +import { Syntax } from 'esprima'; import * as estree from 'estree'; -export default class UpdateOperatorMutator implements Mutator { +export default class UpdateOperatorMutator implements Mutator { name = 'UpdateOperator'; private type = Syntax.UpdateExpression; private operators: { [targetedOperator: string]: estree.UpdateOperator } = { - '++': '--', - '--': '++' + '++': '--', + '--': '++' }; - applyMutations(node: estree.Node, copy: (obj: T, deep?: boolean) => T): void | estree.Node | estree.Node[] { + applyMutations(node: IdentifiedNode, copy: (obj: T, deep?: boolean) => T): void | IdentifiedNode { if (node.type === this.type && this.operators[node.operator]) { let mutatedNode = copy(node); mutatedNode.operator = this.operators[node.operator]; diff --git a/src/reporters/BroadcastReporter.ts b/packages/stryker/src/reporters/BroadcastReporter.ts similarity index 100% rename from src/reporters/BroadcastReporter.ts rename to packages/stryker/src/reporters/BroadcastReporter.ts diff --git a/src/reporters/ClearTextReporter.ts b/packages/stryker/src/reporters/ClearTextReporter.ts similarity index 100% rename from src/reporters/ClearTextReporter.ts rename to packages/stryker/src/reporters/ClearTextReporter.ts diff --git a/src/reporters/DotsReporter.ts b/packages/stryker/src/reporters/DotsReporter.ts similarity index 100% rename from src/reporters/DotsReporter.ts rename to packages/stryker/src/reporters/DotsReporter.ts diff --git a/src/reporters/EventRecorderReporter.ts b/packages/stryker/src/reporters/EventRecorderReporter.ts similarity index 100% rename from src/reporters/EventRecorderReporter.ts rename to packages/stryker/src/reporters/EventRecorderReporter.ts diff --git a/src/reporters/ProgressAppendOnlyReporter.ts b/packages/stryker/src/reporters/ProgressAppendOnlyReporter.ts similarity index 100% rename from src/reporters/ProgressAppendOnlyReporter.ts rename to packages/stryker/src/reporters/ProgressAppendOnlyReporter.ts diff --git a/src/reporters/ProgressBar.ts b/packages/stryker/src/reporters/ProgressBar.ts similarity index 100% rename from src/reporters/ProgressBar.ts rename to packages/stryker/src/reporters/ProgressBar.ts diff --git a/src/reporters/ProgressKeeper.ts b/packages/stryker/src/reporters/ProgressKeeper.ts similarity index 100% rename from src/reporters/ProgressKeeper.ts rename to packages/stryker/src/reporters/ProgressKeeper.ts diff --git a/src/reporters/ProgressReporter.ts b/packages/stryker/src/reporters/ProgressReporter.ts similarity index 100% rename from src/reporters/ProgressReporter.ts rename to packages/stryker/src/reporters/ProgressReporter.ts diff --git a/src/reporters/StrictReporter.ts b/packages/stryker/src/reporters/StrictReporter.ts similarity index 100% rename from src/reporters/StrictReporter.ts rename to packages/stryker/src/reporters/StrictReporter.ts diff --git a/src/stryker-cli.ts b/packages/stryker/src/stryker-cli.ts similarity index 100% rename from src/stryker-cli.ts rename to packages/stryker/src/stryker-cli.ts diff --git a/src/utils/StrykerTempFolder.ts b/packages/stryker/src/utils/StrykerTempFolder.ts similarity index 100% rename from src/utils/StrykerTempFolder.ts rename to packages/stryker/src/utils/StrykerTempFolder.ts diff --git a/src/utils/Task.ts b/packages/stryker/src/utils/Task.ts similarity index 100% rename from src/utils/Task.ts rename to packages/stryker/src/utils/Task.ts diff --git a/src/utils/Timer.ts b/packages/stryker/src/utils/Timer.ts similarity index 100% rename from src/utils/Timer.ts rename to packages/stryker/src/utils/Timer.ts diff --git a/src/utils/fileUtils.ts b/packages/stryker/src/utils/fileUtils.ts similarity index 100% rename from src/utils/fileUtils.ts rename to packages/stryker/src/utils/fileUtils.ts diff --git a/src/utils/objectUtils.ts b/packages/stryker/src/utils/objectUtils.ts similarity index 100% rename from src/utils/objectUtils.ts rename to packages/stryker/src/utils/objectUtils.ts diff --git a/packages/stryker/src/utils/parserUtils.ts b/packages/stryker/src/utils/parserUtils.ts new file mode 100644 index 0000000000..d53c7c6f41 --- /dev/null +++ b/packages/stryker/src/utils/parserUtils.ts @@ -0,0 +1,98 @@ +import * as _ from 'lodash'; +import * as esprima from 'esprima'; +import * as estree from 'estree'; +import { IdentifiedNode, Identified } from 'stryker-api/mutant'; +const escodegen = require('escodegen'); + +/** + * Utility class for parsing and generating code. + * @constructor + */ +const esprimaOptions = { + comment: true, + loc: true, + range: true, + tokens: true, +}; + +/** + * Parses code to generate an Abstract Syntax Tree. + * @function + * @param code - The code which has to be parsed. + * @returns {Object} The generated Abstract Syntax Tree. + */ +export function parse(code: string): estree.Program { + if (code === undefined) { + throw new Error('Code parameter cannot be undefined'); + } + + const abstractSyntaxTree = esprima.parse(code, esprimaOptions); + + return abstractSyntaxTree; +}; + +/** + * Parses a Node to generate code. + * @param The Node which has to be transformed into code. + * @returns The generated code. + */ +export function generate(node: estree.Node): string { + return escodegen.generate(node); +}; + +/** + * Returns n as T & Identified, purely syntactic. + * @param n The estree node which is identified + */ +export function identified(n: T) { + return n as T & Identified; +} + + + +/** + * Represents an object responsible to identify estree nodes (estree.Node). + * Labels all nodes with a `nodeID` recursively. + */ +export class NodeIdentifier { + + private identifiedNodes: Readonly[] = []; + + identifyAndFreeze(program: estree.Program): Readonly[] { + this.identifiedNodes = []; + this.identifyAndFreezeRecursively(program); + return this.identifiedNodes; + } + + private identifyAndFreezeRecursively(maybeNode: any) { + if (this.isNode(maybeNode)) { + if (!this.isIdentified(maybeNode)) { + this.identify(maybeNode); + } + Object.freeze(maybeNode); + + _.forOwn(maybeNode, childNode => { + this.identifyAndFreezeRecursively(childNode); + }); + } else if (Array.isArray(maybeNode)) { + maybeNode.forEach(grandChild => { + this.identifyAndFreezeRecursively(grandChild); + }); + } + } + + private isNode(maybeNode: any): estree.Node { + return !_.isArray(maybeNode) && _.isObject(maybeNode) && maybeNode.type; + } + + private isIdentified(node: estree.Node): node is IdentifiedNode { + const n = node as IdentifiedNode; + return _.isNumber(n.nodeID); + } + + private identify(node: estree.Node) { + const n = node as IdentifiedNode; + n.nodeID = this.identifiedNodes.length; + this.identifiedNodes.push(n); + } +} diff --git a/stryker.conf.js b/packages/stryker/stryker.conf.js similarity index 100% rename from stryker.conf.js rename to packages/stryker/stryker.conf.js diff --git a/test/helpers/TestRunnerMock.ts b/packages/stryker/test/helpers/TestRunnerMock.ts similarity index 100% rename from test/helpers/TestRunnerMock.ts rename to packages/stryker/test/helpers/TestRunnerMock.ts diff --git a/test/helpers/log4jsMock.ts b/packages/stryker/test/helpers/log4jsMock.ts similarity index 100% rename from test/helpers/log4jsMock.ts rename to packages/stryker/test/helpers/log4jsMock.ts diff --git a/test/helpers/producers.ts b/packages/stryker/test/helpers/producers.ts similarity index 100% rename from test/helpers/producers.ts rename to packages/stryker/test/helpers/producers.ts diff --git a/test/helpers/registerChaiPlugins.ts b/packages/stryker/test/helpers/registerChaiPlugins.ts similarity index 100% rename from test/helpers/registerChaiPlugins.ts rename to packages/stryker/test/helpers/registerChaiPlugins.ts diff --git a/test/helpers/registerSinonPlugins.ts b/packages/stryker/test/helpers/registerSinonPlugins.ts similarity index 100% rename from test/helpers/registerSinonPlugins.ts rename to packages/stryker/test/helpers/registerSinonPlugins.ts diff --git a/test/helpers/streamHelpers.ts b/packages/stryker/test/helpers/streamHelpers.ts similarity index 100% rename from test/helpers/streamHelpers.ts rename to packages/stryker/test/helpers/streamHelpers.ts diff --git a/test/integration/config-reader/ConfigReaderSpec.ts b/packages/stryker/test/integration/config-reader/ConfigReaderSpec.ts similarity index 100% rename from test/integration/config-reader/ConfigReaderSpec.ts rename to packages/stryker/test/integration/config-reader/ConfigReaderSpec.ts diff --git a/packages/stryker/test/integration/install-module/Executor.ts b/packages/stryker/test/integration/install-module/Executor.ts new file mode 100644 index 0000000000..689dfb11d4 --- /dev/null +++ b/packages/stryker/test/integration/install-module/Executor.ts @@ -0,0 +1,56 @@ +import {spawn, ChildProcess} from 'child_process'; + +export default class Executor { + + private cwd: string; + + constructor(cwd: string, private silent: boolean = true) { + this.cwd = `${__dirname}/${cwd}`; + } + + exec(command: string, options: any, done: (error: any, stdout: string) => any) { + console.log(`exec: ${this.cwd}/${command}`); + + let args = command.split(' '); + if (!options) { + options = {}; + } + command = args.shift() || ''; + options.cwd = this.cwd; + options.env = options.env || process.env; + if (process.platform.substr(0, 3) === 'win') { + command = command + '.cmd'; + } + let child = spawn(command, args, options); + this.handleProcess(child, done); + } + + private handleProcess(child: ChildProcess, done: (error: any, output: string) => any) { + let stderr = new Buffer(''); + let stdout = new Buffer(''); + if (child.stdout) { + child.stdout.on('data', (buf: Buffer) => { + if (!this.silent) { + console.log(String(buf)); + } + stdout = Buffer.concat([stdout, new Buffer(buf)]); + }); + } + if (child.stderr) { + child.stderr.on('data', (buf: Buffer) => { + if (!this.silent) { + console.error(String(buf)); + } + stderr = Buffer.concat([stderr, new Buffer(buf)]); + }); + } + child.on('close', function(code: number) { + if (code !== 0) { + let error = stdout.toString() + stderr.toString(); + done(error, stdout.toString()); + } else { + done(null, stdout.toString()); + } + }); + } +} \ No newline at end of file diff --git a/test/integration/install-module/install-module.ts b/packages/stryker/test/integration/install-module/install-module.ts similarity index 100% rename from test/integration/install-module/install-module.ts rename to packages/stryker/test/integration/install-module/install-module.ts diff --git a/test/integration/isolated-runner/AdditionalTestRunners.ts b/packages/stryker/test/integration/isolated-runner/AdditionalTestRunners.ts similarity index 100% rename from test/integration/isolated-runner/AdditionalTestRunners.ts rename to packages/stryker/test/integration/isolated-runner/AdditionalTestRunners.ts diff --git a/test/integration/isolated-runner/DiscoverRegexTestRunner.ts b/packages/stryker/test/integration/isolated-runner/DiscoverRegexTestRunner.ts similarity index 100% rename from test/integration/isolated-runner/DiscoverRegexTestRunner.ts rename to packages/stryker/test/integration/isolated-runner/DiscoverRegexTestRunner.ts diff --git a/test/integration/isolated-runner/ErroredTestRunner.ts b/packages/stryker/test/integration/isolated-runner/ErroredTestRunner.ts similarity index 100% rename from test/integration/isolated-runner/ErroredTestRunner.ts rename to packages/stryker/test/integration/isolated-runner/ErroredTestRunner.ts diff --git a/test/integration/isolated-runner/NeverResolvedTestRunner.ts b/packages/stryker/test/integration/isolated-runner/NeverResolvedTestRunner.ts similarity index 100% rename from test/integration/isolated-runner/NeverResolvedTestRunner.ts rename to packages/stryker/test/integration/isolated-runner/NeverResolvedTestRunner.ts diff --git a/test/integration/isolated-runner/ResilientTestRunnerFactorySpec.ts b/packages/stryker/test/integration/isolated-runner/ResilientTestRunnerFactorySpec.ts similarity index 100% rename from test/integration/isolated-runner/ResilientTestRunnerFactorySpec.ts rename to packages/stryker/test/integration/isolated-runner/ResilientTestRunnerFactorySpec.ts diff --git a/test/integration/isolated-runner/SlowInitAndDisposeTestRunner.ts b/packages/stryker/test/integration/isolated-runner/SlowInitAndDisposeTestRunner.ts similarity index 100% rename from test/integration/isolated-runner/SlowInitAndDisposeTestRunner.ts rename to packages/stryker/test/integration/isolated-runner/SlowInitAndDisposeTestRunner.ts diff --git a/test/integration/utils/fileUtilsSpec.ts b/packages/stryker/test/integration/utils/fileUtilsSpec.ts similarity index 100% rename from test/integration/utils/fileUtilsSpec.ts rename to packages/stryker/test/integration/utils/fileUtilsSpec.ts diff --git a/test/unit/InputFileResolverSpec.ts b/packages/stryker/test/unit/InputFileResolverSpec.ts similarity index 100% rename from test/unit/InputFileResolverSpec.ts rename to packages/stryker/test/unit/InputFileResolverSpec.ts diff --git a/test/unit/MutantSpec.ts b/packages/stryker/test/unit/MutantSpec.ts similarity index 100% rename from test/unit/MutantSpec.ts rename to packages/stryker/test/unit/MutantSpec.ts diff --git a/test/unit/MutantTestMatcherSpec.ts b/packages/stryker/test/unit/MutantTestMatcherSpec.ts similarity index 100% rename from test/unit/MutantTestMatcherSpec.ts rename to packages/stryker/test/unit/MutantTestMatcherSpec.ts diff --git a/test/unit/MutatorOrchestratorSpec.ts b/packages/stryker/test/unit/MutatorOrchestratorSpec.ts similarity index 92% rename from test/unit/MutatorOrchestratorSpec.ts rename to packages/stryker/test/unit/MutatorOrchestratorSpec.ts index bd012c39b8..e89998227f 100644 --- a/test/unit/MutatorOrchestratorSpec.ts +++ b/packages/stryker/test/unit/MutatorOrchestratorSpec.ts @@ -2,7 +2,7 @@ import { expect } from 'chai'; import * as fileUtils from '../../src/utils/fileUtils'; import Mutant from '../../src/Mutant'; import MutatorOrchestrator from '../../src/MutatorOrchestrator'; -import { Mutator, MutatorFactory } from 'stryker-api/mutant'; +import { Mutator, MutatorFactory, IdentifiedNode, Identified } from 'stryker-api/mutant'; import * as sinon from 'sinon'; import { Syntax } from 'esprima'; import StrykerTempFolder from '../../src/utils/StrykerTempFolder'; @@ -83,11 +83,11 @@ describe('MutatorOrchestrator', () => { class StubMutator implements Mutator { name: 'stub'; - applyMutations(node: estree.Node, copy: (obj: any, deep?: boolean) => any): estree.Node[] { - let nodes: estree.Node[] = []; + applyMutations(node: IdentifiedNode, copy: (obj: any, deep?: boolean) => any): IdentifiedNode[] { + let nodes: IdentifiedNode[] = []; if (node.type === Syntax.BinaryExpression) { // eg: '1 * 2': push child node - nodes.push((node).left); + nodes.push((node as estree.BinaryExpression).left as estree.Expression & Identified); } else if (node.type === Syntax.IfStatement) { // eg: 'if(true);': push original node nodes.push(node); diff --git a/test/unit/PluginLoaderSpec.ts b/packages/stryker/test/unit/PluginLoaderSpec.ts similarity index 100% rename from test/unit/PluginLoaderSpec.ts rename to packages/stryker/test/unit/PluginLoaderSpec.ts diff --git a/test/unit/ReporterOrchestratorSpec.ts b/packages/stryker/test/unit/ReporterOrchestratorSpec.ts similarity index 100% rename from test/unit/ReporterOrchestratorSpec.ts rename to packages/stryker/test/unit/ReporterOrchestratorSpec.ts diff --git a/test/unit/SandboxCoordinatorSpec.ts b/packages/stryker/test/unit/SandboxCoordinatorSpec.ts similarity index 100% rename from test/unit/SandboxCoordinatorSpec.ts rename to packages/stryker/test/unit/SandboxCoordinatorSpec.ts diff --git a/test/unit/SandboxSpec.ts b/packages/stryker/test/unit/SandboxSpec.ts similarity index 100% rename from test/unit/SandboxSpec.ts rename to packages/stryker/test/unit/SandboxSpec.ts diff --git a/test/unit/StrykerSpec.ts b/packages/stryker/test/unit/StrykerSpec.ts similarity index 100% rename from test/unit/StrykerSpec.ts rename to packages/stryker/test/unit/StrykerSpec.ts diff --git a/test/unit/TestFrameworkOrchestratorSpec.ts b/packages/stryker/test/unit/TestFrameworkOrchestratorSpec.ts similarity index 100% rename from test/unit/TestFrameworkOrchestratorSpec.ts rename to packages/stryker/test/unit/TestFrameworkOrchestratorSpec.ts diff --git a/test/unit/coverage/CoverageInstrumenterSpec.ts b/packages/stryker/test/unit/coverage/CoverageInstrumenterSpec.ts similarity index 100% rename from test/unit/coverage/CoverageInstrumenterSpec.ts rename to packages/stryker/test/unit/coverage/CoverageInstrumenterSpec.ts diff --git a/test/unit/coverage/CoverageInstrumenterStreamSpec.ts b/packages/stryker/test/unit/coverage/CoverageInstrumenterStreamSpec.ts similarity index 100% rename from test/unit/coverage/CoverageInstrumenterStreamSpec.ts rename to packages/stryker/test/unit/coverage/CoverageInstrumenterStreamSpec.ts diff --git a/test/unit/isolated-runner/IsolatedTestRunnerAdapterSpec.ts b/packages/stryker/test/unit/isolated-runner/IsolatedTestRunnerAdapterSpec.ts similarity index 100% rename from test/unit/isolated-runner/IsolatedTestRunnerAdapterSpec.ts rename to packages/stryker/test/unit/isolated-runner/IsolatedTestRunnerAdapterSpec.ts diff --git a/test/unit/isolated-runner/RetryDecoratorSpec.ts b/packages/stryker/test/unit/isolated-runner/RetryDecoratorSpec.ts similarity index 100% rename from test/unit/isolated-runner/RetryDecoratorSpec.ts rename to packages/stryker/test/unit/isolated-runner/RetryDecoratorSpec.ts diff --git a/test/unit/isolated-runner/TestRunnerDecoratorSpec.ts b/packages/stryker/test/unit/isolated-runner/TestRunnerDecoratorSpec.ts similarity index 100% rename from test/unit/isolated-runner/TestRunnerDecoratorSpec.ts rename to packages/stryker/test/unit/isolated-runner/TestRunnerDecoratorSpec.ts diff --git a/test/unit/isolated-runner/TimeoutDecoratorSpec.ts b/packages/stryker/test/unit/isolated-runner/TimeoutDecoratorSpec.ts similarity index 100% rename from test/unit/isolated-runner/TimeoutDecoratorSpec.ts rename to packages/stryker/test/unit/isolated-runner/TimeoutDecoratorSpec.ts diff --git a/test/unit/mutators/ArrayDeclaratorMutatorSpec.ts b/packages/stryker/test/unit/mutators/ArrayDeclaratorMutatorSpec.ts similarity index 87% rename from test/unit/mutators/ArrayDeclaratorMutatorSpec.ts rename to packages/stryker/test/unit/mutators/ArrayDeclaratorMutatorSpec.ts index 7c9c953ce3..edf409f873 100644 --- a/test/unit/mutators/ArrayDeclaratorMutatorSpec.ts +++ b/packages/stryker/test/unit/mutators/ArrayDeclaratorMutatorSpec.ts @@ -1,8 +1,9 @@ -import ArrayDeclaratorMutator from '../../../src/mutators/ArrayDeclaratorMutator'; import { expect } from 'chai'; +import * as estree from 'estree'; +import { Identified } from 'stryker-api/mutant'; +import ArrayDeclaratorMutator from '../../../src/mutators/ArrayDeclaratorMutator'; import * as parser from '../../../src/utils/parserUtils'; import { copy } from '../../../src/utils/objectUtils'; -import * as estree from 'estree'; describe('BlockStatementMutator', () => { let sut: ArrayDeclaratorMutator; @@ -13,17 +14,17 @@ describe('BlockStatementMutator', () => { const getArrayExpression = (program: estree.Program) => { const variableDeclaration = getVariableDeclaration(program); - return (variableDeclaration.declarations[0].init as estree.ArrayExpression); + return (variableDeclaration.declarations[0].init as estree.ArrayExpression & Identified); }; const getArrayCallExpression = (program: estree.Program) => { const variableDeclaration = getVariableDeclaration(program); - return (variableDeclaration.declarations[0].init as estree.SimpleCallExpression); + return (variableDeclaration.declarations[0].init as estree.SimpleCallExpression & Identified); }; const getArrayNewExpression = (program: estree.Program) => { const variableDeclaration = getVariableDeclaration(program); - return (variableDeclaration.declarations[0].init as estree.NewExpression); + return (variableDeclaration.declarations[0].init as estree.NewExpression & Identified); }; it('should mutate when supplied with an array expression', () => { @@ -32,7 +33,7 @@ describe('BlockStatementMutator', () => { const arrayExpression = getArrayExpression(program); // Act - const actual = sut.applyMutations(arrayExpression, copy); + const actual = sut.applyMutations(arrayExpression, copy) as estree.ArrayExpression & Identified; // Assert expect(actual).to.be.ok; @@ -46,7 +47,7 @@ describe('BlockStatementMutator', () => { const arrayExpression = getArrayCallExpression(program); // Act - const actual = sut.applyMutations(arrayExpression, copy); + const actual = sut.applyMutations(arrayExpression, copy) as estree.CallExpression & Identified; // Assert expect(actual).to.be.ok; @@ -60,7 +61,7 @@ describe('BlockStatementMutator', () => { const arrayExpression = getArrayNewExpression(program); // Act - const actual = sut.applyMutations(arrayExpression, copy); + const actual = sut.applyMutations(arrayExpression, copy) as estree.CallExpression & Identified; // Assert expect(actual).to.be.ok; diff --git a/test/unit/mutators/BinaryOperatorMutatorSpec.ts b/packages/stryker/test/unit/mutators/BinaryOperatorMutatorSpec.ts similarity index 77% rename from test/unit/mutators/BinaryOperatorMutatorSpec.ts rename to packages/stryker/test/unit/mutators/BinaryOperatorMutatorSpec.ts index 0900339531..7055eb5a7d 100644 --- a/test/unit/mutators/BinaryOperatorMutatorSpec.ts +++ b/packages/stryker/test/unit/mutators/BinaryOperatorMutatorSpec.ts @@ -1,20 +1,20 @@ +import { expect } from 'chai'; +import * as estree from 'estree'; import * as _ from 'lodash'; import BinaryOperatorMutator from '../../../src/mutators/BinaryOperatorMutator'; -import * as chai from 'chai'; -import * as estree from 'estree'; -let expect = chai.expect; +import { Identified, IdentifiedNode } from 'stryker-api/mutant'; describe('BinaryOperatorMutator', () => { let mutator: BinaryOperatorMutator; - let invalidNode: estree.Node; - let validNode: estree.Node; + let invalidNode: IdentifiedNode; + let validNode: IdentifiedNode; beforeEach(() => { mutator = new BinaryOperatorMutator(); - invalidNode = { + invalidNode = { type: 'Identifier', - }; - validNode = { + } as estree.Node & Identified; + validNode = { nodeID: 23, type: 'BinaryExpression', operator: '+', @@ -24,13 +24,13 @@ describe('BinaryOperatorMutator', () => { value: typeof 6, raw: '6' }, - right: { + right: { nodeID: -2, type: 'Literal', value: typeof 7, raw: '7' } - }; + } as estree.BinaryExpression & Identified; }); describe('should mutate', () => { diff --git a/test/unit/mutators/BlockStatementMutatorSpec.ts b/packages/stryker/test/unit/mutators/BlockStatementMutatorSpec.ts similarity index 61% rename from test/unit/mutators/BlockStatementMutatorSpec.ts rename to packages/stryker/test/unit/mutators/BlockStatementMutatorSpec.ts index 23fd93c2d0..5168f4c3e4 100644 --- a/test/unit/mutators/BlockStatementMutatorSpec.ts +++ b/packages/stryker/test/unit/mutators/BlockStatementMutatorSpec.ts @@ -1,8 +1,9 @@ -import BlockStatementMutator from '../../../src/mutators/BlockStatementMutator'; import { expect } from 'chai'; -import * as parser from '../../../src/utils/parserUtils'; -import { copy } from '../../../src/utils/objectUtils'; import * as estree from 'estree'; +import { Identified } from 'stryker-api/mutant'; +import BlockStatementMutator from '../../../src/mutators/BlockStatementMutator'; +import { parse, identified } from '../../../src/utils/parserUtils'; +import { copy } from '../../../src/utils/objectUtils'; describe('BlockStatementMutator', () => { let sut: BlockStatementMutator; @@ -11,13 +12,13 @@ describe('BlockStatementMutator', () => { it('should mutate when supplied a block statement', () => { // Arrange - const program = parser.parse(`function a () { + const program = parse(`function a () { 'use strict'; }`); - const useStrictBlockStatement = (program.body[0] as estree.FunctionDeclaration).body; + const useStrictBlockStatement = identified((program.body[0] as estree.FunctionDeclaration).body); // Act - const actual = sut.applyMutations(useStrictBlockStatement, copy); + const actual = sut.applyMutations(useStrictBlockStatement, copy) as estree.BlockStatement & Identified; // Assert expect(actual).to.be.ok; @@ -27,10 +28,10 @@ describe('BlockStatementMutator', () => { it('should not mutate an empty expression', () => { // Arrange - const program = parser.parse(`function a () { + const program = parse(`function a () { }`); - const emptyBlockStatement = (program.body[0] as estree.FunctionDeclaration).body; + const emptyBlockStatement = identified((program.body[0] as estree.FunctionDeclaration).body); // Act const actual = sut.applyMutations(emptyBlockStatement, copy); diff --git a/test/unit/mutators/LogicalOperatorMutatorSpec.ts b/packages/stryker/test/unit/mutators/LogicalOperatorMutatorSpec.ts similarity index 73% rename from test/unit/mutators/LogicalOperatorMutatorSpec.ts rename to packages/stryker/test/unit/mutators/LogicalOperatorMutatorSpec.ts index 8d5c05710f..149f2e0619 100644 --- a/test/unit/mutators/LogicalOperatorMutatorSpec.ts +++ b/packages/stryker/test/unit/mutators/LogicalOperatorMutatorSpec.ts @@ -1,8 +1,9 @@ -import LogicalOperatorMutator from '../../../src/mutators/LogicalOperatorMutator'; import { expect } from 'chai'; +import * as estree from 'estree'; +import { Identified } from 'stryker-api/mutant'; +import LogicalOperatorMutator from '../../../src/mutators/LogicalOperatorMutator'; import * as parser from '../../../src/utils/parserUtils'; import { copy } from '../../../src/utils/objectUtils'; -import * as estree from 'estree'; describe('LogicalOperatorMutator', () => { let sut: LogicalOperatorMutator; @@ -12,10 +13,10 @@ describe('LogicalOperatorMutator', () => { it('should mutate \'||\' to \'&&\'', () => { // Arrange const program = parser.parse(`true || false`); - const logicalOperator = ((program.body[0] as estree.ExpressionStatement).expression as estree.LogicalExpression); + const logicalOperator = ((program.body[0] as estree.ExpressionStatement).expression as estree.LogicalExpression & Identified); // Act - const result = sut.applyMutations(logicalOperator, copy)[0]; + const result = sut.applyMutations(logicalOperator, copy)[0] as estree.LogicalExpression & Identified; // Assert expect(result).to.be.ok; @@ -26,10 +27,10 @@ describe('LogicalOperatorMutator', () => { it('should mutate \'&&\' to \'||\'', () => { // Arrange const program = parser.parse(`false && false`); - const logicalOperator = ((program.body[0] as estree.ExpressionStatement).expression as estree.LogicalExpression); + const logicalOperator = ((program.body[0] as estree.ExpressionStatement).expression as estree.LogicalExpression & Identified); // Act - const result = sut.applyMutations(logicalOperator, copy)[0]; + const result = sut.applyMutations(logicalOperator, copy)[0] as estree.LogicalExpression & Identified; // Assert expect(result).to.be.ok; diff --git a/test/unit/mutators/RemoveConditionalsMutatorSpec.ts b/packages/stryker/test/unit/mutators/RemoveConditionalsMutatorSpec.ts similarity index 69% rename from test/unit/mutators/RemoveConditionalsMutatorSpec.ts rename to packages/stryker/test/unit/mutators/RemoveConditionalsMutatorSpec.ts index 28370bcebe..7739de3401 100644 --- a/test/unit/mutators/RemoveConditionalsMutatorSpec.ts +++ b/packages/stryker/test/unit/mutators/RemoveConditionalsMutatorSpec.ts @@ -1,21 +1,21 @@ -import RemoveConditionalsMutator from '../../../src/mutators/RemoveConditionalsMutator'; -import * as parserUtils from '../../../src/utils/parserUtils'; -import { copy } from '../../../src/utils/objectUtils'; import * as chai from 'chai'; -import * as estree from 'estree'; import { Syntax } from 'esprima'; -import 'stryker-api/estree'; +import * as estree from 'estree'; +import { Identified, IdentifiedNode } from 'stryker-api/mutant'; +import RemoveConditionalsMutator from '../../../src/mutators/RemoveConditionalsMutator'; +import { NodeIdentifier, parse, identified } from '../../../src/utils/parserUtils'; +import { copy } from '../../../src/utils/objectUtils'; let expect = chai.expect; describe('RemoveConditionalsMutator', () => { let sut: RemoveConditionalsMutator; - let doWhileLoop: estree.DoWhileStatement; - let forLoop: estree.ForStatement; - let infiniteForLoop: estree.ForStatement; - let whileLoop: estree.WhileStatement; - let ifStatement: estree.IfStatement; - let ternaryExpression: estree.ConditionalExpression; + let doWhileLoop: estree.DoWhileStatement & Identified; + let forLoop: estree.ForStatement & Identified; + let infiniteForLoop: estree.ForStatement & Identified; + let whileLoop: estree.WhileStatement & Identified; + let ifStatement: estree.IfStatement & Identified; + let ternaryExpression: estree.ConditionalExpression & Identified; beforeEach(() => { sut = new RemoveConditionalsMutator(); @@ -40,17 +40,17 @@ describe('RemoveConditionalsMutator', () => { price < 20? 40 : 10; `; - const ast = parserUtils.parse(code); - parserUtils.collectFrozenNodes(ast); - ifStatement = ast.body[1]; - whileLoop = ast.body[2]; - doWhileLoop = ast.body[3]; - forLoop = ast.body[4]; - infiniteForLoop = ast.body[5]; - ternaryExpression = (ast.body[6] as estree.ExpressionStatement).expression as estree.ConditionalExpression; + const ast = parse(code); + new NodeIdentifier().identifyAndFreeze(ast); + ifStatement = ast.body[1] as estree.IfStatement & Identified; + whileLoop = ast.body[2] as estree.WhileStatement & Identified; + doWhileLoop = ast.body[3] as estree.DoWhileStatement & Identified; + forLoop = ast.body[4] as estree.ForStatement & Identified; + infiniteForLoop = ast.body[5] as estree.ForStatement & Identified; + ternaryExpression = (ast.body[6] as estree.ExpressionStatement).expression as estree.ConditionalExpression & Identified; }); - function actMutator(node: estree.IfStatement | estree.DoWhileStatement | estree.WhileStatement | estree.ForStatement | estree.ConditionalExpression) { + function actMutator(node: IdentifiedNode) { const mutants = sut.applyMutations(node, copy); if (Array.isArray(mutants)) { return mutants; @@ -60,13 +60,14 @@ describe('RemoveConditionalsMutator', () => { } describe('should not generate an infinite loop', () => { + it('when given a do-while loop', () => { const mutatedNodes = actMutator(doWhileLoop); const testValue = (mutatedNodes[0]).value; expect(testValue).to.be.false; expect(mutatedNodes[0].nodeID).to.not.eq(doWhileLoop.nodeID); - expect(mutatedNodes[0].nodeID).to.eq(doWhileLoop.test.nodeID); + expect(mutatedNodes[0].nodeID).to.eq(identified(doWhileLoop.test).nodeID); }); it('when given a while loop', () => { @@ -75,7 +76,7 @@ describe('RemoveConditionalsMutator', () => { const testValue = (mutatedNodes[0]).value; expect(testValue).to.be.false; expect(mutatedNodes[0].nodeID).to.not.eq(whileLoop.nodeID); - expect(mutatedNodes[0].nodeID).to.eq(whileLoop.test.nodeID); + expect(mutatedNodes[0].nodeID).to.eq(identified(whileLoop.test).nodeID); }); it('when given a for loop', () => { @@ -85,7 +86,7 @@ describe('RemoveConditionalsMutator', () => { expect(testValue).to.be.false; expect(mutatedNodes[0].nodeID).to.not.eq(forLoop.nodeID); if (forLoop.test) { - expect(mutatedNodes[0].nodeID).to.eq(forLoop.test.nodeID); + expect(mutatedNodes[0].nodeID).to.eq(identified(forLoop.test).nodeID); } else { expect.fail('test.nodeID was expected to be not undefined'); } @@ -97,7 +98,7 @@ describe('RemoveConditionalsMutator', () => { const testValue = forStatementNode.test.value; expect(testValue).to.be.false; expect(forStatementNode.nodeID).to.eq(infiniteForLoop.nodeID); - }else { + } else { expect.fail(`Node ${forStatementNode} unexpected.`); } }); @@ -125,25 +126,25 @@ describe('RemoveConditionalsMutator', () => { describe('should generate multiple mutants', () => { it('when given an if-statement', () => { - let mutatedNodes = actMutator(ifStatement); + let mutatedNodes = actMutator(ifStatement) as [estree.SimpleLiteral & Identified]; expect(mutatedNodes).to.have.length(2); expect(mutatedNodes[0].nodeID).not.to.eq(ifStatement.nodeID); expect(mutatedNodes[1].nodeID).not.to.eq(ifStatement.nodeID); - expect(mutatedNodes[0].nodeID).to.eq(ifStatement.test.nodeID); - expect(mutatedNodes[1].nodeID).to.eq(ifStatement.test.nodeID); + expect(mutatedNodes[0].nodeID).to.eq(identified(ifStatement.test).nodeID); + expect(mutatedNodes[1].nodeID).to.eq(identified(ifStatement.test).nodeID); expect(mutatedNodes[0].value).to.be.false; expect(mutatedNodes[1].value).to.be.true; }); it('when given a ternary-statement', () => { - let mutatedNodes = actMutator(ternaryExpression); + let mutatedNodes = actMutator(ternaryExpression) as [estree.SimpleLiteral & Identified]; expect(mutatedNodes).to.have.length(2); expect(mutatedNodes[0].nodeID).not.to.eq(ternaryExpression.nodeID); expect(mutatedNodes[1].nodeID).not.to.eq(ternaryExpression.nodeID); - expect(mutatedNodes[1].nodeID).to.eq(ternaryExpression.test.nodeID); - expect(mutatedNodes[1].nodeID).to.eq(ternaryExpression.test.nodeID); + expect(mutatedNodes[1].nodeID).to.eq(identified(ternaryExpression.test).nodeID); + expect(mutatedNodes[1].nodeID).to.eq(identified(ternaryExpression.test).nodeID); expect(mutatedNodes[0].value).to.be.false; expect(mutatedNodes[1].value).to.be.true; }); diff --git a/test/unit/mutators/UpdateOperatorMutatorSpec.ts b/packages/stryker/test/unit/mutators/UpdateOperatorMutatorSpec.ts similarity index 74% rename from test/unit/mutators/UpdateOperatorMutatorSpec.ts rename to packages/stryker/test/unit/mutators/UpdateOperatorMutatorSpec.ts index a85d80fb7d..daf420aeeb 100644 --- a/test/unit/mutators/UpdateOperatorMutatorSpec.ts +++ b/packages/stryker/test/unit/mutators/UpdateOperatorMutatorSpec.ts @@ -1,3 +1,4 @@ +import { Identified } from 'stryker-api/mutant'; import UpdateOperatorMutator from '../../../src/mutators/UpdateOperatorMutator'; import { expect } from 'chai'; import * as parser from '../../../src/utils/parserUtils'; @@ -13,10 +14,10 @@ describe('UpdateOperatorMutator', () => { it('"i++" to "i--"', () => { // Arrange const program = parser.parse(`i++`); - const expression = (program.body[0] as estree.ExpressionStatement).expression; + const expression = (program.body[0] as estree.ExpressionStatement).expression as estree.Expression & Identified; // Act - const result = sut.applyMutations(expression, copy); + const result = sut.applyMutations(expression, copy) as estree.UpdateExpression & Identified; // Assert expect(result).to.be.ok; @@ -27,10 +28,10 @@ describe('UpdateOperatorMutator', () => { it('"i--" to "i++"', () => { // Arrange const program = parser.parse(`i--`); - const expression = (program.body[0] as estree.ExpressionStatement).expression; + const expression = (program.body[0] as estree.ExpressionStatement).expression as estree.Expression & Identified; // Act - const result = sut.applyMutations(expression, copy); + const result = sut.applyMutations(expression, copy) as estree.UpdateExpression & Identified; // Assert expect(result).to.be.ok; @@ -41,10 +42,10 @@ describe('UpdateOperatorMutator', () => { it('"++i" to "--i"', () => { // Arrange const program = parser.parse(`++i`); - const expression = (program.body[0] as estree.ExpressionStatement).expression; + const expression = (program.body[0] as estree.ExpressionStatement).expression as estree.Expression & Identified; // Act - const result = sut.applyMutations(expression, copy); + const result = sut.applyMutations(expression, copy) as estree.UpdateExpression & Identified; // Assert expect(result).to.be.ok; @@ -55,10 +56,10 @@ describe('UpdateOperatorMutator', () => { it('"--i" to "++i"', () => { // Arrange const program = parser.parse(`--i`); - const expression = (program.body[0] as estree.ExpressionStatement).expression; + const expression = (program.body[0] as estree.ExpressionStatement).expression as estree.Expression & Identified; // Act - const result = sut.applyMutations(expression, copy); + const result = sut.applyMutations(expression, copy) as estree.UpdateExpression & Identified; // Assert expect(result).to.be.ok; @@ -71,7 +72,7 @@ describe('UpdateOperatorMutator', () => { it('"+i" to "-i"', () => { // Arrange const program = parser.parse(`-i`); - const expression = (program.body[0] as estree.ExpressionStatement).expression; + const expression = (program.body[0] as estree.ExpressionStatement).expression as estree.Expression & Identified; // Act const result = sut.applyMutations(expression, copy); diff --git a/test/unit/reporters/BroadcastReporterSpec.ts b/packages/stryker/test/unit/reporters/BroadcastReporterSpec.ts similarity index 100% rename from test/unit/reporters/BroadcastReporterSpec.ts rename to packages/stryker/test/unit/reporters/BroadcastReporterSpec.ts diff --git a/test/unit/reporters/ClearTextReporterSpec.ts b/packages/stryker/test/unit/reporters/ClearTextReporterSpec.ts similarity index 100% rename from test/unit/reporters/ClearTextReporterSpec.ts rename to packages/stryker/test/unit/reporters/ClearTextReporterSpec.ts diff --git a/test/unit/reporters/DotsReporterSpec.ts b/packages/stryker/test/unit/reporters/DotsReporterSpec.ts similarity index 100% rename from test/unit/reporters/DotsReporterSpec.ts rename to packages/stryker/test/unit/reporters/DotsReporterSpec.ts diff --git a/test/unit/reporters/EventRecorderReporterSpec.ts b/packages/stryker/test/unit/reporters/EventRecorderReporterSpec.ts similarity index 100% rename from test/unit/reporters/EventRecorderReporterSpec.ts rename to packages/stryker/test/unit/reporters/EventRecorderReporterSpec.ts diff --git a/test/unit/reporters/ProgressAppendOnlyReporter.ts b/packages/stryker/test/unit/reporters/ProgressAppendOnlyReporter.ts similarity index 100% rename from test/unit/reporters/ProgressAppendOnlyReporter.ts rename to packages/stryker/test/unit/reporters/ProgressAppendOnlyReporter.ts diff --git a/test/unit/reporters/ProgressReporterSpec.ts b/packages/stryker/test/unit/reporters/ProgressReporterSpec.ts similarity index 100% rename from test/unit/reporters/ProgressReporterSpec.ts rename to packages/stryker/test/unit/reporters/ProgressReporterSpec.ts diff --git a/test/unit/utils/TimerSpec.ts b/packages/stryker/test/unit/utils/TimerSpec.ts similarity index 100% rename from test/unit/utils/TimerSpec.ts rename to packages/stryker/test/unit/utils/TimerSpec.ts diff --git a/test/unit/utils/fileUtilsSpec.ts b/packages/stryker/test/unit/utils/fileUtilsSpec.ts similarity index 100% rename from test/unit/utils/fileUtilsSpec.ts rename to packages/stryker/test/unit/utils/fileUtilsSpec.ts diff --git a/test/unit/utils/parserUtilsSpec.ts b/packages/stryker/test/unit/utils/parserUtilsSpec.ts similarity index 92% rename from test/unit/utils/parserUtilsSpec.ts rename to packages/stryker/test/unit/utils/parserUtilsSpec.ts index b787e66d21..5119f40a62 100644 --- a/test/unit/utils/parserUtilsSpec.ts +++ b/packages/stryker/test/unit/utils/parserUtilsSpec.ts @@ -1,5 +1,3 @@ -'use strict'; - import { expect } from 'chai'; import * as parserUtils from '../../../src/utils/parserUtils'; @@ -17,7 +15,7 @@ describe('parserUtils', () => { catch (e) { process.exit(1); }`); - parserUtils.collectFrozenNodes(parsedTryCatch); + new parserUtils.NodeIdentifier().identifyAndFreeze(parsedTryCatch); }); }); diff --git a/testResources/config-reader/invalid.conf.js b/packages/stryker/testResources/config-reader/invalid.conf.js similarity index 100% rename from testResources/config-reader/invalid.conf.js rename to packages/stryker/testResources/config-reader/invalid.conf.js diff --git a/testResources/config-reader/stryker.conf.js b/packages/stryker/testResources/config-reader/stryker.conf.js similarity index 100% rename from testResources/config-reader/stryker.conf.js rename to packages/stryker/testResources/config-reader/stryker.conf.js diff --git a/testResources/config-reader/syntax-error.conf.js b/packages/stryker/testResources/config-reader/syntax-error.conf.js similarity index 100% rename from testResources/config-reader/syntax-error.conf.js rename to packages/stryker/testResources/config-reader/syntax-error.conf.js diff --git a/testResources/config-reader/valid.conf.js b/packages/stryker/testResources/config-reader/valid.conf.js similarity index 100% rename from testResources/config-reader/valid.conf.js rename to packages/stryker/testResources/config-reader/valid.conf.js diff --git a/packages/stryker/testResources/module/.gitignore b/packages/stryker/testResources/module/.gitignore new file mode 100644 index 0000000000..6262e88244 --- /dev/null +++ b/packages/stryker/testResources/module/.gitignore @@ -0,0 +1,2 @@ +typings +usingStryker.js \ No newline at end of file diff --git a/testResources/module/package.json b/packages/stryker/testResources/module/package.json similarity index 100% rename from testResources/module/package.json rename to packages/stryker/testResources/module/package.json diff --git a/testResources/module/stryker.conf.js b/packages/stryker/testResources/module/stryker.conf.js similarity index 100% rename from testResources/module/stryker.conf.js rename to packages/stryker/testResources/module/stryker.conf.js diff --git a/testResources/module/tsconfig.json b/packages/stryker/testResources/module/tsconfig.json similarity index 100% rename from testResources/module/tsconfig.json rename to packages/stryker/testResources/module/tsconfig.json diff --git a/testResources/module/usingStryker.d.ts b/packages/stryker/testResources/module/usingStryker.d.ts similarity index 100% rename from testResources/module/usingStryker.d.ts rename to packages/stryker/testResources/module/usingStryker.d.ts diff --git a/testResources/module/usingStryker.ts b/packages/stryker/testResources/module/usingStryker.ts similarity index 100% rename from testResources/module/usingStryker.ts rename to packages/stryker/testResources/module/usingStryker.ts diff --git a/testResources/sampleProject/src/Add.js b/packages/stryker/testResources/sampleProject/src/Add.js similarity index 100% rename from testResources/sampleProject/src/Add.js rename to packages/stryker/testResources/sampleProject/src/Add.js diff --git a/testResources/sampleProject/src/Array.js b/packages/stryker/testResources/sampleProject/src/Array.js similarity index 100% rename from testResources/sampleProject/src/Array.js rename to packages/stryker/testResources/sampleProject/src/Array.js diff --git a/testResources/sampleProject/src/Circle.js b/packages/stryker/testResources/sampleProject/src/Circle.js similarity index 100% rename from testResources/sampleProject/src/Circle.js rename to packages/stryker/testResources/sampleProject/src/Circle.js diff --git a/testResources/sampleProject/src/Error.js b/packages/stryker/testResources/sampleProject/src/Error.js similarity index 100% rename from testResources/sampleProject/src/Error.js rename to packages/stryker/testResources/sampleProject/src/Error.js diff --git a/testResources/sampleProject/src/InfiniteAdd.js b/packages/stryker/testResources/sampleProject/src/InfiniteAdd.js similarity index 100% rename from testResources/sampleProject/src/InfiniteAdd.js rename to packages/stryker/testResources/sampleProject/src/InfiniteAdd.js diff --git a/testResources/sampleProject/stryker.conf.js b/packages/stryker/testResources/sampleProject/stryker.conf.js similarity index 100% rename from testResources/sampleProject/stryker.conf.js rename to packages/stryker/testResources/sampleProject/stryker.conf.js diff --git a/testResources/sampleProject/test/AddSpec.js b/packages/stryker/testResources/sampleProject/test/AddSpec.js similarity index 100% rename from testResources/sampleProject/test/AddSpec.js rename to packages/stryker/testResources/sampleProject/test/AddSpec.js diff --git a/testResources/sampleProject/test/CircleSpec.js b/packages/stryker/testResources/sampleProject/test/CircleSpec.js similarity index 100% rename from testResources/sampleProject/test/CircleSpec.js rename to packages/stryker/testResources/sampleProject/test/CircleSpec.js diff --git a/testResources/sampleProject/test/EmptySpec.js b/packages/stryker/testResources/sampleProject/test/EmptySpec.js similarity index 100% rename from testResources/sampleProject/test/EmptySpec.js rename to packages/stryker/testResources/sampleProject/test/EmptySpec.js diff --git a/testResources/sampleProject/test/FailingAddSpec.js b/packages/stryker/testResources/sampleProject/test/FailingAddSpec.js similarity index 100% rename from testResources/sampleProject/test/FailingAddSpec.js rename to packages/stryker/testResources/sampleProject/test/FailingAddSpec.js diff --git a/testResources/vendor/zone.js/zone.file.js b/packages/stryker/testResources/vendor/zone.js/zone.file.js similarity index 100% rename from testResources/vendor/zone.js/zone.file.js rename to packages/stryker/testResources/vendor/zone.js/zone.file.js diff --git a/tsconfig.json b/packages/stryker/tsconfig.json similarity index 100% rename from tsconfig.json rename to packages/stryker/tsconfig.json diff --git a/packages/stryker/tslint.json b/packages/stryker/tslint.json new file mode 100644 index 0000000000..c628ee2f1b --- /dev/null +++ b/packages/stryker/tslint.json @@ -0,0 +1,58 @@ +{ + "rules": { + "class-name": true, + "comment-format": [ + true, + "check-space" + ], + "indent": [ + true, + "spaces" + ], + "no-duplicate-variable": true, + "no-eval": true, + "no-internal-module": true, + "no-trailing-whitespace": false, + "no-unsafe-finally": true, + "no-var-keyword": true, + "one-line": [ + true, + "check-open-brace", + "check-whitespace" + ], + "quotemark": [ + true, + "single" + ], + "semicolon": [ + true, + "always" + ], + "triple-equals": [ + true, + "allow-null-check" + ], + "typedef-whitespace": [ + true, + { + "call-signature": "nospace", + "index-signature": "nospace", + "parameter": "nospace", + "property-declaration": "nospace", + "variable-declaration": "nospace" + } + ], + "variable-name": [ + true, + "ban-keywords" + ], + "whitespace": [ + true, + "check-branch", + "check-decl", + "check-operator", + "check-separator", + "check-type" + ] + } +} \ No newline at end of file diff --git a/src/utils/parserUtils.ts b/src/utils/parserUtils.ts deleted file mode 100644 index dc8ca9e82b..0000000000 --- a/src/utils/parserUtils.ts +++ /dev/null @@ -1,71 +0,0 @@ -import * as _ from 'lodash'; -import * as esprima from 'esprima'; -import * as estree from 'estree'; -const escodegen = require('escodegen'); - -/** - * Utility class for parsing and generating code. - * @constructor - */ -const esprimaOptions = { - comment: true, - loc: true, - range: true, - tokens: true, -}; - -/** - * Parses code to generate an Abstract Syntax Tree. - * @function - * @param code - The code which has to be parsed. - * @returns {Object} The generated Abstract Syntax Tree. - */ -export function parse(code: string): estree.Program { - if (code === undefined) { - throw new Error('Code parameter cannot be undefined'); - } - - const abstractSyntaxTree = esprima.parse(code, esprimaOptions); - - return abstractSyntaxTree; -}; - -/** - * Finds all nodes which have a 'type' property and freezes them. - * @function - * @param abstractSyntaxTree - The current part of the abstract syntax tree which will be investigated. - * @returns All nodes with a type. - */ -export function collectFrozenNodes(abstractSyntaxTree: estree.Node, nodes?: estree.Node[]): estree.Node[] { - nodes = nodes || []; - - if (!_.isArray(abstractSyntaxTree) && _.isObject(abstractSyntaxTree) && abstractSyntaxTree.type && _.isUndefined(abstractSyntaxTree.nodeID)) { - abstractSyntaxTree.nodeID = nodes.length; - nodes.push(abstractSyntaxTree); - } - - Object.freeze(abstractSyntaxTree); - - _.forOwn(abstractSyntaxTree, (childNode, i) => { - if (childNode instanceof Object && !(childNode instanceof Array)) { - collectFrozenNodes(childNode, nodes); - } else if (childNode instanceof Array) { - _.forEach(childNode, (arrayChild) => { - if (arrayChild instanceof Object && !(arrayChild instanceof Array)) { - collectFrozenNodes(arrayChild, nodes); - } - }); - } - }); - - return nodes; -}; - -/** - * Parses a Node to generate code. - * @param The Node which has to be transformed into code. - * @returns The generated code. - */ -export function generate(node: estree.Node): string { - return escodegen.generate(node); -}; \ No newline at end of file