Skip to content

Commit 385e249

Browse files
authored
Fixed: tns emulate --devie id not working (#2399)
Fixed: cannot specify which emulator to start
1 parent 27a4d37 commit 385e249

9 files changed

+276
-4
lines changed

lib/bootstrap.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,3 +124,5 @@ $injector.requireCommand("update", "./commands/update");
124124

125125
$injector.require("iOSLogFilter", "./services/ios-log-filter");
126126
$injector.require("projectChangesService", "./services/project-changes-service");
127+
128+
$injector.require("emulatorPlatformService", "./services/emulator-platform-service");

lib/commands/devices.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
export class DevicesCommand implements ICommand {
22

3-
constructor(private $stringParameter: ICommandParameter) {}
3+
constructor(
4+
private $stringParameter: ICommandParameter,
5+
private $emulatorPlatformService: IEmulatorPlatformService,
6+
private $options: IOptions) {}
47

58
public allowedParameters: ICommandParameter[] = [this.$stringParameter];
69

710
public execute(args: string[]): IFuture<void> {
11+
if (this.$options.availableDevices) {
12+
return this.$emulatorPlatformService.listAvailableEmulators(args[0]);
13+
}
814
return $injector.resolveCommand("device").execute(args);
915
}
1016
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
interface IEmulatorInfo {
2+
name: string;
3+
version: string;
4+
platform: string;
5+
id: string;
6+
type: string;
7+
isRunning?: boolean;
8+
}
9+
10+
interface IEmulatorPlatformService {
11+
listAvailableEmulators(platform: string): IFuture<void>;
12+
getEmulatorInfo(platform: string, nameOfId: string): IFuture<IEmulatorInfo>;
13+
getiOSEmulators(): IFuture<IEmulatorInfo[]>;
14+
getAndroidEmulators(): IFuture<IEmulatorInfo[]>;
15+
startEmulator(info: IEmulatorInfo): IFuture<void>;
16+
}
Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
import * as path from "path";
2+
import * as fiberBootstrap from "../common/fiber-bootstrap";
3+
import { createTable } from "../common/helpers";
4+
import Future = require("fibers/future");
5+
6+
export class EmulatorPlatformService implements IEmulatorPlatformService {
7+
8+
constructor(
9+
private $mobileHelper: Mobile.IMobileHelper,
10+
private $childProcess: IChildProcess,
11+
private $devicesService: Mobile.IDevicesService,
12+
private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants,
13+
private $dispatcher: IFutureDispatcher,
14+
private $options: IOptions,
15+
private $logger: ILogger) {}
16+
17+
public startEmulator(info: IEmulatorInfo): IFuture<void> {
18+
if (!info.isRunning) {
19+
20+
if (this.$mobileHelper.isAndroidPlatform(info.platform)) {
21+
this.$options.avd = this.$options.device;
22+
this.$options.device = null;
23+
let platformsData: IPlatformsData = $injector.resolve("platformsData");
24+
let platformData = platformsData.getPlatformData(info.platform);
25+
let emulatorServices = platformData.emulatorServices;
26+
emulatorServices.checkAvailability();
27+
emulatorServices.checkDependencies().wait();
28+
emulatorServices.startEmulator().wait();
29+
this.$options.avd = null;
30+
return Future.fromResult();
31+
}
32+
33+
if (this.$mobileHelper.isiOSPlatform(info.platform)) {
34+
this.stopEmulator(info.platform).wait();
35+
let future = new Future<void>();
36+
this.$childProcess.exec(`open -a Simulator --args -CurrentDeviceUDID ${info.id}`).wait();
37+
let timeoutFunc = () => {
38+
fiberBootstrap.run(() => {
39+
info = this.getEmulatorInfo("ios", info.id).wait();
40+
if (info.isRunning) {
41+
this.$devicesService.initialize({ platform: info.platform, deviceId: info.id }).wait();
42+
let device = this.$devicesService.getDeviceByIdentifier(info.id);
43+
device.applicationManager.checkForApplicationUpdates().wait();
44+
future.return();
45+
return;
46+
}
47+
setTimeout(timeoutFunc, 2000);
48+
});
49+
};
50+
timeoutFunc();
51+
return future;
52+
}
53+
}
54+
55+
return Future.fromResult();
56+
}
57+
58+
private stopEmulator(platform: string): IFuture<void> {
59+
if (this.$mobileHelper.isiOSPlatform(platform)) {
60+
return this.$childProcess.exec("pkill -9 -f Simulator");
61+
}
62+
return Future.fromResult();
63+
}
64+
65+
public getEmulatorInfo(platform: string, idOrName: string): IFuture<IEmulatorInfo> {
66+
return (() => {
67+
68+
if (this.$mobileHelper.isAndroidPlatform(platform)) {
69+
let androidEmulators = this.getAndroidEmulators().wait();
70+
let found = androidEmulators.filter((info:IEmulatorInfo) => info.id === idOrName);
71+
if (found.length > 0) {
72+
return found[0];
73+
}
74+
this.$devicesService.initialize({platform: platform, deviceId: null, skipInferPlatform: true}).wait();
75+
let info:IEmulatorInfo = null;
76+
let action = (device:Mobile.IDevice) => {
77+
return (() => {
78+
if (device.deviceInfo.identifier === idOrName) {
79+
info = {
80+
id: device.deviceInfo.identifier,
81+
name: device.deviceInfo.displayName,
82+
version: device.deviceInfo.version,
83+
platform: "Android",
84+
type: "emulator",
85+
isRunning: true
86+
};
87+
}
88+
}).future<void>()();
89+
};
90+
this.$devicesService.execute(action, undefined, {allowNoDevices: true}).wait();
91+
return info;
92+
}
93+
94+
if (this.$mobileHelper.isiOSPlatform(platform)) {
95+
let emulators = this.getiOSEmulators().wait();
96+
let sdk: string = null;
97+
let versionStart = idOrName.indexOf("(");
98+
if (versionStart > 0) {
99+
sdk = idOrName.substring(versionStart+1, idOrName.indexOf(")", versionStart)).trim();
100+
idOrName = idOrName.substring(0, versionStart-1).trim();
101+
}
102+
let found = emulators.filter((info:IEmulatorInfo) => {
103+
let sdkMatch = sdk ? info.version === sdk : true;
104+
return sdkMatch && info.id === idOrName || info.name === idOrName;
105+
});
106+
return found.length>0 ? found[0] : null;
107+
}
108+
109+
return null;
110+
111+
}).future<IEmulatorInfo>()();
112+
}
113+
114+
public listAvailableEmulators(platform: string): IFuture<void> {
115+
return (() => {
116+
let emulators: IEmulatorInfo[] = [];
117+
if (!platform || this.$mobileHelper.isiOSPlatform(platform)) {
118+
let iosEmulators = this.getiOSEmulators().wait();
119+
if (iosEmulators) {
120+
emulators = emulators.concat(iosEmulators);
121+
}
122+
}
123+
if (!platform || this.$mobileHelper.isAndroidPlatform(platform)) {
124+
let androidEmulators = this.getAndroidEmulators().wait();
125+
if (androidEmulators) {
126+
emulators = emulators.concat(androidEmulators);
127+
}
128+
}
129+
this.outputEmulators("\nAvailable emulators", emulators);
130+
this.$logger.out("\nConnected devices & emulators");
131+
$injector.resolveCommand("device").execute(platform ? [platform] : []).wait();
132+
}).future<void>()();
133+
}
134+
135+
public getiOSEmulators(): IFuture<IEmulatorInfo[]> {
136+
return (()=>{
137+
let output = this.$childProcess.exec("xcrun simctl list --json").wait();
138+
let list = JSON.parse(output);
139+
let emulators: IEmulatorInfo[] = [];
140+
for (let osName in list["devices"]) {
141+
if (osName.indexOf("iOS") === -1) {
142+
continue;
143+
}
144+
let os = list["devices"][osName];
145+
let version = this.parseiOSVersion(osName);
146+
for (let device of os) {
147+
if (device["availability"] !== "(available)") {
148+
continue;
149+
}
150+
let emulatorInfo: IEmulatorInfo = {
151+
id: device["udid"],
152+
name: device["name"],
153+
isRunning: device["state"] === "Booted",
154+
type: "simulator",
155+
version: version,
156+
platform: "iOS"
157+
};
158+
emulators.push(emulatorInfo);
159+
}
160+
}
161+
return emulators;
162+
}).future<IEmulatorInfo[]>()();
163+
}
164+
165+
public getAndroidEmulators(): IFuture<IEmulatorInfo[]> {
166+
return (() => {
167+
let androidPath = path.join(process.env.ANDROID_HOME, "tools", "android");
168+
let text:string = this.$childProcess.exec(`${androidPath} list avd`).wait();
169+
let notLoadedIndex = text.indexOf("The following");
170+
if (notLoadedIndex > 0) {
171+
text = text.substring(0, notLoadedIndex);
172+
}
173+
let textBlocks = text.split("---------");
174+
let emulators: IEmulatorInfo[] = [];
175+
for (let block of textBlocks) {
176+
let lines = block.split("\n");
177+
let info:IEmulatorInfo = { name: "", version: "", id: "", platform: "Android", type: "Emulator" };
178+
for (let line of lines) {
179+
if (line.indexOf("Target") >= 0) {
180+
info.version = line.substring(line.indexOf(":")+1).replace("Android", "").trim();
181+
}
182+
if (line.indexOf("Name") >= 0) {
183+
info.id = line.substring(line.indexOf(":")+1).trim();
184+
}
185+
if (line.indexOf("Device") >= 0) {
186+
info.name = line.substring(line.indexOf(":")+1).trim();
187+
}
188+
info.isRunning = false;
189+
}
190+
emulators.push(info);
191+
}
192+
return emulators;
193+
}).future<IEmulatorInfo[]>()();
194+
}
195+
196+
private parseiOSVersion(osName: string): string {
197+
osName = osName.replace("com.apple.CoreSimulator.SimRuntime.iOS-", "");
198+
osName = osName.replace(/-/g, ".");
199+
osName = osName.replace("iOS", "");
200+
osName = osName.trim();
201+
return osName;
202+
}
203+
204+
private outputEmulators(title: string, emulators: IEmulatorInfo[]) {
205+
this.$logger.out(title);
206+
let table: any = createTable(["Device Name", "Platform", "Version", "Device Identifier"], []);
207+
for (let info of emulators) {
208+
table.push([info.name, info.platform, info.version, info.id]);
209+
}
210+
this.$logger.out(table.toString());
211+
}
212+
}
213+
$injector.register("emulatorPlatformService", EmulatorPlatformService);

lib/services/platform-service.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ export class PlatformService implements IPlatformService {
3737
private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants,
3838
private $deviceAppDataFactory: Mobile.IDeviceAppDataFactory,
3939
private $projectChangesService: IProjectChangesService,
40+
private $emulatorPlatformService: IEmulatorPlatformService,
4041
private $childProcess: IChildProcess) { }
4142

4243
public addPlatforms(platforms: string[]): IFuture<void> {
@@ -432,6 +433,7 @@ export class PlatformService implements IPlatformService {
432433
this.$logger.out(`Successfully started on device with identifier '${device.deviceInfo.identifier}'.`);
433434
}).future<void>()();
434435
};
436+
this.$devicesService.initialize({ platform: platform, deviceId: this.$options.device }).wait();
435437
this.$devicesService.execute(action, this.getCanExecuteAction(platform)).wait();
436438
}).future<void>()();
437439
}
@@ -442,9 +444,19 @@ export class PlatformService implements IPlatformService {
442444
return Future.fromResult();
443445
}
444446
if (this.$options.availableDevices) {
445-
return $injector.resolveCommand("device").execute([platform]);
447+
return this.$emulatorPlatformService.listAvailableEmulators(platform);
448+
}
449+
if (this.$options.device) {
450+
let info = this.$emulatorPlatformService.getEmulatorInfo(platform, this.$options.device).wait();
451+
if (info) {
452+
if (!info.isRunning) {
453+
this.$emulatorPlatformService.startEmulator(info).wait();
454+
}
455+
this.$options.device = null;
456+
}
446457
}
447458
this.$options.emulator = true;
459+
this.deployPlatform(platform).wait();
448460
return this.runPlatform(platform);
449461
}
450462

