Skip to content

Commit bed39cd

Browse files
authored
feat: support list test names (#43)
1 parent 67d2c81 commit bed39cd

File tree

12 files changed

+604
-29
lines changed

12 files changed

+604
-29
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
/dist
55

66
/build*
7-
/transform/listFunctions.mjs
7+
/transform/*.mjs
88
/transform/tsconfig.tsbuildinfo
99

1010
/coverage

assembly/env.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ export namespace assertResult {
88
export declare function removeDescription(): void;
99

1010

11+
@external("__unittest_framework_env","registerTestFunction")
12+
export declare function registerTestFunction(index: u32): void;
13+
14+
1115
@external("__unittest_framework_env","collectCheckResult")
1216
export declare function collectCheckResult(
1317
result: bool,

assembly/implement.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export function describeImpl(
1111
}
1212
export function testImpl(description: string, testFunction: () => void): void {
1313
assertResult.addDescription(description);
14+
assertResult.registerTestFunction(testFunction.index);
1415
testFunction();
1516
assertResult.removeDescription();
1617
mockFunctionStatus.clear();

bin/cli.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ program
1616
.option("--temp <path>", "test template file folder")
1717
.option("--output <path>", "coverage report output folder")
1818
.option("--mode <output mode>", "test result output format")
19-
.option("--coverageLimit [error warning...]", "set warn(yellow) and error(red) upper limit in coverage report");
19+
.option("--coverageLimit [error warning...]", "set warn(yellow) and error(red) upper limit in coverage report")
20+
.option("--testNamePattern <test name pattern>", "run only tests with a name that matches the regex pattern");
2021

2122
program.parse(process.argv);
2223
const options = program.opts();
@@ -53,9 +54,11 @@ let outputFolder = options.output || config.output || "coverage";
5354
let errorLimit = options.coverageLimit?.at(0);
5455
let warnLimit = options.coverageLimit?.at(1);
5556

57+
let testNamePattern = options.testNamePattern;
58+
5659
validatArgument(includes, excludes);
5760
start_unit_test(
58-
{ includes, excludes, testcases },
61+
{ includes, excludes, testcases, testNamePattern },
5962
{ flags, imports },
6063
{ tempFolder, outputFolder, mode, warnLimit, errorLimit }
6164
)

src/core/executionRecorder.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export class ExecutionRecorder implements IAssertResult {
55
total: number = 0;
66
fail: number = 0;
77
failed_info: AssertFailMessage = {};
8+
registerFunctions: [string, number][] = [];
89
_currentTestDescriptions: string[] = [];
910

1011
_addDescription(description: string): void {
@@ -13,6 +14,10 @@ export class ExecutionRecorder implements IAssertResult {
1314
_removeDescription(): void {
1415
this._currentTestDescriptions.pop();
1516
}
17+
registerTestFunction(fncIndex: number): void {
18+
const testCaseFullName = this._currentTestDescriptions.join(" - ");
19+
this.registerFunctions.push([testCaseFullName, fncIndex]);
20+
}
1621
collectCheckResult(result: boolean, codeInfoIndex: number, actualValue: string, expectValue: string): void {
1722
this.total++;
1823
if (!result) {
@@ -33,6 +38,9 @@ export class ExecutionRecorder implements IAssertResult {
3338
removeDescription: (): void => {
3439
this._removeDescription();
3540
},
41+
registerTestFunction: (index: number): void => {
42+
this.registerTestFunction(index);
43+
},
3644
collectCheckResult: (result: number, codeInfoIndex: number, actualValue: number, expectValue: number): void => {
3745
this.collectCheckResult(
3846
result !== 0,

src/core/precompile.ts

Lines changed: 44 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,34 +9,73 @@ import { getIncludeFiles } from "../utils/pathResolver.js";
99
import { SourceFunctionInfo, UnittestPackage } from "../interface.js";
1010
import { projectRoot } from "../utils/projectRoot.js";
1111

12-
const sourceFunctions = new Map<string, SourceFunctionInfo[]>();
1312
export async function precompile(
1413
includes: string[],
1514
excludes: string[],
1615
testcases: string[] | undefined,
17-
flags: string,
18-
transformFunction = join(projectRoot, "transform", "listFunctions.mjs")
16+
testNamePattern: string | undefined,
17+
flags: string
1918
): Promise<UnittestPackage> {
2019
// if specify testcases, use testcases for unittest
2120
// otherwise, get testcases(*.test.ts) in includes directory
2221
const testCodePaths = testcases ?? getRelatedFiles(includes, excludes, (path: string) => path.endsWith(".test.ts"));
2322

24-
const sourceCodePaths = getRelatedFiles(includes, excludes, (path: string) => !path.endsWith(".test.ts"));
23+
const matchedTestNames: string[] = [];
24+
if (testNamePattern) {
25+
const testNameInfos = new Map<string, string[]>();
26+
const testNameTransformFunction = join(projectRoot, "transform", "listTestNames.mjs");
27+
for (const testCodePath of testCodePaths) {
28+
await transform(testNameTransformFunction, testCodePath, flags, () => {
29+
testNameInfos.set(testCodePath, testNames);
30+
});
31+
}
32+
const regexPattern = new RegExp(testNamePattern);
33+
for (const testNames of testNameInfos.values()) {
34+
for (const testName of testNames) {
35+
if (regexPattern.test(testName)) {
36+
matchedTestNames.push(testName);
37+
}
38+
}
39+
}
40+
}
2541

42+
const sourceCodePaths = getRelatedFiles(includes, excludes, (path: string) => !path.endsWith(".test.ts"));
43+
const sourceFunctions = new Map<string, SourceFunctionInfo[]>();
44+
const sourceTransformFunction = join(projectRoot, "transform", "listFunctions.mjs");
2645
// The batchSize = 2 is empirical data after benchmarking
2746
const batchSize = 2;
2847
for (let i = 0; i < sourceCodePaths.length; i += batchSize) {
2948
await Promise.all(
30-
sourceCodePaths.slice(i, i + batchSize).map((sourcePath) => transform(sourcePath, transformFunction, flags))
49+
sourceCodePaths.slice(i, i + batchSize).map((sourcePath) =>
50+
transform(sourceTransformFunction, sourcePath, flags, () => {
51+
sourceFunctions.set(sourcePath, functionInfos);
52+
})
53+
)
3154
);
3255
}
3356

3457
return {
3558
testCodePaths,
59+
matchedTestNames,
3660
sourceFunctions,
3761
};
3862
}
3963

64+
async function transform(transformFunction: string, codePath: string, flags: string, collectCallback: () => void) {
65+
let ascArgv = [codePath, "--noEmit", "--disableWarning", "--transform", transformFunction, "-O0"];
66+
if (flags) {
67+
const argv = flags.split(" ");
68+
ascArgv = ascArgv.concat(argv);
69+
}
70+
const { error, stderr } = await main(ascArgv);
71+
if (error) {
72+
// eslint-disable-next-line @typescript-eslint/no-base-to-string
73+
console.error(stderr.toString());
74+
throw error;
75+
}
76+
collectCallback();
77+
}
78+
4079
// a. include in config
4180
// b. exclude in config
4281
export function getRelatedFiles(includes: string[], excludes: string[], filter: (path: string) => boolean) {
@@ -58,18 +97,3 @@ export function getRelatedFiles(includes: string[], excludes: string[], filter:
5897
}
5998
return result;
6099
}
61-
62-
async function transform(sourceCodePath: string, transformFunction: string, flags: string) {
63-
let ascArgv = [sourceCodePath, "--noEmit", "--disableWarning", "--transform", transformFunction, "-O0"];
64-
if (flags) {
65-
const argv = flags.split(" ");
66-
ascArgv = ascArgv.concat(argv);
67-
}
68-
const { error, stderr } = await main(ascArgv);
69-
if (error) {
70-
// eslint-disable-next-line @typescript-eslint/no-base-to-string
71-
console.error(stderr.toString());
72-
throw error;
73-
}
74-
sourceFunctions.set(sourceCodePath, functionInfos);
75-
}

src/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ export interface FileOption {
5555
includes: string[];
5656
excludes: string[];
5757
testcases: string[] | undefined;
58+
testNamePattern: string | undefined;
5859
}
5960
export interface TestOption {
6061
flags: string;
@@ -75,7 +76,7 @@ export type OutputMode = "html" | "json" | "table";
7576
export async function start_unit_test(fo: FileOption, to: TestOption, oo: OutputOption): Promise<boolean> {
7677
emptydirSync(oo.outputFolder);
7778
emptydirSync(oo.tempFolder);
78-
const unittestPackage = await precompile(fo.includes, fo.excludes, fo.testcases, to.flags);
79+
const unittestPackage = await precompile(fo.includes, fo.excludes, fo.testcases, fo.testNamePattern, to.flags);
7980
console.log(chalk.blueBright("code analysis: ") + chalk.bold.greenBright("OK"));
8081
const wasmPaths = await compile(unittestPackage.testCodePaths, oo.tempFolder, to.flags);
8182
console.log(chalk.blueBright("compile testcases: ") + chalk.bold.greenBright("OK"));

src/interface.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ export class CodeCoverage {
165165

166166
export interface UnittestPackage {
167167
readonly testCodePaths: string[];
168+
readonly matchedTestNames: string[];
168169
readonly sourceFunctions: Map<string, SourceFunctionInfo[]>;
169170
}
170171

@@ -173,5 +174,10 @@ export interface SourceFunctionInfo {
173174
range: [number, number];
174175
}
175176

177+
export interface TestNameInfo {
178+
testName: string;
179+
testFilePath: string;
180+
}
181+
176182
export const OrganizationName = "wasm-ecosystem";
177183
export const Repository = "https://github.com/wasm-ecosystem/assemblyscript-unittest-framework";

src/type/global.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import { SourceFunctionInfo } from "../interface.ts";
33
declare global {
44
// store listFunctions transform results in global
55
let functionInfos: SourceFunctionInfo[];
6+
// store listTestNames transform results in global
7+
let testNames: string[];
68
}
79

810
export {};

tests/ts/test/core/precompile.test.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
1-
import { join } from "node:path";
21
import { precompile } from "../../../../src/core/precompile.js";
3-
import { projectRoot } from "../../../../src/utils/projectRoot.js";
42

53
test("listFunction transform", async () => {
6-
const transformFunction = join(projectRoot, "transform", "listFunctions.mjs");
7-
const unittestPackages = await precompile(["tests/ts/fixture/transformFunction.ts"], [], [], "", transformFunction);
4+
const unittestPackages = await precompile(["tests/ts/fixture/transformFunction.ts"], [], undefined, undefined, "");
85
expect(unittestPackages.testCodePaths).toEqual([]);
96
expect(unittestPackages.sourceFunctions).toMatchSnapshot();
107
});

0 commit comments

Comments
 (0)