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.

144 lines (125 loc) 7.13 kB
import { SharedArray, sharedArraySpecialization } from "./shared-array.js"; import { SanitizedEmscriptenTestModule } from "../emscripten/sanitized-emscripten-test-module.js"; import { _Debug } from "../../debug/_debug.js"; import utilTestModule from "../../external/test-module.mjs"; import { Test_setDefaultFlags } from "../../test-util/test_set-default-flags.js"; import { getTestModuleOptions } from "../../test-util/test-utils.js"; import { fpRunWithin } from "../../fp/impl/fp-run-within.js"; import { blockScope } from "../../lifecycle/block-scoped-lifecycle.js"; import type { ISharedArray } from "./i-shared-array.js"; import type { TTypedArrayCtor } from "../../array/typed-array/t-typed-array-ctor.js"; import { getNumberSpecialization, numberSpecializations } from "../../runtime/rtti-interop.js"; describe("=> SharedArray", () => { const testModule = new SanitizedEmscriptenTestModule(utilTestModule, getTestModuleOptions()); beforeEach(async () => { Test_setDefaultFlags(); await testModule.initialize(); }); afterEach(() => { testModule.endEmscriptenProgram(); }); describe("=> type construction", () => { it("| creates an array of the correct length", fpRunWithin([blockScope, _Debug.labelBlock("construction")], () => { const expectCommonShape = <TCtor extends TTypedArrayCtor> ( sharedArray: ISharedArray<TCtor>, ctor: TCtor ) => { const actualArray = sharedArray.getArray(); expect(actualArray.length).toBe(8); expect(sharedArray.getArray()).toBeInstanceOf(ctor); const numberSpec = getNumberSpecialization(ctor); expect(testModule.wrapper.interopIds.getId(numberSpec)).toBeGreaterThanOrEqual(0); expect(testModule.wrapper.interopIds.hasId(sharedArray, numberSpec)).toBe(true); expect(testModule.wrapper.interopIds.hasId(sharedArray, sharedArraySpecialization)).toBe(true); for (let i = 0; i < 8; ++i) { // this isn't going to trigger the ASAN, but it's better than nothing... expect(sharedArray.getArray()[i]).toBe(0); } }; // use one for sanity checking purposes... const u8Array = SharedArray.createOne(testModule.wrapper, Uint8Array, testModule.wrapper.rootNode, 8, true); expect(testModule.wrapper.interopIds.hasId(u8Array, numberSpecializations.f64)).toBe(false); expectCommonShape(u8Array, Uint8Array); expectCommonShape(SharedArray.createOne(testModule.wrapper, Int8Array, testModule.wrapper.rootNode, 8, true), Int8Array); expectCommonShape(SharedArray.createOne(testModule.wrapper, Uint16Array, testModule.wrapper.rootNode, 8, true), Uint16Array); expectCommonShape(SharedArray.createOne(testModule.wrapper, Int16Array, testModule.wrapper.rootNode, 8, true), Int16Array); expectCommonShape(SharedArray.createOne(testModule.wrapper, Uint32Array, testModule.wrapper.rootNode, 8, true), Uint32Array); expectCommonShape(SharedArray.createOne(testModule.wrapper, Int32Array, testModule.wrapper.rootNode, 8, true), Int32Array); expectCommonShape(SharedArray.createOne(testModule.wrapper, Float32Array, testModule.wrapper.rootNode, 8, true), Float32Array); expectCommonShape(SharedArray.createOne(testModule.wrapper, Float64Array, testModule.wrapper.rootNode, 8, true), Float64Array); testModule.wrapper.rootNode.getLinked().unlinkAll(); })); }); describe("=> getArray", () => { let sharedArray: SharedArray<Float32ArrayConstructor>; beforeEach(fpRunWithin([_Debug.labelBlock("TestSetup")], () => { sharedArray = SharedArray.createOne(testModule.wrapper, Float32Array, testModule.wrapper.rootNode, 8, true); })); afterEach(() => { testModule.wrapper.rootNode.getLinked().unlink(sharedArray.resourceHandle); }); it("| throws an exception if there isn't enough memory", fpRunWithin([_Debug.labelBlock("OOMTest"), blockScope], () => { testModule.wrapper.rootNode.getLinked().unlink(sharedArray.resourceHandle); expect( () => SharedArray.createOne(testModule.wrapper, Float32Array, testModule.wrapper.rootNode, 0xffffffff) ).toThrowError("Failed to allocate memory for shared array."); })); describe("=> debug mode", () => { it("| errors when called after release", fpRunWithin([_Debug.labelBlock("ErrorAfterReleaseTest")], () => { testModule.wrapper.rootNode.getLinked().unlink(sharedArray.resourceHandle); expect(() => sharedArray.getArray()).toThrow(); })); it( "| errors when array members are accessed after release", fpRunWithin([_Debug.labelBlock("UseAfterFree")], () => { const instance = sharedArray.getArray(); testModule.wrapper.rootNode.getLinked().unlink(sharedArray.resourceHandle); expect(() => instance.length).toThrow(); }) ); it( "| errors when array members are accessed and memory may have resized", fpRunWithin([blockScope, _Debug.labelBlock("InvalidatedMemoryTest")], () => { const instance = sharedArray.getArray(); expect(instance[0]).toBe(0); const sharedArray2 = SharedArray.createOne(testModule.wrapper, Float32Array, testModule.wrapper.rootNode, 8); if (testModule.wrapper.instance._isDebugBuild()) { expect(() => instance[0]).toThrow(); } else { expect(() => instance[0]).not.toThrow(); } testModule.wrapper.rootNode.getLinked().unlink(sharedArray.resourceHandle); testModule.wrapper.rootNode.getLinked().unlink(sharedArray2.resourceHandle); }) ); it("| returns new instance on memory growth", fpRunWithin([blockScope, _Debug.labelBlock("ResizeTest")], () => { const sharedArray = SharedArray.createOne(testModule.wrapper, Float32Array, testModule.wrapper.rootNode, 8); const i1 = sharedArray.getArray(); const sharedArray2 = SharedArray.createOne(testModule.wrapper, Float32Array, testModule.wrapper.rootNode, 2097152); expect(i1 === sharedArray.getArray()).toBeFalse(); testModule.wrapper.rootNode.getLinked().unlink(sharedArray.resourceHandle); testModule.wrapper.rootNode.getLinked().unlink(sharedArray2.resourceHandle); })); }); }); });