chore: fix oop loading, prepare to watch (#20618)

This commit is contained in:
Pavel Feldman 2023-02-03 09:11:02 -08:00 committed by GitHub
parent adab7a817e
commit ffb719385b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 49 additions and 27 deletions

View file

@ -159,7 +159,7 @@ async function runTests(args: string[], opts: { [key: string]: any }) {
configLoader.ignoreProjectDependencies(); configLoader.ignoreProjectDependencies();
const config = configLoader.fullConfig(); const config = configLoader.fullConfig();
config._internal.testFileFilters = args.map(arg => { config._internal.cliFileFilters = args.map(arg => {
const match = /^(.*?):(\d+):?(\d+)?$/.exec(arg); const match = /^(.*?):(\d+):?(\d+)?$/.exec(arg);
return { return {
re: forceRegExp(match ? match[1] : arg), re: forceRegExp(match ? match[1] : arg),
@ -169,9 +169,9 @@ async function runTests(args: string[], opts: { [key: string]: any }) {
}); });
const grepMatcher = opts.grep ? createTitleMatcher(forceRegExp(opts.grep)) : () => true; const grepMatcher = opts.grep ? createTitleMatcher(forceRegExp(opts.grep)) : () => true;
const grepInvertMatcher = opts.grepInvert ? createTitleMatcher(forceRegExp(opts.grepInvert)) : () => false; const grepInvertMatcher = opts.grepInvert ? createTitleMatcher(forceRegExp(opts.grepInvert)) : () => false;
config._internal.testTitleMatcher = (title: string) => !grepInvertMatcher(title) && grepMatcher(title); config._internal.cliTitleMatcher = (title: string) => !grepInvertMatcher(title) && grepMatcher(title);
config._internal.listOnly = !!opts.list; config._internal.listOnly = !!opts.list;
config._internal.projectFilter = opts.project || undefined; config._internal.cliProjectFilter = opts.project || undefined;
config._internal.passWithNoTests = !!opts.passWithNoTests; config._internal.passWithNoTests = !!opts.passWithNoTests;
const runner = new Runner(config); const runner = new Runner(config);

View file

@ -94,7 +94,12 @@ export function serializeCompilationCache(): any {
}; };
} }
export function initializeCompilationCache(payload: any) { export function clearCompilationCache() {
sourceMaps.clear();
memoryCache.clear();
}
export function addToCompilationCache(payload: any) {
for (const entry of payload.sourceMaps) for (const entry of payload.sourceMaps)
sourceMaps.set(entry[0], entry[1]); sourceMaps.set(entry[0], entry[1]);
for (const entry of payload.memoryCache) for (const entry of payload.memoryCache)

View file

@ -450,8 +450,8 @@ export const baseFullConfig: FullConfigInternal = {
maxConcurrentTestGroups: 0, maxConcurrentTestGroups: 0,
ignoreSnapshots: false, ignoreSnapshots: false,
plugins: [], plugins: [],
testTitleMatcher: () => true, cliTitleMatcher: () => true,
testFileFilters: [], cliFileFilters: [],
listOnly: false, listOnly: false,
} }
}; };

View file

@ -50,9 +50,9 @@ type ConfigInternal = {
webServers: Exclude<FullConfigPublic['webServer'], null>[]; webServers: Exclude<FullConfigPublic['webServer'], null>[];
plugins: TestRunnerPluginRegistration[]; plugins: TestRunnerPluginRegistration[];
listOnly: boolean; listOnly: boolean;
testFileFilters: TestFileFilter[]; cliFileFilters: TestFileFilter[];
testTitleMatcher: Matcher; cliTitleMatcher: Matcher;
projectFilter?: string[]; cliProjectFilter?: string[];
passWithNoTests?: boolean; passWithNoTests?: boolean;
}; };

View file

