Extract getSimulators to IOSBridge
Summary: Extracts `getSimulator` interrnals to `SimctlBridge`. This allows this functionality to be used independently of things like the the flipper server. For now this just moves the functionality, but future diffs will build on top of this. Reviewed By: passy Differential Revision: D33842986 fbshipit-source-id: bae26a9bd5c21c9813f8a2b10c3b3e3efc1c5929
This commit is contained in:
committed by
Facebook GitHub Bot
parent
959a2a77d7
commit
aeb0b5f317
@@ -9,6 +9,7 @@
|
||||
|
||||
import fs from 'fs-extra';
|
||||
import child_process from 'child_process';
|
||||
import type {IOSDeviceParams} from 'flipper-common';
|
||||
import {DeviceType} from 'flipper-common';
|
||||
import {v1 as uuid} from 'uuid';
|
||||
import path from 'path';
|
||||
@@ -21,6 +22,15 @@ export const ERR_NO_IDB_OR_XCODE_AVAILABLE =
|
||||
export const ERR_PHYSICAL_DEVICE_LOGS_WITHOUT_IDB =
|
||||
'Cannot provide logs from a physical device without idb.';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
type iOSSimulatorDevice = {
|
||||
state: 'Booted' | 'Shutdown' | 'Shutting Down';
|
||||
availability?: string;
|
||||
isAvailable?: 'YES' | 'NO' | true | false;
|
||||
name: string;
|
||||
udid: string;
|
||||
};
|
||||
|
||||
export interface IOSBridge {
|
||||
startLogListener: (
|
||||
udid: string,
|
||||
@@ -115,12 +125,49 @@ export class SimctlBridge implements IOSBridge {
|
||||
);
|
||||
}
|
||||
|
||||
async getActiveDevices(bootedOnly: boolean): Promise<Array<IOSDeviceParams>> {
|
||||
return execFile('xcrun', [
|
||||
'simctl',
|
||||
...getDeviceSetPath(),
|
||||
'list',
|
||||
'devices',
|
||||
'--json',
|
||||
])
|
||||
.then(({stdout}) => JSON.parse(stdout!.toString()).devices)
|
||||
.then((simulatorDevices: Array<iOSSimulatorDevice>) => {
|
||||
const simulators = Object.values(simulatorDevices).flat();
|
||||
return simulators
|
||||
.filter(
|
||||
(simulator) =>
|
||||
(!bootedOnly || simulator.state === 'Booted') &&
|
||||
isSimulatorAvailable(simulator),
|
||||
)
|
||||
.map((simulator) => {
|
||||
return {
|
||||
...simulator,
|
||||
type: 'emulator',
|
||||
} as IOSDeviceParams;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async launchSimulator(udid: string): Promise<any> {
|
||||
await execFile('xcrun', ['simctl', ...getDeviceSetPath(), 'boot', udid]);
|
||||
await execFile('open', ['-a', 'simulator']);
|
||||
}
|
||||
}
|
||||
|
||||
function isSimulatorAvailable(simulator: iOSSimulatorDevice): boolean {
|
||||
// For some users "availability" is set, for others it's "isAvailable"
|
||||
// It's not clear which key is set, so we are checking both.
|
||||
// We've also seen isAvailable return "YES" and true, depending on version.
|
||||
return (
|
||||
simulator.availability === '(available)' ||
|
||||
simulator.isAvailable === 'YES' ||
|
||||
simulator.isAvailable === true
|
||||
);
|
||||
}
|
||||
|
||||
async function isAvailable(idbPath: string): Promise<boolean> {
|
||||
if (!idbPath) {
|
||||
return false;
|
||||
|
||||
@@ -18,7 +18,17 @@ import {
|
||||
setFlipperServerConfig,
|
||||
} from '../../../FlipperServerConfig';
|
||||
|
||||
let fakeSimctlBridge: any;
|
||||
let hasCalledSimctlActiveDevices = false;
|
||||
|
||||
beforeEach(() => {
|
||||
hasCalledSimctlActiveDevices = false;
|
||||
fakeSimctlBridge = {
|
||||
getActiveDevices: async (_bootedOnly: boolean) => {
|
||||
hasCalledSimctlActiveDevices = true;
|
||||
return [];
|
||||
},
|
||||
};
|
||||
setFlipperServerConfig(getRenderHostInstance().serverConfig);
|
||||
});
|
||||
|
||||
@@ -73,11 +83,13 @@ test('test getAllPromisesForQueryingDevices when xcode detected', () => {
|
||||
);
|
||||
flipperServer.ios.iosBridge = {} as IOSBridge;
|
||||
(flipperServer.ios as any).idbConfig = getFlipperServerConfig().settings;
|
||||
flipperServer.ios.simctlBridge = fakeSimctlBridge;
|
||||
const promises = flipperServer.ios.getAllPromisesForQueryingDevices(
|
||||
true,
|
||||
false,
|
||||
);
|
||||
expect(promises.length).toEqual(2);
|
||||
expect(hasCalledSimctlActiveDevices).toEqual(true);
|
||||
});
|
||||
|
||||
test('test getAllPromisesForQueryingDevices when xcode is not detected', () => {
|
||||
@@ -87,11 +99,13 @@ test('test getAllPromisesForQueryingDevices when xcode is not detected', () => {
|
||||
);
|
||||
flipperServer.ios.iosBridge = {} as IOSBridge;
|
||||
(flipperServer.ios as any).idbConfig = getFlipperServerConfig().settings;
|
||||
flipperServer.ios.simctlBridge = fakeSimctlBridge;
|
||||
const promises = flipperServer.ios.getAllPromisesForQueryingDevices(
|
||||
false,
|
||||
true,
|
||||
);
|
||||
expect(promises.length).toEqual(1);
|
||||
expect(hasCalledSimctlActiveDevices).toEqual(false);
|
||||
});
|
||||
|
||||
test('test getAllPromisesForQueryingDevices when xcode and idb are both unavailable', () => {
|
||||
@@ -101,11 +115,13 @@ test('test getAllPromisesForQueryingDevices when xcode and idb are both unavaila
|
||||
);
|
||||
flipperServer.ios.iosBridge = {} as IOSBridge;
|
||||
(flipperServer.ios as any).idbConfig = getFlipperServerConfig().settings;
|
||||
flipperServer.ios.simctlBridge = fakeSimctlBridge;
|
||||
const promises = flipperServer.ios.getAllPromisesForQueryingDevices(
|
||||
false,
|
||||
false,
|
||||
);
|
||||
expect(promises.length).toEqual(0);
|
||||
expect(hasCalledSimctlActiveDevices).toEqual(false);
|
||||
});
|
||||
|
||||
test('test getAllPromisesForQueryingDevices when both idb and xcode are available', () => {
|
||||
@@ -115,9 +131,11 @@ test('test getAllPromisesForQueryingDevices when both idb and xcode are availabl
|
||||
);
|
||||
flipperServer.ios.iosBridge = {} as IOSBridge;
|
||||
(flipperServer.ios as any).idbConfig = getFlipperServerConfig().settings;
|
||||
flipperServer.ios.simctlBridge = fakeSimctlBridge;
|
||||
const promises = flipperServer.ios.getAllPromisesForQueryingDevices(
|
||||
true,
|
||||
true,
|
||||
);
|
||||
expect(promises.length).toEqual(2);
|
||||
expect(hasCalledSimctlActiveDevices).toEqual(false);
|
||||
});
|
||||
|
||||
@@ -11,14 +11,13 @@ import {ChildProcess} from 'child_process';
|
||||
import type {IOSDeviceParams} from 'flipper-common';
|
||||
import path from 'path';
|
||||
import childProcess from 'child_process';
|
||||
import {exec, execFile} from 'promisify-child-process';
|
||||
import {exec} from 'promisify-child-process';
|
||||
import iosUtil from './iOSContainerUtility';
|
||||
import IOSDevice from './IOSDevice';
|
||||
import {
|
||||
ERR_NO_IDB_OR_XCODE_AVAILABLE,
|
||||
IOSBridge,
|
||||
makeIOSBridge,
|
||||
getDeviceSetPath,
|
||||
SimctlBridge,
|
||||
} from './IOSBridge';
|
||||
import {FlipperServerImpl} from '../../FlipperServerImpl';
|
||||
@@ -27,26 +26,6 @@ import {getFlipperServerConfig} from '../../FlipperServerConfig';
|
||||
import {IdbConfig, setIdbConfig} from './idbConfig';
|
||||
import {assertNotNull} from 'flipper-server-core/src/comms/Utilities';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
type iOSSimulatorDevice = {
|
||||
state: 'Booted' | 'Shutdown' | 'Shutting Down';
|
||||
availability?: string;
|
||||
isAvailable?: 'YES' | 'NO' | true | false;
|
||||
name: string;
|
||||
udid: string;
|
||||
};
|
||||
|
||||
function isAvailable(simulator: iOSSimulatorDevice): boolean {
|
||||
// For some users "availability" is set, for others it's "isAvailable"
|
||||
// It's not clear which key is set, so we are checking both.
|
||||
// We've also seen isAvailable return "YES" and true, depending on version.
|
||||
return (
|
||||
simulator.availability === '(available)' ||
|
||||
simulator.isAvailable === 'YES' ||
|
||||
simulator.isAvailable === true
|
||||
);
|
||||
}
|
||||
|
||||
export class IOSDeviceManager {
|
||||
private portForwarders: Array<ChildProcess> = [];
|
||||
private idbConfig?: IdbConfig;
|
||||
@@ -214,30 +193,7 @@ export class IOSDeviceManager {
|
||||
}
|
||||
|
||||
getSimulators(bootedOnly: boolean): Promise<Array<IOSDeviceParams>> {
|
||||
return execFile('xcrun', [
|
||||
'simctl',
|
||||
...getDeviceSetPath(),
|
||||
'list',
|
||||
'devices',
|
||||
'--json',
|
||||
])
|
||||
.then(({stdout}) => JSON.parse(stdout!.toString()).devices)
|
||||
.then((simulatorDevices: Array<iOSSimulatorDevice>) => {
|
||||
const simulators = Object.values(simulatorDevices).flat();
|
||||
return simulators
|
||||
.filter(
|
||||
(simulator) =>
|
||||
(!bootedOnly || simulator.state === 'Booted') &&
|
||||
isAvailable(simulator),
|
||||
)
|
||||
.map((simulator) => {
|
||||
return {
|
||||
...simulator,
|
||||
type: 'emulator',
|
||||
} as IOSDeviceParams;
|
||||
});
|
||||
})
|
||||
.catch((e: Error) => {
|
||||
return this.simctlBridge.getActiveDevices(bootedOnly).catch((e: Error) => {
|
||||
console.warn('Failed to query simulators:', e);
|
||||
if (e.message.includes('Xcode license agreements')) {
|
||||
this.flipperServer.emit('notification', {
|
||||
|
||||
Reference in New Issue
Block a user