UNPKG

rc-js-util

Version:

A collection of TS and C++ utilities to help writing performant and correct applications, achieved through strict typing and (removable) invariant checking.

106 lines (88 loc) 4.2 kB
import { ReferenceCountedNode } from "../../lifecycle/reference-counted-node.js"; import { lifecycleStack } from "./lifecycle-stack.js"; import { IManagedObject, type IManagedResourceNode } from "../../lifecycle/manged-resources.js"; import { ReferenceCountedStrategy } from "./reference-counted-strategy.js"; import { _Debug } from "../../debug/_debug.js"; import { SanitizedEmscriptenTestModule } from "./sanitized-emscripten-test-module.js"; import { getTestModuleOptions, TestGarbageCollector } from "../../test-util/test-utils.js"; import utilTestModule from "../../external/test-module.mjs"; import { SharedArray } from "../shared-array/shared-array.js"; import type { IJsUtilBindings } from "../i-js-util-bindings.js"; import type { IEmscriptenWrapper } from "./i-emscripten-wrapper.js"; import type { IInteropBindings } from "./i-interop-bindings.js"; describe("=> ReferenceCountedStrategy", () => { let strategy: ReferenceCountedStrategy; beforeEach(() => { strategy = new ReferenceCountedStrategy(); }); it("| creates nodes bound to the last block scope", () => { const blockNode = new ReferenceCountedNode(); spyOn(lifecycleStack, "getTop").and.returnValue(blockNode); spyOn(lifecycleStack, "push").and.returnValue(blockNode); spyOn(blockNode.getLinked(), "link").and.callThrough(); const node = strategy.createNode(null); expect(blockNode.getLinked().link).toHaveBeenCalledWith(node); expect(blockNode.getLinked().isLinkedTo(node)).toBe(true); blockNode.getLinked().unlinkAll(); expect(node.getIsDestroyed()).toBe(true); }); it("| creates root nodes", () => { const rootNode = strategy.createRootNode(); expect(rootNode.getIsDestroyed()).toBe(false); expect(rootNode.getLinked().getLinkedNodes().length).toBe(0); }); it("| provides finalization checks in debug for pointers", async () => { if (!_BUILD.DEBUG || !TestGarbageCollector.isAvailable) { return pending("Test not available in this environment"); } const errorSpy = spyOn(_Debug, "error"); const testModule = new SanitizedEmscriptenTestModule(utilTestModule, getTestModuleOptions()); await testModule.initialize(); expect(errorSpy).not.toHaveBeenCalled(); // this is an error in all cases with reference counting (forgot to use return) SharedArray.createOne(testModule.wrapper, Float32Array, testModule.wrapper.rootNode, 100); expect(await TestGarbageCollector.testFriendlyGc()).toBeGreaterThan(0); expect(errorSpy).toHaveBeenCalledWith(jasmine.stringMatching(/^A shared object was leaked/)); testModule.wrapper.rootNode.getLinked().unlinkAll(); testModule.endEmscriptenProgram(); }); it("| provides finalization checks in debug", async () => { if (!_BUILD.DEBUG || !TestGarbageCollector.isAvailable) { return pending("Test not available in this environment"); } const errorSpy = spyOn(_Debug, "error"); const testModule = new SanitizedEmscriptenTestModule(utilTestModule, getTestModuleOptions()); await testModule.initialize(); expect(errorSpy).not.toHaveBeenCalled(); // leak the object while registering it testModule.wrapper.lifecycleStrategy.onManagedObjectCreated(createTestManagedObject(testModule)); expect(await TestGarbageCollector.testFriendlyGc()).toBeGreaterThan(0); expect(errorSpy).toHaveBeenCalled(); testModule.wrapper.rootNode.getLinked().unlinkAll(); testModule.endEmscriptenProgram(); }); }); class TestManagedObject implements IManagedObject { public constructor( public resourceHandle: IManagedResourceNode ) { } public getWrapper(): IEmscriptenWrapper<IInteropBindings> { throw new Error("not implemented"); } } function createTestManagedObject(testModule: SanitizedEmscriptenTestModule<IJsUtilBindings, object>): IManagedObject { return new TestManagedObject(testModule.wrapper.lifecycleStrategy.createNode(testModule.wrapper.rootNode)); }