Skip to content

Commit 1d97cc7

Browse files
committed
add ScriptCreator tests; improve VmWorker tests
1 parent 1315756 commit 1d97cc7

5 files changed

Lines changed: 409 additions & 13 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "locobasic",
3-
"version": "0.5.46",
3+
"version": "0.5.47",
44
"description": "# LocoBasic - Loco BASIC",
55
"type": "commonjs",
66
"main": "./dist/locobasic.js",

src/ScriptCreator.ts

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,16 @@ export class ScriptCreator {
1010
}
1111

1212
// Build dependency map by analyzing vm properties
13-
analyzeDependencies(vmObj: VMObject): Record<string, string[]> {
13+
private analyzeDependencies(vmObj: VMObject): Record<string, string[]> {
14+
const regexPrefix = "vm\\.";
15+
// Find all <prefix>propertyName references (default prefix: vm.)
16+
const regex = new RegExp(`${regexPrefix}([\\w$]+)`, "g");
1417
const depMap: Record<string, string[]> = {};
1518

1619
for (const key in vmObj) {
1720
const deps: string[] = [];
1821
const noPropertyDeps = key === "cls"; // no property deps for cls function
19-
const value = vmObj[key];
20-
const valueStr = String(value);
21-
22-
// Find all vm.propertyName references
23-
const regex = /vm\.([\w$]+)/g;
22+
const valueStr = String(vmObj[key]);
2423
let match;
2524
while ((match = regex.exec(valueStr)) !== null) {
2625
const refProp = match[1];
@@ -37,7 +36,7 @@ export class ScriptCreator {
3736
};
3837

3938
// Calculate transitive closure of dependencies
40-
getTransitiveDeps(usedFunctions: string[], depMap: Record<string, string[]>): Set<string> {
39+
private getTransitiveDeps(usedFunctions: string[], depMap: Record<string, string[]>): Set<string> {
4140
const result = new Set<string>(usedFunctions);
4241
let changed = true;
4342

@@ -58,7 +57,7 @@ export class ScriptCreator {
5857
}
5958

6059
// Filter vm object based on used functions
61-
filterVM(vmObj: VMObject, usedFunctions: string[]): Partial<VMObject> {
60+
private filterVM(vmObj: VMObject, usedFunctions: string[]): Partial<VMObject> {
6261
const depMap = this.analyzeDependencies(vmObj);
6362

6463
if (this.debug) {
@@ -149,7 +148,7 @@ export class ScriptCreator {
149148
}
150149

151150
// Generate source code for filtered vm object
152-
generateSource(vmObj: Partial<VMObject>): string {
151+
private generateSource(vmObj: Partial<VMObject>): string {
153152
const indent = " ".repeat(8);
154153
let output = "";
155154

tests/ScriptCreator.test.ts

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import { describe, expect, it, vi } from "vitest";
2+
import { ScriptCreator } from "../src/ScriptCreator";
3+
import type { NodeWorkerFnType } from "../src/Interfaces";
4+
5+
const createVmFunction = <T>(content: string) => new Function("vm", content) as unknown as (() => T);
6+
7+
describe("ScriptCreator", () => {
8+
it("createStandaloneScript includes transitive deps and required hooks", () => {
9+
const scriptCreator = new ScriptCreator(0);
10+
const workerFn: NodeWorkerFnType = {
11+
workerFn: () => ({
12+
helper: createVmFunction<unknown>("return vm.flush();"),
13+
used: createVmFunction<unknown>("return vm.helper();"),
14+
unused: () => "drop me",
15+
flush: () => undefined,
16+
onMessageHandler: () => undefined
17+
})
18+
};
19+
20+
const script = scriptCreator.createStandaloneScript(workerFn, "return 42;", ["used"]);
21+
22+
expect(script).toContain("return 42;");
23+
expect(script).toContain("used:");
24+
expect(script).toContain("helper:");
25+
expect(script).toContain("flush:");
26+
expect(script).toContain("onMessageHandler:");
27+
expect(script).not.toContain("unused:");
28+
expect(script).toContain("globalThis.LocoBasicVm = vm;");
29+
expect(script).toContain("parentPort.on('message', (data) => vm.onMessageHandler(data));");
30+
expect(script).toMatch(/\bon\s*\(_event,/);
31+
});
32+
33+
it("createStandaloneScript warns for missing used instructions in debug mode", () => {
34+
const scriptCreator = new ScriptCreator(1);
35+
const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => undefined);
36+
const workerFn: NodeWorkerFnType = {
37+
workerFn: () => ({
38+
existing: () => undefined,
39+
flush: () => undefined,
40+
onMessageHandler: () => undefined
41+
})
42+
};
43+
44+
try {
45+
scriptCreator.createStandaloneScript(workerFn, "return 0;", ["missing"]);
46+
47+
expect(warnSpy).toHaveBeenCalledWith("Warning: Function 'missing' does not exist in vm object");
48+
} finally {
49+
warnSpy.mockRestore();
50+
}
51+
});
52+
53+
it("createStandaloneScript serializes required values/functions and prunes unused ones", () => {
54+
const scriptCreator = new ScriptCreator(0);
55+
const workerFn: NodeWorkerFnType = {
56+
workerFn: () => ({
57+
count: 3,
58+
text: "abc",
59+
run: createVmFunction<number>("return vm.count;"),
60+
flush: () => undefined,
61+
onMessageHandler: () => undefined
62+
})
63+
};
64+
65+
/*
66+
const test1 = workerFn.workerFn({
67+
on: () => undefined,
68+
postMessage: () => undefined,
69+
});
70+
expect(test1.count).toBe(3);
71+
*/
72+
73+
const script = scriptCreator.createStandaloneScript(workerFn, "return 1;", ["run"]);
74+
75+
expect(script).toContain("count: 3");
76+
expect(script).toContain("run: function anonymous");
77+
expect(script).not.toContain('text: "abc"');
78+
});
79+
80+
it("createStandaloneScript excludes cls private underscore dependencies (special)", () => {
81+
const scriptCreator = new ScriptCreator(0);
82+
const workerFn: NodeWorkerFnType = {
83+
workerFn: () => ({
84+
_hidden: 1,
85+
helper: () => "ok",
86+
cls: createVmFunction<string>("vm._hidden; return vm.helper();"),
87+
flush: () => undefined,
88+
onMessageHandler: () => undefined
89+
})
90+
};
91+
92+
const script = scriptCreator.createStandaloneScript(workerFn, "return 0;", ["cls"]);
93+
94+
expect(script).toContain("helper:");
95+
expect(script).toContain("cls:");
96+
expect(script).toContain("flush:");
97+
expect(script).toContain("onMessageHandler:");
98+
expect(script).not.toContain("_hidden:");
99+
});
100+
});

0 commit comments

Comments
 (0)