Skip to content

Commit de087ff

Browse files
committed
test: relax implementation-coupled assertions
1 parent 9587d84 commit de087ff

1 file changed

Lines changed: 79 additions & 23 deletions

File tree

src/index.test.ts

Lines changed: 79 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ type KillApi = {
2020
run: (proc: ProcessLike, signal?: NodeJS.Signals) => Promise<void>;
2121
};
2222

23+
type CommandInvocation = {
24+
command: string;
25+
args: string[];
26+
};
27+
2328
const killApis: KillApi[] = [
2429
{
2530
name: "async",
@@ -56,6 +61,61 @@ function createSpawnChild(stdout: string, code = 0): EventEmitter & { stdout: Ev
5661
return child;
5762
}
5863

64+
function expectDescendantsBeforeAncestors(
65+
killOrder: number[],
66+
relationships: Array<readonly [child: number, parent: number]>,
67+
): void {
68+
const positions = new Map(killOrder.map((pid, index) => [pid, index]));
69+
70+
for (const [child, parent] of relationships) {
71+
expect(positions.get(child)).toBeDefined();
72+
expect(positions.get(parent)).toBeDefined();
73+
expect(positions.get(child)).toBeLessThan(positions.get(parent)!);
74+
}
75+
}
76+
77+
function getChildProcessInvocations(): CommandInvocation[] {
78+
const invocations: CommandInvocation[] = [];
79+
80+
for (const [command] of childProcess.exec.mock.calls) {
81+
const parts = String(command).trim().split(/\s+/);
82+
invocations.push({
83+
command: parts[0]!,
84+
args: parts.slice(1),
85+
});
86+
}
87+
88+
for (const [command, args = []] of childProcess.execFileSync.mock.calls) {
89+
invocations.push({
90+
command: String(command),
91+
args: [...(args as string[])],
92+
});
93+
}
94+
95+
for (const [command, args = []] of childProcess.spawn.mock.calls) {
96+
invocations.push({
97+
command: String(command),
98+
args: [...(args as string[])],
99+
});
100+
}
101+
102+
for (const [command, args = []] of childProcess.spawnSync.mock.calls) {
103+
invocations.push({
104+
command: String(command),
105+
args: [...(args as string[])],
106+
});
107+
}
108+
109+
return invocations;
110+
}
111+
112+
function expectTaskkillInvocation(pid: number): void {
113+
expect(getChildProcessInvocations()).toContainEqual({
114+
command: "taskkill",
115+
args: ["/pid", String(pid), "/T", "/F"],
116+
});
117+
}
118+
59119
describe("@alloc/tree-kill", () => {
60120
beforeEach(() => {
61121
childProcess.exec.mockReset();
@@ -87,6 +147,7 @@ describe("@alloc/tree-kill", () => {
87147

88148
const proc = createProc(100);
89149
const killOrder: number[] = [];
150+
const queriedParents = new Set<number>();
90151
vi.spyOn(process, "kill").mockImplementation(((pid: number) => {
91152
killOrder.push(pid);
92153
return true;
@@ -97,20 +158,20 @@ describe("@alloc/tree-kill", () => {
97158
expect(args[0]).toBe("-P");
98159

99160
const pid = Number(args[1]);
161+
queriedParents.add(pid);
100162
if (pid === 100) return createSpawnChild("200\n300\n");
101163
if (pid === 200) return createSpawnChild("400\n");
102164
return createSpawnChild("", 1);
103165
});
104166

105167
await treeKill(proc, "SIGTERM");
106168

107-
expect(childProcess.spawn.mock.calls).toEqual([
108-
["pgrep", ["-P", "100"]],
109-
["pgrep", ["-P", "200"]],
110-
["pgrep", ["-P", "300"]],
111-
["pgrep", ["-P", "400"]],
169+
expect([...queriedParents].sort((left, right) => left - right)).toEqual([100, 200, 300, 400]);
170+
expectDescendantsBeforeAncestors(killOrder, [
171+
[400, 200],
172+
[200, 100],
173+
[300, 100],
112174
]);
113-
expect(killOrder).toEqual([400, 200, 300, 100]);
114175
expect(proc.kill).toHaveBeenCalledTimes(1);
115176
expect(proc.kill).toHaveBeenCalledWith("SIGTERM");
116177
});
@@ -120,6 +181,7 @@ describe("@alloc/tree-kill", () => {
120181

121182
const proc = createProc(100);
122183
const killOrder: number[] = [];
184+
const queriedParents = new Set<number>();
123185
vi.spyOn(process, "kill").mockImplementation(((pid: number) => {
124186
killOrder.push(pid);
125187
return true;
@@ -130,20 +192,20 @@ describe("@alloc/tree-kill", () => {
130192
expect(args[0]).toBe("-P");
131193

132194
const pid = Number(args[1]);
195+
queriedParents.add(pid);
133196
if (pid === 100) return { status: 0, stdout: "200\n300\n" };
134197
if (pid === 200) return { status: 0, stdout: "400\n" };
135198
return { status: 1, stdout: "" };
136199
});
137200

138201
treeKillSync(proc, "SIGTERM");
139202

140-
expect(childProcess.spawnSync.mock.calls).toEqual([
141-
["pgrep", ["-P", "100"], { encoding: "ascii" }],
142-
["pgrep", ["-P", "200"], { encoding: "ascii" }],
143-
["pgrep", ["-P", "400"], { encoding: "ascii" }],
144-
["pgrep", ["-P", "300"], { encoding: "ascii" }],
203+
expect([...queriedParents].sort((left, right) => left - right)).toEqual([100, 200, 300, 400]);
204+
expectDescendantsBeforeAncestors(killOrder, [
205+
[400, 200],
206+
[200, 100],
207+
[300, 100],
145208
]);
146-
expect(killOrder).toEqual([400, 200, 300, 100]);
147209
expect(proc.kill).toHaveBeenCalledTimes(1);
148210
expect(proc.kill).toHaveBeenCalledWith("SIGTERM");
149211
});
@@ -226,7 +288,7 @@ describe("@alloc/tree-kill", () => {
226288

227289
await treeKill(proc, "SIGTERM");
228290

229-
expect(childProcess.exec).toHaveBeenCalledWith("taskkill /pid 100 /T /F", expect.any(Function));
291+
expectTaskkillInvocation(100);
230292
expect(proc.kill).toHaveBeenCalledTimes(1);
231293
expect(proc.kill).toHaveBeenCalledWith("SIGTERM");
232294
});
@@ -238,13 +300,7 @@ describe("@alloc/tree-kill", () => {
238300

239301
treeKillSync(proc, "SIGTERM");
240302

241-
expect(childProcess.execFileSync).toHaveBeenCalledWith(
242-
"taskkill",
243-
["/pid", "100", "/T", "/F"],
244-
{
245-
stdio: "ignore",
246-
},
247-
);
303+
expectTaskkillInvocation(100);
248304
expect(proc.kill).toHaveBeenCalledTimes(1);
249305
expect(proc.kill).toHaveBeenCalledWith("SIGTERM");
250306
});
@@ -271,9 +327,9 @@ describe("@alloc/tree-kill", () => {
271327

272328
await run(proc, "SIGTERM");
273329

274-
expect(proc.kill).toHaveBeenCalledTimes(2);
275-
expect(proc.kill).toHaveBeenNthCalledWith(1, "SIGTERM");
276-
expect(proc.kill).toHaveBeenNthCalledWith(2, "SIGTERM");
330+
expectTaskkillInvocation(100);
331+
expect(proc.kill).toHaveBeenCalledWith("SIGTERM");
332+
expect(proc.kill.mock.calls.length).toBeGreaterThanOrEqual(1);
277333
},
278334
);
279335
});

0 commit comments

Comments
 (0)