mirror of
https://seed.radicle.dev/rad:z4V1sjrXqjvFdnCUbxPFqd5p4DtH5.git
synced 2026-10-18 19:30:27 +08:00
121 lines
2.9 KiB
TypeScript
121 lines
2.9 KiB
TypeScript
import { describe, expect, test, vi } from "vitest";
|
||
|
||
import * as mutexExecutor from "@app/lib/mutexExecutor";
|
||
import { sleep } from "@app/lib/sleep";
|
||
|
||
describe("executor", () => {
|
||
test("cancels running task", async () => {
|
||
const e = mutexExecutor.create();
|
||
|
||
const first = e.run(async () => {
|
||
await sleep(10);
|
||
return "first";
|
||
});
|
||
const second = e.run(async () => {
|
||
return "second";
|
||
});
|
||
|
||
expect(await first).toBe(undefined);
|
||
expect(await second).toBe("second");
|
||
|
||
const third = e.run(async () => {
|
||
await sleep(10);
|
||
return "third";
|
||
});
|
||
const fourth = e.run(async () => {
|
||
return "fourth";
|
||
});
|
||
|
||
expect(await third).toBe(undefined);
|
||
expect(await fourth).toBe("fourth");
|
||
});
|
||
|
||
test("cancels multiple tasks", async () => {
|
||
const e = mutexExecutor.create();
|
||
|
||
const canceled1 = e.run(async () => {
|
||
await sleep(10);
|
||
return true;
|
||
});
|
||
const canceled2 = e.run(async () => {
|
||
await sleep(10);
|
||
return true;
|
||
});
|
||
const canceled3 = e.run(async () => {
|
||
await sleep(10);
|
||
return true;
|
||
});
|
||
const last = e.run(async () => {
|
||
return true;
|
||
});
|
||
|
||
expect(await canceled1).toBe(undefined);
|
||
expect(await canceled2).toBe(undefined);
|
||
expect(await canceled3).toBe(undefined);
|
||
expect(await last).toBe(true);
|
||
});
|
||
|
||
test("triggers abort signal event", async () => {
|
||
const e = mutexExecutor.create();
|
||
const abortListener = vi.fn();
|
||
|
||
void e.run(async abort => {
|
||
abort.addEventListener("abort", abortListener);
|
||
await sleep(10);
|
||
return "first";
|
||
});
|
||
expect(abortListener).not.toHaveBeenCalled();
|
||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||
void e.run(async () => {});
|
||
expect(abortListener).toHaveBeenCalled();
|
||
});
|
||
|
||
test("don’t throw error on aborted task", async () => {
|
||
const e = mutexExecutor.create();
|
||
|
||
const first = e.run(async () => {
|
||
await sleep(10);
|
||
throw new Error();
|
||
});
|
||
const second = e.run(async () => {
|
||
return "second";
|
||
});
|
||
|
||
expect(await first).toBe(undefined);
|
||
expect(await second).toBe("second");
|
||
});
|
||
});
|
||
|
||
describe("worker", () => {
|
||
test("sequential work", async () => {
|
||
const w = mutexExecutor.createWorker(async (value: number) => {
|
||
await sleep(10);
|
||
return value;
|
||
});
|
||
|
||
const outputs: number[] = [];
|
||
w.output.onValue(value => outputs.push(value));
|
||
|
||
await w.submit(1);
|
||
await w.submit(2);
|
||
await w.submit(3);
|
||
|
||
expect(outputs).toEqual([1, 2, 3]);
|
||
});
|
||
|
||
test("overlapping work cancels", async () => {
|
||
const w = mutexExecutor.createWorker(async (value: number) => {
|
||
await sleep(10);
|
||
return value;
|
||
});
|
||
|
||
const nextOutput = w.output.firstToPromise();
|
||
|
||
void w.submit(1);
|
||
void w.submit(2);
|
||
void w.submit(3);
|
||
|
||
expect(await nextOutput).toEqual(3);
|
||
});
|
||
});
|