Skip to content

Commit 204a320

Browse files
authored
perf: fast-path macos device resolution (#525)
1 parent 13f30dd commit 204a320

4 files changed

Lines changed: 75 additions & 18 deletions

File tree

src/core/__tests__/dispatch-resolve.test.ts

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -190,12 +190,45 @@ test('resolveTargetDevice uses injected device inventory without local discovery
190190
});
191191

192192
test('resolveTargetDevice treats empty injected inventory as authoritative', async () => {
193-
const err = await withDeviceInventoryProvider(
194-
async () => [],
195-
async () => await resolveTargetDevice({ platform: 'ios' }),
196-
).catch((error) => error);
193+
await expectDeviceNotFound(() =>
194+
withDeviceInventoryProvider(
195+
async () => [],
196+
async () => await resolveTargetDevice({ platform: 'ios' }),
197+
),
198+
);
199+
200+
assert.equal(mockListAppleDevices.mock.calls.length, 0);
201+
});
202+
203+
test('resolveTargetDevice fast-paths explicit macOS without Apple mobile discovery', async () => {
204+
const result = await resolveTargetDevice({ platform: 'macos' });
205+
206+
assert.equal(result.platform, 'macos');
207+
assert.equal(result.id, 'host-macos-local');
208+
assert.equal(mockListAppleDevices.mock.calls.length, 0);
209+
});
210+
211+
test('resolveTargetDevice fast-paths Apple desktop target without simulator-set discovery', async () => {
212+
const result = await resolveTargetDevice({
213+
platform: 'apple',
214+
target: 'desktop',
215+
iosSimulatorDeviceSet: '/tmp/simulators',
216+
});
217+
218+
assert.equal(result.platform, 'macos');
219+
assert.equal(result.target, 'desktop');
220+
assert.equal(mockListAppleDevices.mock.calls.length, 0);
221+
});
222+
223+
test('resolveTargetDevice fast-path preserves macOS selector validation', async () => {
224+
await expectDeviceNotFound(() => resolveTargetDevice({ platform: 'macos', udid: 'other-mac' }));
197225

198-
assert.ok(err instanceof AppError);
199-
assert.equal(err.code, 'DEVICE_NOT_FOUND');
200226
assert.equal(mockListAppleDevices.mock.calls.length, 0);
201227
});
228+
229+
async function expectDeviceNotFound(action: () => Promise<unknown>): Promise<void> {
230+
const err = await action().catch((error) => error);
231+
232+
assert.ok(err instanceof AppError);
233+
assert.equal(err.code, 'DEVICE_NOT_FOUND');
234+
}

src/core/dispatch-resolve.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { listAndroidDevices } from '../platforms/android/devices.ts';
1212
import { ensureAdb } from '../platforms/android/index.ts';
1313
import { findBootableIosSimulator, listAppleDevices } from '../platforms/ios/devices.ts';
1414
import { listLinuxDevices } from '../platforms/linux/devices.ts';
15+
import { listMacosDevices } from '../platforms/macos/devices.ts';
1516
import { withDiagnosticTimer } from '../utils/diagnostics.ts';
1617
import {
1718
resolveAndroidSerialAllowlist,
@@ -163,6 +164,11 @@ export async function resolveTargetDevice(flags: ResolveDeviceFlags): Promise<De
163164
);
164165
}
165166

167+
if (shouldUseHostMacFastPath(selector)) {
168+
const devices = await listMacosDevices();
169+
return cacheResolvedTargetDevice(cacheKey, await resolveDevice(devices, selector));
170+
}
171+
166172
if (selector.platform === 'linux') {
167173
const devices = await listLinuxDevices();
168174
return cacheResolvedTargetDevice(cacheKey, await resolveDevice(devices, selector));
@@ -205,6 +211,16 @@ export async function resolveTargetDevice(flags: ResolveDeviceFlags): Promise<De
205211
);
206212
}
207213

214+
function shouldUseHostMacFastPath(selector: {
215+
platform?: PlatformSelector;
216+
target?: DeviceTarget;
217+
}): boolean {
218+
return (
219+
selector.platform === 'macos' ||
220+
(selector.platform === 'apple' && selector.target === 'desktop')
221+
);
222+
}
223+
208224
export async function withResolveTargetDeviceCacheScope<T>(task: () => Promise<T>): Promise<T> {
209225
if (resolveTargetDeviceCacheScope.getStore()) return await task();
210226
return await resolveTargetDeviceCacheScope.run(new Map(), task);

src/platforms/ios/devices.ts

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { AppError } from '../../utils/errors.ts';
66
import type { DeviceInfo, DeviceTarget } from '../../utils/device.ts';
77
import { resolveTimeoutMs } from '../../utils/timeouts.ts';
88
import { resolveIosSimulatorDeviceSetPath } from '../../utils/device-isolation.ts';
9+
import { buildHostMacDevice } from '../macos/devices.ts';
910
import { buildSimctlArgs } from './simctl.ts';
1011

1112
const IOS_DEVICECTL_LIST_TIMEOUT_MS = resolveTimeoutMs(
@@ -47,7 +48,6 @@ type IosDeviceDiscoveryOptions = {
4748
simulatorSetPath?: string;
4849
};
4950

50-
const HOST_MAC_DEVICE_ID = 'host-macos-local';
5151
const XCTRACE_SECTION_HEADER_PATTERN = /^==\s*(.+?)\s*==$/;
5252
const XCTRACE_DEVICE_LINE_PATTERN = /^(?<name>.+?)\s+\[(?<id>[^[\]]+)\]\s*$/;
5353

@@ -181,17 +181,6 @@ export async function findBootableIosSimulator(
181181
return bestBooted ?? bestMobile ?? bestAny;
182182
}
183183

184-
function buildHostMacDevice(): DeviceInfo {
185-
return {
186-
platform: 'macos',
187-
id: HOST_MAC_DEVICE_ID,
188-
name: os.hostname(),
189-
kind: 'device',
190-
target: 'desktop',
191-
booted: true,
192-
};
193-
}
194-
195184
function parseSimctlAppleDevices(
196185
payload: SimctlListDevicesPayload,
197186
simulatorSetPath: string | undefined,

src/platforms/macos/devices.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import os from 'node:os';
2+
import type { DeviceInfo } from '../../utils/device.ts';
3+
4+
const HOST_MAC_DEVICE_ID = 'host-macos-local';
5+
6+
export function buildHostMacDevice(): DeviceInfo {
7+
return {
8+
platform: 'macos',
9+
id: HOST_MAC_DEVICE_ID,
10+
name: os.hostname(),
11+
kind: 'device',
12+
target: 'desktop',
13+
booted: true,
14+
};
15+
}
16+
17+
export async function listMacosDevices(): Promise<DeviceInfo[]> {
18+
return [buildHostMacDevice()];
19+
}

0 commit comments

Comments
 (0)