Skip to content

Commit

Permalink
feat(test runner api): remove sandboxFileNames injectable values (#…
Browse files Browse the repository at this point in the history
…2369)

Remove `sandboxFileNames` as injectable value for test runner plugins. Test runner plugins are expected to do their own file discovery in the current working directory. The list of sandbox files that Stryker internally copied over might differ from the list of files in the sandbox, because the user might have configured a `buildCommand` that compiled the files to their final representation.

* Remove `sandboxFileNames` from dependency injection context
* Rename `OptionsContext` to `PluginContext` and remove the `PluginContexts` mapped type (no longer needed)
* Move generic require cache clean functionality to `@stryker-mutator/util` 
    * Use this in the mocha-runner 
    * Use this in the jasmine-runner.

Fixed #2351
  • Loading branch information
nicojs committed Aug 11, 2020
1 parent 55f27f2 commit 92f3bf5
Show file tree
Hide file tree
Showing 44 changed files with 319 additions and 247 deletions.
20 changes: 1 addition & 19 deletions packages/api/src/plugin/Contexts.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { MutatorDescriptor, StrykerOptions } from '../../core';
import { Logger, LoggerFactoryMethod } from '../../logging';

import { PluginKind } from './PluginKind';
import { PluginResolver } from './Plugins';
import { commonTokens } from './tokens';

