mirror of
https://github.com/denoland/std.git
synced 2024-11-22 04:59:05 +00:00
564 lines
15 KiB
TypeScript
564 lines
15 KiB
TypeScript
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
|
import {
|
|
assert,
|
|
assertEquals,
|
|
assertInstanceOf,
|
|
assertNotEquals,
|
|
assertRejects,
|
|
assertStrictEquals,
|
|
} from "./asserts.ts";
|
|
import { FakeTime } from "./time.ts";
|
|
import { _internals } from "./_time.ts";
|
|
import { assertSpyCall, spy, SpyCall } from "./mock.ts";
|
|
|
|
function fromNow(): () => number {
|
|
const start: number = Date.now();
|
|
return () => Date.now() - start;
|
|
}
|
|
|
|
Deno.test("Date unchanged if FakeTime is uninitialized", () => {
|
|
assertStrictEquals(Date, _internals.Date);
|
|
});
|
|
|
|
Deno.test("Date is fake if FakeTime is initialized", () => {
|
|
const time = new FakeTime(9001);
|
|
try {
|
|
assertNotEquals(Date, _internals.Date);
|
|
} finally {
|
|
time.restore();
|
|
}
|
|
assertStrictEquals(Date, _internals.Date);
|
|
});
|
|
|
|
Deno.test("Fake Date parse and UTC behave the same", () => {
|
|
const expectedUTC = Date.UTC(96, 1, 2, 3, 4, 5);
|
|
const expectedParse = Date.parse("04 Dec 1995 00:12:00 GMT");
|
|
|
|
const time = new FakeTime();
|
|
try {
|
|
assertEquals(
|
|
Date.UTC(96, 1, 2, 3, 4, 5),
|
|
expectedUTC,
|
|
);
|
|
assertEquals(
|
|
Date.parse("04 Dec 1995 00:12:00 GMT"),
|
|
expectedParse,
|
|
);
|
|
} finally {
|
|
time.restore();
|
|
}
|
|
});
|
|
|
|
Deno.test("Fake Date.now returns current fake time", () => {
|
|
const time: FakeTime = new FakeTime(9001);
|
|
const now = spy(_internals.Date, "now");
|
|
try {
|
|
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);
|
|
} finally {
|
|
now.restore();
|
|
}
|
|
});
|
|
|
|
Deno.test("Fake Date instance methods passthrough to real Date instance methods", () => {
|
|
const time = new FakeTime();
|
|
try {
|
|
const now = new Date("2020-05-25T05:00:00.12345Z");
|
|
assertEquals(now.toISOString(), "2020-05-25T05:00:00.123Z");
|
|
|
|
const func1 = spy(
|
|
_internals.Date.prototype,
|
|
"toISOString",
|
|
);
|
|
try {
|
|
now.toISOString();
|
|
assertSpyCall(func1, 0, {
|
|
args: [],
|
|
returned: "2020-05-25T05:00:00.123Z",
|
|
});
|
|
assertInstanceOf(func1.calls[0].self, _internals.Date);
|
|
} finally {
|
|
func1.restore();
|
|
}
|
|
|
|
const func2 = spy(
|
|
_internals.Date.prototype,
|
|
Symbol.toPrimitive,
|
|
);
|
|
try {
|
|
Number(now);
|
|
assertSpyCall(func2, 0, {
|
|
args: ["number"],
|
|
returned: 1590382800123,
|
|
});
|
|
} finally {
|
|
func2.restore();
|
|
}
|
|
} finally {
|
|
time.restore();
|
|
}
|
|
});
|
|
|
|
Deno.test("timeout functions unchanged if FakeTime is uninitialized", () => {
|
|
assertStrictEquals(setTimeout, _internals.setTimeout);
|
|
assertStrictEquals(clearTimeout, _internals.clearTimeout);
|
|
});
|
|
|
|
Deno.test("timeout functions are fake if FakeTime is initialized", () => {
|
|
const time: FakeTime = new FakeTime();
|
|
try {
|
|
assertNotEquals(setTimeout, _internals.setTimeout);
|
|
assertNotEquals(clearTimeout, _internals.clearTimeout);
|
|
} finally {
|
|
time.restore();
|
|
}
|
|
assertStrictEquals(setTimeout, _internals.setTimeout);
|
|
assertStrictEquals(clearTimeout, _internals.clearTimeout);
|
|
});
|
|
|
|
Deno.test("FakeTime only ticks forward when setting now or calling tick", () => {
|
|
const time: FakeTime = new FakeTime();
|
|
const start: number = Date.now();
|
|
|
|
try {
|
|
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);
|
|
} finally {
|
|
time.restore();
|
|
}
|
|
});
|
|
|
|
Deno.test("FakeTime controls timeouts", () => {
|
|
const time: FakeTime = new FakeTime();
|
|
const start: number = Date.now();
|
|
const cb = spy(fromNow());
|
|
const expected: SpyCall[] = [];
|
|
|
|
try {
|
|
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);
|
|
} finally {
|
|
time.restore();
|
|
}
|
|
});
|
|
|
|
Deno.test("interval functions unchanged if FakeTime is uninitialized", () => {
|
|
assertStrictEquals(setInterval, _internals.setInterval);
|
|
assertStrictEquals(clearInterval, _internals.clearInterval);
|
|
});
|
|
|
|
Deno.test("interval functions are fake if FakeTime is initialized", () => {
|
|
const time: FakeTime = new FakeTime();
|
|
try {
|
|
assertNotEquals(setInterval, _internals.setInterval);
|
|
assertNotEquals(clearInterval, _internals.clearInterval);
|
|
} finally {
|
|
time.restore();
|
|
}
|
|
assertStrictEquals(setInterval, _internals.setInterval);
|
|
assertStrictEquals(clearInterval, _internals.clearInterval);
|
|
});
|
|
|
|
Deno.test("FakeTime controls intervals", () => {
|
|
const time: FakeTime = new FakeTime();
|
|
const cb = spy(fromNow());
|
|
const expected: SpyCall[] = [];
|
|
try {
|
|
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);
|
|
} finally {
|
|
time.restore();
|
|
}
|
|
});
|
|
|
|
Deno.test("FakeTime calls timeout and interval callbacks in correct order", () => {
|
|
const time: FakeTime = new FakeTime();
|
|
const cb = spy(fromNow());
|
|
const timeoutCb = spy(cb);
|
|
const intervalCb = spy(cb);
|
|
const expected: SpyCall[] = [];
|
|
const timeoutExpected: SpyCall[] = [];
|
|
const intervalExpected: SpyCall[] = [];
|
|
try {
|
|
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);
|
|
} finally {
|
|
time.restore();
|
|
}
|
|
});
|
|
|
|
Deno.test("FakeTime restoreFor restores real time temporarily", async () => {
|
|
const time: FakeTime = new FakeTime();
|
|
const start: number = Date.now();
|
|
|
|
try {
|
|
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);
|
|
} finally {
|
|
time.restore();
|
|
}
|
|
});
|
|
|
|
Deno.test("delay uses real time", async () => {
|
|
const time: FakeTime = new FakeTime();
|
|
const start: number = Date.now();
|
|
|
|
try {
|
|
assertEquals(Date.now(), start);
|
|
await time.delay(20);
|
|
assert(_internals.Date.now() >= start + 20);
|
|
assertEquals(Date.now(), start);
|
|
} finally {
|
|
time.restore();
|
|
}
|
|
});
|
|
|
|
Deno.test("delay runs all microtasks before resolving", async () => {
|
|
const time: FakeTime = new FakeTime();
|
|
|
|
try {
|
|
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]);
|
|
} finally {
|
|
time.restore();
|
|
}
|
|
});
|
|
|
|
Deno.test("delay with abort", async () => {
|
|
const time: FakeTime = new FakeTime();
|
|
|
|
try {
|
|
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]);
|
|
} finally {
|
|
time.restore();
|
|
}
|
|
});
|
|
|
|
Deno.test("runMicrotasks runs all microtasks before resolving", async () => {
|
|
const time: FakeTime = new FakeTime();
|
|
const start: number = Date.now();
|
|
|
|
try {
|
|
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);
|
|
} finally {
|
|
time.restore();
|
|
}
|
|
});
|
|
|
|
Deno.test("tickAsync runs all microtasks and runs timers if ticks past due", async () => {
|
|
const time: FakeTime = new FakeTime();
|
|
const start: number = Date.now();
|
|
const cb = spy(fromNow());
|
|
const expected: SpyCall[] = [];
|
|
const seq: number[] = [];
|
|
|
|
try {
|
|
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]);
|
|
} finally {
|
|
time.restore();
|
|
}
|
|
});
|
|
|
|
Deno.test("runNext runs next timer without running microtasks", async () => {
|
|
const time: FakeTime = new FakeTime();
|
|
const start: number = Date.now();
|
|
const cb = spy(fromNow());
|
|
const seq: number[] = [];
|
|
|
|
try {
|
|
setTimeout(cb, 1000);
|
|
queueMicrotask(() => seq.push(3));
|
|
queueMicrotask(() => seq.push(4));
|
|
seq.push(1);
|
|
time.next();
|
|
seq.push(2);
|
|
const expectedCalls = [{ args: [] as [], returned: 1000 }];
|
|
assertEquals(cb.calls, expectedCalls);
|
|
assertEquals(Date.now(), start + 1000);
|
|
await time.runMicrotasks();
|
|
|
|
queueMicrotask(() => seq.push(7));
|
|
queueMicrotask(() => seq.push(8));
|
|
seq.push(5);
|
|
time.next();
|
|
seq.push(6);
|
|
await time.runMicrotasks();
|
|
|
|
assertEquals(cb.calls, expectedCalls);
|
|
assertEquals(Date.now(), start + 1000);
|
|
assertEquals(seq, [1, 2, 3, 4, 5, 6, 7, 8]);
|
|
} finally {
|
|
time.restore();
|
|
}
|
|
});
|
|
|
|
Deno.test("runNextAsync runs all microtasks and next timer", async () => {
|
|
const time: FakeTime = new FakeTime();
|
|
const start: number = Date.now();
|
|
const cb = spy(fromNow());
|
|
const seq: number[] = [];
|
|
|
|
try {
|
|
setTimeout(cb, 1000);
|
|
queueMicrotask(() => seq.push(2));
|
|
queueMicrotask(() => seq.push(3));
|
|
seq.push(1);
|
|
await time.nextAsync();
|
|
seq.push(4);
|
|
const expectedCalls = [{ args: [] as [], returned: 1000 }];
|
|
assertEquals(cb.calls, expectedCalls);
|
|
assertEquals(Date.now(), start + 1000);
|
|
|
|
queueMicrotask(() => seq.push(6));
|
|
queueMicrotask(() => seq.push(7));
|
|
seq.push(5);
|
|
await time.nextAsync();
|
|
seq.push(8);
|
|
|
|
assertEquals(cb.calls, expectedCalls);
|
|
assertEquals(Date.now(), start + 1000);
|
|
assertEquals(seq, [1, 2, 3, 4, 5, 6, 7, 8]);
|
|
} finally {
|
|
time.restore();
|
|
}
|
|
});
|
|
|
|
Deno.test("runAll runs all timers without running microtasks", async () => {
|
|
const time: FakeTime = new FakeTime();
|
|
const start: number = Date.now();
|
|
const cb = spy(fromNow());
|
|
const seq: number[] = [];
|
|
|
|
try {
|
|
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]);
|
|
} finally {
|
|
time.restore();
|
|
}
|
|
});
|
|
|
|
Deno.test("runAllAsync runs all microtasks and timers", async () => {
|
|
const time: FakeTime = new FakeTime();
|
|
const start: number = Date.now();
|
|
const cb = spy(fromNow());
|
|
const seq: number[] = [];
|
|
|
|
try {
|
|
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]);
|
|
} finally {
|
|
time.restore();
|
|
}
|
|
});
|
|
|
|
const Date_ = Date;
|
|
|
|
Deno.test("Date from FakeTime is structured cloneable", () => {
|
|
const time: FakeTime = new FakeTime();
|
|
try {
|
|
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_);
|
|
} finally {
|
|
time.restore();
|
|
}
|
|
});
|