Skip to content

Commit 08fc575

Browse files
committed
fix: bound iOS crash artifact lookup
Poll crash report sources for longer while avoiding simctl diagnose on CI, and return as soon as either device logs or host DiagnosticReports yields an artifact.
1 parent f7da7f0 commit 08fc575

2 files changed

Lines changed: 245 additions & 129 deletions

File tree

Lines changed: 87 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,63 @@
1-
import { beforeEach, describe, expect, it, vi } from 'vitest';
1+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
22
import fs from 'node:fs';
33
import { join } from 'node:path';
44
import { tmpdir } from 'node:os';
55
import { createCrashArtifactWriter } from '@react-native-harness/tools';
6-
import { collectCrashArtifacts } from '../crash-diagnostics.js';
7-
import * as simctl from '../xcrun/simctl.js';
6+
import {
7+
collectCrashArtifacts,
8+
waitForCrashArtifact,
9+
} from '../crash-diagnostics.js';
810
import * as devicectl from '../xcrun/devicectl.js';
911

12+
const writeIosIpsCrashReport = (path: string) => {
13+
fs.writeFileSync(
14+
path,
15+
[
16+
JSON.stringify({
17+
app_name: 'HarnessPlayground',
18+
bundleID: 'com.harnessplayground',
19+
timestamp: '2026-03-12 11:35:08 +0000',
20+
}),
21+
JSON.stringify({
22+
pid: 1234,
23+
procName: 'HarnessPlayground',
24+
procPath:
25+
'/Users/me/Library/Developer/CoreSimulator/Devices/sim-udid/data/Containers/Bundle/Application/ABC/HarnessPlayground.app/HarnessPlayground',
26+
exception: {
27+
type: 'EXC_BREAKPOINT',
28+
signal: 'SIGTRAP',
29+
},
30+
}),
31+
].join('\n'),
32+
'utf8',
33+
);
34+
};
35+
1036
describe('collectCrashArtifacts', () => {
37+
const originalDiagnosticReportsDir =
38+
process.env.RN_HARNESS_IOS_DIAGNOSTIC_REPORTS_DIR;
39+
let diagnosticReportsDir: string;
40+
1141
beforeEach(() => {
1242
vi.restoreAllMocks();
43+
diagnosticReportsDir = fs.mkdtempSync(
44+
join(tmpdir(), 'rn-harness-diagnostic-reports-'),
45+
);
46+
process.env.RN_HARNESS_IOS_DIAGNOSTIC_REPORTS_DIR = diagnosticReportsDir;
1347
});
1448

15-
it('collects simulator crash artifacts from simctl diagnose output', async () => {
16-
const outputRoot = fs.mkdtempSync(
17-
join(tmpdir(), 'rn-harness-simctl-diagnose-'),
18-
);
19-
const crashPath = join(outputRoot, 'HarnessPlayground.ips');
20-
fs.writeFileSync(
21-
crashPath,
22-
[
23-
JSON.stringify({
24-
app_name: 'HarnessPlayground',
25-
bundleID: 'com.harnessplayground',
26-
timestamp: '2026-03-12 11:35:08 +0000',
27-
}),
28-
JSON.stringify({
29-
pid: 1234,
30-
procName: 'HarnessPlayground',
31-
procPath:
32-
'/Users/me/Library/Developer/CoreSimulator/Devices/sim-udid/data/Containers/Bundle/Application/ABC/HarnessPlayground.app/HarnessPlayground',
33-
exception: {
34-
type: 'EXC_BREAKPOINT',
35-
signal: 'SIGTRAP',
36-
},
37-
}),
38-
].join('\n'),
39-
'utf8',
40-
);
49+
afterEach(() => {
50+
if (originalDiagnosticReportsDir === undefined) {
51+
delete process.env.RN_HARNESS_IOS_DIAGNOSTIC_REPORTS_DIR;
52+
} else {
53+
process.env.RN_HARNESS_IOS_DIAGNOSTIC_REPORTS_DIR =
54+
originalDiagnosticReportsDir;
55+
}
56+
});
4157

42-
vi.spyOn(simctl, 'diagnose').mockImplementation(
43-
async (_udid, outputDir) => {
44-
fs.mkdirSync(outputDir, { recursive: true });
45-
fs.copyFileSync(crashPath, join(outputDir, 'HarnessPlayground.ips'));
46-
},
58+
it('collects simulator crash artifacts from host DiagnosticReports', async () => {
59+
writeIosIpsCrashReport(
60+
join(diagnosticReportsDir, 'HarnessPlayground-2026-03-12-113508.ips'),
4761
);
4862

4963
const artifacts = await collectCrashArtifacts({
@@ -107,43 +121,14 @@ describe('collectCrashArtifacts', () => {
107121
});
108122

109123
it('persists matched crash artifacts with the provided writer', async () => {
110-
const sourceRoot = fs.mkdtempSync(
111-
join(tmpdir(), 'rn-harness-crash-diagnostics-'),
112-
);
113-
const sourcePath = join(sourceRoot, 'HarnessPlayground.ips');
114-
fs.writeFileSync(
115-
sourcePath,
116-
[
117-
JSON.stringify({
118-
app_name: 'HarnessPlayground',
119-
bundleID: 'com.harnessplayground',
120-
timestamp: '2026-03-12 11:35:08 +0000',
121-
}),
122-
JSON.stringify({
123-
pid: 1234,
124-
procName: 'HarnessPlayground',
125-
procPath:
126-
'/Users/me/Library/Developer/CoreSimulator/Devices/sim-udid/data/Containers/Bundle/Application/ABC/HarnessPlayground.app/HarnessPlayground',
127-
exception: {
128-
type: 'EXC_BREAKPOINT',
129-
signal: 'SIGTRAP',
130-
},
131-
}),
132-
].join('\n'),
133-
'utf8',
134-
);
135-
136-
vi.spyOn(simctl, 'diagnose').mockImplementation(
137-
async (_udid, outputDir) => {
138-
fs.mkdirSync(outputDir, { recursive: true });
139-
fs.copyFileSync(sourcePath, join(outputDir, 'HarnessPlayground.ips'));
140-
},
124+
writeIosIpsCrashReport(
125+
join(diagnosticReportsDir, 'HarnessPlayground-2026-03-12-113508.ips'),
141126
);
142127

143128
const writer = createCrashArtifactWriter({
144129
runnerName: 'ios-sim',
145130
platformId: 'ios',
146-
rootDir: join(sourceRoot, '.harness', 'crash-reports'),
131+
rootDir: join(diagnosticReportsDir, '.harness', 'crash-reports'),
147132
runTimestamp: '2026-03-12T11-35-08-000Z',
148133
});
149134

@@ -158,4 +143,40 @@ describe('collectCrashArtifacts', () => {
158143
expect(artifacts[0]?.artifactPath).toContain('/.harness/crash-reports/');
159144
expect(fs.existsSync(artifacts[0]?.artifactPath ?? '')).toBe(true);
160145
});
146+
147+
it('returns a host crash report without waiting for device crash log lookup to finish', async () => {
148+
writeIosIpsCrashReport(
149+
join(diagnosticReportsDir, 'HarnessPlayground-2026-03-12-113508.ips'),
150+
);
151+
152+
vi.spyOn(devicectl, 'listFiles').mockImplementation(
153+
() =>
154+
new Promise(() => {
155+
// Keep the device-side collector pending so the host lookup must win.
156+
}),
157+
);
158+
159+
const artifact = await waitForCrashArtifact({
160+
lookup: {
161+
processName: 'HarnessPlayground',
162+
pid: 1234,
163+
occurredAt: Date.parse('2026-03-12T11:35:08.000Z'),
164+
},
165+
options: {
166+
targetId: 'device-udid',
167+
targetType: 'device',
168+
processNames: ['HarnessPlayground'],
169+
bundleId: 'com.harnessplayground',
170+
minOccurredAt: Date.parse('2026-03-12T11:35:07.000Z'),
171+
},
172+
getFallbackArtifact: () => null,
173+
recordArtifact: vi.fn(),
174+
});
175+
176+
expect(artifact).toMatchObject({
177+
processName: 'HarnessPlayground',
178+
pid: 1234,
179+
signal: 'SIGTRAP',
180+
});
181+
});
161182
});

0 commit comments

Comments
 (0)