Expand All @@ -18,24 +17,7 @@ export interface BaseContext {
* The dependency injection context for most of Stryker's plugins.
* Can inject basic stuff as well as the Stryker options
*/
export interface OptionsContext extends BaseContext {
export interface PluginContext extends BaseContext {
[commonTokens.options]: StrykerOptions;
[commonTokens.mutatorDescriptor]: MutatorDescriptor;
}

/**
* The dependency injection context for a `TestRunnerPlugin`
*/
export interface TestRunnerPluginContext extends OptionsContext {
[commonTokens.sandboxFileNames]: readonly string[];
}

/**
* Lookup type for plugin contexts by kind.
*/
export interface PluginContexts {
[PluginKind.Reporter]: OptionsContext;
[PluginKind.TestRunner]: TestRunnerPluginContext;
[PluginKind.TestRunner2]: TestRunnerPluginContext;
[PluginKind.Checker]: OptionsContext;
}
22 changes: 11 additions & 11 deletions packages/api/src/plugin/Plugins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,39 +5,39 @@ import { TestRunner } from '../../test_runner';
import { TestRunner2 } from '../../test_runner2';
import { Checker } from '../../check';

import { PluginContexts } from './Contexts';
import { PluginContext } from './Contexts';
import { PluginKind } from './PluginKind';

/**
* Represents a StrykerPlugin
*/
export type Plugin<TPluginKind extends PluginKind> =
| FactoryPlugin<TPluginKind, Array<InjectionToken<PluginContexts[TPluginKind]>>>
| ClassPlugin<TPluginKind, Array<InjectionToken<PluginContexts[TPluginKind]>>>;
| FactoryPlugin<TPluginKind, Array<InjectionToken<PluginContext>>>
| ClassPlugin<TPluginKind, Array<InjectionToken<PluginContext>>>;

/**
* Represents a plugin that is created with a factory method
*/
export interface FactoryPlugin<TPluginKind extends PluginKind, Tokens extends Array<InjectionToken<PluginContexts[TPluginKind]>>> {
export interface FactoryPlugin<TPluginKind extends PluginKind, Tokens extends Array<InjectionToken<PluginContext>>> {
readonly kind: TPluginKind;
readonly name: string;
/**
* The factory method used to create the plugin
*/
readonly factory: InjectableFunction<PluginContexts[TPluginKind], PluginInterfaces[TPluginKind], Tokens>;
readonly factory: InjectableFunction<PluginContext, PluginInterfaces[TPluginKind], Tokens>;
}

/**
* Represents a plugin that is created by instantiating a class.
*/
export interface ClassPlugin<TPluginKind extends PluginKind, Tokens extends Array<InjectionToken<PluginContexts[TPluginKind]>>> {
export interface ClassPlugin<TPluginKind extends PluginKind, Tokens extends Array<InjectionToken<PluginContext>>> {
readonly kind: TPluginKind;
readonly name: string;
/**
* The prototype function (class) used to create the plugin.
* Not called `class` here, because that is a keyword
*/
readonly injectableClass: InjectableClass<PluginContexts[TPluginKind], PluginInterfaces[TPluginKind], Tokens>;
readonly injectableClass: InjectableClass<PluginContext, PluginInterfaces[TPluginKind], Tokens>;
}

/**
Expand All @@ -46,10 +46,10 @@ export interface ClassPlugin<TPluginKind extends PluginKind, Tokens extends Arra
* @param name The name of the plugin
* @param injectableClass The class to be instantiated for the plugin
*/
export function declareClassPlugin<TPluginKind extends PluginKind, Tokens extends Array<InjectionToken<PluginContexts[TPluginKind]>>>(
export function declareClassPlugin<TPluginKind extends PluginKind, Tokens extends Array<InjectionToken<PluginContext>>>(
kind: TPluginKind,
name: string,
injectableClass: InjectableClass<PluginContexts[TPluginKind], PluginInterfaces[TPluginKind], Tokens>
injectableClass: InjectableClass<PluginContext, PluginInterfaces[TPluginKind], Tokens>
): ClassPlugin<TPluginKind, Tokens> {
return {
injectableClass,
Expand All @@ -64,10 +64,10 @@ export function declareClassPlugin<TPluginKind extends PluginKind, Tokens extend
* @param name The name of the plugin
* @param factory The factory used to instantiate the plugin
*/
export function declareFactoryPlugin<TPluginKind extends PluginKind, Tokens extends Array<InjectionToken<PluginContexts[TPluginKind]>>>(
export function declareFactoryPlugin<TPluginKind extends PluginKind, Tokens extends Array<InjectionToken<PluginContext>>>(
kind: TPluginKind,
name: string,
factory: InjectableFunction<PluginContexts[TPluginKind], PluginInterfaces[TPluginKind], Tokens>
factory: InjectableFunction<PluginContext, PluginInterfaces[TPluginKind], Tokens>
): FactoryPlugin<TPluginKind, Tokens> {
return {
factory,
Expand Down
1 change: 0 additions & 1 deletion packages/api/src/plugin/tokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ export const commonTokens = Object.freeze({
options: stringLiteral('options'),
pluginResolver: stringLiteral('pluginResolver'),
produceSourceMaps: stringLiteral('produceSourceMaps'),
sandboxFileNames: stringLiteral('sandboxFileNames'),
target,
});

Expand Down
1 change: 0 additions & 1 deletion packages/api/test/unit/plugin/tokens.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,5 @@ describe('commonTokens', () => {
itShouldProvideToken('logger');
itShouldProvideToken('pluginResolver');
itShouldProvideToken('produceSourceMaps');
itShouldProvideToken('sandboxFileNames');
itShouldProvideToken('getLogger');
});
4 changes: 2 additions & 2 deletions packages/core/src/checker/CheckerWorker.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Checker, CheckResult, CheckStatus } from '@stryker-mutator/api/check';
import { StrykerOptions, Mutant } from '@stryker-mutator/api/core';

import { PluginKind, tokens, commonTokens, OptionsContext, Injector } from '@stryker-mutator/api/plugin';
import { PluginKind, tokens, commonTokens, PluginContext, Injector } from '@stryker-mutator/api/plugin';

import { StrykerError } from '@stryker-mutator/util';

Expand All @@ -11,7 +11,7 @@ export class CheckerWorker implements Checker {
private readonly innerCheckers: Array<{ name: string; checker: Checker }> = [];

public static inject = tokens(commonTokens.options, commonTokens.injector);
constructor(options: StrykerOptions, injector: Injector<OptionsContext>) {
constructor(options: StrykerOptions, injector: Injector<PluginContext>) {
const pluginCreator = injector.injectFunction(PluginCreator.createFactory(PluginKind.Checker));
this.innerCheckers = options.checkers.map((name) => ({ name, checker: pluginCreator.create(name) }));
}
Expand Down
6 changes: 3 additions & 3 deletions packages/core/src/child-proxy/ChildProcessProxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { ChildProcess, fork } from 'child_process';
import * as os from 'os';

import { File, StrykerOptions } from '@stryker-mutator/api/core';
import { OptionsContext } from '@stryker-mutator/api/plugin';
import { PluginContext } from '@stryker-mutator/api/plugin';
import { isErrnoException, Task, ExpirableTask } from '@stryker-mutator/util';
import { getLogger } from 'log4js';
import { Disposable, InjectableClass, InjectionToken } from 'typed-inject';
Expand Down Expand Up @@ -71,13 +71,13 @@ export default class ChildProcessProxy<T> implements Disposable {
/**
* @description Creates a proxy where each function of the object created using the constructorFunction arg is ran inside of a child process
*/
public static create<TAdditionalContext, R, Tokens extends Array<InjectionToken<OptionsContext & TAdditionalContext>>>(
public static create<TAdditionalContext, R, Tokens extends Array<InjectionToken<PluginContext & TAdditionalContext>>>(
requirePath: string,
loggingContext: LoggingClientContext,
options: StrykerOptions,
additionalInjectableValues: TAdditionalContext,
workingDirectory: string,
InjectableClass: InjectableClass<TAdditionalContext & OptionsContext, R, Tokens>
InjectableClass: InjectableClass<TAdditionalContext & PluginContext, R, Tokens>
): ChildProcessProxy<R> {
return new ChildProcessProxy(requirePath, InjectableClass.name, loggingContext, options, additionalInjectableValues, workingDirectory);
}
Expand Down
14 changes: 7 additions & 7 deletions packages/core/src/di/PluginCreator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
commonTokens,
FactoryPlugin,
Plugin,
PluginContexts,
PluginContext,
PluginInterfaces,
PluginKind,
PluginResolver,
Expand All @@ -15,7 +15,7 @@ export class PluginCreator<TPluginKind extends PluginKind> {
private constructor(
private readonly kind: TPluginKind,
private readonly pluginResolver: PluginResolver,
private readonly injector: Injector<PluginContexts[TPluginKind]>
private readonly injector: Injector<PluginContext>
) {}

public create(name: string): PluginInterfaces[TPluginKind] {
Expand All @@ -29,14 +29,14 @@ export class PluginCreator<TPluginKind extends PluginKind> {
}
}

private isFactoryPlugin(plugin: Plugin<PluginKind>): plugin is FactoryPlugin<TPluginKind, Array<InjectionToken<PluginContexts[TPluginKind]>>> {
return !!(plugin as FactoryPlugin<TPluginKind, Array<InjectionToken<PluginContexts[TPluginKind]>>>).factory;
private isFactoryPlugin(plugin: Plugin<PluginKind>): plugin is FactoryPlugin<TPluginKind, Array<InjectionToken<PluginContext>>> {
return !!(plugin as FactoryPlugin<TPluginKind, Array<InjectionToken<PluginContext>>>).factory;
}
private isClassPlugin(plugin: Plugin<PluginKind>): plugin is ClassPlugin<TPluginKind, Array<InjectionToken<PluginContexts[TPluginKind]>>> {
return !!(plugin as ClassPlugin<TPluginKind, Array<InjectionToken<PluginContexts[TPluginKind]>>>).injectableClass;
private isClassPlugin(plugin: Plugin<PluginKind>): plugin is ClassPlugin<TPluginKind, Array<InjectionToken<PluginContext>>> {
return !!(plugin as ClassPlugin<TPluginKind, Array<InjectionToken<PluginContext>>>).injectableClass;
}

public static createFactory<TPluginKind extends PluginKind, TContext extends PluginContexts[TPluginKind]>(
public static createFactory<TPluginKind extends PluginKind, TContext extends PluginContext>(
kind: TPluginKind
): InjectableFunctionWithInject<TContext, PluginCreator<TPluginKind>, [typeof commonTokens.pluginResolver, typeof commonTokens.injector]> {
function factory(pluginResolver: PluginResolver, injector: Injector<TContext>): PluginCreator<TPluginKind> {
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/di/buildChildProcessInjector.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { StrykerOptions } from '@stryker-mutator/api/core';
import { commonTokens, Injector, OptionsContext, Scope, tokens } from '@stryker-mutator/api/plugin';
import { commonTokens, Injector, PluginContext, Scope, tokens } from '@stryker-mutator/api/plugin';
import { getLogger } from 'log4js';
import { rootInjector } from 'typed-inject';

import { loggerFactory, mutatorDescriptorFactory, pluginResolverFactory } from './factoryMethods';

import { coreTokens } from '.';

export function buildChildProcessInjector(options: StrykerOptions): Injector<OptionsContext> {
export function buildChildProcessInjector(options: StrykerOptions): Injector<PluginContext> {
return rootInjector
.provideValue(commonTokens.options, options)
.provideValue(commonTokens.getLogger, getLogger)
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/di/buildMainInjector.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import execa = require('execa');
import { StrykerOptions, strykerCoreSchema, PartialStrykerOptions } from '@stryker-mutator/api/core';
import { commonTokens, Injector, OptionsContext, PluginKind, Scope, tokens } from '@stryker-mutator/api/plugin';
import { commonTokens, Injector, PluginContext, PluginKind, Scope, tokens } from '@stryker-mutator/api/plugin';
import { Reporter } from '@stryker-mutator/api/report';
import { getLogger } from 'log4js';

Expand All @@ -14,7 +14,7 @@ import { loggerFactory, mutatorDescriptorFactory, pluginResolverFactory } from '

import { coreTokens, PluginCreator } from '.';

export interface MainContext extends OptionsContext {
export interface MainContext extends PluginContext {
[coreTokens.reporter]: Required<Reporter>;
[coreTokens.pluginCreatorReporter]: PluginCreator<PluginKind.Reporter>;
[coreTokens.pluginCreatorChecker]: PluginCreator<PluginKind.Checker>;
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/reporters/dashboard-reporter/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { commonTokens, Injector, OptionsContext, tokens } from '@stryker-mutator/api/plugin';
import { commonTokens, Injector, PluginContext, tokens } from '@stryker-mutator/api/plugin';
import { HttpClient } from 'typed-rest-client/HttpClient';

import { determineCIProvider } from '../ci/Provider';
Expand All @@ -7,7 +7,7 @@ import DashboardReporter from './DashboardReporter';
import DashboardReporterClient from './DashboardReporterClient';
import { dashboardReporterTokens } from './tokens';

export function dashboardReporterFactory(injector: Injector<OptionsContext>): DashboardReporter {
export function dashboardReporterFactory(injector: Injector<PluginContext>): DashboardReporter {
return injector
.provideValue(dashboardReporterTokens.httpClient, new HttpClient('stryker-dashboard-reporter'))
.provideClass(dashboardReporterTokens.dashboardReporterClient, DashboardReporterClient)
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/sandbox/create-preprocessor.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { tokens, Injector, commonTokens, OptionsContext } from '@stryker-mutator/api/plugin';
import { tokens, Injector, commonTokens, PluginContext } from '@stryker-mutator/api/plugin';

import { TSConfigPreprocessor } from './ts-config-preprocessor';
import { FileHeaderPreprocessor } from './file-header-preprocessor';
Expand All @@ -7,7 +7,7 @@ import { MultiPreprocessor } from './multi-preprocessor';
import { StripCommentsPreprocessor } from './strip-comments-preprocessor';

createPreprocessor.inject = tokens(commonTokens.injector);
export function createPreprocessor(injector: Injector<OptionsContext>): FilePreprocessor {
export function createPreprocessor(injector: Injector<PluginContext>): FilePreprocessor {
return new MultiPreprocessor([
injector.injectClass(StripCommentsPreprocessor),
injector.injectClass(TSConfigPreprocessor),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ const MAX_WAIT_FOR_DISPOSE = 2000;
export default class ChildProcessTestRunnerDecorator implements TestRunner2 {
private readonly worker: ChildProcessProxy<ChildProcessTestRunnerWorker>;

constructor(options: StrykerOptions, sandboxFileNames: readonly string[], sandboxWorkingDirectory: string, loggingContext: LoggingClientContext) {
constructor(options: StrykerOptions, sandboxWorkingDirectory: string, loggingContext: LoggingClientContext) {
this.worker = ChildProcessProxy.create(
require.resolve(`./${ChildProcessTestRunnerWorker.name}`),
loggingContext,
options,
{ sandboxFileNames },
{},
sandboxWorkingDirectory,
ChildProcessTestRunnerWorker
);
Expand Down
11 changes: 4 additions & 7 deletions packages/core/src/test-runner/ChildProcessTestRunnerWorker.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { StrykerOptions } from '@stryker-mutator/api/core';
import { commonTokens, Injector, OptionsContext, PluginKind, tokens } from '@stryker-mutator/api/plugin';
import { commonTokens, Injector, PluginContext, PluginKind, tokens } from '@stryker-mutator/api/plugin';
import {
TestRunner2,
DryRunOptions,
Expand All @@ -17,12 +17,9 @@ import { PluginCreator } from '../di';
export class ChildProcessTestRunnerWorker implements TestRunner2 {
private readonly underlyingTestRunner: TestRunner2;

public static inject = tokens(commonTokens.sandboxFileNames, commonTokens.options, commonTokens.injector);
constructor(sandboxFileNames: readonly string[], { testRunner }: StrykerOptions, injector: Injector<OptionsContext>) {
this.underlyingTestRunner = injector
.provideValue(commonTokens.sandboxFileNames, sandboxFileNames)
.injectFunction(PluginCreator.createFactory(PluginKind.TestRunner2))
.create(testRunner);
public static inject = tokens(commonTokens.options, commonTokens.injector);
constructor({ testRunner }: StrykerOptions, injector: Injector<PluginContext>) {
this.underlyingTestRunner = injector.injectFunction(PluginCreator.createFactory(PluginKind.TestRunner2)).create(testRunner);
}

public async init(): Promise<void> {
Expand Down
5 changes: 1 addition & 4 deletions packages/core/src/test-runner/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,6 @@ export function createTestRunnerFactory(
return () => new RetryDecorator(() => new TimeoutDecorator(() => new CommandTestRunner(sandbox.workingDirectory, options)));
} else {
return () =>
new RetryDecorator(
() =>
new TimeoutDecorator(() => new ChildProcessTestRunnerDecorator(options, sandbox.sandboxFileNames, sandbox.workingDirectory, loggingContext))
);
new RetryDecorator(() => new TimeoutDecorator(() => new ChildProcessTestRunnerDecorator(options, sandbox.workingDirectory, loggingContext)));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,15 @@ describe(ChildProcessTestRunnerDecorator.name, () => {
plugins: ['foo-plugin', 'bar-plugin'],
});
loggingContext = { port: 4200, level: LogLevel.Fatal };
sut = new ChildProcessTestRunnerDecorator(options, [], 'a working directory', loggingContext);
sut = new ChildProcessTestRunnerDecorator(options, 'a working directory', loggingContext);
});

it('should create the child process proxy', () => {
expect(childProcessProxyCreateStub).calledWith(
require.resolve('../../../src/test-runner/ChildProcessTestRunnerWorker.js'),
loggingContext,
options,
{ sandboxFileNames: [] },
{},
'a working directory',
ChildProcessTestRunnerWorker
);
Expand Down
Loading

0 comments on commit 92f3bf5

Please sign in to comment.