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.
155 lines (135 loc) • 7.89 kB
text/typescript
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 { TTypedArrayCtor } from "../../array/typed-array/t-typed-array-ctor.js";
import { getNumberSpecialization, numberSpecializations } from "../../runtime/rtti-interop.js";
import { ResizableArray, resizableArraySpecialization } from "./resizable-array.js";
import type { ISharedArray } from "../shared-array/i-shared-array.js";
import { _Array } from "../../array/_array.js";
import { IJsUtilBindings } from "../i-js-util-bindings.js";
import type { ITestOnlyBindings } from "../i-test-only-bindings.js";
import { _Fp } from "../../fp/_fp.js";
describe("=> ResizableArray", () =>
{
const testModule = new SanitizedEmscriptenTestModule<IJsUtilBindings, ITestOnlyBindings>(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>
(
resizableArray: ISharedArray<TCtor>,
ctor: TCtor
) =>
{
const actualArray = resizableArray.getArray();
expect(actualArray.length).toBe(8);
expect(resizableArray.getArray()).toBeInstanceOf(ctor);
const numberSpec = getNumberSpecialization(ctor);
expect(testModule.wrapper.interopIds.getId(numberSpec)).toBeGreaterThanOrEqual(0);
expect(testModule.wrapper.interopIds.hasId(resizableArray, numberSpec)).toBe(true);
expect(testModule.wrapper.interopIds.hasId(resizableArray, resizableArraySpecialization)).toBe(true);
for (let i = 0; i < 8; ++i)
{
// this isn't going to trigger the ASAN, but it's better than nothing...
expect(resizableArray.getArray()[i]).toBe(0);
}
};
// use one for sanity checking purposes...
const u8Array = ResizableArray.createOne(testModule.wrapper, Uint8Array, testModule.wrapper.rootNode, 8);
expect(testModule.wrapper.interopIds.hasId(u8Array, numberSpecializations.f64)).toBe(false);
expectCommonShape(u8Array, Uint8Array);
expectCommonShape(ResizableArray.createOne(testModule.wrapper, Int8Array, testModule.wrapper.rootNode, 8), Int8Array);
expectCommonShape(ResizableArray.createOne(testModule.wrapper, Uint16Array, testModule.wrapper.rootNode, 8), Uint16Array);
expectCommonShape(ResizableArray.createOne(testModule.wrapper, Int16Array, testModule.wrapper.rootNode, 8), Int16Array);
expectCommonShape(ResizableArray.createOne(testModule.wrapper, Uint32Array, testModule.wrapper.rootNode, 8), Uint32Array);
expectCommonShape(ResizableArray.createOne(testModule.wrapper, Int32Array, testModule.wrapper.rootNode, 8), Int32Array);
expectCommonShape(ResizableArray.createOne(testModule.wrapper, Float32Array, testModule.wrapper.rootNode, 8), Float32Array);
expectCommonShape(ResizableArray.createOne(testModule.wrapper, Float64Array, testModule.wrapper.rootNode, 8), Float64Array);
testModule.wrapper.rootNode.getLinked().unlinkAll();
}));
});
describe("=> getArray", () =>
{
let resizableArray: ResizableArray<Float32ArrayConstructor>;
beforeEach(fpRunWithin([_Debug.labelBlock("TestSetup")], () =>
{
resizableArray = ResizableArray.createOne(testModule.wrapper, Float32Array, testModule.wrapper.rootNode, 8);
}));
afterEach(() =>
{
testModule.wrapper.rootNode.getLinked().unlink(resizableArray.resourceHandle);
});
it("| throws an exception if there isn't enough memory", fpRunWithin([_Debug.labelBlock("OOMTest"), blockScope], () =>
{
testModule.wrapper.rootNode.getLinked().unlink(resizableArray.resourceHandle);
expect(
() => ResizableArray.createOne(testModule.wrapper, Float32Array, testModule.wrapper.rootNode, 0xffffffff)
).toThrowError("Failed to allocate memory for resizable array.");
}));
xit("| can be used from C without triggering sanitizers", _Fp.runWithin([blockScope], () =>
{
const sharedArray = ResizableArray.createOne(testModule.wrapper, Uint16Array, null, 12);
_Array.forEachRange(12, 1, (value, index) => sharedArray.getArray()[index] = value);
expect(testModule.wrapper.instance.testResizableArray_readWriteU16(sharedArray.pointer)).toBe(0);
}));
describe("=> debug mode", () =>
{
it("| errors when called after release", fpRunWithin([_Debug.labelBlock("ErrorAfterReleaseTest")], () =>
{
testModule.wrapper.rootNode.getLinked().unlink(resizableArray.resourceHandle);
expect(() => resizableArray.getArray()).toThrow();
}));
it(
"| errors when array members are accessed after release",
fpRunWithin([_Debug.labelBlock("UseAfterFree")], () =>
{
const instance = resizableArray.getArray();
testModule.wrapper.rootNode.getLinked().unlink(resizableArray.resourceHandle);
expect(() => instance.length).toThrow();
})
);
it(
"| errors when array members are accessed and memory may have resized",
fpRunWithin([blockScope, _Debug.labelBlock("InvalidatedMemoryTest")], () =>
{
const instance = resizableArray.getArray();
expect(instance[0]).toBe(0);
const sharedArray2 = ResizableArray.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(resizableArray.resourceHandle);
testModule.wrapper.rootNode.getLinked().unlink(sharedArray2.resourceHandle);
})
);
it("| returns new instance on memory growth", fpRunWithin([blockScope, _Debug.labelBlock("ResizeTest")], () =>
{
const resizableArray = ResizableArray.createOne(testModule.wrapper, Float32Array, testModule.wrapper.rootNode, 8);
const i1 = resizableArray.getArray();
const sharedArray2 = ResizableArray.createOne(testModule.wrapper, Float32Array, testModule.wrapper.rootNode, 2097152);
expect(i1 === resizableArray.getArray()).toBeFalse();
testModule.wrapper.rootNode.getLinked().unlink(resizableArray.resourceHandle);
testModule.wrapper.rootNode.getLinked().unlink(sharedArray2.resourceHandle);
}));
});
});
});