Skip to content

Commit 2abd6ce

Browse files
FatmeFatme
authored andcommitted
Merge pull request #2 from telerik/fatme/type-of-emulated-device
Add support for specifying the type of emulated device
2 parents 45740a4 + 7b828dc commit 2abd6ce

11 files changed

+251
-45
lines changed

lib/command-executor.ts

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
///<reference path="./.d.ts"/>
22
"use strict";
33

4+
import Future = require("fibers/future");
45
import util = require("util");
6+
require("colors");
7+
8+
import errors = require("./errors");
59
import options = require("./options");
610

711
export class CommandExecutor implements ICommandExecutor {
@@ -14,12 +18,22 @@ export class CommandExecutor implements ICommandExecutor {
1418
}
1519

1620
private executeCore(commandName: string, commandArguments: string[]): IFuture<void> {
17-
var command = new (require("./commands/" + commandName).Command)();
18-
if(!command) {
19-
throw new Error(util.format("Unable to resolve commandName %s", commandName));
20-
}
21-
22-
return command.execute(commandArguments);
21+
return (() => {
22+
var command: ICommand = new (require("./commands/" + commandName).Command)();
23+
if(!command) {
24+
errors.fail("Unable to resolve commandName %s", commandName);
25+
}
26+
27+
try {
28+
command.execute(commandArguments).wait();
29+
} catch(e) {
30+
if(options.debug) {
31+
throw e;
32+
} else {
33+
console.log( "\x1B[31;1m" + e.message + "\x1B[0m");
34+
}
35+
}
36+
}).future<void>()();
2337
}
2438

2539
private getCommandArguments(): string[] {

lib/commands/device-types.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
///<reference path=".././.d.ts"/>
2+
"use strict";
3+
import iphoneSimulatorLibPath = require("./../iphone-simulator");
4+
5+
export class Command implements ICommand {
6+
public execute(args: string[]): IFuture<void> {
7+
var iphoneSimulator = new iphoneSimulatorLibPath.iPhoneSimulator();
8+
return iphoneSimulator.printDeviceTypes();
9+
}
10+
}

lib/declarations.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
interface IiPhoneSimulator {
55
run(appName: string): IFuture<void>;
6+
printDeviceTypes(): void;
67
}
78

89
interface ICommand {
@@ -11,4 +12,24 @@ interface ICommand {
1112

1213
interface ICommandExecutor {
1314
execute(): IFuture<void>;
15+
}
16+
17+
interface IDevice {
18+
device: any; // NodObjC wrapper to device
19+
deviceTypeIdentifier: string;
20+
runtimeVersion: string;
21+
}
22+
23+
interface IDictionary<T> {
24+
[key: string]: T;
25+
}
26+
27+
interface ISimulator {
28+
validDeviceIdentifiers: string[];
29+
setSimulatedDevice(config: any): void;
30+
}
31+
32+
interface IExecuteOptions {
33+
canRunMainLoop: boolean;
34+
appPath?: string;
1435
}

lib/errors.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
///<reference path="./.d.ts"/>
2+
"use strict";
3+
4+
import util = require("util");
5+
6+
export function fail(errorMessage: string, ...args: string[]) {
7+
args.unshift(errorMessage);
8+
throw new Error(util.format.apply(null, args));
9+
}

lib/ios-sim.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import commandExecutorLibPath = require("./command-executor");
1010
var fiber = Fiber(() => {
1111
var commandExecutor: ICommandExecutor = new commandExecutorLibPath.CommandExecutor();
1212
commandExecutor.execute().wait();
13+
Future.assertNoFutureLeftBehind();
1314
});
1415

1516
fiber.run();

lib/iphone-simulator-xcode-5.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
///<reference path="./.d.ts"/>
2+
"use strict";
3+
4+
import errors = require("./errors");
5+
import options = require("./options");
6+
import utils = require("./utils");
7+
import util = require("util");
8+
9+
var $ = require("NodObjC");
10+
11+
export class XCode5Simulator implements ISimulator {
12+
13+
private static DEFAULT_DEVICE_IDENTIFIER = "iPhone";
14+
15+
private static allowedDeviceIdentifiers: IDictionary<string> = {
16+
"iPhone": "iPhone",
17+
"iPhone-Retina-3.5-inch": "iPhone Retina (3.5-inch)",
18+
"iPhone-Retina-4-inch": "iPhone Retina (4-inch)",
19+
"iPhone-Retina-4-inch-64-bit": "iPhone Retina (4-inch 64-bit)",
20+
"iPad": "iPad",
21+
"iPad-Retina": "iPad Retina",
22+
"iPad-Retina-64-bit": "iPad Retina (64-bit)"
23+
}
24+
25+
public get validDeviceIdentifiers(): string[] {
26+
return _.keys(XCode5Simulator.allowedDeviceIdentifiers);
27+
}
28+
29+
public setSimulatedDevice(config:any): void {
30+
config("setSimulatedDeviceInfoName", $(this.deviceIdentifier));
31+
}
32+
33+
private get deviceIdentifier(): string {
34+
var identifier = options.device || XCode5Simulator.DEFAULT_DEVICE_IDENTIFIER;
35+
return XCode5Simulator.allowedDeviceIdentifiers[identifier];
36+
}
37+
}

lib/iphone-simulator-xcode-6.ts

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
///<reference path="./.d.ts"/>
2+
"use strict";
3+
import errors = require("./errors");
4+
import options = require("./options");
5+
import utils = require("./utils");
6+
import util = require("util");
7+
var $ = require("NodObjC");
8+
9+
export class XCode6Simulator implements ISimulator {
10+
11+
private static DEVICE_IDENTIFIER_PREFIX = "com.apple.CoreSimulator.SimDeviceType";
12+
private static DEFAULT_DEVICE_IDENTIFIER = "iPhone-4s";
13+
14+
private static allowedDeviceIdentifiers = [
15+
"iPhone-4s",
16+
"iPhone-5",
17+
"iPhone-5s",
18+
"iPhone-6",
19+
"iPhone-6-Plus",
20+
"Resizable-iPhone",
21+
"iPad-2",
22+
"iPad-Retina",
23+
"iPad-Air",
24+
"Resizable-iPad"
25+
];
26+
27+
private availableDevices: IDictionary<IDevice>;
28+
29+
constructor() {
30+
this.availableDevices = Object.create(null);
31+
}
32+
33+
public get validDeviceIdentifiers(): string[] {
34+
return XCode6Simulator.allowedDeviceIdentifiers;
35+
}
36+
37+
public setSimulatedDevice(config: any): void {
38+
var device = this.getDeviceByIdentifier(this.deviceIdentifier);
39+
config("setDevice", device);
40+
}
41+
42+
private get deviceIdentifier(): string {
43+
return options.device || XCode6Simulator.DEFAULT_DEVICE_IDENTIFIER;
44+
}
45+
46+
private getAvailableDevices(): IDictionary<IDevice> {
47+
if(_.isEmpty(this.availableDevices)) {
48+
var deviceSet = $.classDefinition.getClassByName("SimDeviceSet")("defaultSet");
49+
var devices = deviceSet("availableDevices");
50+
var count = devices("count");
51+
if(count > 0) {
52+
for(var index=0; index<count; index++) {
53+
var device = devices("objectAtIndex", index);
54+
var deviceTypeIdentifier = device("deviceType")("identifier").toString();
55+
var runtimeVersion = device("runtime")("versionString").toString();
56+
this.availableDevices[deviceTypeIdentifier] = {
57+
device: device,
58+
deviceTypeIdentifier: deviceTypeIdentifier,
59+
runtimeVersion: runtimeVersion
60+
};
61+
}
62+
}
63+
}
64+
65+
return this.availableDevices;
66+
}
67+
68+
private getDeviceByIdentifier(deviceIdentifier: string): any {
69+
var fullDeviceIdentifier = util.format("%s.%s", XCode6Simulator.DEVICE_IDENTIFIER_PREFIX, deviceIdentifier);
70+
var availableDevices = this.getAvailableDevices();
71+
if(!_.isEmpty(availableDevices)) {
72+
var selectedDevice = availableDevices[fullDeviceIdentifier];
73+
if(selectedDevice) {
74+
return selectedDevice.device;
75+
}
76+
}
77+
78+
errors.fail("Unable to find device with identifier ", deviceIdentifier);
79+
}
80+
}

lib/iphone-simulator.ts

Lines changed: 66 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@ import Future = require("fibers/future");
77
import path = require("path");
88
import util = require("util");
99

10+
import errors = require("./errors");
1011
import options = require("./options");
12+
import utils = require("./utils");
13+
import xcode6SimulatorLib = require("./iphone-simulator-xcode-6");
14+
import xcode5SimulatorLib = require("./iphone-simulator-xcode-5");
1115

1216
var $ = require("NodObjC");
1317

@@ -23,46 +27,55 @@ export class iPhoneSimulator implements IiPhoneSimulator {
2327
private static SIMULATOR_FRAMEWORK_RELATIVE_PATH = "../SharedFrameworks/DVTiPhoneSimulatorRemoteClient.framework";
2428

2529
public run(appPath: string): IFuture<void> {
26-
return (() => {
27-
if(!fs.existsSync(appPath)) {
28-
throw new Error(util.format("Path does not exist ", appPath));
29-
}
30+
if(!fs.existsSync(appPath)) {
31+
errors.fail("Path does not exist ", appPath);
32+
}
33+
34+
return this.execute(this.launch, { canRunMainLoop: true, appPath: appPath});
35+
}
36+
37+
public printDeviceTypes(): IFuture<void> {
38+
39+
var action = () => {
40+
var simulator = this.createSimulator();
41+
_.each(simulator.validDeviceIdentifiers, (identifier: any) => console.log(identifier));
42+
}
3043

44+
return this.execute(action, { canRunMainLoop: false });
45+
}
46+
47+
private execute(action: (appPath?: string) => any, opts: IExecuteOptions): IFuture<void> {
48+
return (() => {
3149
$.importFramework(iPhoneSimulator.FOUNDATION_FRAMEWORK_NAME);
3250
$.importFramework(iPhoneSimulator.APPKIT_FRAMEWORK_NAME);
3351

3452
var pool = $.NSAutoreleasePool("alloc")("init");
3553

3654
var developerDirectoryPath = this.findDeveloperDirectory().wait();
3755
if(!developerDirectoryPath) {
38-
throw new Error("Unable to find developer directory");
56+
errors.fail("Unable to find developer directory");
3957
}
4058

4159
this.loadFrameworks(developerDirectoryPath);
42-
this.launch(developerDirectoryPath, appPath);
4360

44-
$.NSRunLoop("mainRunLoop")("run");
61+
action.apply(this, [opts.appPath]);
62+
63+
if(opts.canRunMainLoop) {
64+
$.NSRunLoop("mainRunLoop")("run");
65+
}
4566

4667
pool("release");
4768
}).future<void>()();
4869
}
4970

50-
private launch(developerDirectoryPath: string, appPath: string): void {
51-
this.loadFrameworks(developerDirectoryPath);
52-
71+
private launch(appPath: string): void {
5372
var sessionDelegate = $.NSObject.extend("DTiPhoneSimulatorSessionDelegate");
5473
sessionDelegate.addMethod("session:didEndWithError:", "v@:@@", function(self: any, sel: any, sess: any, error: any) {
55-
console.log("Session ended with error: ");
56-
console.log(error);
57-
process.exit(1);
74+
iPhoneSimulator.logSessionInfo(error, "Session ended without errors.", "Session ended with error ");
75+
process.exit(0);
5876
});
59-
sessionDelegate.addMethod("session:didStart:withError:", "v@:@c@", function(self: any, sel: any, sess: any, did: any, err:any) {
60-
if(err) {
61-
console.log("Session started with error ", err);
62-
process.exit(1);
63-
} else {
64-
console.log("Session started without errors");
65-
}
77+
sessionDelegate.addMethod("session:didStart:withError:", "v@:@c@", function(self: any, sel: any, sess: any, did: any, error:any) {
78+
iPhoneSimulator.logSessionInfo(error, "Session started without errors.", "Session started with error ");
6679
});
6780
sessionDelegate.register();
6881

@@ -73,22 +86,14 @@ export class iPhoneSimulator implements IiPhoneSimulator {
7386
var sdkRoot = options.sdkRoot ? $(options.sdkRoot) : this.getClassByName("DTiPhoneSimulatorSystemRoot")("defaultRoot");
7487
config("setSimulatedSystemRoot", sdkRoot);
7588

76-
var family = 1;
77-
if(options.family) {
78-
if(options.family.toLowerCase() === "ipad") {
79-
family = 2;
89+
var simulator = this.createSimulator(config);
90+
if(options.device) {
91+
var validDeviceIdentifiers = simulator.validDeviceIdentifiers;
92+
if(!_.contains(validDeviceIdentifiers, options.device)) {
93+
errors.fail("Invalid device identifier %s. Valid device identifiers are %s.", options.device, utils.stringify(validDeviceIdentifiers));
8094
}
8195
}
82-
config("setSimulatedDeviceFamily", $.NSNumber("numberWithInt", family));
83-
84-
if(options.env) {
85-
var env = $.NSMutableDictionary("dictionary");
86-
Object.keys(env).forEach(key => {
87-
env("setObject", $(env[key]), "forKey", $(key));
88-
});
89-
90-
config("setSimulatedApplicationLaunchEnvironment", env);
91-
}
96+
simulator.setSimulatedDevice(config);
9297

9398
config("setLocalizedClientName", $("ios-sim-portable"));
9499

@@ -101,7 +106,7 @@ export class iPhoneSimulator implements IiPhoneSimulator {
101106
session("setDelegate", delegate);
102107

103108
if(!session("requestStartWithConfig", config, "timeout", timeout, "error", sessionError)) {
104-
throw new Error(util.format("Could not start simulator session ", sessionError));
109+
errors.fail("Could not start simulator session ", sessionError);
105110
}
106111
}
107112

@@ -116,7 +121,7 @@ export class iPhoneSimulator implements IiPhoneSimulator {
116121
var platformsError: string = null;
117122
var dvtPlatformClass = this.getClassByName("DVTPlatform");
118123
if(!dvtPlatformClass("loadAllPlatformsReturningError", platformsError)) {
119-
throw new Error(util.format("Unable to loadAllPlatformsReturningError ", platformsError));
124+
errors.fail("Unable to loadAllPlatformsReturningError ", platformsError);
120125
}
121126

122127
var simulatorFrameworkPath = path.join(developerDirectoryPath, iPhoneSimulator.SIMULATOR_FRAMEWORK_RELATIVE_PATH_LEGACY);
@@ -129,7 +134,7 @@ export class iPhoneSimulator implements IiPhoneSimulator {
129134
private loadFramework(frameworkPath: string) {
130135
var bundle = $.NSBundle("bundleWithPath", $(frameworkPath));
131136
if(!bundle("load")) {
132-
throw new Error(util.format("Unable to load ", frameworkPath));
137+
errors.fail("Unable to load ", frameworkPath);
133138
}
134139
}
135140

@@ -167,4 +172,28 @@ export class iPhoneSimulator implements IiPhoneSimulator {
167172
private getClassByName(className: string): any {
168173
return $.classDefinition.getClassByName(className);
169174
}
175+
176+
private static logSessionInfo(error: any, successfulMessage: string, errorMessage: string): void {
177+
if(error) {
178+
console.log(util.format("%s %s", errorMessage, error));
179+
process.exit(1);
180+
}
181+
182+
console.log(successfulMessage);
183+
}
184+
185+
private createSimulator(config?: any): ISimulator {
186+
if(!config) {
187+
config = this.getClassByName("DTiPhoneSimulatorSessionConfig")("alloc")("init")("autorelease");
188+
}
189+
190+
var simulator: ISimulator;
191+
if(_.contains(config.methods(), "setDevice:")) {
192+
simulator = new xcode6SimulatorLib.XCode6Simulator();
193+
} else {
194+
simulator = new xcode5SimulatorLib.XCode5Simulator();
195+
}
196+
197+
return simulator;
198+
}
170199
}

0 commit comments

Comments
 (0)