-
-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathiphone-simulator-xcode-simctl.ts
executable file
·257 lines (205 loc) · 7.61 KB
/
iphone-simulator-xcode-simctl.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
///<reference path="./.d.ts"/>
"use strict";
import childProcess = require("./child-process");
import * as child_process from "child_process";
import errors = require("./errors");
import common = require("./iphone-simulator-common");
import options = require("./options");
import path = require("path");
import { Simctl } from "./simctl";
import util = require("util");
import utils = require("./utils");
import xcode = require("./xcode");
import * as _ from "lodash";
import { IPhoneSimulatorNameGetter } from "./iphone-simulator-name-getter";
const osenv = require("osenv");
export class XCodeSimctlSimulator extends IPhoneSimulatorNameGetter implements ISimulator {
private static DEVICE_IDENTIFIER_PREFIX = "com.apple.CoreSimulator.SimDeviceType";
private deviceLogChildProcess: any = null;
private isDeviceLogOperationStarted = false;
public defaultDeviceIdentifier = "iPhone 6";
private simctl: ISimctl = null;
constructor() {
super();
this.simctl = new Simctl();
}
public getDevices(): IDevice[] {
return this.simctl.getDevices();
}
public getSdks(): ISdk[] {
let devices = this.simctl.getDevices();
return _.map(devices, device => {
return {
displayName: `iOS ${device.runtimeVersion}`,
version: device.runtimeVersion
};
});
}
public run(applicationPath: string, applicationIdentifier: string): void {
let device = this.getDeviceToRun();
let currentBootedDevice = _.find(this.getDevices(), device => this.isDeviceBooted(device));
if (currentBootedDevice && (currentBootedDevice.name.toLowerCase() !== device.name.toLowerCase() || currentBootedDevice.runtimeVersion !== device.runtimeVersion)) {
this.killSimulator();
}
this.startSimulator(device);
if (!options.skipInstall) {
this.simctl.install(device.id, applicationPath);
}
let launchResult = this.simctl.launch(device.id, applicationIdentifier);
if (options.logging) {
this.printDeviceLog(device.id, launchResult);
}
}
public sendNotification(notification: string): void {
let device = this.getBootedDevice();
if (!device) {
errors.fail("Could not find device.");
}
this.simctl.notifyPost("booted", notification);
}
public getApplicationPath(deviceId: string, applicationIdentifier: string): string {
return this.simctl.getAppContainer(deviceId, applicationIdentifier);
}
public getInstalledApplications(deviceId: string): IApplication[] {
return common.getInstalledApplications(deviceId);
}
public installApplication(deviceId: string, applicationPath: string): void {
return this.simctl.install(deviceId, applicationPath);
}
public uninstallApplication(deviceId: string, appIdentifier: string): void {
return this.simctl.uninstall(deviceId, appIdentifier, { skipError: true });
}
public startApplication(deviceId: string, appIdentifier: string): string {
return this.simctl.launch(deviceId, appIdentifier);
}
public stopApplication(deviceId: string, appIdentifier: string, bundleExecutable: string): string {
try {
let xcodeMajorVersion: number = null;
try {
const xcodeVersion = xcode.getXcodeVersionData();
xcodeMajorVersion = +xcodeVersion.major;
} catch (err) {
// Ignore the error.
}
let resultOfTermination: string;
if (xcodeMajorVersion && xcodeMajorVersion < 8) {
// Xcode 7.x does not have support for `xcrun simctl terminate` command
resultOfTermination = childProcess.execSync(`killall ${bundleExecutable}`, { skipError: true });
} else {
resultOfTermination = this.simctl.terminate(deviceId, appIdentifier);
}
// killall command does not terminate the processes immediately and we have to wait a little bit,
// just to ensure all related processes and services are dead.
// Same is valid for simctl terminate when Simulator's OS version is below 10.
utils.sleep(0.5);
return resultOfTermination;
} catch (e) {
}
}
public printDeviceLog(deviceId: string, launchResult?: string): child_process.ChildProcess {
let pid = "";
let deviceLogChildProcess;
if (launchResult) {
pid = launchResult.split(":")[1].trim();
}
if (!this.isDeviceLogOperationStarted) {
deviceLogChildProcess = this.getDeviceLogProcess(deviceId);
if (deviceLogChildProcess.stdout) {
deviceLogChildProcess.stdout.on("data", this.logDataHandler.bind(this, pid));
}
if (deviceLogChildProcess.stderr) {
deviceLogChildProcess.stderr.on("data", this.logDataHandler.bind(this, pid));
}
}
return deviceLogChildProcess;
}
private logDataHandler(pid: string, logData: NodeBuffer): void {
const dataAsString = logData.toString();
if (pid) {
if (dataAsString.indexOf(`[${pid}]`) > -1) {
process.stdout.write(dataAsString);
}
} else {
process.stdout.write(dataAsString);
}
}
public getDeviceLogProcess(deviceId: string, predicate?: string): child_process.ChildProcess {
if (!this.isDeviceLogOperationStarted) {
const device = this.getDeviceFromIdentifier(deviceId);
const deviceVersion = device ? device.runtimeVersion : "";
const majorVersion = deviceVersion.split(".")[0];
if (majorVersion && parseInt(majorVersion) >= 11) {
this.deviceLogChildProcess = this.simctl.getLog(deviceId, predicate);
} else {
const logFilePath = path.join(osenv.home(), "Library", "Logs", "CoreSimulator", deviceId, "system.log");
this.deviceLogChildProcess = require("child_process").spawn("tail", ['-f', '-n', '1', logFilePath]);
}
this.isDeviceLogOperationStarted = true;
}
return this.deviceLogChildProcess;
}
private getDeviceToRun(): IDevice {
let devices = this.simctl.getDevices(),
sdkVersion = options.sdkVersion || options.sdk;
let result = _.find(devices, (device: IDevice) => {
if (sdkVersion && !options.device) {
return device.runtimeVersion === sdkVersion;
}
if (options.device && !sdkVersion) {
return device.name === options.device;
}
if (options.device && sdkVersion) {
return device.runtimeVersion === sdkVersion && device.name === options.device;
}
if (!sdkVersion && !options.device) {
return this.isDeviceBooted(device);
}
});
if (!result) {
result = _.find(devices, (device: IDevice) => device.name === this.defaultDeviceIdentifier);
}
if (!result) {
let sortedDevices = _.sortBy(devices, (device) => device.runtimeVersion);
result = _.last(sortedDevices);
}
return result;
}
private isDeviceBooted(device: IDevice): boolean {
return device.state === 'Booted';
}
private getBootedDevice(): IDevice {
let devices = this.simctl.getDevices();
return _.find(devices, device => this.isDeviceBooted(device));
}
public startSimulator(device?: IDevice): void {
device = device || this.getDeviceToRun();
// In case the id is undefined, skip verification - we'll start default simulator.
if (device.id) {
this.verifyDevice(device);
}
if (!this.isDeviceBooted(device)) {
let bootedDevice = this.getBootedDevice();
if (bootedDevice && bootedDevice.id !== device.id) {
this.killSimulator();
}
common.startSimulator(device.id);
// startSimulaltor doesn't always finish immediately, and the subsequent
// install fails since the simulator is not running.
// Give it some time to start before we attempt installing.
utils.sleep(1);
}
}
private verifyDevice(device: IDevice): void {
const availableDevices = this.getDevices();
if (!_.find(availableDevices, { id: device.id })) {
errors.fail(`No simulator image available for device identifier '${device.id}'.`);
}
}
private getDeviceFromIdentifier(deviceId: string) {
const availableDevices = this.getDevices();
return _.find(availableDevices, { id: deviceId });
}
private killSimulator(): void {
childProcess.execSync("pkill -9 -f Simulator");
}
}