diff --git a/src/core/covRecorder.ts b/src/core/covRecorder.ts index aa5eaf8..b69b5ee 100644 --- a/src/core/covRecorder.ts +++ b/src/core/covRecorder.ts @@ -1,4 +1,3 @@ -import assert from "node:assert"; import { writeFileSync } from "node:fs"; export class CoverageRecorder { @@ -25,9 +24,7 @@ export class CoverageRecorder { }; } - outputTrace(wasm: string) { - assert(wasm.endsWith("instrumented.wasm")); - const traceOutputFile = wasm.slice(0, -17).concat("trace"); - writeFileSync(traceOutputFile, JSON.stringify(this._runtimeTrace)); + outputTrace(traceFile: string) { + writeFileSync(traceFile, JSON.stringify(this._runtimeTrace)); } } diff --git a/src/core/execute.ts b/src/core/execute.ts index 18a28db..f3050d6 100644 --- a/src/core/execute.ts +++ b/src/core/execute.ts @@ -1,7 +1,6 @@ import { WASI } from "node:wasi"; import { promises } from "node:fs"; import { ensureDirSync } from "fs-extra"; -import { basename } from "node:path"; import { instantiate, Imports as ASImports } from "@assemblyscript/loader"; import { AssertResult } from "../assertResult.js"; import { Imports, ImportsArgument } from "../index.js"; @@ -14,9 +13,13 @@ import { CoverageRecorder } from "./covRecorder.js"; const readFile = promises.readFile; -async function nodeExecutor(wasm: string, outFolder: string, imports: Imports): Promise { +async function nodeExecutor( + instrumentResult: InstrumentResult, + outFolder: string, + imports: Imports +): Promise { const wasi = new WASI({ - args: ["node", basename(wasm)], + args: ["node", instrumentResult.baseName], env: process.env, preopens: { "/": outFolder, @@ -36,7 +39,7 @@ async function nodeExecutor(wasm: string, outFolder: string, imports: Imports): ...coverageRecorder.getCollectionFuncSet(), ...userDefinedImportsObject, } as ASImports; - const binaryBuffer = await readFile(wasm); + const binaryBuffer = await readFile(instrumentResult.instrumentedWasm); const binary = binaryBuffer.buffer.slice(binaryBuffer.byteOffset, binaryBuffer.byteOffset + binaryBuffer.byteLength); const importFuncList = parseImportFunctionInfo(binary as ArrayBuffer); supplyDefaultFunction(importFuncList, importObject); @@ -52,22 +55,21 @@ async function nodeExecutor(wasm: string, outFolder: string, imports: Imports): } throw new Error("node executor abort."); } - coverageRecorder.outputTrace(wasm); + coverageRecorder.outputTrace(instrumentResult.traceFile); return executionRecorder; } -export async function execWasmBinarys( +export async function execWasmBinaries( outFolder: string, - instrumentResult: InstrumentResult[], + instrumentResults: InstrumentResult[], imports: Imports ): Promise { const assertRes = new AssertResult(); ensureDirSync(outFolder); await Promise.all( - instrumentResult.map(async (res): Promise => { - const { instrumentedWasm, expectInfo } = res; - const recorder: ExecutionRecorder = await nodeExecutor(instrumentedWasm, outFolder, imports); - await assertRes.merge(recorder, expectInfo); + instrumentResults.map(async (instrumentResult): Promise => { + const recorder: ExecutionRecorder = await nodeExecutor(instrumentResult, outFolder, imports); + await assertRes.merge(recorder, instrumentResult.expectInfo); }) ); return assertRes; diff --git a/src/core/instrument.ts b/src/core/instrument.ts index b8cff25..c87cb4f 100644 --- a/src/core/instrument.ts +++ b/src/core/instrument.ts @@ -10,27 +10,19 @@ export async function instrument(sourceWasms: string[], sourceCodePaths: string[ const instrumenter = await initInstrumenter(); for (const sourceFile of sourceWasms) { const baseName = sourceFile.slice(0, -5); - const outputFile = baseName.concat(".instrumented.wasm"); + const result = new InstrumentResult(baseName); + const reportFunction = "covInstrument/traceExpression"; - const sourceMapFile = baseName.concat(".wasm.map"); - const debugInfoFile = baseName.concat(".debugInfo.json"); - const expectInfoFile = baseName.concat(".expectInfo.json"); const source = instrumenter.allocateUTF8(sourceFile); - const output = instrumenter.allocateUTF8(outputFile); + const output = instrumenter.allocateUTF8(result.instrumentedWasm); const report = instrumenter.allocateUTF8(reportFunction); - const sourceMap = instrumenter.allocateUTF8(sourceMapFile); - const debugInfo = instrumenter.allocateUTF8(debugInfoFile); - const expectInfo = instrumenter.allocateUTF8(expectInfoFile); + const sourceMap = instrumenter.allocateUTF8(result.sourceMap); + const debugInfo = instrumenter.allocateUTF8(result.debugInfo); + const expectInfo = instrumenter.allocateUTF8(result.expectInfo); const include = instrumenter.allocateUTF8(includeFilter); instrumenter._wasm_instrument(source, output, report, sourceMap, expectInfo, debugInfo, include, 0, true); - const result: InstrumentResult = { - sourceWasm: sourceFile, - instrumentedWasm: outputFile, - debugInfo: debugInfoFile, - expectInfo: expectInfoFile, - }; for (const ptr of [source, output, report, sourceMap, debugInfo, expectInfo, include]) { instrumenter._free(ptr); } diff --git a/src/index.ts b/src/index.ts index e6458cf..c09149c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,7 +6,7 @@ import { compile } from "./core/compile.js"; import { AssertResult } from "./assertResult.js"; import { precompile } from "./core/precompile.js"; import { instrument } from "./core/instrument.js"; -import { execWasmBinarys } from "./core/execute.js"; +import { execWasmBinaries } from "./core/execute.js"; import { generateReport, reportConfig } from "./generator/index.js"; function logAssertResult(trace: AssertResult): void { @@ -81,12 +81,11 @@ export async function start_unit_test(fo: FileOption, to: TestOption, oo: Output console.log(chalk.blueBright("compile testcases: ") + chalk.bold.greenBright("OK")); const instrumentResult = await instrument(wasmPaths, Array.from(unittestPackage.sourceFunctions.keys())); console.log(chalk.blueBright("instrument: ") + chalk.bold.greenBright("OK")); - const executedResult = await execWasmBinarys(oo.tempFolder, instrumentResult, to.imports); + const executedResult = await execWasmBinaries(oo.tempFolder, instrumentResult, to.imports); console.log(chalk.blueBright("execute testcases: ") + chalk.bold.greenBright("OK")); logAssertResult(executedResult); - const debugInfoFiles = instrumentResult.map((res) => res.debugInfo); const parser = new Parser(); - const fileCoverageInfo = await parser.parse(debugInfoFiles, unittestPackage.sourceFunctions); + const fileCoverageInfo = await parser.parse(instrumentResult, unittestPackage.sourceFunctions); reportConfig.warningLimit = oo.warnLimit ?? reportConfig.warningLimit; reportConfig.errorLimit = oo.errorLimit ?? reportConfig.errorLimit; generateReport(oo.mode, oo.outputFolder, fileCoverageInfo); diff --git a/src/interface.ts b/src/interface.ts index 705a646..07c956d 100644 --- a/src/interface.ts +++ b/src/interface.ts @@ -6,11 +6,26 @@ import { Type } from "wasmparser"; // instrumented file information -export interface InstrumentResult { - sourceWasm: string; - instrumentedWasm: string; - debugInfo: string; - expectInfo: string; +export class InstrumentResult { + constructor(public baseName: string) {} + get sourceWasm() { + return this.baseName.concat(".wasm"); + } + get instrumentedWasm(): string { + return this.baseName.concat(".instrumented.wasm"); + } + get sourceMap(): string { + return this.baseName.concat(".wasm.map"); + } + get debugInfo(): string { + return this.baseName.concat(".debugInfo.json"); + } + get expectInfo(): string { + return this.baseName.concat(".expectInfo.json"); + } + get traceFile(): string { + return this.baseName.concat(".trace"); + } } export type CodeSnippetIndex = number; diff --git a/src/parser/index.ts b/src/parser/index.ts index 33f21b3..6872535 100644 --- a/src/parser/index.ts +++ b/src/parser/index.ts @@ -14,6 +14,7 @@ import { FunctionCoverageResult, SourceFunctionInfo, LineRange, + InstrumentResult, } from "../interface.js"; export class Parser { @@ -25,11 +26,11 @@ export class Parser { functionCovInfoMap = new Map(); async parse( - debugInfoFiles: string[], + instrumentResults: InstrumentResult[], sourceFunctions: Map ): Promise { - for (const debugInfoFile of debugInfoFiles) { - await this.traceParse(debugInfoFile); + for (const instrumentResult of instrumentResults) { + await this.traceParse(instrumentResult); } this.generateFunctionCoverage(); await this.generateFileCoverage(sourceFunctions); @@ -41,9 +42,9 @@ export class Parser { const tempCovTraceMap = new Map(); for (const trace of traces) { const [funIndex, codeSnippetIndex] = trace; - const codeIndexs = tempCovTraceMap.get(funIndex); - if (codeIndexs) { - codeIndexs.push(codeSnippetIndex); + const codeIndexes = tempCovTraceMap.get(funIndex); + if (codeIndexes) { + codeIndexes.push(codeSnippetIndex); } else { tempCovTraceMap.set(funIndex, [codeSnippetIndex]); } @@ -63,12 +64,10 @@ export class Parser { * parse debugInfo and traceInfo to functionCovTraceMap and functionCovInfoMap * @param debugInfoFile debugInfo file path generated by instrumenter */ - async traceParse(debugInfoFile: string) { - const traceFile = debugInfoFile.slice(0, -14).concat("trace"); - + async traceParse(instrumentResult: InstrumentResult) { const [tempCovTraceMap, { debugInfos, debugFiles }] = await Promise.all([ - this.getTempCovTraceMap(traceFile), - this.getDebugInfos(debugInfoFile), + this.getTempCovTraceMap(instrumentResult.traceFile), + this.getDebugInfos(instrumentResult.debugInfo), ]); for (const [name, info] of debugInfos) { @@ -86,7 +85,7 @@ export class Parser { .filter((range) => { const filename = debugFiles[range[0]]; if (filename === undefined) { - throw new Error(`unknown erorr: not find fileIndex ${range[0]} in ${debugInfoFile}`); + throw new Error(`unknown error: not find fileIndex ${range[0]} in ${instrumentResult.debugInfo}`); } // if basicBlock is inline function from other files, ignore it return checkFunctionName(filename, name); diff --git a/tests/ts/test/core/instrument.test.ts b/tests/ts/test/core/instrument.test.ts index 217e931..22f8195 100644 --- a/tests/ts/test/core/instrument.test.ts +++ b/tests/ts/test/core/instrument.test.ts @@ -10,19 +10,20 @@ const outputDir = join(tmpdir(), "assemblyscript-unittest-framework"); test("Instrument", async () => { await compile([fixturePath], outputDir, "--memoryBase 16 --exportTable"); + const base = join(outputDir, "constructor"); const wasmPath = join(outputDir, "constructor.wasm"); const sourceCodePath = "tests/ts/fixture/constructor.ts"; - const result = await instrument([wasmPath], [sourceCodePath]); - expect(result.length).toEqual(1); + const results = await instrument([wasmPath], [sourceCodePath]); + expect(results.length).toEqual(1); + const result = results[0]!; const instrumentedWasm = join(outputDir, "constructor.instrumented.wasm"); const debugInfo = join(outputDir, "constructor.debugInfo.json"); const expectInfo = join(outputDir, "constructor.expectInfo.json"); - expect(result[0]).toEqual({ - sourceWasm: wasmPath, - instrumentedWasm, - debugInfo, - expectInfo, - }); + expect(result.baseName).toEqual(base); + expect(result.sourceWasm).toEqual(wasmPath); + expect(result.instrumentedWasm).toEqual(instrumentedWasm); + expect(result.debugInfo).toEqual(debugInfo); + expect(result.expectInfo).toEqual(expectInfo); expect(fs.existsSync(instrumentedWasm)).toEqual(true); expect(fs.existsSync(debugInfo)).toEqual(true); expect(fs.existsSync(expectInfo)).toEqual(true); diff --git a/tests/ts/test/parser/parser.test.ts b/tests/ts/test/parser/parser.test.ts index 10f926d..7142bf9 100644 --- a/tests/ts/test/parser/parser.test.ts +++ b/tests/ts/test/parser/parser.test.ts @@ -2,7 +2,7 @@ import { join } from "node:path"; // eslint-disable-next-line n/no-extraneous-import import { jest } from "@jest/globals"; import { fileURLToPath, URL } from "node:url"; -import { SourceFunctionInfo } from "../../../../src/interface.js"; +import { InstrumentResult, SourceFunctionInfo } from "../../../../src/interface.js"; jest.unstable_mockModule("node:fs/promises", () => ({ readFile: jest.fn(() => "\n".repeat(50)), @@ -16,8 +16,7 @@ describe("Parser", () => { const parser = new Parser(); test("traceParse", async () => { - const debugFilePath = join(dirname, "..", "..", "fixture", "traceParse.debugInfo.json"); - await parser.traceParse(debugFilePath); + await parser.traceParse(new InstrumentResult(join(dirname, "..", "..", "fixture", "traceParse"))); expect(parser.functionCovInfoMap).toMatchSnapshot(); expect(parser.functionCovTraceMap).toMatchSnapshot(); });