Skip to content

Commit 978b7c9

Browse files
authored
feat: support to print log messages of failed test case in summary (#44)
1 parent bed39cd commit 978b7c9

File tree

14 files changed

+200
-54
lines changed

14 files changed

+200
-54
lines changed

assembly/env.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ export namespace assertResult {
1212
export declare function registerTestFunction(index: u32): void;
1313

1414

15+
@external("__unittest_framework_env","finishTestFunction")
16+
export declare function finishTestFunction(): void;
17+
18+
1519
@external("__unittest_framework_env","collectCheckResult")
1620
export declare function collectCheckResult(
1721
result: bool,

assembly/implement.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export function testImpl(description: string, testFunction: () => void): void {
1313
assertResult.addDescription(description);
1414
assertResult.registerTestFunction(testFunction.index);
1515
testFunction();
16+
assertResult.finishTestFunction();
1617
assertResult.removeDescription();
1718
mockFunctionStatus.clear();
1819
}

example/as-test.config.cjs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,13 @@ module.exports = {
1111
* @returns
1212
*/
1313
imports(runtime) {
14-
return {};
14+
return {
15+
env: {
16+
log: (msg) => {
17+
runtime.framework.log(runtime.exports.__getString(msg));
18+
},
19+
},
20+
};
1521
},
1622

1723
/** template file path, default "coverage" */

example/as-test.config.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,13 @@ export default {
1111
* @returns
1212
*/
1313
imports(runtime) {
14-
return {};
14+
return {
15+
env: {
16+
log: (msg) => {
17+
runtime.framework.log(runtime.exports.__getString(msg));
18+
},
19+
},
20+
};
1521
},
1622

1723
/** template file path, default "coverage" */

example/env.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export declare function log(msg: string): void;

example/source.test.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { describe, test, expect, mock, endTest, unmock, remock } from "../assembly";
2-
1+
import { describe, test, expect, mock } from "../assembly";
2+
import { log } from "./env";
33
import { add, Test } from "./source";
44

55
describe("example 1", () => {
@@ -16,6 +16,7 @@ describe("example 1", () => {
1616
expect(add(2, 2)).equal(3);
1717
});
1818
test("out of mock range, this test should be failed", () => {
19+
log("2 + 2 should be 4");
1920
expect(add(2, 2)).equal(3);
2021
});
2122
});
@@ -36,5 +37,3 @@ describe("example 2", () => {
3637
expect(fn.calls).equal(1);
3738
});
3839
});
39-
40-
endTest();

example/source2.test.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { describe, test, expect, mock, endTest } from "../assembly";
1+
import { describe, test, expect } from "../assembly";
22
import { quick_sort } from "./source2";
33

44
describe("quick_sork", () => {
@@ -14,5 +14,3 @@ describe("quick_sork", () => {
1414
expect(d).equal([1, 2, 3, 4, 5]);
1515
});
1616
});
17-
18-
endTest();

src/assertResult.ts

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import { promises } from "node:fs";
22
import { json2map } from "./utils/index.js";
3-
import { AssertErrorMessages, AssertMessage, ErrorMessages, ExpectInfo, IAssertResult } from "./interface.js";
3+
import { FailedInfoMap, AssertMessage, ExpectInfo, IAssertResult } from "./interface.js";
4+
import chalk from "chalk";
45

56
const readFile = promises.readFile;
67

78
export class AssertResult {
89
fail = 0;
910
total = 0;
10-
failed_info: AssertErrorMessages = new Map();
11+
failedInfos: FailedInfoMap = new Map();
1112

1213
async merge(result: IAssertResult, expectInfoFilePath: string) {
1314
this.fail += result.fail;
@@ -17,8 +18,8 @@ export class AssertResult {
1718
try {
1819
const expectContent = await readFile(expectInfoFilePath, { encoding: "utf8" });
1920
expectInfo = json2map(JSON.parse(expectContent) as ExpectInfo);
20-
for (const [key, value] of json2map<AssertMessage[]>(result.failed_info)) {
21-
const errorMsgs: ErrorMessages = [];
21+
for (const [testcaseName, value] of json2map<AssertMessage[]>(result.failed_info)) {
22+
const errorMsgs: string[] = [];
2223
for (const msg of value) {
2324
const [index, actualValue, expectValue] = msg;
2425
const debugLocation = expectInfo.get(index);
@@ -28,13 +29,36 @@ export class AssertResult {
2829
}
2930
errorMsgs.push(errorMsg);
3031
}
31-
this.failed_info.set(key, errorMsgs);
32+
this.failedInfos.set(testcaseName, {
33+
assertMessages: errorMsgs,
34+
logMessages: result.failedLogMessages[testcaseName],
35+
});
3236
}
3337
} catch (error) {
3438
if (error instanceof Error) {
3539
console.error(error.stack);
3640
}
37-
throw new Error(`maybe forget call "endTest()" at the end of "*.test.ts" or Job abort before output`);
41+
throw error;
42+
}
43+
}
44+
}
45+
46+
print(log: (msg: string) => void): void {
47+
const rate =
48+
(this.fail === 0 ? chalk.greenBright(this.total) : chalk.redBright(this.total - this.fail)) +
49+
"/" +
50+
this.total.toString();
51+
log(`\ntest case: ${rate} (success/total)\n`);
52+
if (this.fail !== 0) {
53+
log(chalk.red("Error Message: "));
54+
for (const [testcaseName, { assertMessages, logMessages }] of this.failedInfos.entries()) {
55+
log(`\t${testcaseName}: `);
56+
for (const assertMessage of assertMessages) {
57+
log("\t\t" + chalk.yellow(assertMessage));
58+
}
59+
for (const logMessage of logMessages ?? []) {
60+
log(chalk.gray(logMessage));
61+
}
3862
}
3963
}
4064
}

src/core/execute.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ async function nodeExecutor(
3030
const executionRecorder = new ExecutionRecorder();
3131
const coverageRecorder = new CoverageRecorder();
3232

33-
const importsArg = new ImportsArgument();
33+
const importsArg = new ImportsArgument(executionRecorder);
3434
const userDefinedImportsObject = imports === null ? {} : imports(importsArg);
3535
const importObject: ASImports = {
3636
wasi_snapshot_preview1: wasi.wasiImport,

src/core/executionRecorder.ts

Lines changed: 59 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,81 @@
1-
import { ImportsArgument } from "../index.js";
2-
import { AssertFailMessage, AssertMessage, IAssertResult } from "../interface.js";
1+
import { ImportsArgument, UnitTestFramework } from "../index.js";
2+
import { AssertFailMessage, AssertMessage, IAssertResult, FailedLogMessages } from "../interface.js";
33

4-
export class ExecutionRecorder implements IAssertResult {
4+
class LogRecorder {
5+
#currentTestLogMessages: string[] = [];
6+
#isTestFailed: boolean = false;
7+
8+
addLog(msg: string): void {
9+
this.#currentTestLogMessages.push(msg);
10+
}
11+
markTestFailed(): void {
12+
this.#isTestFailed = true;
13+
}
14+
15+
onStartTest(): void {
16+
this.#currentTestLogMessages = [];
17+
this.#isTestFailed = false;
18+
}
19+
onFinishTest(): string[] | null {
20+
if (this.#currentTestLogMessages.length === 0) {
21+
return null;
22+
}
23+
if (this.#isTestFailed === false) {
24+
return null;
25+
}
26+
return this.#currentTestLogMessages;
27+
}
28+
}
29+
30+
// to do: split execution environment and recorder
31+
export class ExecutionRecorder implements IAssertResult, UnitTestFramework {
532
total: number = 0;
633
fail: number = 0;
734
failed_info: AssertFailMessage = {};
35+
failedLogMessages: FailedLogMessages = {};
36+
837
registerFunctions: [string, number][] = [];
9-
_currentTestDescriptions: string[] = [];
38+
#currentTestDescriptions: string[] = [];
39+
#logRecorder = new LogRecorder();
40+
41+
get #currentTestDescription(): string {
42+
return this.#currentTestDescriptions.join(" - ");
43+
}
1044

1145
_addDescription(description: string): void {
12-
this._currentTestDescriptions.push(description);
46+
this.#currentTestDescriptions.push(description);
1347
}
1448
_removeDescription(): void {
15-
this._currentTestDescriptions.pop();
49+
this.#currentTestDescriptions.pop();
1650
}
1751
registerTestFunction(fncIndex: number): void {
18-
const testCaseFullName = this._currentTestDescriptions.join(" - ");
19-
this.registerFunctions.push([testCaseFullName, fncIndex]);
52+
this.registerFunctions.push([this.#currentTestDescription, fncIndex]);
53+
this.#logRecorder.onStartTest();
54+
}
55+
_finishTestFunction(): void {
56+
const logMessages: string[] | null = this.#logRecorder.onFinishTest();
57+
if (logMessages !== null) {
58+
const testCaseFullName = this.#currentTestDescription;
59+
this.failedLogMessages[testCaseFullName] = (this.failedLogMessages[testCaseFullName] || []).concat(logMessages);
60+
}
2061
}
62+
2163
collectCheckResult(result: boolean, codeInfoIndex: number, actualValue: string, expectValue: string): void {
2264
this.total++;
2365
if (!result) {
66+
this.#logRecorder.markTestFailed();
2467
this.fail++;
25-
const testCaseFullName = this._currentTestDescriptions.join(" - ");
68+
const testCaseFullName = this.#currentTestDescription;
2669
const assertMessage: AssertMessage = [codeInfoIndex.toString(), actualValue, expectValue];
2770
this.failed_info[testCaseFullName] = this.failed_info[testCaseFullName] || [];
2871
this.failed_info[testCaseFullName].push(assertMessage);
2972
}
3073
}
3174

75+
log(msg: string): void {
76+
this.#logRecorder.addLog(msg);
77+
}
78+
3279
getCollectionFuncSet(arg: ImportsArgument): Record<string, Record<string, unknown>> {
3380
return {
3481
__unittest_framework_env: {
@@ -41,6 +88,9 @@ export class ExecutionRecorder implements IAssertResult {
4188
registerTestFunction: (index: number): void => {
4289
this.registerTestFunction(index);
4390
},
91+
finishTestFunction: () => {
92+
this._finishTestFunction();
93+
},
4494
collectCheckResult: (result: number, codeInfoIndex: number, actualValue: number, expectValue: number): void => {
4595
this.collectCheckResult(
4696
result !== 0,

0 commit comments

Comments
 (0)