Skip to content

Commit 8a9411f

Browse files
authored
refactor: move coverage collector logic from AS to JS (#39)
1 parent 80498be commit 8a9411f

File tree

7 files changed

+80
-94
lines changed

7 files changed

+80
-94
lines changed

assembly/covInstrument.ts

Lines changed: 0 additions & 1 deletion
This file was deleted.

assembly/index.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import {
66
testImpl,
77
unmockImpl,
88
} from "./implement";
9-
import { output } from "./output";
109
import { MockFn } from "./mockInstrument";
1110
export { MockFn } from "./mockInstrument";
1211

@@ -57,6 +56,7 @@ export function expect<T>(value: T): Value<T> {
5756
return new Value<T>(value);
5857
}
5958

60-
export function endTest(): void {
61-
output();
62-
}
59+
/**
60+
* @deprecated no need to use endTest now
61+
*/
62+
export function endTest(): void {}

assembly/output.ts

Lines changed: 0 additions & 5 deletions
This file was deleted.

src/core/covRecorder.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import assert from "node:assert";
2+
import { writeFileSync } from "node:fs";
3+
4+
export class CoverageRecorder {
5+
private _runtimeTrace: Array<[number, number]> = [];
6+
7+
getCollectionFuncSet(): Record<string, unknown> {
8+
return {
9+
covInstrument: {
10+
traceExpression: (functionIndex: number, basicBlockIndex: number, type: number): void => {
11+
switch (type) {
12+
case 1: // call in
13+
case 2: {
14+
// call out
15+
// do not need for now
16+
break;
17+
}
18+
case 0: {
19+
this._runtimeTrace.push([functionIndex, basicBlockIndex]);
20+
break;
21+
}
22+
}
23+
},
24+
},
25+
};
26+
}
27+
28+
outputTrace(wasm: string) {
29+
assert(wasm.endsWith("instrumented.wasm"));
30+
const traceOutputFile = wasm.slice(0, -17).concat("trace");
31+
writeFileSync(traceOutputFile, JSON.stringify(this._runtimeTrace));
32+
}
33+
}

src/core/execute.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@ import { instantiate, Imports as ASImports } from "@assemblyscript/loader";
66
import { AssertResult } from "../assertResult.js";
77
import { Imports, ImportsArgument } from "../index.js";
88
import { InstrumentResult } from "../interface.js";
9-
import { mockInstruFunc, covInstruFunc } from "../utils/import.js";
9+
import { mockInstrumentFunc } from "../utils/import.js";
1010
import { supplyDefaultFunction } from "../utils/index.js";
1111
import { parseImportFunctionInfo } from "../utils/wasmparser.js";
1212
import { ExecutionRecorder } from "./executionRecorder.js";
13+
import { CoverageRecorder } from "./covRecorder.js";
1314

1415
const readFile = promises.readFile;
1516

@@ -23,15 +24,16 @@ async function nodeExecutor(wasm: string, outFolder: string, imports: Imports):
2324
version: "preview1",
2425
});
2526

26-
const recorder = new ExecutionRecorder();
27+
const executionRecorder = new ExecutionRecorder();
28+
const coverageRecorder = new CoverageRecorder();
2729

2830
const importsArg = new ImportsArgument();
2931
const userDefinedImportsObject = imports === null ? {} : imports(importsArg);
3032
const importObject: ASImports = {
3133
wasi_snapshot_preview1: wasi.wasiImport,
32-
...recorder.getCollectionFuncSet(importsArg),
33-
mockInstrument: mockInstruFunc,
34-
...covInstruFunc(wasm),
34+
...executionRecorder.getCollectionFuncSet(importsArg),
35+
mockInstrument: mockInstrumentFunc,
36+
...coverageRecorder.getCollectionFuncSet(),
3537
...userDefinedImportsObject,
3638
} as ASImports;
3739
const binaryBuffer = await readFile(wasm);
@@ -50,7 +52,8 @@ async function nodeExecutor(wasm: string, outFolder: string, imports: Imports):
5052
}
5153
throw new Error("node executor abort.");
5254
}
53-
return recorder;
55+
coverageRecorder.outputTrace(wasm);
56+
return executionRecorder;
5457
}
5558

