Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 28 additions & 11 deletions src/daemon/main.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -530,8 +530,10 @@ describe("startDaemon wires core and leasing together", () => {
readonly device: { readonly id: string };
};

/** Lets real I/O that a mutated daemon would still be doing finish, so "nothing happened" is
* a statement about the daemon and not about how early the test looked. */
/** Lets the work a mutated daemon would still be doing finish, so "nothing happened" is a
* statement about the daemon and not about how early the test looked. Only sound when that
* work is all in memory: a timer fires after it however loaded the machine is, but real I/O
* (a process spawn, a real file) can outlast it. */
const settleRealTime = async () => new Promise<void>((resolve) => setTimeout(resolve, 25));

async function leasedIos(options: ConstructorParameters<typeof FakeDriver>[0]) {
Expand Down Expand Up @@ -634,11 +636,18 @@ describe("startDaemon wires core and leasing together", () => {
const writeHeld = new Promise<void>((resolve) => {
releaseWrite = resolve;
});
let reachWrite: () => void = () => undefined;
const writeReached = new Promise<void>((resolve) => {
reachWrite = resolve;
});
class HoldingFilesystem extends MemoryFilesystem {
async writeFileAtomic(
...arguments_: Parameters<MemoryFilesystem["writeFileAtomic"]>
): Promise<void> {
if (holdWrites && arguments_[0] === statePath) await writeHeld;
if (holdWrites && arguments_[0] === statePath) {
reachWrite();
await writeHeld;
}
return super.writeFileAtomic(...arguments_);
}
}
Expand All @@ -655,7 +664,6 @@ describe("startDaemon wires core and leasing together", () => {
}) as StartDaemonOptions;
const failing = new FakeDriver({ availableOsVersions: ["26.5"], clock, platform: "ios" });
failing.failOn("reclaim", 1, new DriverCrashError("purge exploded"));
// The previous process: a device left quarantined, and one request still open.
const first = await startDaemon(options(failing));
const grant = (await first.dispatch("lease.request", ios, agent)) as Grant;
await first.dispatch("lease.release", { leaseId: grant.lease.id }, agent);
Expand All @@ -672,20 +680,19 @@ describe("startDaemon wires core and leasing together", () => {
request: ios,
requesterId: "agent-1",
});
// Leasing's startup settles the open request by writing the registry; hold that write.
holdWrites = true;

const starting = startDaemon(
options(new FakeDriver({ availableOsVersions: ["26.5"], clock, platform: "ios" })),
);
await settleRealTime();
// Waits on the write itself, not on real time: how long startup takes to reach it depends on
// the machine's load. The race lets a startup that never writes fail on the count below.
await Promise.race([writeReached, starting]);
const timersWhileLeasingStartupPending = clock.pendingTimerCount;
releaseWrite();
runningDaemons.push(await starting);
const timersAfterStartup = clock.pendingTimerCount;

// One timer is startup's own (nothing to do with devices). The quarantined device's retry
// timer is armed by core's convergence, which must not have begun.
expect(timersWhileLeasingStartupPending).toBe(1);
expect(timersAfterStartup).toBeGreaterThan(timersWhileLeasingStartupPending);
});
Expand All @@ -700,11 +707,18 @@ describe("startDaemon wires core and leasing together", () => {
const writeHeld = new Promise<void>((resolve) => {
releaseWrite = resolve;
});
let reachWrite: () => void = () => undefined;
const writeReached = new Promise<void>((resolve) => {
reachWrite = resolve;
});
class HoldingFilesystem extends MemoryFilesystem {
async writeFileAtomic(
...arguments_: Parameters<MemoryFilesystem["writeFileAtomic"]>
): Promise<void> {
if (holdWrites && arguments_[0] === statePath) await writeHeld;
if (holdWrites && arguments_[0] === statePath) {
reachWrite();
await writeHeld;
}
return super.writeFileAtomic(...arguments_);
}
}
Expand All @@ -723,15 +737,18 @@ describe("startDaemon wires core and leasing together", () => {
const grant = (await first.dispatch("lease.request", ios, agent)) as Grant;
await first.stop("test");
// The restarted process's driver knows no device, so the leased one is gone from its platform.
// Ending that lease writes the registry; hold that write.
holdWrites = true;

let started = false;
const starting = startDaemon(options()).then((daemon) => {
started = true;
return daemon;
});
await settleRealTime();
// Waits on the write, not on real time: the steps before it include real I/O, so under load
// they outlast any fixed sleep. From the write on, a startup that did not wait for it only
// has in-memory work left, which finishes before the next macrotask.
await Promise.race([writeReached, starting]);
await new Promise((resolve) => setImmediate(resolve));
const startedWhileEndingPending = started;
releaseWrite();
const daemon = await starting;
Expand Down
Loading