diff --git a/bin/cli.js b/bin/cli.js index c529148..fcfbbab 100755 --- a/bin/cli.js +++ b/bin/cli.js @@ -65,10 +65,10 @@ start_unit_test( .then((success) => { if (!success) { console.error(chalk.redBright("Test Failed") + "\n"); - exit(-1); + exit(255); } }) .catch((e) => { console.error(chalk.redBright(" Test crash, error message: ") + chalk.yellowBright(`${e.stack}`) + "\n"); - exit(-1); + exit(255); }); diff --git a/docs/api-documents/configuration.md b/docs/api-documents/configuration.md index da8c497..75aef04 100644 --- a/docs/api-documents/configuration.md +++ b/docs/api-documents/configuration.md @@ -49,3 +49,52 @@ module.exports = { // mode: ["html", "json", "table"], }; ``` + +### Imports + +```typescript +export interface ImportsArgument { + module: WebAssembly.Module; + instance: WebAssembly.Instance; + exports: (ASUtil & Record); + framework: UnitTestFramework; +} +export abstract class UnitTestFramework { + /** + * function to redirect log message to unittest framework + * @param msg: message to log + */ + abstract log(msg: string): void; +} +``` + +There are 2 useful fields. + +- `exports`: contains exported function from test cases and [AS help API](https://github.com/AssemblyScript/assemblyscript/blob/3defefd5b09248d697a2e6bd1e7201c0cf98def1/lib/loader/index.d.ts#L23). +- `framework`: contains runtime provided function. + + - `log`: redirect log message from test cases to unittest framework. It will be showed in failed info. + ::: details + + ```typescript + test("failed test", () => { + log("This is a log message for the failed test."); // log to be redirect + expect(1 + 1).equal(3); + }); + + test("succeed test", () => { + log("This is a log message for the succeed test."); // log to be redirect + expect(1 + 1).equal(2); + }); + ``` + + will output + + ``` + Error Message: + failed test: + *.test.ts:6:2 value: 2 expect: = 3 + This is a log message for the failed test. <- only log in failed test will be showed here + ``` + + ::: diff --git a/package-lock.json b/package-lock.json index 4a495d4..0e8b6ea 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31,6 +31,7 @@ "@types/node": "^22.0.0", "assemblyscript-prettier": "^3.0.1", "cross-env": "^7.0.3", + "diff": "^8.0.2", "jest": "^29.5.0", "prettier": "^3.0.0", "ts-jest": "^29.1.0", @@ -5243,9 +5244,9 @@ } }, "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.2.tgz", + "integrity": "sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -11768,6 +11769,16 @@ } } }, + "node_modules/ts-node/node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/tsconfig-paths": { "version": "3.15.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", diff --git a/package.json b/package.json index d5cef8f..a0cd06b 100644 --- a/package.json +++ b/package.json @@ -12,11 +12,13 @@ }, "prettier": "@schleifner/prettier-config", "scripts": { + "watch:ts": "tsc --build ./src/tsconfig.json --watch", "build": "node scripts/build_instrumentation.js -j 2 && tsc --build ./transform/tsconfig.json && tsc --build ./src/tsconfig.json", "test:as": "node bin/as-test.js", "test:ts": "cross-env NODE_OPTIONS=--experimental-vm-modules jest", "test:cpp": "cmake -B build -S . && cmake --build build --parallel 2 --target wasm-instrumentation-test wasm-opt && build/bin/wasm-instrumentation-test", - "test": "npm run test:as && npm run test:ts && npm run test:cpp", + "test:e2e": " node tests/e2e/run.js", + "test": "npm run test:as && npm run test:ts && npm run test:cpp && npm run test:e2e", "lint:ts": "eslint src transform tests/ts/test --max-warnings=0", "lint:as": "npx eslint --config ./assembly/eslint.config.mjs assembly --max-warnings=0", "lint": "npm run lint:ts && prettier -c .", @@ -48,6 +50,7 @@ "@types/node": "^22.0.0", "assemblyscript-prettier": "^3.0.1", "cross-env": "^7.0.3", + "diff": "^8.0.2", "jest": "^29.5.0", "prettier": "^3.0.0", "ts-jest": "^29.1.0", diff --git a/src/executionResult.ts b/src/executionResult.ts index 878ebda..7594787 100644 --- a/src/executionResult.ts +++ b/src/executionResult.ts @@ -23,9 +23,9 @@ export class ExecutionResult { for (const msg of value) { const [index, actualValue, expectValue] = msg; const debugLocation = expectInfo.get(index); - let errorMsg = `${debugLocation ?? ""}\tvalue: ${actualValue}\texpect: ${expectValue}`; + let errorMsg = `${debugLocation ?? ""} value: ${actualValue} expect: ${expectValue}`; if (errorMsg.length > 160) { - errorMsg = `${debugLocation ?? ""}\nvalue: \n\t${actualValue}\nexpect: \n\t${expectValue}`; + errorMsg = `${debugLocation ?? ""}\nvalue: \n ${actualValue}\nexpect: \n ${expectValue}`; } errorMsgs.push(errorMsg); } @@ -52,9 +52,9 @@ export class ExecutionResult { if (this.fail !== 0) { log(chalk.red("Error Message: ")); for (const [testcaseName, { assertMessages, logMessages }] of this.failedInfos.entries()) { - log(`\t${testcaseName}: `); + log(` ${testcaseName}: `); for (const assertMessage of assertMessages) { - log("\t\t" + chalk.yellow(assertMessage)); + log(" " + chalk.yellow(assertMessage)); } for (const logMessage of logMessages ?? []) { log(chalk.gray(logMessage)); diff --git a/tests/e2e/printLogInFailedInfo/as-test.config.js b/tests/e2e/printLogInFailedInfo/as-test.config.js new file mode 100644 index 0000000..2a55a40 --- /dev/null +++ b/tests/e2e/printLogInFailedInfo/as-test.config.js @@ -0,0 +1,20 @@ +import os from "node:os"; +import path from "node:path"; + +const tmpFolder = path.join(os.tmpdir(), "as-test-e2e"); + +export default { + include: ["tests/e2e/printLogInFailedInfo"], + imports(runtime) { + return { + env: { + log: (msg) => { + runtime.framework.log(runtime.exports.__getString(msg)); + }, + }, + }; + }, + temp: tmpFolder, + output: tmpFolder, + mode: [], +}; diff --git a/tests/e2e/printLogInFailedInfo/env.ts b/tests/e2e/printLogInFailedInfo/env.ts new file mode 100644 index 0000000..b8c4440 --- /dev/null +++ b/tests/e2e/printLogInFailedInfo/env.ts @@ -0,0 +1 @@ +export declare function log(msg: string): void; diff --git a/tests/e2e/printLogInFailedInfo/source.test.ts b/tests/e2e/printLogInFailedInfo/source.test.ts new file mode 100644 index 0000000..0571689 --- /dev/null +++ b/tests/e2e/printLogInFailedInfo/source.test.ts @@ -0,0 +1,12 @@ +import { test, expect } from "../../../assembly"; +import { log } from "./env"; + +test("failed test", () => { + log("This is a log message for the failed test."); + expect(1 + 1).equal(3); +}); + +test("succeed test", () => { + log("This is a log message for the succeed test."); + expect(1 + 1).equal(2); +}); diff --git a/tests/e2e/printLogInFailedInfo/tsconfig.json b/tests/e2e/printLogInFailedInfo/tsconfig.json new file mode 100644 index 0000000..798b474 --- /dev/null +++ b/tests/e2e/printLogInFailedInfo/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "assemblyscript/std/assembly.json", + "include": ["./**/*.ts"] +} diff --git a/tests/e2e/run.js b/tests/e2e/run.js new file mode 100644 index 0000000..dea54df --- /dev/null +++ b/tests/e2e/run.js @@ -0,0 +1,42 @@ +import assert from "node:assert"; +import { exec } from "node:child_process"; +import { diffChars } from "diff"; +import chalk from "chalk"; + +function assertStringEq(s1, s2) { + const parts = diffChars(s1, s2); + const diffs = []; + let hasDiff = false; + for (const part of parts) { + if (part.added) { + hasDiff = true; + diffs.push(chalk.bgGreen(part.value)); + } else if (part.removed) { + hasDiff = true; + diffs.push(chalk.bgRed(part.value)); + } else { + diffs.push(part.value); + } + } + assert(!hasDiff, diffs.join("")); +} + +console.log("Running e2e test: printLogInFailedInfo"); +exec("node ./bin/as-test.js --config tests/e2e/printLogInFailedInfo/as-test.config.js", (error, stdout, stderr) => { + assert(error.code === 255); + const expectStdOut = ` +code analysis: OK +compile testcases: OK +instrument: OK +execute testcases: OK + +test case: 1/2 (success/total) + +Error Message: + failed test: + tests/e2e/printLogInFailedInfo/source.test.ts:6:2 value: 2 expect: = 3 +This is a log message for the failed test. +`.trimStart(); + + assertStringEq(expectStdOut, stdout); +}); diff --git a/tests/ts/test/__snapshots__/executionResult.test.ts.snap b/tests/ts/test/__snapshots__/executionResult.test.ts.snap index c5c6e23..9549f42 100644 --- a/tests/ts/test/__snapshots__/executionResult.test.ts.snap +++ b/tests/ts/test/__snapshots__/executionResult.test.ts.snap @@ -5,8 +5,8 @@ exports[`print 1`] = ` test case: 27/28 (success/total) Error Message: - A: - tests/as/comparison.test.ts:10:20 value: 100 expect: = 200 + A: + tests/as/comparison.test.ts:10:20 value: 100 expect: = 200 log message 1 log message 2 log message 3" @@ -17,8 +17,8 @@ exports[`print 2`] = ` test case: 27/28 (success/total) Error Message:  - A: - tests/as/comparison.test.ts:10:20 value: 100 expect: = 200 + A: + tests/as/comparison.test.ts:10:20 value: 100 expect: = 200 log message 1 log message 2 log message 3" diff --git a/tests/ts/test/executionResult.test.ts b/tests/ts/test/executionResult.test.ts index 497d79c..cf99f88 100644 --- a/tests/ts/test/executionResult.test.ts +++ b/tests/ts/test/executionResult.test.ts @@ -45,10 +45,10 @@ test("equal failed", async () => { const expectFailedInfo: FailedInfoMap = new Map(); expectFailedInfo.set("A", { assertMessages: [ - "tests/as/comparison.test.ts:10:20\tvalue: 100\texpect: = 200", - "tests/as/comparison.test.ts:15:27\tvalue: [10]\texpect: = [1]", - "tests/as/comparison.test.ts:59:22\tvalue: { 1 : 1.5, 2 : 2.5 }\texpect: = { 1: 1.5, 2 : 2.0 }", - `tests/as/comparison.test.ts:48:47\nvalue: \n\t${actualString}\nexpect: \n\t${expectString}`, + "tests/as/comparison.test.ts:10:20 value: 100 expect: = 200", + "tests/as/comparison.test.ts:15:27 value: [10] expect: = [1]", + "tests/as/comparison.test.ts:59:22 value: { 1 : 1.5, 2 : 2.5 } expect: = { 1: 1.5, 2 : 2.0 }", + `tests/as/comparison.test.ts:48:47\nvalue: \n ${actualString}\nexpect: \n ${expectString}`, ], logMessages: ["log message 1", "log message 2", "log message 3"], });