test/npm-support.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ function createTestInjector(): IInjector {
7979
testInjector.register("xmlValidator", XmlValidator);
8080
testInjector.register("config", StaticConfigLib.Configuration);
8181
testInjector.register("projectChangesService", ProjectChangesLib.ProjectChangesService);
82-
82+
testInjector.register("emulatorPlatformService", stubs.EmulatorPlatformService);
8383
return testInjector;
8484
}
8585

test/platform-commands.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ function createTestInjector() {
141141
testInjector.register("npm", {});
142142
testInjector.register("childProcess", ChildProcessLib.ChildProcess);
143143
testInjector.register("projectChangesService", ProjectChangesLib.ProjectChangesService);
144-
144+
testInjector.register("emulatorPlatformService", stubs.EmulatorPlatformService);
145145
return testInjector;
146146
}
147147

test/platform-service.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ function createTestInjector() {
8080
});
8181
testInjector.register("childProcess", ChildProcessLib.ChildProcess);
8282
testInjector.register("projectChangesService", ProjectChangesLib.ProjectChangesService);
83+
testInjector.register("emulatorPlatformService", stubs.EmulatorPlatformService);
8384

8485
return testInjector;
8586
}

test/stubs.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -667,3 +667,25 @@ export class PlatformServiceStub implements IPlatformService {
667667
return Future.fromResult("");
668668
}
669669
}
670+
671+
export class EmulatorPlatformService implements IEmulatorPlatformService {
672+
public listAvailableEmulators(platform: string): IFuture<void> {
673+
return Future.fromResult();
674+
}
675+
676+
public getEmulatorInfo(platform: string, nameOfId: string): IFuture<IEmulatorInfo> {
677+
return Future.fromResult(null);
678+
}
679+
680+
public getiOSEmulators(): IFuture<IEmulatorInfo[]> {
681+
return Future.fromResult(null);
682+
}
683+
684+
public getAndroidEmulators(): IFuture<IEmulatorInfo[]> {
685+
return Future.fromResult(null);
686+
}
687+
688+
public startEmulator(info: IEmulatorInfo): IFuture<void> {
689+
return Future.fromResult();
690+
}
691+
}

0 commit comments

Comments
 (0)