UNPKG

3.94 kBPlain TextView Raw
1import { execaCommand } from 'execa';
2import { Injector, tokens, commonTokens, PluginContext } from '@stryker-mutator/api/plugin';
3import { createInstrumenter, InstrumentResult } from '@stryker-mutator/instrumenter';
4import { StrykerOptions } from '@stryker-mutator/api/core';
5import { Reporter } from '@stryker-mutator/api/src/report';
6import { I } from '@stryker-mutator/util';
7
8import { coreTokens } from '../di/index.js';
9import { Sandbox } from '../sandbox/sandbox.js';
10import { LoggingClientContext } from '../logging/index.js';
11import { ConcurrencyTokenProvider, createCheckerPool } from '../concurrent/index.js';
12import { createCheckerFactory } from '../checker/index.js';
13import { createPreprocessor } from '../sandbox/index.js';
14import { Timer } from '../utils/timer.js';
15import { TemporaryDirectory } from '../utils/temporary-directory.js';
16import { UnexpectedExitHandler } from '../unexpected-exit-handler.js';
17import { FileSystem, Project } from '../fs/index.js';
18
19import { IdGenerator } from '../child-proxy/id-generator.js';
20
21import { DryRunContext } from './3-dry-run-executor.js';
22
23export interface MutantInstrumenterContext extends PluginContext {
24 [commonTokens.options]: StrykerOptions;
25 [coreTokens.project]: Project;
26 [coreTokens.loggingContext]: LoggingClientContext;
27 [coreTokens.reporter]: Required<Reporter>;
28 [coreTokens.timer]: I<Timer>;
29 [coreTokens.temporaryDirectory]: I<TemporaryDirectory>;
30 [coreTokens.execa]: typeof execaCommand;
31 [coreTokens.process]: NodeJS.Process;
32 [coreTokens.unexpectedExitRegistry]: I<UnexpectedExitHandler>;
33 [coreTokens.pluginModulePaths]: readonly string[];
34 [coreTokens.fs]: I<FileSystem>;
35}
36
37export class MutantInstrumenterExecutor {
38 public static readonly inject = tokens(commonTokens.injector, coreTokens.project, commonTokens.options);
39 constructor(
40 private readonly injector: Injector<MutantInstrumenterContext>,
41 private readonly project: Project,
42 private readonly options: StrykerOptions
43 ) {}
44
45 public async execute(): Promise<Injector<DryRunContext>> {
46 // Create the checker and instrumenter
47 const instrumenter = this.injector.injectFunction(createInstrumenter);
48
49 // Instrument files in-memory
50 const instrumentResult = await instrumenter.instrument(await this.readFilesToMutate(), this.options.mutator);
51
52 // Preprocess the project
53 const preprocess = this.injector.injectFunction(createPreprocessor);
54 this.writeInstrumentedFiles(instrumentResult);
55 await preprocess.preprocess(this.project);
56
57 // Initialize the checker pool
58 const concurrencyTokenProviderProvider = this.injector.provideClass(coreTokens.concurrencyTokenProvider, ConcurrencyTokenProvider);
59 const concurrencyTokenProvider = concurrencyTokenProviderProvider.resolve(coreTokens.concurrencyTokenProvider);
60
61 const checkerPoolProvider = concurrencyTokenProviderProvider
62 .provideValue(coreTokens.checkerConcurrencyTokens, concurrencyTokenProvider.checkerToken$)
63 .provideClass(coreTokens.workerIdGenerator, IdGenerator)
64 .provideFactory(coreTokens.checkerFactory, createCheckerFactory)
65 .provideFactory(coreTokens.checkerPool, createCheckerPool);
66 const checkerPool = checkerPoolProvider.resolve(coreTokens.checkerPool);
67 await checkerPool.init();
68
69 // Feed the sandbox
70 const dryRunProvider = checkerPoolProvider.provideClass(coreTokens.sandbox, Sandbox).provideValue(coreTokens.mutants, instrumentResult.mutants);
71 const sandbox = dryRunProvider.resolve(coreTokens.sandbox);
72 await sandbox.init();
73 return dryRunProvider;
74 }
75
76 private readFilesToMutate() {
77 return Promise.all([...this.project.filesToMutate.values()].map((file) => file.toInstrumenterFile()));
78 }
79
80 private writeInstrumentedFiles(instrumentResult: InstrumentResult): void {
81 for (const { name, content } of Object.values(instrumentResult.files)) {
82 this.project.files.get(name)!.setContent(content);
83 }
84 }
85}