From b4bf7d504428d956effaecd9abbac9a46c6205a7 Mon Sep 17 00:00:00 2001 From: Yulong Wang <7679871+fs-eire@users.noreply.github.com> Date: Tue, 11 Jul 2023 14:34:40 -0700 Subject: [PATCH] [js/web/test] accelerate 'npm test' suite0/1 init time (#16558) ### Description This change reduces the number of calls to globby functions so that it accelerates the initialization for 'npm test' with suite0/1 tests from ~14sec to <2sec. --- js/web/script/test-runner-cli.ts | 94 +++++++++++++++++--------------- 1 file changed, 50 insertions(+), 44 deletions(-) diff --git a/js/web/script/test-runner-cli.ts b/js/web/script/test-runner-cli.ts index 49f5de3cdd..382c2eba73 100644 --- a/js/web/script/test-runner-cli.ts +++ b/js/web/script/test-runner-cli.ts @@ -71,15 +71,20 @@ async function main() { if (shouldLoadSuiteTestData) { npmlog.verbose('TestRunnerCli.Init', 'Loading test groups for suite test...'); + // collect all model test folders + const allNodeTestsFolders = + DEFAULT_OPSET_VERSIONS + .map(version => { + const suiteRootFolder = path.join(TEST_DATA_MODEL_NODE_ROOT, `opset${version}`); + if (!fs.existsSync(suiteRootFolder) || !fs.statSync(suiteRootFolder).isDirectory()) { + throw new Error(`model test root folder '${suiteRootFolder}' does not exist.`); + } + return fs.readdirSync(suiteRootFolder).map(f => `opset${version}/${f}`); + }) + .flat(); + for (const backend of DEFAULT_BACKENDS) { - for (const version of DEFAULT_OPSET_VERSIONS) { - let nodeTest = nodeTests.get(backend); - if (!nodeTest) { - nodeTest = []; - nodeTests.set(backend, nodeTest); - } - nodeTest.push(loadNodeTests(backend, version)); - } + nodeTests.set(backend, loadNodeTests(backend, allNodeTestsFolders)); opTests.set(backend, loadOpTests(backend)); } } @@ -171,12 +176,11 @@ async function main() { const testCaseName = typeof testCase === 'string' ? testCase : testCase.name; let found = false; for (const testGroup of nodeTest) { - found = found || - testGroup.tests.some( - test => minimatch( - test.modelUrl, - path.join('**', testCaseName, '*.+(onnx|ort)').replace(/\\/g, '/'), - )); + found ||= minimatch + .match( + testGroup.tests.map(test => test.modelUrl).filter(url => url !== ''), + path.join('**', testCaseName, '*.+(onnx|ort)').replace(/\\/g, '/'), {matchBase: true}) + .length > 0; } if (!found) { throw new Error(`node model test case '${testCaseName}' in test list does not exist.`); @@ -208,39 +212,41 @@ async function main() { } } - function loadNodeTests(backend: string, version: string): Test.ModelTestGroup { - return suiteFromFolder( - `node-opset_v${version}-${backend}`, path.join(TEST_DATA_MODEL_NODE_ROOT, `opset${version}`), backend, - testlist[backend].node); - } + function loadNodeTests(backend: string, allFolders: string[]): Test.ModelTestGroup[] { + const allTests = testlist[backend]?.node; - function suiteFromFolder( - name: string, suiteRootFolder: string, backend: string, - testlist?: readonly Test.TestList.Test[]): Test.ModelTestGroup { - const sessions: Test.ModelTest[] = []; - const tests = fs.readdirSync(suiteRootFolder); - for (const test of tests) { - let condition: Test.Condition|undefined; - let times: number|undefined; - if (testlist) { - const matches = testlist.filter( - p => minimatch( - path.join(suiteRootFolder, test), - path.join('**', typeof p === 'string' ? p : p.name).replace(/\\/g, '/'))); - if (matches.length === 0) { - times = 0; - } else if (matches.length === 1) { - const match = matches[0]; - if (typeof match !== 'string') { - condition = match.condition; - } - } else { - throw new Error(`multiple testlist rules matches test: ${path.join(suiteRootFolder, test)}`); - } + // key is folder name, value is test index array + const folderTestMatchCount = new Map(allFolders.map(f => [f, []])); + // key is test category, value is a list of model test + const opsetTests = new Map(); + + allTests.forEach((test, i) => { + const testName = typeof test === 'string' ? test : test.name; + const matches = minimatch.match(allFolders, path.join('**', testName).replace(/\\/g, '/')); + matches.forEach(m => folderTestMatchCount.get(m)!.push(i)); + }); + + for (const folder of allFolders) { + const testIds = folderTestMatchCount.get(folder); + const times = testIds ? testIds.length : 0; + if (times > 1) { + throw new Error(`multiple testlist rules matches test: ${path.join(TEST_DATA_MODEL_NODE_ROOT, folder)}`); } - sessions.push(modelTestFromFolder(path.resolve(suiteRootFolder, test), backend, condition, times)); + + const test = testIds && testIds.length > 0 ? allTests[testIds[0]] : undefined; + const condition = test && typeof test !== 'string' ? test.condition : undefined; + + const opsetVersion = folder.split('/')[0]; + const category = `node-${opsetVersion}-${backend}`; + let modelTests = opsetTests.get(category); + if (!modelTests) { + modelTests = []; + opsetTests.set(category, modelTests); + } + modelTests.push(modelTestFromFolder(path.resolve(TEST_DATA_MODEL_NODE_ROOT, folder), backend, condition, times)); } - return {name, tests: sessions}; + + return Array.from(opsetTests.keys()).map(category => ({name: category, tests: opsetTests.get(category)!})); } function modelTestFromFolder(