Extract idb device querying to IOSBridge

Summary:
Again this is just a code move for now.

However, we now have a common method between simctl and idb cases. This means that the next diff can call an `IOSBridge` with polymorpism taking over. This is still delegated out, but there's an argument to be made to move `iosUtil` functionality back so that this all lives in the same place.

Reviewed By: passy

Differential Revision: D33843093

fbshipit-source-id: 5cd884140817df851cccf63e5780582b16d4231c
This commit is contained in:
Lawrence Lomax
2022-01-31 07:23:29 -08:00
committed by Facebook GitHub Bot
parent aeb0b5f317
commit ccae37aa71
3 changed files with 61 additions and 19 deletions

View File

@@ -8,6 +8,8 @@
*/ */
import fs from 'fs-extra'; import fs from 'fs-extra';
import iosUtil from './iOSContainerUtility';
import child_process from 'child_process'; import child_process from 'child_process';
import type {IOSDeviceParams} from 'flipper-common'; import type {IOSDeviceParams} from 'flipper-common';
import {DeviceType} from 'flipper-common'; import {DeviceType} from 'flipper-common';
@@ -42,10 +44,22 @@ export interface IOSBridge {
serial: string, serial: string,
outputFile: string, outputFile: string,
) => child_process.ChildProcess; ) => child_process.ChildProcess;
getActiveDevices: (bootedOnly: boolean) => Promise<Array<IOSDeviceParams>>;
} }
class IDBBridge implements IOSBridge { export class IDBBridge implements IOSBridge {
constructor(private idbPath: string) {} constructor(
private idbPath: string,
private enablePhysicalDevices: boolean,
) {}
async getActiveDevices(_bootedOnly: boolean): Promise<IOSDeviceParams[]> {
return iosUtil
.targets(this.idbPath, this.enablePhysicalDevices)
.catch((e) => {
console.error('Failed to get active iOS devices:', e.message);
return [];
});
}
async navigate(serial: string, location: string): Promise<void> { async navigate(serial: string, location: string): Promise<void> {
this._execIdb(`open --udid ${serial} "${location}"`); this._execIdb(`open --udid ${serial} "${location}"`);
@@ -216,11 +230,12 @@ export function getDeviceSetPath() {
export async function makeIOSBridge( export async function makeIOSBridge(
idbPath: string, idbPath: string,
isXcodeDetected: boolean, isXcodeDetected: boolean,
enablePhysicalDevices: boolean,
isAvailableFn: (idbPath: string) => Promise<boolean> = isAvailable, isAvailableFn: (idbPath: string) => Promise<boolean> = isAvailable,
): Promise<IOSBridge> { ): Promise<IOSBridge> {
// prefer idb // prefer idb
if (await isAvailableFn(idbPath)) { if (await isAvailableFn(idbPath)) {
return new IDBBridge(idbPath); return new IDBBridge(idbPath, enablePhysicalDevices);
} }
// no idb, if it's a simulator and xcode is available, we can use xcrun // no idb, if it's a simulator and xcode is available, we can use xcrun

View File

@@ -26,7 +26,7 @@ afterEach(() => {
}); });
test('uses xcrun with no idb when xcode is detected', async () => { test('uses xcrun with no idb when xcode is detected', async () => {
const ib = await makeIOSBridge('', true); const ib = await makeIOSBridge('', true, false);
ib.startLogListener('deadbeef', 'emulator'); ib.startLogListener('deadbeef', 'emulator');
@@ -50,7 +50,12 @@ test('uses xcrun with no idb when xcode is detected', async () => {
}); });
test('uses idb when present and xcode detected', async () => { test('uses idb when present and xcode detected', async () => {
const ib = await makeIOSBridge('/usr/local/bin/idb', true, async (_) => true); const ib = await makeIOSBridge(
'/usr/local/bin/idb',
true,
true,
async (_) => true,
);
ib.startLogListener('deadbeef', 'emulator'); ib.startLogListener('deadbeef', 'emulator');
@@ -77,7 +82,12 @@ test('uses idb when present and xcode detected', async () => {
}); });
test('uses idb when present and xcode detected and physical device connected', async () => { test('uses idb when present and xcode detected and physical device connected', async () => {
const ib = await makeIOSBridge('/usr/local/bin/idb', true, async (_) => true); const ib = await makeIOSBridge(
'/usr/local/bin/idb',
true,
true,
async (_) => true,
);
ib.startLogListener('deadbeef', 'physical'); ib.startLogListener('deadbeef', 'physical');
@@ -99,12 +109,12 @@ test('uses idb when present and xcode detected and physical device connected', a
}); });
test("without idb physical devices can't log", async () => { test("without idb physical devices can't log", async () => {
const ib = await makeIOSBridge('', true); const ib = await makeIOSBridge('', true, false);
expect(ib.startLogListener).toBeDefined(); // since we have xcode expect(ib.startLogListener).toBeDefined(); // since we have xcode
}); });
test('throws if no iOS support', async () => { test('throws if no iOS support', async () => {
await expect(makeIOSBridge('', false)).rejects.toThrow( await expect(makeIOSBridge('', false, false)).rejects.toThrow(
'Neither Xcode nor idb available. Cannot provide iOS device functionality.', 'Neither Xcode nor idb available. Cannot provide iOS device functionality.',
); );
}); });
@@ -112,7 +122,7 @@ test('throws if no iOS support', async () => {
test.unix( test.unix(
'uses xcrun to take screenshots with no idb when xcode is detected', 'uses xcrun to take screenshots with no idb when xcode is detected',
async () => { async () => {
const ib = await makeIOSBridge('', true); const ib = await makeIOSBridge('', true, false);
await expect(() => ib.screenshot('deadbeef')).rejects.toThrow(); await expect(() => ib.screenshot('deadbeef')).rejects.toThrow();
@@ -123,7 +133,12 @@ test.unix(
); );
test.unix('uses idb to take screenshots when available', async () => { test.unix('uses idb to take screenshots when available', async () => {
const ib = await makeIOSBridge('/usr/local/bin/idb', true, async (_) => true); const ib = await makeIOSBridge(
'/usr/local/bin/idb',
true,
true,
async (_) => true,
);
await expect(() => ib.screenshot('deadbeef')).rejects.toThrow(); await expect(() => ib.screenshot('deadbeef')).rejects.toThrow();
@@ -133,7 +148,7 @@ test.unix('uses idb to take screenshots when available', async () => {
}); });
test('uses xcrun to navigate with no idb when xcode is detected', async () => { test('uses xcrun to navigate with no idb when xcode is detected', async () => {
const ib = await makeIOSBridge('', true); const ib = await makeIOSBridge('', true, false);
await ib.navigate('deadbeef', 'fb://dummy'); await ib.navigate('deadbeef', 'fb://dummy');
@@ -143,7 +158,12 @@ test('uses xcrun to navigate with no idb when xcode is detected', async () => {
}); });
test('uses idb to navigate when available', async () => { test('uses idb to navigate when available', async () => {
const ib = await makeIOSBridge('/usr/local/bin/idb', true, async (_) => true); const ib = await makeIOSBridge(
'/usr/local/bin/idb',
true,
true,
async (_) => true,
);
await ib.navigate('deadbeef', 'fb://dummy'); await ib.navigate('deadbeef', 'fb://dummy');
@@ -153,7 +173,7 @@ test('uses idb to navigate when available', async () => {
}); });
test('uses xcrun to record with no idb when xcode is detected', async () => { test('uses xcrun to record with no idb when xcode is detected', async () => {
const ib = await makeIOSBridge('', true); const ib = await makeIOSBridge('', true, false);
ib.recordVideo('deadbeef', '/tmp/video.mp4'); ib.recordVideo('deadbeef', '/tmp/video.mp4');
@@ -163,7 +183,12 @@ test('uses xcrun to record with no idb when xcode is detected', async () => {
}); });
test('uses idb to record when available', async () => { test('uses idb to record when available', async () => {
const ib = await makeIOSBridge('/usr/local/bin/idb', true, async (_) => true); const ib = await makeIOSBridge(
'/usr/local/bin/idb',
true,
true,
async (_) => true,
);
ib.recordVideo('deadbeef', '/tmo/video.mp4'); ib.recordVideo('deadbeef', '/tmo/video.mp4');

View File

@@ -17,6 +17,7 @@ import IOSDevice from './IOSDevice';
import { import {
ERR_NO_IDB_OR_XCODE_AVAILABLE, ERR_NO_IDB_OR_XCODE_AVAILABLE,
IOSBridge, IOSBridge,
IDBBridge,
makeIOSBridge, makeIOSBridge,
SimctlBridge, SimctlBridge,
} from './IOSBridge'; } from './IOSBridge';
@@ -174,7 +175,11 @@ export class IOSDeviceManager {
} }
try { try {
// Awaiting the promise here to trigger immediate error handling. // Awaiting the promise here to trigger immediate error handling.
this.iosBridge = await makeIOSBridge(settings.idbPath, isDetected); this.iosBridge = await makeIOSBridge(
settings.idbPath,
isDetected,
settings.enablePhysicalIOS,
);
this.queryDevicesForever(); this.queryDevicesForever();
} catch (err) { } catch (err) {
// This case is expected if both Xcode and idb are missing. // This case is expected if both Xcode and idb are missing.
@@ -252,10 +257,7 @@ function getActiveDevices(
idbPath: string, idbPath: string,
isPhysicalDeviceEnabled: boolean, isPhysicalDeviceEnabled: boolean,
): Promise<Array<IOSDeviceParams>> { ): Promise<Array<IOSDeviceParams>> {
return iosUtil.targets(idbPath, isPhysicalDeviceEnabled).catch((e) => { return new IDBBridge(idbPath, isPhysicalDeviceEnabled).getActiveDevices(true);
console.error('Failed to get active iOS devices:', e.message);
return [];
});
} }
export function parseXcodeFromCoreSimPath( export function parseXcodeFromCoreSimPath(