2024-01-01 21:11:32 +00:00
|
|
|
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
2022-04-14 06:29:17 +00:00
|
|
|
import {
|
|
|
|
assert,
|
2024-06-25 06:51:24 +00:00
|
|
|
assertAlmostEquals,
|
2022-04-14 06:29:17 +00:00
|
|
|
assertEquals,
|
|
|
|
assertInstanceOf,
|
2024-06-24 23:35:53 +00:00
|
|
|
assertMatch,
|
2022-04-14 06:29:17 +00:00
|
|
|
assertNotEquals,
|
|
|
|
assertRejects,
|
|
|
|
assertStrictEquals,
|
2024-06-25 05:15:25 +00:00
|
|
|
assertThrows,
|
2024-04-29 02:57:30 +00:00
|
|
|
} from "@std/assert";
|
2023-08-14 09:33:35 +00:00
|
|
|
import { FakeTime, TimeError } from "./time.ts";
|
2022-04-14 06:29:17 +00:00
|
|
|
import { _internals } from "./_time.ts";
|
2024-02-27 21:57:25 +00:00
|
|
|
import { assertSpyCall, spy, type SpyCall } from "./mock.ts";
|
2024-07-22 04:07:48 +00:00
|
|
|
import { deadline, delay } from "@std/async";
|
2022-04-14 06:29:17 +00:00
|
|
|
|
2024-07-22 04:07:48 +00:00
|
|
|
function fromNow(): (..._args: unknown[]) => number {
|
2022-04-14 06:29:17 +00:00
|
|
|
const start: number = Date.now();
|
|
|
|
return () => Date.now() - start;
|
|
|
|
}
|
|
|
|
|
2024-04-15 06:39:17 +00:00
|
|
|
Deno.test("FakeTime doesn't affect Date unchanged if uninitialized", () => {
|
2022-04-14 06:29:17 +00:00
|
|
|
assertStrictEquals(Date, _internals.Date);
|
|
|
|
});
|
|
|
|
|
2024-04-15 06:39:17 +00:00
|
|
|
Deno.test("FakeTime fakes Date", () => {
|
2023-12-14 20:47:50 +00:00
|
|
|
{
|
|
|
|
using _time = new FakeTime(9001);
|
2022-04-14 06:29:17 +00:00
|
|
|
assertNotEquals(Date, _internals.Date);
|
|
|
|
}
|
|
|
|
assertStrictEquals(Date, _internals.Date);
|
|
|
|
});
|
|
|
|
|
2024-04-15 06:39:17 +00:00
|
|
|
Deno.test("FakeTime causes date parse and UTC behave the same", () => {
|
2022-04-14 06:29:17 +00:00
|
|
|
const expectedUTC = Date.UTC(96, 1, 2, 3, 4, 5);
|
|
|
|
const expectedParse = Date.parse("04 Dec 1995 00:12:00 GMT");
|
|
|
|
|
2023-12-14 20:47:50 +00:00
|
|
|
using _time = new FakeTime();
|
|
|
|
assertEquals(
|
|
|
|
Date.UTC(96, 1, 2, 3, 4, 5),
|
|
|
|
expectedUTC,
|
|
|
|
);
|
|
|
|
assertEquals(
|
|
|
|
Date.parse("04 Dec 1995 00:12:00 GMT"),
|
|
|
|
expectedParse,
|
|
|
|
);
|
2022-04-14 06:29:17 +00:00
|
|
|
});
|
|
|
|
|
2024-04-15 06:39:17 +00:00
|
|
|
Deno.test("FakeTime causes Date.now() returns current fake time", () => {
|
2022-04-14 06:29:17 +00:00
|
|
|
const time: FakeTime = new FakeTime(9001);
|
|
|
|
const now = spy(_internals.Date, "now");
|
|
|
|
try {
|
2023-06-10 10:56:34 +00:00
|
|
|
try {
|
|
|
|
assertEquals(Date.now(), 9001);
|
|
|
|
assertEquals(now.calls.length, 0);
|
|
|
|
time.tick(1523);
|
|
|
|
assertEquals(Date.now(), 10524);
|
|
|
|
assertEquals(now.calls.length, 0);
|
|
|
|
} finally {
|
|
|
|
time.restore();
|
|
|
|
}
|
|
|
|
assertNotEquals(Date.now(), 10524);
|
|
|
|
assertEquals(now.calls.length, 1);
|
2022-04-14 06:29:17 +00:00
|
|
|
} finally {
|
|
|
|
now.restore();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2024-04-15 06:39:17 +00:00
|
|
|
Deno.test("FakeTime causes Date instance methods passthrough to real Date instance methods", () => {
|
2023-12-14 20:47:50 +00:00
|
|
|
using _time = new FakeTime();
|
|
|
|
const now = new Date("2020-05-25T05:00:00.12345Z");
|
|
|
|
assertEquals(now.toISOString(), "2020-05-25T05:00:00.123Z");
|
2022-04-26 13:34:42 +00:00
|
|
|
|
2023-12-14 20:47:50 +00:00
|
|
|
const func1 = spy(
|
|
|
|
_internals.Date.prototype,
|
|
|
|
"toISOString",
|
|
|
|
);
|
|
|
|
try {
|
|
|
|
now.toISOString();
|
|
|
|
assertSpyCall(func1, 0, {
|
|
|
|
args: [],
|
|
|
|
returned: "2020-05-25T05:00:00.123Z",
|
|
|
|
});
|
2024-01-04 22:26:55 +00:00
|
|
|
assertInstanceOf(func1.calls?.[0]?.self, _internals.Date);
|
2023-12-14 20:47:50 +00:00
|
|
|
} finally {
|
|
|
|
func1.restore();
|
|
|
|
}
|
2022-04-26 13:34:42 +00:00
|
|
|
|
2023-12-14 20:47:50 +00:00
|
|
|
const func2 = spy(
|
|
|
|
_internals.Date.prototype,
|
|
|
|
Symbol.toPrimitive,
|
|
|
|
);
|
|
|
|
try {
|
|
|
|
Number(now);
|
|
|
|
assertSpyCall(func2, 0, {
|
|
|
|
args: ["number"],
|
|
|
|
returned: 1590382800123,
|
|
|
|
});
|
2022-04-14 06:29:17 +00:00
|
|
|
} finally {
|
2023-12-14 20:47:50 +00:00
|
|
|
func2.restore();
|
2022-04-14 06:29:17 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2024-06-24 23:35:53 +00:00
|
|
|
Deno.test("FakeTime causes Date function to return the string representation of the current faked time", () => {
|
|
|
|
using _time = new FakeTime(24 * 60 * 60 * 1000);
|
|
|
|
assertMatch(Date(), /(Fri|Thu) Jan 0(1|2) 1970/);
|
|
|
|
});
|
|
|
|
|
2024-04-15 06:39:17 +00:00
|
|
|
Deno.test("FakeTime timeout functions unchanged if FakeTime is uninitialized", () => {
|
2022-04-14 06:29:17 +00:00
|
|
|
assertStrictEquals(setTimeout, _internals.setTimeout);
|
|
|
|
assertStrictEquals(clearTimeout, _internals.clearTimeout);
|
|
|
|
});
|
|
|
|
|
2024-04-15 06:39:17 +00:00
|
|
|
Deno.test("FakeTime timeout functions are fake if FakeTime is initialized", () => {
|
2023-12-14 20:47:50 +00:00
|
|
|
{
|
|
|
|
using _time: FakeTime = new FakeTime();
|
2022-04-14 06:29:17 +00:00
|
|
|
assertNotEquals(setTimeout, _internals.setTimeout);
|
|
|
|
assertNotEquals(clearTimeout, _internals.clearTimeout);
|
|
|
|
}
|
|
|
|
assertStrictEquals(setTimeout, _internals.setTimeout);
|
|
|
|
assertStrictEquals(clearTimeout, _internals.clearTimeout);
|
|
|
|
});
|
|
|
|
|
|
|
|
Deno.test("FakeTime only ticks forward when setting now or calling tick", () => {
|
2023-12-14 20:47:50 +00:00
|
|
|
using time: FakeTime = new FakeTime();
|
2022-04-14 06:29:17 +00:00
|
|
|
const start: number = Date.now();
|
|
|
|
|
2023-12-14 20:47:50 +00:00
|
|
|
assertEquals(Date.now(), start);
|
|
|
|
time.tick(5);
|
|
|
|
assertEquals(Date.now(), start + 5);
|
|
|
|
time.now = start + 1000;
|
|
|
|
assertEquals(Date.now(), start + 1000);
|
|
|
|
assert(_internals.Date.now() < start + 1000);
|
2022-04-14 06:29:17 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
Deno.test("FakeTime controls timeouts", () => {
|
2023-12-14 20:47:50 +00:00
|
|
|
using time: FakeTime = new FakeTime();
|
2022-04-14 06:29:17 +00:00
|
|
|
const start: number = Date.now();
|
|
|
|
const cb = spy(fromNow());
|
|
|
|
const expected: SpyCall[] = [];
|
|
|
|
|
2023-12-14 20:47:50 +00:00
|
|
|
setTimeout(cb, 1000);
|
|
|
|
time.tick(250);
|
|
|
|
assertEquals(cb.calls, expected);
|
|
|
|
time.tick(250);
|
|
|
|
assertEquals(cb.calls, expected);
|
|
|
|
time.tick(500);
|
|
|
|
expected.push({ args: [], returned: 1000 });
|
|
|
|
assertEquals(cb.calls, expected);
|
|
|
|
time.tick(2500);
|
|
|
|
assertEquals(cb.calls, expected);
|
|
|
|
|
|
|
|
setTimeout(cb, 1000, "a");
|
|
|
|
setTimeout(cb, 2000, "b");
|
|
|
|
setTimeout(cb, 1500, "c");
|
|
|
|
assertEquals(cb.calls, expected);
|
|
|
|
time.tick(2500);
|
|
|
|
expected.push({ args: ["a"], returned: 4500 });
|
|
|
|
expected.push({ args: ["c"], returned: 5000 });
|
|
|
|
expected.push({ args: ["b"], returned: 5500 });
|
|
|
|
assertEquals(cb.calls, expected);
|
|
|
|
|
|
|
|
setTimeout(cb, 1000, "a");
|
|
|
|
setTimeout(cb, 1500, "b");
|
|
|
|
const timeout: number = setTimeout(cb, 1750, "c");
|
|
|
|
setTimeout(cb, 2000, "d");
|
|
|
|
time.tick(1250);
|
|
|
|
expected.push({ args: ["a"], returned: 7000 });
|
|
|
|
assertEquals(cb.calls, expected);
|
|
|
|
assertEquals(Date.now(), start + 7250);
|
|
|
|
clearTimeout(timeout);
|
|
|
|
time.tick(500);
|
|
|
|
expected.push({ args: ["b"], returned: 7500 });
|
|
|
|
assertEquals(cb.calls, expected);
|
|
|
|
assertEquals(Date.now(), start + 7750);
|
|
|
|
time.tick(250);
|
|
|
|
expected.push({ args: ["d"], returned: 8000 });
|
|
|
|
assertEquals(cb.calls, expected);
|
2022-04-14 06:29:17 +00:00
|
|
|
});
|
|
|
|
|
2024-04-15 06:39:17 +00:00
|
|
|
Deno.test("FakeTime interval functions unchanged if FakeTime is uninitialized", () => {
|
2022-04-14 06:29:17 +00:00
|
|
|
assertStrictEquals(setInterval, _internals.setInterval);
|
|
|
|
assertStrictEquals(clearInterval, _internals.clearInterval);
|
|
|
|
});
|
|
|
|
|
2024-04-15 06:39:17 +00:00
|
|
|
Deno.test("FakeTime fakes interval functions", () => {
|
2023-12-14 20:47:50 +00:00
|
|
|
{
|
|
|
|
using _time: FakeTime = new FakeTime();
|
2022-04-14 06:29:17 +00:00
|
|
|
assertNotEquals(setInterval, _internals.setInterval);
|
|
|
|
assertNotEquals(clearInterval, _internals.clearInterval);
|
|
|
|
}
|
|
|
|
assertStrictEquals(setInterval, _internals.setInterval);
|
|
|
|
assertStrictEquals(clearInterval, _internals.clearInterval);
|
|
|
|
});
|
|
|
|
|
|
|
|
Deno.test("FakeTime controls intervals", () => {
|
2023-12-14 20:47:50 +00:00
|
|
|
using time: FakeTime = new FakeTime();
|
2022-04-14 06:29:17 +00:00
|
|
|
const cb = spy(fromNow());
|
|
|
|
const expected: SpyCall[] = [];
|
2023-12-14 20:47:50 +00:00
|
|
|
|
|
|
|
const interval: number = setInterval(cb, 1000);
|
|
|
|
time.tick(250);
|
|
|
|
assertEquals(cb.calls, expected);
|
|
|
|
time.tick(250);
|
|
|
|
assertEquals(cb.calls, expected);
|
|
|
|
time.tick(500);
|
|
|
|
expected.push({ args: [], returned: 1000 });
|
|
|
|
assertEquals(cb.calls, expected);
|
|
|
|
time.tick(2500);
|
|
|
|
expected.push({ args: [], returned: 2000 });
|
|
|
|
expected.push({ args: [], returned: 3000 });
|
|
|
|
assertEquals(cb.calls, expected);
|
|
|
|
|
|
|
|
clearInterval(interval);
|
|
|
|
time.tick(1000);
|
|
|
|
assertEquals(cb.calls, expected);
|
2022-04-14 06:29:17 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
Deno.test("FakeTime calls timeout and interval callbacks in correct order", () => {
|
2023-12-14 20:47:50 +00:00
|
|
|
using time: FakeTime = new FakeTime();
|
2022-04-14 06:29:17 +00:00
|
|
|
const cb = spy(fromNow());
|
|
|
|
const timeoutCb = spy(cb);
|
|
|
|
const intervalCb = spy(cb);
|
|
|
|
const expected: SpyCall[] = [];
|
|
|
|
const timeoutExpected: SpyCall[] = [];
|
|
|
|
const intervalExpected: SpyCall[] = [];
|
2023-12-14 20:47:50 +00:00
|
|
|
|
|
|
|
const interval: number = setInterval(intervalCb, 1000);
|
|
|
|
setTimeout(timeoutCb, 500);
|
|
|
|
time.tick(250);
|
|
|
|
assertEquals(intervalCb.calls, intervalExpected);
|
|
|
|
time.tick(250);
|
|
|
|
setTimeout(timeoutCb, 1000);
|
|
|
|
let expect: SpyCall = { args: [], returned: 500 };
|
|
|
|
expected.push(expect);
|
|
|
|
timeoutExpected.push(expect);
|
|
|
|
assertEquals(cb.calls, expected);
|
|
|
|
assertEquals(timeoutCb.calls, timeoutExpected);
|
|
|
|
assertEquals(cb.calls, expected);
|
|
|
|
assertEquals(intervalCb.calls, intervalExpected);
|
|
|
|
time.tick(500);
|
|
|
|
expect = { args: [], returned: 1000 };
|
|
|
|
expected.push(expect);
|
|
|
|
intervalExpected.push(expect);
|
|
|
|
assertEquals(cb.calls, expected);
|
|
|
|
assertEquals(intervalCb.calls, intervalExpected);
|
|
|
|
time.tick(2500);
|
|
|
|
expect = { args: [], returned: 1500 };
|
|
|
|
expected.push(expect);
|
|
|
|
timeoutExpected.push(expect);
|
|
|
|
expect = { args: [], returned: 2000 };
|
|
|
|
expected.push(expect);
|
|
|
|
intervalExpected.push(expect);
|
|
|
|
expect = { args: [], returned: 3000 };
|
|
|
|
expected.push(expect);
|
|
|
|
intervalExpected.push(expect);
|
|
|
|
assertEquals(cb.calls, expected);
|
|
|
|
assertEquals(timeoutCb.calls, timeoutExpected);
|
|
|
|
assertEquals(intervalCb.calls, intervalExpected);
|
|
|
|
|
|
|
|
clearInterval(interval);
|
|
|
|
time.tick(1000);
|
|
|
|
assertEquals(cb.calls, expected);
|
|
|
|
assertEquals(timeoutCb.calls, timeoutExpected);
|
|
|
|
assertEquals(intervalCb.calls, intervalExpected);
|
2022-04-14 06:29:17 +00:00
|
|
|
});
|
|
|
|
|
2024-04-15 06:39:17 +00:00
|
|
|
Deno.test("FakeTime.restoreFor() restores real time temporarily", async () => {
|
2023-12-14 20:47:50 +00:00
|
|
|
using time: FakeTime = new FakeTime();
|
2022-04-14 06:29:17 +00:00
|
|
|
const start: number = Date.now();
|
|
|
|
|
2023-12-14 20:47:50 +00:00
|
|
|
assertEquals(Date.now(), start);
|
|
|
|
time.tick(1000);
|
|
|
|
assertEquals(Date.now(), start + 1000);
|
|
|
|
assert(_internals.Date.now() < start + 1000);
|
|
|
|
await FakeTime.restoreFor(() => {
|
|
|
|
assert(Date.now() < start + 1000);
|
|
|
|
});
|
|
|
|
assertEquals(Date.now(), start + 1000);
|
|
|
|
assert(_internals.Date.now() < start + 1000);
|
2022-04-14 06:29:17 +00:00
|
|
|
});
|
|
|
|
|
2024-04-15 06:39:17 +00:00
|
|
|
Deno.test("FakeTime.restoreFor() restores real time and re-overridden atomically", async () => {
|
2023-12-14 20:47:50 +00:00
|
|
|
using _time: FakeTime = new FakeTime();
|
2023-08-14 09:33:35 +00:00
|
|
|
const fakeSetTimeout = setTimeout;
|
|
|
|
const actualSetTimeouts: (typeof setTimeout)[] = [];
|
|
|
|
|
2023-12-14 20:47:50 +00:00
|
|
|
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,
|
|
|
|
]);
|
2023-08-14 09:33:35 +00:00
|
|
|
});
|
|
|
|
|
2024-04-15 06:39:17 +00:00
|
|
|
Deno.test("FakeTime.restoreFor() returns promise that resolved to result of callback", async () => {
|
2023-12-14 20:47:50 +00:00
|
|
|
using _time: FakeTime = new FakeTime();
|
2023-08-14 09:33:35 +00:00
|
|
|
|
2023-12-14 20:47:50 +00:00
|
|
|
const resultSync = await FakeTime.restoreFor(() => "a");
|
|
|
|
assertEquals(resultSync, "a");
|
|
|
|
const resultAsync = await FakeTime.restoreFor(() => Promise.resolve("b"));
|
|
|
|
assertEquals(resultAsync, "b");
|
2023-08-14 09:33:35 +00:00
|
|
|
});
|
|
|
|
|
2024-04-15 06:39:17 +00:00
|
|
|
Deno.test("FakeTime.restoreFor() returns promise that rejected to error in callback", async () => {
|
2023-12-14 20:47:50 +00:00
|
|
|
using _time: FakeTime = new FakeTime();
|
2023-08-14 09:33:35 +00:00
|
|
|
|
2023-12-14 20:47:50 +00:00
|
|
|
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",
|
|
|
|
);
|
2023-08-14 09:33:35 +00:00
|
|
|
});
|
|
|
|
|
2024-04-15 06:39:17 +00:00
|
|
|
Deno.test("FakeTime.restoreFor() returns promise that rejected to TimeError if FakeTime is uninitialized", async () => {
|
2023-08-14 09:33:35 +00:00
|
|
|
await assertRejects(
|
|
|
|
() => FakeTime.restoreFor(() => {}),
|
|
|
|
TimeError,
|
2024-08-26 05:22:16 +00:00
|
|
|
"Cannot restore time: time is not faked",
|
2023-08-14 09:33:35 +00:00
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2024-04-15 06:39:17 +00:00
|
|
|
Deno.test("FakeTime.delay() uses real time", async () => {
|
2023-12-14 20:47:50 +00:00
|
|
|
using time: FakeTime = new FakeTime();
|
2022-04-14 06:29:17 +00:00
|
|
|
const start: number = Date.now();
|
|
|
|
|
2023-12-14 20:47:50 +00:00
|
|
|
assertEquals(Date.now(), start);
|
|
|
|
await time.delay(20);
|
|
|
|
assert(_internals.Date.now() >= start + 20);
|
|
|
|
assertEquals(Date.now(), start);
|
2022-04-14 06:29:17 +00:00
|
|
|
});
|
|
|
|
|
2024-04-15 06:39:17 +00:00
|
|
|
Deno.test("FakeTime.delay() runs all microtasks before resolving", async () => {
|
2023-12-14 20:47:50 +00:00
|
|
|
using time: FakeTime = new FakeTime();
|
|
|
|
|
|
|
|
const seq = [];
|
|
|
|
queueMicrotask(() => seq.push(2));
|
|
|
|
queueMicrotask(() => seq.push(3));
|
|
|
|
seq.push(1);
|
|
|
|
await time.delay(20);
|
|
|
|
seq.push(4);
|
|
|
|
assertEquals(seq, [1, 2, 3, 4]);
|
2022-04-14 06:29:17 +00:00
|
|
|
});
|
|
|
|
|
2024-04-15 06:39:17 +00:00
|
|
|
Deno.test("FakeTime.delay() works with abort", async () => {
|
2023-12-14 20:47:50 +00:00
|
|
|
using time: FakeTime = new FakeTime();
|
|
|
|
|
|
|
|
const seq = [];
|
|
|
|
const abort = new AbortController();
|
|
|
|
const { signal } = abort;
|
|
|
|
const delayedPromise = time.delay(100, { signal });
|
|
|
|
seq.push(1);
|
|
|
|
await FakeTime.restoreFor(() => {
|
|
|
|
setTimeout(() => {
|
|
|
|
seq.push(2);
|
|
|
|
abort.abort();
|
|
|
|
}, 0);
|
|
|
|
});
|
|
|
|
await assertRejects(
|
|
|
|
() => delayedPromise,
|
|
|
|
DOMException,
|
|
|
|
"Delay was aborted",
|
|
|
|
);
|
|
|
|
seq.push(3);
|
|
|
|
assertEquals(seq, [1, 2, 3]);
|
2022-04-14 06:29:17 +00:00
|
|
|
});
|
|
|
|
|
2024-04-15 06:39:17 +00:00
|
|
|
Deno.test("FakeTime.runMicrotasks() runs all microtasks before resolving", async () => {
|
2023-12-14 20:47:50 +00:00
|
|
|
using time: FakeTime = new FakeTime();
|
2022-04-14 06:29:17 +00:00
|
|
|
const start: number = Date.now();
|
|
|
|
|
2023-12-14 20:47:50 +00:00
|
|
|
const seq = [];
|
|
|
|
queueMicrotask(() => seq.push(2));
|
|
|
|
queueMicrotask(() => seq.push(3));
|
|
|
|
seq.push(1);
|
|
|
|
await time.runMicrotasks();
|
|
|
|
seq.push(4);
|
|
|
|
assertEquals(seq, [1, 2, 3, 4]);
|
|
|
|
assertEquals(Date.now(), start);
|
2022-04-14 06:29:17 +00:00
|
|
|
});
|
|
|
|
|
2024-04-15 06:39:17 +00:00
|
|
|
Deno.test("FakeTime.tickAsync() runs all microtasks and runs timers if ticks past due", async () => {
|
2023-12-14 20:47:50 +00:00
|
|
|
using time: FakeTime = new FakeTime();
|
2022-04-14 06:29:17 +00:00
|
|
|
const start: number = Date.now();
|
|
|
|
const cb = spy(fromNow());
|
|
|
|
const expected: SpyCall[] = [];
|
|
|
|
const seq: number[] = [];
|
|
|
|
|
2023-12-14 20:47:50 +00:00
|
|
|
setTimeout(cb, 1000);
|
|
|
|
queueMicrotask(() => seq.push(2));
|
|
|
|
queueMicrotask(() => seq.push(3));
|
|
|
|
seq.push(1);
|
|
|
|
await time.tickAsync(250);
|
|
|
|
seq.push(4);
|
|
|
|
assertEquals(cb.calls, expected);
|
|
|
|
await time.tickAsync(250);
|
|
|
|
assertEquals(cb.calls, expected);
|
|
|
|
queueMicrotask(() => seq.push(6));
|
|
|
|
seq.push(5);
|
|
|
|
await time.tickAsync(500);
|
|
|
|
seq.push(7);
|
|
|
|
expected.push({ args: [], returned: 1000 });
|
|
|
|
assertEquals(cb.calls, expected);
|
|
|
|
assertEquals(Date.now(), start + 1000);
|
|
|
|
assertEquals(seq, [1, 2, 3, 4, 5, 6, 7]);
|
2022-04-14 06:29:17 +00:00
|
|
|
});
|
|
|
|
|
2024-04-15 06:39:17 +00:00
|
|
|
Deno.test("FakeTime.next() runs next timer without running microtasks", async () => {
|
2023-12-14 20:47:50 +00:00
|
|
|
using time: FakeTime = new FakeTime();
|
2022-04-14 06:29:17 +00:00
|
|
|
const start: number = Date.now();
|
|
|
|
const cb = spy(fromNow());
|
|
|
|
const seq: number[] = [];
|
|
|
|
|
2023-12-14 20:47:50 +00:00
|
|
|
// Callback is called by `next`.
|
|
|
|
setTimeout(cb, 1000);
|
|
|
|
queueMicrotask(() => seq.push(3));
|
|
|
|
queueMicrotask(() => seq.push(4));
|
|
|
|
seq.push(1);
|
|
|
|
const hasNextTimer = time.next();
|
|
|
|
seq.push(2);
|
|
|
|
|
|
|
|
assertEquals(hasNextTimer, true);
|
|
|
|
const expectedCalls = [{ args: [] as [], returned: 1000 }];
|
|
|
|
assertEquals(cb.calls, expectedCalls);
|
|
|
|
assertEquals(Date.now(), start + 1000);
|
|
|
|
await time.runMicrotasks();
|
|
|
|
|
|
|
|
// Callback is already called before `next` called.
|
|
|
|
queueMicrotask(() => seq.push(7));
|
|
|
|
queueMicrotask(() => seq.push(8));
|
|
|
|
seq.push(5);
|
|
|
|
const hasNextTimerAfterCalled = time.next();
|
|
|
|
seq.push(6);
|
|
|
|
await time.runMicrotasks();
|
|
|
|
|
|
|
|
assertEquals(hasNextTimerAfterCalled, false);
|
|
|
|
assertEquals(cb.calls, expectedCalls);
|
|
|
|
assertEquals(Date.now(), start + 1000);
|
|
|
|
|
|
|
|
// Callbacks are cleared before `next` called.
|
|
|
|
const id1 = setTimeout(cb, 1000);
|
|
|
|
const id2 = setTimeout(cb, 2000);
|
|
|
|
clearTimeout(id1);
|
|
|
|
clearTimeout(id2);
|
|
|
|
queueMicrotask(() => seq.push(11));
|
|
|
|
queueMicrotask(() => seq.push(12));
|
|
|
|
seq.push(9);
|
|
|
|
const hasNextTimerAfterCleared = time.next();
|
|
|
|
seq.push(10);
|
|
|
|
await time.runMicrotasks();
|
|
|
|
|
|
|
|
assertEquals(hasNextTimerAfterCleared, false);
|
|
|
|
assertEquals(cb.calls, expectedCalls);
|
|
|
|
assertEquals(Date.now(), start + 1000);
|
|
|
|
|
|
|
|
// Callback is partially cleared before `next` called.
|
|
|
|
const id3 = setTimeout(cb, 1500);
|
|
|
|
setTimeout(cb, 1500);
|
|
|
|
clearTimeout(id3);
|
|
|
|
queueMicrotask(() => seq.push(15));
|
|
|
|
queueMicrotask(() => seq.push(16));
|
|
|
|
seq.push(13);
|
|
|
|
const hasNextTimerAfterPartiallyCleared = time.next();
|
|
|
|
seq.push(14);
|
|
|
|
await time.runMicrotasks();
|
|
|
|
|
|
|
|
assertEquals(hasNextTimerAfterPartiallyCleared, true);
|
|
|
|
const expectedCalls2 = [
|
|
|
|
{ args: [] as [], returned: 1000 },
|
|
|
|
{ args: [] as [], returned: 1000 + 1500 },
|
|
|
|
];
|
|
|
|
assertEquals(cb.calls, expectedCalls2);
|
|
|
|
assertEquals(Date.now(), start + 1000 + 1500);
|
|
|
|
|
|
|
|
assertEquals(seq, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
|
2022-04-14 06:29:17 +00:00
|
|
|
});
|
|
|
|
|
2024-04-15 06:39:17 +00:00
|
|
|
Deno.test("FakeTime.nextAsync() runs all microtasks and next timer", async () => {
|
2023-12-14 20:47:50 +00:00
|
|
|
using time: FakeTime = new FakeTime();
|
2022-04-14 06:29:17 +00:00
|
|
|
const start: number = Date.now();
|
|
|
|
const cb = spy(fromNow());
|
|
|
|
const seq: number[] = [];
|
|
|
|
|
2023-12-14 20:47:50 +00:00
|
|
|
// Callback is called by `nextAsync`.
|
|
|
|
setTimeout(cb, 1000);
|
|
|
|
queueMicrotask(() => seq.push(2));
|
|
|
|
queueMicrotask(() => seq.push(3));
|
|
|
|
seq.push(1);
|
|
|
|
const hasNextTimer = await time.nextAsync();
|
|
|
|
seq.push(4);
|
|
|
|
|
|
|
|
assertEquals(hasNextTimer, true);
|
|
|
|
const expectedCalls = [{ args: [] as [], returned: 1000 }];
|
|
|
|
assertEquals(cb.calls, expectedCalls);
|
|
|
|
assertEquals(Date.now(), start + 1000);
|
|
|
|
|
|
|
|
// Callback is already called before `nextAsync` called.
|
|
|
|
queueMicrotask(() => seq.push(6));
|
|
|
|
queueMicrotask(() => seq.push(7));
|
|
|
|
seq.push(5);
|
|
|
|
const hasNextTimerAfterCalled = await time.nextAsync();
|
|
|
|
seq.push(8);
|
|
|
|
|
|
|
|
assertEquals(hasNextTimerAfterCalled, false);
|
|
|
|
assertEquals(cb.calls, expectedCalls);
|
|
|
|
assertEquals(Date.now(), start + 1000);
|
|
|
|
|
|
|
|
// Callbacks are cleared before `nextAsync` called.
|
|
|
|
const id1 = setTimeout(cb, 1000);
|
|
|
|
const id2 = setTimeout(cb, 2000);
|
|
|
|
clearTimeout(id1);
|
|
|
|
clearTimeout(id2);
|
|
|
|
queueMicrotask(() => seq.push(10));
|
|
|
|
queueMicrotask(() => seq.push(11));
|
|
|
|
seq.push(9);
|
|
|
|
const hasNextTimerAfterCleared = await time.nextAsync();
|
|
|
|
seq.push(12);
|
|
|
|
|
|
|
|
assertEquals(hasNextTimerAfterCleared, false);
|
|
|
|
assertEquals(cb.calls, expectedCalls);
|
|
|
|
assertEquals(Date.now(), start + 1000);
|
|
|
|
|
|
|
|
// Callback is partially cleared before `nextAsync` called.
|
|
|
|
const id3 = setTimeout(cb, 1500);
|
|
|
|
setTimeout(cb, 1500);
|
|
|
|
clearTimeout(id3);
|
|
|
|
queueMicrotask(() => seq.push(14));
|
|
|
|
queueMicrotask(() => seq.push(15));
|
|
|
|
seq.push(13);
|
|
|
|
const hasNextTimerAfterPartiallyCleared = await time.nextAsync();
|
|
|
|
seq.push(16);
|
|
|
|
await time.runMicrotasks();
|
|
|
|
|
|
|
|
assertEquals(hasNextTimerAfterPartiallyCleared, true);
|
|
|
|
const expectedCalls2 = [
|
|
|
|
{ args: [] as [], returned: 1000 },
|
|
|
|
{ args: [] as [], returned: 1000 + 1500 },
|
|
|
|
];
|
|
|
|
assertEquals(cb.calls, expectedCalls2);
|
|
|
|
assertEquals(Date.now(), start + 1000 + 1500);
|
|
|
|
|
|
|
|
assertEquals(seq, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
|
2022-04-14 06:29:17 +00:00
|
|
|
});
|
|
|
|
|
2024-04-15 06:39:17 +00:00
|
|
|
Deno.test("FakeTime.runAll() runs all timers without running microtasks", async () => {
|
2023-12-14 20:47:50 +00:00
|
|
|
using time: FakeTime = new FakeTime();
|
2022-04-14 06:29:17 +00:00
|
|
|
const start: number = Date.now();
|
|
|
|
const cb = spy(fromNow());
|
|
|
|
const seq: number[] = [];
|
|
|
|
|
2023-12-14 20:47:50 +00:00
|
|
|
setTimeout(cb, 1000);
|
|
|
|
setTimeout(cb, 1500);
|
|
|
|
queueMicrotask(() => seq.push(3));
|
|
|
|
queueMicrotask(() => seq.push(4));
|
|
|
|
seq.push(1);
|
|
|
|
time.runAll();
|
|
|
|
seq.push(2);
|
|
|
|
const expectedCalls = [
|
|
|
|
{ args: [] as [], returned: 1000 },
|
|
|
|
{ args: [] as [], returned: 1500 },
|
|
|
|
];
|
|
|
|
assertEquals(cb.calls, expectedCalls);
|
|
|
|
assertEquals(Date.now(), start + 1500);
|
|
|
|
await time.runMicrotasks();
|
|
|
|
|
|
|
|
queueMicrotask(() => seq.push(7));
|
|
|
|
queueMicrotask(() => seq.push(8));
|
|
|
|
seq.push(5);
|
|
|
|
time.runAll();
|
|
|
|
seq.push(6);
|
|
|
|
await time.runMicrotasks();
|
|
|
|
|
|
|
|
assertEquals(cb.calls, expectedCalls);
|
|
|
|
assertEquals(Date.now(), start + 1500);
|
|
|
|
assertEquals(seq, [1, 2, 3, 4, 5, 6, 7, 8]);
|
2022-04-14 06:29:17 +00:00
|
|
|
});
|
|
|
|
|
2024-04-15 06:39:17 +00:00
|
|
|
Deno.test("FakeTime.runAllAsync() runs all microtasks and timers", async () => {
|
2023-12-14 20:47:50 +00:00
|
|
|
using time: FakeTime = new FakeTime();
|
2022-04-14 06:29:17 +00:00
|
|
|
const start: number = Date.now();
|
|
|
|
const cb = spy(fromNow());
|
|
|
|
const seq: number[] = [];
|
|
|
|
|
2023-12-14 20:47:50 +00:00
|
|
|
setTimeout(cb, 1000);
|
|
|
|
setTimeout(cb, 1500);
|
|
|
|
queueMicrotask(() => seq.push(2));
|
|
|
|
queueMicrotask(() => seq.push(3));
|
|
|
|
seq.push(1);
|
|
|
|
await time.runAllAsync();
|
|
|
|
seq.push(4);
|
|
|
|
const expectedCalls = [
|
|
|
|
{ args: [] as [], returned: 1000 },
|
|
|
|
{ args: [] as [], returned: 1500 },
|
|
|
|
];
|
|
|
|
assertEquals(cb.calls, expectedCalls);
|
|
|
|
assertEquals(Date.now(), start + 1500);
|
|
|
|
|
|
|
|
queueMicrotask(() => seq.push(6));
|
|
|
|
queueMicrotask(() => seq.push(7));
|
|
|
|
seq.push(5);
|
|
|
|
await time.runAllAsync();
|
|
|
|
seq.push(8);
|
|
|
|
|
|
|
|
assertEquals(cb.calls, expectedCalls);
|
|
|
|
assertEquals(Date.now(), start + 1500);
|
|
|
|
assertEquals(seq, [1, 2, 3, 4, 5, 6, 7, 8]);
|
2022-04-14 06:29:17 +00:00
|
|
|
});
|
2023-06-10 10:56:34 +00:00
|
|
|
|
|
|
|
const Date_ = Date;
|
|
|
|
|
|
|
|
Deno.test("Date from FakeTime is structured cloneable", () => {
|
2023-12-14 20:47:50 +00:00
|
|
|
using _time: FakeTime = new FakeTime();
|
|
|
|
const date: Date = new Date();
|
|
|
|
assert(date instanceof Date);
|
|
|
|
assert(date instanceof Date_);
|
|
|
|
const cloned: Date = structuredClone(date);
|
|
|
|
assertEquals(cloned.getTime(), date.getTime());
|
|
|
|
assert(date instanceof Date);
|
|
|
|
assert(cloned instanceof Date_);
|
2023-06-10 10:56:34 +00:00
|
|
|
});
|
2024-06-25 05:15:25 +00:00
|
|
|
|
|
|
|
Deno.test("new FakeTime() throws if the time is already faked", () => {
|
|
|
|
using _time = new FakeTime();
|
|
|
|
assertThrows(() => new FakeTime());
|
|
|
|
});
|
2024-06-25 06:51:24 +00:00
|
|
|
|
|
|
|
Deno.test("Faked timer functions throws when called after FakeTime is restored", () => {
|
|
|
|
let fakeSetTimeout: typeof setTimeout;
|
|
|
|
let fakeClearTimeout: typeof clearTimeout;
|
|
|
|
let fakeSetInterval: typeof setInterval;
|
|
|
|
let fakeClearInterval: typeof clearInterval;
|
|
|
|
{
|
|
|
|
using _time: FakeTime = new FakeTime();
|
|
|
|
fakeSetTimeout = setTimeout;
|
|
|
|
fakeClearTimeout = clearTimeout;
|
|
|
|
fakeSetInterval = setInterval;
|
|
|
|
fakeClearInterval = clearInterval;
|
|
|
|
}
|
2024-07-24 06:48:23 +00:00
|
|
|
assertThrows(
|
|
|
|
() => fakeSetTimeout(() => {}, 0),
|
|
|
|
TimeError,
|
2024-08-26 05:22:16 +00:00
|
|
|
"Cannot set timeout: time is not faked",
|
|
|
|
);
|
|
|
|
assertThrows(
|
|
|
|
() => fakeClearTimeout(0),
|
|
|
|
TimeError,
|
|
|
|
"Cannot clear timeout: time is not faked",
|
2024-07-24 06:48:23 +00:00
|
|
|
);
|
|
|
|
assertThrows(
|
|
|
|
() => fakeSetInterval(() => {}, 0),
|
|
|
|
TimeError,
|
2024-08-26 05:22:16 +00:00
|
|
|
"Cannot set interval: time is not faked",
|
|
|
|
);
|
|
|
|
assertThrows(
|
|
|
|
() => fakeClearInterval(0),
|
|
|
|
TimeError,
|
|
|
|
"Cannot clear interval: time is not faked",
|
2024-07-24 06:48:23 +00:00
|
|
|
);
|
2024-06-25 06:51:24 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
Deno.test("Faked Date.now returns real time after FakeTime is restored", () => {
|
|
|
|
let fakeDateNow: typeof Date.now;
|
|
|
|
{
|
|
|
|
using _time: FakeTime = new FakeTime();
|
|
|
|
fakeDateNow = Date.now;
|
|
|
|
}
|
|
|
|
assertAlmostEquals(Date.now(), fakeDateNow());
|
|
|
|
});
|
|
|
|
|
|
|
|
Deno.test("FakeTime can be constructed with number, Date, or string", () => {
|
|
|
|
{
|
|
|
|
using _time = new FakeTime(1000);
|
|
|
|
assertEquals(Date.now(), 1000);
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
using _time = new FakeTime(new Date(2000));
|
|
|
|
assertEquals(Date.now(), 2000);
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
using _time = new FakeTime("Thu Jan 01 1970 00:00:03 GMT+0000");
|
|
|
|
assertEquals(Date.now(), 3000);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
Deno.test("FakeTime throws when NaN is provided", () => {
|
2024-08-26 05:22:16 +00:00
|
|
|
assertThrows(
|
|
|
|
() => new FakeTime(NaN),
|
|
|
|
TypeError,
|
|
|
|
"Cannot construct FakeTime: invalid start time NaN",
|
|
|
|
);
|
2024-06-25 06:51:24 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
Deno.test("FakeTime.restore() throws when the time is already restored", () => {
|
|
|
|
const _time = new FakeTime();
|
|
|
|
FakeTime.restore();
|
2024-08-26 05:22:16 +00:00
|
|
|
assertThrows(
|
|
|
|
() => FakeTime.restore(),
|
|
|
|
TimeError,
|
|
|
|
"Cannot restore time: time is already restored",
|
|
|
|
);
|
2024-06-25 06:51:24 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
Deno.test("time.restore() throws when the time is already restored", () => {
|
|
|
|
const time = new FakeTime();
|
|
|
|
time.restore();
|
2024-08-26 05:22:16 +00:00
|
|
|
assertThrows(
|
|
|
|
() => time.restore(),
|
|
|
|
TimeError,
|
|
|
|
"Cannot restore time: time is already restored",
|
|
|
|
);
|
2024-06-25 06:51:24 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
Deno.test("time.now = N throws when N < time.now", () => {
|
|
|
|
using time = new FakeTime(1000);
|
|
|
|
assertThrows(
|
|
|
|
() => {
|
|
|
|
time.now = 999;
|
|
|
|
},
|
2024-07-29 04:48:44 +00:00
|
|
|
RangeError,
|
2024-08-26 05:22:16 +00:00
|
|
|
"Cannot set current time in the past, time must be >= 1000: received 999",
|
2024-06-25 06:51:24 +00:00
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
Deno.test("time.start returns the started time of the fake time", () => {
|
|
|
|
using time = new FakeTime(1000);
|
|
|
|
time.now = 2000;
|
|
|
|
assertEquals(time.start, 1000);
|
|
|
|
});
|
2024-07-22 04:07:48 +00:00
|
|
|
|
|
|
|
Deno.test("FakeTime doesn't affect AbortSignal.timeout unchanged if uninitialized", () => {
|
|
|
|
assertStrictEquals(AbortSignal.timeout, _internals.AbortSignalTimeout);
|
|
|
|
});
|
|
|
|
|
|
|
|
Deno.test("FakeTime fakes AbortSignal.timeout", () => {
|
|
|
|
{
|
|
|
|
using _time = new FakeTime(9001);
|
|
|
|
assertNotEquals(AbortSignal.timeout, _internals.AbortSignalTimeout);
|
|
|
|
}
|
|
|
|
assertStrictEquals(AbortSignal.timeout, _internals.AbortSignalTimeout);
|
|
|
|
});
|
|
|
|
|
|
|
|
Deno.test("FakeTime controls AbortSignal.timeout", () => {
|
|
|
|
using time: FakeTime = new FakeTime();
|
|
|
|
const cb = spy(fromNow());
|
|
|
|
const expected: SpyCall[] = [];
|
|
|
|
|
|
|
|
const signal = AbortSignal.timeout(1000);
|
|
|
|
signal.onabort = () => cb();
|
|
|
|
time.tick(250);
|
|
|
|
assertEquals(cb.calls, expected);
|
|
|
|
time.tick(250);
|
|
|
|
assertEquals(cb.calls, expected);
|
|
|
|
time.tick(500);
|
|
|
|
expected.push({ args: [], returned: 1000 });
|
|
|
|
assertEquals(cb.calls, expected);
|
|
|
|
time.tick(2500);
|
|
|
|
assertEquals(cb.calls, expected);
|
|
|
|
|
|
|
|
assertEquals(signal.aborted, true);
|
|
|
|
assertInstanceOf(signal.reason, DOMException);
|
|
|
|
assertEquals(signal.reason.name, "TimeoutError");
|
|
|
|
assertEquals(signal.reason.message, "Signal timed out.");
|
|
|
|
|
|
|
|
const signalA = AbortSignal.timeout(1000);
|
|
|
|
signalA.addEventListener("abort", () => cb("a"));
|
|
|
|
const signalB = AbortSignal.timeout(2000);
|
|
|
|
signalB.addEventListener("abort", () => cb("b"));
|
|
|
|
const signalC = AbortSignal.timeout(1500);
|
|
|
|
signalC.addEventListener("abort", () => cb("c"));
|
|
|
|
assertEquals(cb.calls, expected);
|
|
|
|
time.tick(2500);
|
|
|
|
expected.push({ args: ["a"], returned: 4500 });
|
|
|
|
expected.push({ args: ["c"], returned: 5000 });
|
|
|
|
expected.push({ args: ["b"], returned: 5500 });
|
|
|
|
assertEquals(cb.calls, expected);
|
|
|
|
});
|
|
|
|
|
|
|
|
// https://github.com/denoland/std/issues/5499
|
|
|
|
Deno.test("FakeTime regression test for issue #5499", async () => {
|
|
|
|
using t = new FakeTime();
|
|
|
|
const p = deadline(delay(1_000), 10);
|
|
|
|
let state: "pending" | "rejected" | "fulfilled" = "pending";
|
|
|
|
p.then(() => {
|
|
|
|
state = "fulfilled";
|
|
|
|
}).catch(() => {
|
|
|
|
state = "rejected";
|
|
|
|
});
|
|
|
|
await t.tickAsync(10);
|
|
|
|
await t.runMicrotasks();
|
|
|
|
assertEquals(state, "rejected");
|
|
|
|
});
|