@ -20,6 +20,7 @@ import { ProcessRunner } from '../common/process';
import type { FullConfigInternal } from '../common/types'; import type { FullConfigInternal } from '../common/types';
import { loadTestFile } from '../common/testLoader'; import { loadTestFile } from '../common/testLoader';
import type { TestError } from '../../reporter'; import type { TestError } from '../../reporter';
import { addToCompilationCache, serializeCompilationCache } from '../common/compilationCache';
export class LoaderMain extends ProcessRunner { export class LoaderMain extends ProcessRunner {
private _serializedConfig: SerializedConfig; private _serializedConfig: SerializedConfig;
@ -27,6 +28,7 @@ export class LoaderMain extends ProcessRunner {
constructor(serializedConfig: SerializedConfig) { constructor(serializedConfig: SerializedConfig) {
super(); super();
addToCompilationCache(serializedConfig.compilationCache);
this._serializedConfig = serializedConfig; this._serializedConfig = serializedConfig;
} }
@ -39,8 +41,12 @@ export class LoaderMain extends ProcessRunner {
async loadTestFile(params: { file: string }) { async loadTestFile(params: { file: string }) {
const testErrors: TestError[] = []; const testErrors: TestError[] = [];
const config = await this._config(); const config = await this._config();
const rootSuite = await loadTestFile(params.file, config.rootDir, testErrors); const fileSuite = await loadTestFile(params.file, config.rootDir, testErrors);
return { rootSuite: rootSuite._deepSerialize(), testErrors }; return { fileSuite: fileSuite._deepSerialize(), testErrors };
}
async serializeCompilationCache() {
return serializeCompilationCache();
} }
} }

View file

@ -21,15 +21,15 @@ import type { LoaderHost } from './loaderHost';
import { Suite } from '../common/test'; import { Suite } from '../common/test';
import type { TestCase } from '../common/test'; import type { TestCase } from '../common/test';
import type { FullConfigInternal, FullProjectInternal } from '../common/types'; import type { FullConfigInternal, FullProjectInternal } from '../common/types';
import { createFileMatcherFromFilters, createTitleMatcher, errorWithFile } from '../util'; import { createTitleMatcher, errorWithFile } from '../util';
import type { Matcher, TestFileFilter } from '../util'; import type { Matcher, TestFileFilter } from '../util';
import { buildProjectsClosure, collectFilesForProject, filterProjects } from './projectUtils'; import { buildProjectsClosure, collectFilesForProject, filterProjects } from './projectUtils';
import { requireOrImport } from '../common/transform'; import { requireOrImport } from '../common/transform';
import { buildFileSuiteForProject, filterByFocusedLine, filterOnly, filterTestsRemoveEmptySuites } from '../common/suiteUtils'; import { buildFileSuiteForProject, filterByFocusedLine, filterOnly, filterTestsRemoveEmptySuites } from '../common/suiteUtils';
import { filterForShard } from './testGroups'; import { filterForShard } from './testGroups';
export async function loadAllTests(config: FullConfigInternal, errors: TestError[]): Promise<Suite> { export async function loadAllTests(config: FullConfigInternal, projectsToIgnore: Set<FullProjectInternal>, fileMatcher: Matcher, errors: TestError[]): Promise<Suite> {
const projects = filterProjects(config.projects, config._internal.projectFilter); const projects = filterProjects(config.projects, config._internal.cliProjectFilter);
let filesToRunByProject = new Map<FullProjectInternal, string[]>(); let filesToRunByProject = new Map<FullProjectInternal, string[]>();
let topLevelProjects: FullProjectInternal[]; let topLevelProjects: FullProjectInternal[];
@ -41,14 +41,15 @@ export async function loadAllTests(config: FullConfigInternal, errors: TestError
// First collect all files for the projects in the command line, don't apply any file filters. // First collect all files for the projects in the command line, don't apply any file filters.
const allFilesForProject = new Map<FullProjectInternal, string[]>(); const allFilesForProject = new Map<FullProjectInternal, string[]>();
for (const project of projects) { for (const project of projects) {
if (projectsToIgnore.has(project))
continue;
const files = await collectFilesForProject(project, fsCache); const files = await collectFilesForProject(project, fsCache);
allFilesForProject.set(project, files); allFilesForProject.set(project, files);
} }
// Filter files based on the file filters, eliminate the empty projects. // Filter files based on the file filters, eliminate the empty projects.
const commandLineFileMatcher = config._internal.testFileFilters.length ? createFileMatcherFromFilters(config._internal.testFileFilters) : null;
for (const [project, files] of allFilesForProject) { for (const [project, files] of allFilesForProject) {
const filteredFiles = commandLineFileMatcher ? files.filter(commandLineFileMatcher) : files; const filteredFiles = files.filter(fileMatcher);
if (filteredFiles.length) if (filteredFiles.length)
filesToRunByProject.set(project, filteredFiles); filesToRunByProject.set(project, filteredFiles);
} }
@ -67,6 +68,9 @@ export async function loadAllTests(config: FullConfigInternal, errors: TestError
topLevelProjects = filteredProjectClosure.filter(p => p._internal.type === 'top-level'); topLevelProjects = filteredProjectClosure.filter(p => p._internal.type === 'top-level');
dependencyProjects = filteredProjectClosure.filter(p => p._internal.type === 'dependency'); dependencyProjects = filteredProjectClosure.filter(p => p._internal.type === 'dependency');
topLevelProjects = topLevelProjects.filter(p => !projectsToIgnore.has(p));
dependencyProjects = dependencyProjects.filter(p => !projectsToIgnore.has(p));
// (Re-)add all files for dependent projects, disregard filters. // (Re-)add all files for dependent projects, disregard filters.
for (const project of dependencyProjects) { for (const project of dependencyProjects) {
const files = allFilesForProject.get(project) || await collectFilesForProject(project, fsCache); const files = allFilesForProject.get(project) || await collectFilesForProject(project, fsCache);
@ -113,7 +117,7 @@ export async function loadAllTests(config: FullConfigInternal, errors: TestError
// Prepend the projects that are dependencies. // Prepend the projects that are dependencies.
for (const project of dependencyProjects) { for (const project of dependencyProjects) {
const projectSuite = await createProjectSuite(fileSuits, project, { testFileFilters: [], testTitleMatcher: undefined }, filesToRunByProject.get(project)!); const projectSuite = await createProjectSuite(fileSuits, project, { cliFileFilters: [], cliTitleMatcher: undefined }, filesToRunByProject.get(project)!);
if (projectSuite) if (projectSuite)
rootSuite._prependSuite(projectSuite); rootSuite._prependSuite(projectSuite);
} }
@ -121,7 +125,7 @@ export async function loadAllTests(config: FullConfigInternal, errors: TestError
return rootSuite; return rootSuite;
} }
async function createProjectSuite(fileSuits: Suite[], project: FullProjectInternal, options: { testFileFilters: TestFileFilter[], testTitleMatcher?: Matcher }, files: string[]): Promise<Suite | null> { async function createProjectSuite(fileSuits: Suite[], project: FullProjectInternal, options: { cliFileFilters: TestFileFilter[], cliTitleMatcher?: Matcher }, files: string[]): Promise<Suite | null> {
const fileSuitesMap = new Map<string, Suite>(); const fileSuitesMap = new Map<string, Suite>();
for (const fileSuite of fileSuits) for (const fileSuite of fileSuits)
fileSuitesMap.set(fileSuite._requireFile, fileSuite); fileSuitesMap.set(fileSuite._requireFile, fileSuite);
@ -140,7 +144,7 @@ async function createProjectSuite(fileSuits: Suite[], project: FullProjectIntern
} }
} }
// Filter tests to respect line/column filter. // Filter tests to respect line/column filter.
filterByFocusedLine(projectSuite, options.testFileFilters); filterByFocusedLine(projectSuite, options.cliFileFilters);
const grepMatcher = createTitleMatcher(project.grep); const grepMatcher = createTitleMatcher(project.grep);
const grepInvertMatcher = project.grepInvert ? createTitleMatcher(project.grepInvert) : null; const grepInvertMatcher = project.grepInvert ? createTitleMatcher(project.grepInvert) : null;
@ -149,7 +153,7 @@ async function createProjectSuite(fileSuits: Suite[], project: FullProjectIntern
const grepTitle = test.titlePath().join(' '); const grepTitle = test.titlePath().join(' ');
if (grepInvertMatcher?.(grepTitle)) if (grepInvertMatcher?.(grepTitle))
return false; return false;
return grepMatcher(grepTitle) && (!options.testTitleMatcher || options.testTitleMatcher(grepTitle)); return grepMatcher(grepTitle) && (!options.cliTitleMatcher || options.cliTitleMatcher(grepTitle));
}; };
if (filterTestsRemoveEmptySuites(projectSuite, titleMatcher)) if (filterTestsRemoveEmptySuites(projectSuite, titleMatcher))

View file

@ -22,6 +22,7 @@ import { loadTestFile } from '../common/testLoader';
import type { LoadError } from '../common/fixtures'; import type { LoadError } from '../common/fixtures';
import type { FullConfigInternal } from '../common/types'; import type { FullConfigInternal } from '../common/types';
import { PoolBuilder } from '../common/poolBuilder'; import { PoolBuilder } from '../common/poolBuilder';
import { addToCompilationCache } from '../common/compilationCache';
export abstract class LoaderHost { export abstract class LoaderHost {
protected _config: FullConfigInternal; protected _config: FullConfigInternal;
@ -56,18 +57,20 @@ export class OutOfProcessLoaderHost extends LoaderHost {
constructor(config: FullConfigInternal) { constructor(config: FullConfigInternal) {
super(config); super(config);
this._processHost = new ProcessHost(require.resolve('../loaderMain.js'), 'loader'); this._processHost = new ProcessHost(require.resolve('../loader/loaderMain.js'), 'loader');
this._startPromise = this._processHost.startRunner(serializeConfig(config), true, {}); this._startPromise = this._processHost.startRunner(serializeConfig(config), true, {});
} }
async doLoadTestFile(file: string, loadErrors: LoadError[]): Promise<Suite> { async doLoadTestFile(file: string, loadErrors: LoadError[]): Promise<Suite> {
await this._startPromise; await this._startPromise;
const result = await this._processHost.sendMessage({ method: 'loadTestFile', params: { file } }) as any; const result = await this._processHost.sendMessage({ method: 'loadTestFile', params: { file } }) as any;
loadErrors.push(...result.loadErrors); loadErrors.push(...result.testErrors);
return Suite._deepParse(result.fileSuite); return Suite._deepParse(result.fileSuite);
} }
override async stop() { override async stop() {
const result = await this._processHost.sendMessage({ method: 'serializeCompilationCache' }) as any;
addToCompilationCache(result);
await this._processHost.stop(); await this._processHost.stop();
} }
} }

View file

@ -28,6 +28,8 @@ import { TaskRunner } from './taskRunner';
import type { Suite } from '../common/test'; import type { Suite } from '../common/test';
import type { FullConfigInternal, FullProjectInternal } from '../common/types'; import type { FullConfigInternal, FullProjectInternal } from '../common/types';
import { loadAllTests, loadGlobalHook } from './loadUtils'; import { loadAllTests, loadGlobalHook } from './loadUtils';
import { createFileMatcherFromFilters } from '../util';
import type { Matcher } from '../util';
const removeFolderAsync = promisify(rimraf); const removeFolderAsync = promisify(rimraf);
const readDirAsync = promisify(fs.readdir); const readDirAsync = promisify(fs.readdir);
@ -106,7 +108,7 @@ function createRemoveOutputDirsTask(): Task<TaskRunnerState> {
return async ({ config }) => { return async ({ config }) => {
const outputDirs = new Set<string>(); const outputDirs = new Set<string>();
for (const p of config.projects) { for (const p of config.projects) {
if (!config._internal.projectFilter || config._internal.projectFilter.includes(p.name)) if (!config._internal.cliProjectFilter || config._internal.cliProjectFilter.includes(p.name))
outputDirs.add(p.outputDir); outputDirs.add(p.outputDir);
} }
@ -124,10 +126,12 @@ function createRemoveOutputDirsTask(): Task<TaskRunnerState> {
}; };
} }
function createLoadTask(): Task<TaskRunnerState> { function createLoadTask(projectsToIgnore = new Set<FullProjectInternal>(), additionalFileMatcher?: Matcher): Task<TaskRunnerState> {
return async (context, errors) => { return async (context, errors) => {
const { config } = context; const { config } = context;
context.rootSuite = await loadAllTests(config, errors); const cliMatcher = config._internal.cliFileFilters.length ? createFileMatcherFromFilters(config._internal.cliFileFilters) : () => true;
const fileMatcher = (value: string) => cliMatcher(value) && (additionalFileMatcher ? additionalFileMatcher(value) : true);
context.rootSuite = await loadAllTests(config, projectsToIgnore, fileMatcher, errors);
// Fail when no tests. // Fail when no tests.
if (!context.rootSuite.allTests().length && !config._internal.passWithNoTests && !config.shard) if (!context.rootSuite.allTests().length && !config._internal.passWithNoTests && !config.shard)
throw new Error(`No tests found`); throw new Error(`No tests found`);

View file

@ -31,7 +31,7 @@ import { ProcessRunner } from '../common/process';
import { loadTestFile } from '../common/testLoader'; import { loadTestFile } from '../common/testLoader';
import { buildFileSuiteForProject, filterTestsRemoveEmptySuites } from '../common/suiteUtils'; import { buildFileSuiteForProject, filterTestsRemoveEmptySuites } from '../common/suiteUtils';
import { PoolBuilder } from '../common/poolBuilder'; import { PoolBuilder } from '../common/poolBuilder';
import { initializeCompilationCache } from '../common/compilationCache'; import { addToCompilationCache } from '../common/compilationCache';
const removeFolderAsync = util.promisify(rimraf); const removeFolderAsync = util.promisify(rimraf);
@ -67,7 +67,7 @@ export class WorkerMain extends ProcessRunner {
process.env.TEST_WORKER_INDEX = String(params.workerIndex); process.env.TEST_WORKER_INDEX = String(params.workerIndex);
process.env.TEST_PARALLEL_INDEX = String(params.parallelIndex); process.env.TEST_PARALLEL_INDEX = String(params.parallelIndex);
setIsWorkerProcess(); setIsWorkerProcess();
initializeCompilationCache(params.config.compilationCache); addToCompilationCache(params.config.compilationCache);
this._params = params; this._params = params;
this._fixtureRunner = new FixtureRunner(); this._fixtureRunner = new FixtureRunner();