5659
export async function execWasmBinarys(

src/utils/import.ts

Lines changed: 11 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,89 +1,62 @@
11
import assert from "node:assert";
2-
import { writeFileSync } from "node:fs";
32

43
interface MockValue {
54
calls: number;
65
ignore: boolean;
76
newIndex: number;
87
}
98

10-
export const mockInstruFunc = {
9+
export const mockInstrumentFunc = {
1110
// isCall = true, return -1 if not mocked;
1211
// isCall = false, return oldIndex if not mocked.
1312
checkMock(index: number, isCall: boolean): number {
14-
if (mockInstruFunc["mockFunctionStatus.has"](index)) {
15-
return mockInstruFunc["mockFunctionStatus.get"](index);
13+
if (mockInstrumentFunc["mockFunctionStatus.has"](index)) {
14+
return mockInstrumentFunc["mockFunctionStatus.get"](index);
1615
}
1716
return isCall ? -1 : index;
1817
},
1918
"mockFunctionStatus.last": 0,
2019
"mockFunctionStatus.state": new Map<number, MockValue>(),
2120
"mockFunctionStatus.clear": function () {
22-
mockInstruFunc["mockFunctionStatus.state"].clear();
21+
mockInstrumentFunc["mockFunctionStatus.state"].clear();
2322
},
2423
"mockFunctionStatus.set": function (k: number, v: number) {
2524
const value: MockValue = {
2625
calls: 0,
2726
ignore: false,
2827
newIndex: v,
2928
};
30-
mockInstruFunc["mockFunctionStatus.state"].set(k, value);
29+
mockInstrumentFunc["mockFunctionStatus.state"].set(k, value);
3130
},
3231
"mockFunctionStatus.get": function (k: number): number {
33-
const fn = mockInstruFunc["mockFunctionStatus.state"].get(k);
32+
const fn = mockInstrumentFunc["mockFunctionStatus.state"].get(k);
3433
assert(fn);
3534
fn.calls++;
36-
mockInstruFunc["mockFunctionStatus.last"] = k;
35+
mockInstrumentFunc["mockFunctionStatus.last"] = k;
3736
return fn.newIndex;
3837
},
3938
"mockFunctionStatus.lastGet": function (): number {
40-
return mockInstruFunc["mockFunctionStatus.last"];
39+
return mockInstrumentFunc["mockFunctionStatus.last"];
4140
},
4241
"mockFunctionStatus.has": function (k: number): boolean {
43-
const fn = mockInstruFunc["mockFunctionStatus.state"].get(k);
42+
const fn = mockInstrumentFunc["mockFunctionStatus.state"].get(k);
4443
if (fn === undefined) {
4544
return false;
4645
}
4746
return !fn.ignore;
4847
},
4948
"mockFunctionStatus.getCalls": function (oldIndex: number, newIndex: number): number {
50-
const fn = mockInstruFunc["mockFunctionStatus.state"].get(oldIndex);
49+
const fn = mockInstrumentFunc["mockFunctionStatus.state"].get(oldIndex);
5150
if (fn === undefined || fn.newIndex !== newIndex) {
5251
return 0;
5352
}
5453
return fn.calls;
5554
},
5655
"mockFunctionStatus.setIgnore": function (k: number, v: boolean) {
57-
const fn = mockInstruFunc["mockFunctionStatus.state"].get(k);
56+
const fn = mockInstrumentFunc["mockFunctionStatus.state"].get(k);
5857
if (fn === undefined) {
5958
return;
6059
}
6160
fn.ignore = v;
6261
},
6362
};
64-
65-
export function covInstruFunc(wasm: string) {
66-
const covInstrument = {
67-
runtimeTrace: new Array<[number, number]>(),
68-
traceExpression(functionIndex: number, basicBlockIndex: number, type: number) {
69-
switch (type) {
70-
case 1: // call in
71-
case 2: {
72-
// call out
73-
// do not need for now
74-
break;
75-
}
76-
case 0: {
77-
covInstrument.runtimeTrace.push([functionIndex, basicBlockIndex]);
78-
break;
79-
}
80-
}
81-
},
82-
outputTrace() {
83-
assert(wasm.endsWith("instrumented.wasm"));
84-
const traceOutputFile = wasm.slice(0, -17).concat("trace");
85-
writeFileSync(traceOutputFile, JSON.stringify(covInstrument.runtimeTrace));
86-
},
87-
};
88-
return { covInstrument };
89-
}

tests/ts/test/utils/import.test.ts

Lines changed: 23 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -6,52 +6,35 @@ jest.unstable_mockModule("node:fs", () => ({
66
writeFileSync: mockWriteFile,
77
}));
88

9-
const { mockInstruFunc, covInstruFunc } = await import("../../../../src/utils/import.js");
10-
const fs = await import("node:fs");
9+
const { mockInstrumentFunc } = await import("../../../../src/utils/import.js");
1110

1211
describe("imports", () => {
1312
test("mockInstrument", () => {
1413
// mock(oldFunctionIndex, newFunctionIndex);
15-
mockInstruFunc["mockFunctionStatus.set"](1, 4);
16-
expect(mockInstruFunc.checkMock(1, true)).toEqual(4);
17-
expect(mockInstruFunc.checkMock(2, false)).toEqual(2);
18-
expect(mockInstruFunc.checkMock(2, true)).toEqual(-1);
19-
expect(mockInstruFunc["mockFunctionStatus.lastGet"]()).toEqual(1);
20-
expect(mockInstruFunc["mockFunctionStatus.getCalls"](1, 4)).toEqual(1);
21-
expect(mockInstruFunc["mockFunctionStatus.getCalls"](2, 4)).toEqual(0);
22-
expect(mockInstruFunc["mockFunctionStatus.getCalls"](1, 3)).toEqual(0);
23-
expect(() => mockInstruFunc["mockFunctionStatus.get"](2)).toThrow();
14+
mockInstrumentFunc["mockFunctionStatus.set"](1, 4);
15+
expect(mockInstrumentFunc.checkMock(1, true)).toEqual(4);
16+
expect(mockInstrumentFunc.checkMock(2, false)).toEqual(2);
17+
expect(mockInstrumentFunc.checkMock(2, true)).toEqual(-1);
18+
expect(mockInstrumentFunc["mockFunctionStatus.lastGet"]()).toEqual(1);
19+
expect(mockInstrumentFunc["mockFunctionStatus.getCalls"](1, 4)).toEqual(1);
20+
expect(mockInstrumentFunc["mockFunctionStatus.getCalls"](2, 4)).toEqual(0);
21+
expect(mockInstrumentFunc["mockFunctionStatus.getCalls"](1, 3)).toEqual(0);
22+
expect(() => mockInstrumentFunc["mockFunctionStatus.get"](2)).toThrow();
2423
// unmock(oldFunction)
25-
mockInstruFunc["mockFunctionStatus.setIgnore"](1, true);
26-
expect(mockInstruFunc.checkMock(1, false)).toEqual(1);
27-
expect(mockInstruFunc.checkMock(1, true)).toEqual(-1);
28-
mockInstruFunc["mockFunctionStatus.setIgnore"](2, true);
29-
expect(mockInstruFunc.checkMock(2, false)).toEqual(2);
30-
expect(mockInstruFunc.checkMock(2, true)).toEqual(-1);
24+
mockInstrumentFunc["mockFunctionStatus.setIgnore"](1, true);
25+
expect(mockInstrumentFunc.checkMock(1, false)).toEqual(1);
26+
expect(mockInstrumentFunc.checkMock(1, true)).toEqual(-1);
27+
mockInstrumentFunc["mockFunctionStatus.setIgnore"](2, true);
28+
expect(mockInstrumentFunc.checkMock(2, false)).toEqual(2);
29+
expect(mockInstrumentFunc.checkMock(2, true)).toEqual(-1);
3130
// remock(oldFunction)
32-
mockInstruFunc["mockFunctionStatus.setIgnore"](1, false);
33-
expect(mockInstruFunc.checkMock(1, false)).toEqual(4);
34-
mockInstruFunc["mockFunctionStatus.setIgnore"](2, false);
35-
expect(mockInstruFunc.checkMock(2, false)).toEqual(2);
36-
expect(mockInstruFunc.checkMock(2, true)).toEqual(-1);
31+
mockInstrumentFunc["mockFunctionStatus.setIgnore"](1, false);
32+
expect(mockInstrumentFunc.checkMock(1, false)).toEqual(4);
33+
mockInstrumentFunc["mockFunctionStatus.setIgnore"](2, false);
34+
expect(mockInstrumentFunc.checkMock(2, false)).toEqual(2);
35+
expect(mockInstrumentFunc.checkMock(2, true)).toEqual(-1);
3736
// clear
38-
mockInstruFunc["mockFunctionStatus.clear"]();
39-
expect(mockInstruFunc["mockFunctionStatus.state"].size).toEqual(0);
40-
});
41-
42-
test("covInstrument", () => {
43-
const { covInstrument } = covInstruFunc("test.instrumented.wasm");
44-
expect(jest.isMockFunction(fs.writeFileSync)).toBeTruthy();
45-
covInstrument.traceExpression(1, -1, 1);
46-
covInstrument.traceExpression(1, 1, 0);
47-
covInstrument.traceExpression(1, 3, 0);
48-
covInstrument.traceExpression(1, -1, 2);
49-
covInstrument.outputTrace();
50-
expect(covInstrument.runtimeTrace).toEqual([
51-
[1, 1],
52-
[1, 3],
53-
]);
54-
expect(mockWriteFile).toHaveBeenCalledTimes(1);
55-
expect(mockWriteFile).toHaveBeenCalledWith("test.trace", "[[1,1],[1,3]]");
37+
mockInstrumentFunc["mockFunctionStatus.clear"]();
38+
expect(mockInstrumentFunc["mockFunctionStatus.state"].size).toEqual(0);
5639
});
5740
});

0 commit comments

Comments
 (0)