cesium/packages/engine/Specs/Core/TaskProcessorSpec.js

280 lines
8.5 KiB
JavaScript

import {
buildModuleUrl,
FeatureDetection,
RuntimeError,
TaskProcessor,
} from "../../index.js";
import absolutize from "../../../../Specs/absolutize.js";
describe("Core/TaskProcessor", function () {
let taskProcessor;
afterEach(function () {
TaskProcessor._workerModulePrefix =
TaskProcessor._defaultWorkerModulePrefix;
if (taskProcessor && !taskProcessor.isDestroyed()) {
taskProcessor = taskProcessor.destroy();
}
});
it("throws runtime error if browser is not supported", async function () {
spyOn(FeatureDetection, "supportsEsmWebWorkers").and.returnValue(false);
taskProcessor = new TaskProcessor(
absolutize("../Specs/Build/TestWorkers/returnParameters.js"),
);
expect(() => taskProcessor.scheduleTask()).toThrowError(RuntimeError);
});
it("works with a simple worker", async function () {
taskProcessor = new TaskProcessor(
absolutize("../Build/Specs/TestWorkers/returnParameters.js"),
);
const parameters = {
prop: "blah",
obj: {
val: true,
},
};
await expectAsync(taskProcessor.scheduleTask(parameters)).toBeResolvedTo(
parameters,
);
});
it("works with a simple worker defined as relative to TaskProcessor._workerModulePrefix", async function () {
window.CESIUM_WORKERS = undefined;
TaskProcessor._workerModulePrefix = absolutize(
"../Build/Specs/TestWorkers/",
);
taskProcessor = new TaskProcessor("returnParameters.js");
const parameters = {
prop: "blah",
obj: {
val: true,
},
};
await expectAsync(taskProcessor.scheduleTask(parameters)).toBeResolvedTo(
parameters,
);
});
it("when workers loaded via module ID and it is cross-origin, loads worker with appropriate shim", async function () {
// Setup a cross origin BASE_URL
const oldCESIUM_BASE_URL = window.CESIUM_BASE_URL;
window.CESIUM_BASE_URL = "http://test.com/source/";
buildModuleUrl._clearBaseResource();
const blobSpy = spyOn(window, "Blob").and.callThrough();
// Provide just the module ID, as is prevalent in the codebase
taskProcessor = new TaskProcessor("transferTypedArrayTest");
// Create the worker, but don't execute the task this frame
taskProcessor._activeTasks = taskProcessor._maximumActiveTasks;
await taskProcessor.scheduleTask();
expect(blobSpy).toHaveBeenCalledWith(
[`import "http://test.com/source/Workers/transferTypedArrayTest.js";`],
{ type: "application/javascript" },
);
// Reset old values for BASE_URL
window.CESIUM_BASE_URL = oldCESIUM_BASE_URL;
buildModuleUrl._clearBaseResource();
});
it("when provided a cross-origin URI, loads worker with appropriate shim", async function () {
const blobSpy = spyOn(window, "Blob").and.callThrough();
taskProcessor = new TaskProcessor("http://test.com/Workers/testing.js");
// Create the worker, but don't execute the task this frame
taskProcessor._activeTasks = taskProcessor._maximumActiveTasks;
await taskProcessor.scheduleTask();
expect(blobSpy).toHaveBeenCalledWith(
[`import "http://test.com/Workers/testing.js";`],
{ type: "application/javascript" },
);
});
it("can be destroyed", function () {
taskProcessor = new TaskProcessor(
absolutize("../Specs/Build/TestWorkers/returnParameters.js"),
);
expect(taskProcessor.isDestroyed()).toEqual(false);
taskProcessor.destroy();
expect(taskProcessor.isDestroyed()).toEqual(true);
});
it("can transfer array buffer", async function () {
taskProcessor = new TaskProcessor(
absolutize("../Build/Specs/TestWorkers/returnByteLength.js"),
);
const byteLength = 100;
const parameters = new ArrayBuffer(byteLength);
expect(parameters.byteLength).toEqual(byteLength);
const canTransferArrayBuffer = await TaskProcessor._canTransferArrayBuffer;
const result = await taskProcessor.scheduleTask(parameters, [parameters]);
// the worker should see the array with proper byte length
if (canTransferArrayBuffer) {
// array buffer should be neutered when transferred
expect(parameters.byteLength).toEqual(0);
}
expect(result).toEqual(byteLength);
});
it("can transfer array buffer back from worker", async function () {
taskProcessor = new TaskProcessor(
absolutize("../Build/Specs/TestWorkers/transferArrayBuffer.js"),
);
const byteLength = 100;
const parameters = {
byteLength: byteLength,
};
// the worker should see the array with proper byte length
const result = await taskProcessor.scheduleTask(parameters);
expect(result.byteLength).toEqual(100);
});
it("rejects promise if worker throws", async function () {
taskProcessor = new TaskProcessor(
absolutize("../Build/Specs/TestWorkers/throwError.js"),
);
const message = "foo";
const parameters = {
message: message,
};
await expectAsync(
taskProcessor.scheduleTask(parameters),
).toBeRejectedWithError(Error, message);
});
it("rejects promise if worker returns a non-clonable result", async function () {
taskProcessor = new TaskProcessor(
absolutize("../Build/Specs/TestWorkers/returnNonCloneable.js"),
);
const message = "foo";
const parameters = {
message: message,
};
await expectAsync(taskProcessor.scheduleTask(parameters)).toBeRejectedWith(
jasmine.stringContaining("postMessage failed"),
);
});
it("successful task raises the taskCompletedEvent", async function () {
taskProcessor = new TaskProcessor(
absolutize("../Build/Specs/TestWorkers/returnParameters.js"),
);
const parameters = {
prop: "blah",
obj: {
val: true,
},
};
let eventRaised = false;
const removeListenerCallback =
TaskProcessor.taskCompletedEvent.addEventListener(function () {
eventRaised = true;
});
await expectAsync(taskProcessor.scheduleTask(parameters)).toBeResolved();
expect(eventRaised).toBe(true);
removeListenerCallback();
});
it("unsuccessful task raises the taskCompletedEvent with error", async function () {
taskProcessor = new TaskProcessor(
absolutize("../Build/Specs/TestWorkers/returnNonCloneable.js"),
);
const message = "foo";
const parameters = {
message: message,
};
let eventRaised = false;
const removeListenerCallback =
TaskProcessor.taskCompletedEvent.addEventListener(function (error) {
eventRaised = true;
expect(error).toBeDefined();
});
await expectAsync(taskProcessor.scheduleTask(parameters)).toBeRejected();
expect(eventRaised).toBe(true);
removeListenerCallback();
});
it("can load and compile web assembly module", async function () {
const binaryUrl = absolutize("../Specs/TestWorkers/TestWasm/testWasm.wasm");
taskProcessor = new TaskProcessor(
absolutize("../Build/Specs/TestWorkers/returnWasmConfig.js", 5),
);
const result = await taskProcessor.initWebAssemblyModule({
wasmBinaryFile: binaryUrl,
fallbackModulePath: "TestWasm/testWasmFallback",
});
expect(result).toBeDefined();
if (FeatureDetection.supportsWebAssembly()) {
expect(result.wasmBinary).toBeDefined();
expect(result.wasmBinaryFile).toEqual(binaryUrl);
}
});
it("uses a backup module if web assembly is not supported", async function () {
const binaryUrl = absolutize("../Specs/TestWorkers/TestWasm/testWasm.wasm");
taskProcessor = new TaskProcessor(
absolutize("../Build/Specs/TestWorkers/returnWasmConfig.js", 5),
);
spyOn(FeatureDetection, "supportsWebAssembly").and.returnValue(false);
const result = await taskProcessor.initWebAssemblyModule({
wasmBinaryFile: binaryUrl,
fallbackModulePath: "TestWasm/testWasmFallback",
});
expect(result).toBeDefined();
expect(result.modulePath).toMatch(/TestWasm\/testWasmFallback/);
expect(result.wasmBinary).not.toBeDefined();
});
it("throws runtime error if web assembly is not supported and no backup is provided", async function () {
const binaryUrl = absolutize("../Specs/TestWorkers/TestWasm/testWasm.wasm");
taskProcessor = new TaskProcessor(
absolutize("../Build/Specs/TestWorkers/returnWasmConfig.js", 5),
);
spyOn(FeatureDetection, "supportsWebAssembly").and.returnValue(false);
await expectAsync(
taskProcessor.initWebAssemblyModule({
wasmBinaryFile: binaryUrl,
}),
).toBeRejectedWithError(RuntimeError);
});
});