Skip to content

Commit 37a1c9d

Browse files
Fatme HavaluovaFatme Havaluova
authored andcommitted
Integrate iOS simulator with device discovery
1 parent d552d95 commit 37a1c9d

8 files changed

+291
-59
lines changed

lib/child-process.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,16 @@ import * as errors from "./errors";
66
import Future = require("fibers/future");
77
import * as util from "util";
88

9-
export function exec(command: string): IFuture<any> {
9+
export function exec(command: string, opts?: any): IFuture<any> {
1010
var future = new Future<any>();
1111

1212
child_process.exec(command, (error: Error, stdout: NodeBuffer, stderr: NodeBuffer) => {
1313
if(error) {
14-
future.throw(new Error(`Error ${error.message} while executing ${command}.`));
14+
if (opts && opts.skipError) {
15+
future.return(error);
16+
} else {
17+
future.throw(new Error(`Error ${error.message} while executing ${command}.`));
18+
}
1519
} else {
1620
future.return(stdout ? stdout.toString() : "");
1721
}
@@ -20,7 +24,7 @@ export function exec(command: string): IFuture<any> {
2024
return future;
2125
}
2226

23-
export function spawn(command: string, args: string[]): IFuture<string> {
27+
export function spawn(command: string, args: string[], opts?: any): IFuture<string> {
2428
let future = new Future<string>();
2529
let capturedOut = "";
2630
let capturedErr = "";
@@ -44,7 +48,11 @@ export function spawn(command: string, args: string[]): IFuture<string> {
4448
if(exitCode === 0) {
4549
future.return(capturedOut ? capturedOut.trim() : null);
4650
} else {
47-
future.throw(new Error(util.format("Command %s with arguments %s failed with exit code %s. Error output:\n %s", command, args.join(" "), exitCode, capturedErr)));
51+
if (opts && opts.skipError) {
52+
future.return(capturedErr);
53+
} else {
54+
future.throw(new Error(util.format("Command %s with arguments %s failed with exit code %s. Error output:\n %s", command, args.join(" "), exitCode, capturedErr)));
55+
}
4856
}
4957
});
5058

lib/declarations.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ interface IDevice {
2929
interface ISimctl {
3030
launch(deviceId: string, applicationIdentifier: string): IFuture<string>;
3131
install(deviceId: string, applicationPath: string): IFuture<void>;
32-
uninstall(deviceId: string, applicationIdentifier: string): IFuture<void>;
32+
uninstall(deviceId: string, applicationIdentifier: string, opts?: any): IFuture<void>;
3333
notifyPost(deviceId: string, notification: string): IFuture<void>;
3434
getDevices(): IFuture<IDevice[]>;
3535
getAppContainer(deviceId: string, applicationIdentifier: string): IFuture<string>;
@@ -50,6 +50,19 @@ interface ISimulator {
5050
run(applicationPath: string, applicationIdentifier: string): IFuture<void>;
5151
sendNotification(notification: string): IFuture<void>;
5252
getApplicationPath(deviceId: string, applicationIdentifier: string): IFuture<string>;
53+
getInstalledApplications(deviceId: string): IFuture<IApplication[]>;
54+
installApplication(deviceId: string, applicationPath: string): IFuture<void>;
55+
uninstallApplication(deviceId: string, appIdentifier: string): IFuture<void>;
56+
startApplication(deviceId: string, appIdentifier: string): IFuture<string>;
57+
stopApplication(deviceId: string, appIdentifier: string): IFuture<string>;
58+
printDeviceLog(deviceId: string): void;
59+
startSimulator(): IFuture<void>;
60+
}
61+
62+
interface IApplication {
63+
guid: string;
64+
appIdentifier: string;
65+
path: string;
5366
}
5467

5568
interface IExecuteOptions {

lib/ios-sim.ts

Lines changed: 75 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,15 @@ var fiber = Fiber(() => {
1515

1616
fiber.run();
1717

18+
function getSimulator(): IFuture<ISimulator> {
19+
return (() => {
20+
let libraryPath = require("./iphone-simulator");
21+
let obj = new libraryPath.iPhoneSimulator();
22+
let simulator = obj.createSimulator().wait();
23+
return simulator;
24+
}).future<ISimulator>()();
25+
}
26+
1827
global.publicApi = {};
1928

2029
Object.defineProperty(global.publicApi, "getRunningSimulator", {
@@ -42,14 +51,77 @@ Object.defineProperty(global.publicApi, "getRunningSimulator", {
4251
Object.defineProperty(global.publicApi, "getApplicationPath", {
4352
get: () => {
4453
return (...args: any[]) => {
45-
let libraryPath = require("./iphone-simulator");
46-
let obj = new libraryPath.iPhoneSimulator();
47-
let simulator = obj.createSimulator().wait();
54+
let simulator = getSimulator().wait();
4855
let result = simulator.getApplicationPath.apply(simulator, args).wait();
4956
return result;
5057
}
5158
}
5259
});
5360

61+
Object.defineProperty(global.publicApi, "getInstalledApplications", {
62+
get: () => {
63+
return (...args: any[]) => {
64+
let simulator = getSimulator().wait();
65+
let installedApplications: IApplication[] = simulator.getInstalledApplications.apply(simulator, args).wait();
66+
let result = _.map(installedApplications, application => application.appIdentifier);
67+
console.log("RESULT!!!!! ", result);
68+
return result;
69+
}
70+
}
71+
});
72+
73+
Object.defineProperty(global.publicApi, "installApplication", {
74+
get: () => {
75+
return (...args: any[]) => {
76+
let simulator = getSimulator().wait();
77+
return simulator.installApplication.apply(simulator, args);
78+
}
79+
}
80+
});
81+
82+
Object.defineProperty(global.publicApi, "uninstallApplication", {
83+
get: () => {
84+
return (...args: any[]) => {
85+
let simulator = getSimulator().wait();
86+
return simulator.uninstallApplication.apply(simulator, args);
87+
}
88+
}
89+
});
90+
91+
Object.defineProperty(global.publicApi, "startApplication", {
92+
get: () => {
93+
return (...args: any[]) => {
94+
let simulator = getSimulator().wait();
95+
return simulator.startApplication.apply(simulator, args);
96+
}
97+
}
98+
});
99+
100+
Object.defineProperty(global.publicApi, "stopApplication", {
101+
get: () => {
102+
return (...args: any[]) => {
103+
let simulator = getSimulator().wait();
104+
return simulator.stopApplication.apply(simulator, args);
105+
}
106+
}
107+
});
108+
109+
Object.defineProperty(global.publicApi, "printDeviceLog", {
110+
get: () => {
111+
return (...args: any[]) => {
112+
let simulator = getSimulator().wait();
113+
return simulator.printDeviceLog.apply(simulator, args);
114+
}
115+
}
116+
});
117+
118+
Object.defineProperty(global.publicApi, "startSimulator", {
119+
get: () => {
120+
return (...args: any[]) => {
121+
let simulator = getSimulator().wait();
122+
return simulator.startSimulator.apply(simulator, args);
123+
}
124+
}
125+
});
54126

55127
module.exports = global.publicApi;

lib/iphone-simulator-common.ts

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
///<reference path="./.d.ts"/>
2+
"use strict";
3+
4+
import childProcess = require("./child-process");
5+
import Future = require("fibers/future");
6+
import * as fs from "fs";
7+
import * as path from "path";
8+
import * as os from "os";
9+
import xcode = require("./xcode");
10+
let bplistParser = require("bplist-parser");
11+
let osenv = require("osenv");
12+
13+
export function getInstalledApplications(deviceId: string): IFuture<IApplication[]> {
14+
return (() => {
15+
let rootApplicationsPath = path.join(osenv.home(), `/Library/Developer/CoreSimulator/Devices/${deviceId}/data/Containers/Bundle/Application`);
16+
if(!fs.existsSync(rootApplicationsPath)) {
17+
rootApplicationsPath = path.join(osenv.home(), `/Library/Developer/CoreSimulator/Devices/${deviceId}/data/Applications`);
18+
}
19+
let applicationGuids = fs.readdirSync(rootApplicationsPath);
20+
let result: IApplication[] = [];
21+
_.each(applicationGuids, applicationGuid => {
22+
let fullApplicationPath = path.join(rootApplicationsPath, applicationGuid);
23+
if (fs.statSync(fullApplicationPath).isDirectory()) {
24+
let applicationDirContents = fs.readdirSync(fullApplicationPath);
25+
let applicationName = _.find(applicationDirContents, fileName => path.extname(fileName) === ".app");
26+
let plistFilePath = path.join(fullApplicationPath, applicationName, "Info.plist");
27+
let applicationData = parseFile(plistFilePath).wait();
28+
result.push({
29+
guid: applicationGuid,
30+
appIdentifier: applicationData[0].CFBundleIdentifier,
31+
path: path.join(fullApplicationPath, applicationName)
32+
});
33+
}
34+
});
35+
36+
return result;
37+
}).future<IApplication[]>()();
38+
}
39+
40+
export function printDeviceLog(deviceId: string): void {
41+
let logFilePath = path.join(osenv.home(), "Library", "Logs", "CoreSimulator", deviceId, "system.log");
42+
43+
let childProcess = require("child_process").spawn("tail", ['-f', '-n', '1', logFilePath]);
44+
if (childProcess.stdout) {
45+
childProcess.stdout.on("data", (data: NodeBuffer) => {
46+
console.log(data.toString());
47+
});
48+
}
49+
50+
if (childProcess.stderr) {
51+
childProcess.stderr.on("data", (data: string) => {
52+
console.error(data.toString());
53+
});
54+
}
55+
}
56+
57+
export function startSimulator(deviceId: string): IFuture<void> {
58+
return (() => {
59+
let simulatorPath = path.resolve(xcode.getPathFromXcodeSelect().wait(), "Applications", "Simulator.app");
60+
let args = [simulatorPath, '--args', '-CurrentDeviceUDID', deviceId];
61+
childProcess.spawn("open", args).wait();
62+
}).future<void>()();
63+
}
64+
65+
function parseFile(plistFilePath: string): IFuture<any> {
66+
let future = new Future<any>();
67+
bplistParser.parseFile(plistFilePath, (err: Error, obj: any) => {
68+
if(err) {
69+
future.throw(err);
70+
} else {
71+
future.return(obj);
72+
}
73+
});
74+
return future;
75+
}

lib/iphone-simulator-xcode-5.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,32 @@ export class XCode5Simulator extends iPhoneSimulatorBaseLib.IPhoneInteropSimulat
6363
return Future.fromResult("");
6464
}
6565

66+
public getInstalledApplications(deviceId: string): IFuture<IApplication[]> {
67+
return Future.fromResult(<IApplication[]>[]);
68+
}
69+
70+
public installApplication(deviceId: string, applicationPath: string): IFuture<void> {
71+
return Future.fromResult();
72+
}
73+
74+
public uninstallApplication(deviceId: string, appIdentifier: string): IFuture<void> {
75+
return Future.fromResult();
76+
}
77+
78+
public startApplication(deviceId: string, appIdentifier: string): IFuture<string> {
79+
return Future.fromResult("");
80+
}
81+
82+
public stopApplication(deviceId: string, appIdentifier: string): IFuture<string> {
83+
return Future.fromResult("");
84+
}
85+
86+
public printDeviceLog(deviceId: string): void { }
87+
88+
public startSimulator(): IFuture<void> {
89+
return Future.fromResult();
90+
}
91+
6692
private get deviceIdentifier(): string {
6793
let identifier = options.device || XCode5Simulator.DEFAULT_DEVICE_IDENTIFIER;
6894
return XCode5Simulator.allowedDeviceIdentifiers[identifier];

lib/iphone-simulator-xcode-6.ts

Lines changed: 41 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
///<reference path="./.d.ts"/>
22
"use strict";
3+
import childProcess = require("./child-process");
34
import * as errors from "./errors";
45
import * as options from "./options";
56
import * as utils from "./utils";
6-
import * as fs from "fs";
7+
78
import Future = require("fibers/future");
9+
import * as fs from "fs";
810
import * as path from "path";
911
import * as util from "util";
1012
import * as os from "os";
13+
import common = require("./iphone-simulator-common");
14+
import { Simctl } from "./simctl";
1115
let $ = require("nodobjc");
12-
let bplistParser = require("bplist-parser");
1316
let osenv = require("osenv");
1417

1518
import iPhoneSimulatorBaseLib = require("./iphone-interop-simulator-base");
@@ -20,11 +23,13 @@ export class XCode6Simulator extends iPhoneSimulatorBaseLib.IPhoneInteropSimulat
2023
private static DEFAULT_DEVICE_IDENTIFIER = "iPhone-4s";
2124

2225
private cachedDevices: IDevice[];
26+
private simctl: ISimctl;
2327

2428
constructor() {
2529
super(this);
2630

2731
this.cachedDevices = null;
32+
this.simctl = new Simctl();
2833
}
2934

3035
public setSimulatedDevice(config: any): void {
@@ -46,38 +51,43 @@ export class XCode6Simulator extends iPhoneSimulatorBaseLib.IPhoneInteropSimulat
4651

4752
public getApplicationPath(deviceId: string, applicationIdentifier: string): IFuture<string> {
4853
return (() => {
49-
let rootApplicationsPath = path.join(osenv.home(), `/Library/Developer/CoreSimulator/Devices/${deviceId}/data/Containers/Bundle/Application`);
50-
if(!fs.existsSync(rootApplicationsPath)) {
51-
rootApplicationsPath = path.join(osenv.home(), `/Library/Developer/CoreSimulator/Devices/${deviceId}/data/Applications`);
52-
}
53-
let applicationGuids = fs.readdirSync(rootApplicationsPath);
54-
let result: string = null;
55-
_.each(applicationGuids, applicationGuid => {
56-
let fullApplicationPath = path.join(rootApplicationsPath, applicationGuid);
57-
let applicationDirContents = fs.readdirSync(fullApplicationPath);
58-
let applicationName = _.find(applicationDirContents, fileName => path.extname(fileName) === ".app");
59-
let plistFilePath = path.join(fullApplicationPath, applicationName, "Info.plist");
60-
let applicationData = this.parseFile(plistFilePath).wait();
61-
if(applicationData[0].CFBundleIdentifier === applicationIdentifier) {
62-
result = path.join(fullApplicationPath, applicationName);
63-
return false;
64-
}
65-
});
66-
67-
return result;
54+
let applications = this.getInstalledApplications(deviceId).wait();
55+
let application = _.find(applications, app => app.appIdentifier === applicationIdentifier);
56+
return application ? application.path : null;
6857
}).future<string>()();
6958
}
7059

71-
private parseFile(plistFilePath: string): IFuture<any> {
72-
let future = new Future<any>();
73-
bplistParser.parseFile(plistFilePath, (err: Error, obj: any) => {
74-
if(err) {
75-
future.throw(err);
76-
} else {
77-
future.return(obj);
78-
}
79-
});
80-
return future;
60+
public getInstalledApplications(deviceId: string): IFuture<IApplication[]> {
61+
return common.getInstalledApplications(deviceId);
62+
}
63+
64+
public installApplication(deviceId: string, applicationPath: string): IFuture<void> {
65+
return this.simctl.install(deviceId, applicationPath);
66+
}
67+
68+
public uninstallApplication(deviceId: string, appIdentifier: string): IFuture<void> {
69+
return this.simctl.uninstall(deviceId, appIdentifier);
70+
}
71+
72+
public startApplication(deviceId: string, appIdentifier: string): IFuture<string> {
73+
return this.simctl.launch(deviceId, appIdentifier);
74+
}
75+
76+
public stopApplication(deviceId: string, appIdentifier: string): IFuture<string> {
77+
try {
78+
return childProcess.exec(`killall ${appIdentifier.split(".")[2]}.app`);
79+
} catch(e) {
80+
//this.$logger.trace("Unable to kill simulator: " + e);
81+
}
82+
}
83+
84+
public printDeviceLog(deviceId: string): void {
85+
common.printDeviceLog(deviceId);
86+
}
87+
88+
public startSimulator(): IFuture<void> {
89+
let device = this.devices[0];
90+
return common.startSimulator(device.id);
8191
}
8292

8393
private get devices(): IDevice[] {

0 commit comments

Comments
 (0)