Skip to content

Commit

Permalink
fix(testing/time): fix FakeTime.restoreFor accuracy for sync callbacks (
Browse files Browse the repository at this point in the history
  • Loading branch information
Milly authored Aug 14, 2023
1 parent 6927208 commit bca446e
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 7 deletions.
17 changes: 11 additions & 6 deletions testing/time.ts
Original file line number Diff line number Diff line change
Expand Up @@ -252,21 +252,26 @@ export class FakeTime {
/**
* Restores real time temporarily until callback returns and resolves.
*/
static async restoreFor<T>(
static restoreFor<T>(
// deno-lint-ignore no-explicit-any
callback: (...args: any[]) => Promise<T> | T,
// deno-lint-ignore no-explicit-any
...args: any[]
): Promise<T> {
if (!time) throw new TimeError("no fake time");
let result: T;
if (!time) return Promise.reject(new TimeError("no fake time"));
restoreGlobals();
try {
result = await callback.apply(null, args);
} finally {
const result = callback.apply(null, args);
if (result instanceof Promise) {
return result.finally(() => overrideGlobals());
} else {
overrideGlobals();
return Promise.resolve(result);
}
} catch (e) {
overrideGlobals();
return Promise.reject(e);
}
return result;
}

/**
Expand Down
76 changes: 75 additions & 1 deletion testing/time_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
assertRejects,
assertStrictEquals,
} from "./asserts.ts";
import { FakeTime } from "./time.ts";
import { FakeTime, TimeError } from "./time.ts";
import { _internals } from "./_time.ts";
import { assertSpyCall, spy, SpyCall } from "./mock.ts";

Expand Down Expand Up @@ -304,6 +304,80 @@ Deno.test("FakeTime restoreFor restores real time temporarily", async () => {
}
});

Deno.test("FakeTime restoreFor restores real time and re-overridden atomically", async () => {
const time: FakeTime = new FakeTime();
const fakeSetTimeout = setTimeout;
const actualSetTimeouts: (typeof setTimeout)[] = [];

try {
const asyncFn = async () => {
actualSetTimeouts.push(setTimeout);
await Promise.resolve();
actualSetTimeouts.push(setTimeout);
await Promise.resolve();
actualSetTimeouts.push(setTimeout);
};
const promise = asyncFn();
await new Promise((resolve) => {
FakeTime.restoreFor(() => setTimeout(resolve, 0));
});
await promise;
assertEquals(actualSetTimeouts, [
fakeSetTimeout,
fakeSetTimeout,
fakeSetTimeout,
]);
} finally {
time.restore();
}
});

Deno.test("FakeTime restoreFor returns promise that resolved to result of callback", async () => {
const time: FakeTime = new FakeTime();

try {
const resultSync = await FakeTime.restoreFor(() => "a");
assertEquals(resultSync, "a");
const resultAsync = await FakeTime.restoreFor(() => Promise.resolve("b"));
assertEquals(resultAsync, "b");
} finally {
time.restore();
}
});

Deno.test("FakeTime restoreFor returns promise that rejected to error in callback", async () => {
const time: FakeTime = new FakeTime();

try {
await assertRejects(
() =>
FakeTime.restoreFor(() => {
throw new Error("Error in sync callback");
}),
Error,
"Error in sync callback",
);
await assertRejects(
() =>
FakeTime.restoreFor(() => {
return Promise.reject(new Error("Error in async callback"));
}),
Error,
"Error in async callback",
);
} finally {
time.restore();
}
});

Deno.test("FakeTime restoreFor returns promise that rejected to TimeError if FakeTime is uninitialized", async () => {
await assertRejects(
() => FakeTime.restoreFor(() => {}),
TimeError,
"no fake time",
);
});

Deno.test("delay uses real time", async () => {
const time: FakeTime = new FakeTime();
const start: number = Date.now();
Expand Down

0 comments on commit bca446e

Please sign in to comment.