xref: /aosp_15_r20/external/cronet/testing/iossim/iossim.mm (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1*6777b538SAndroid Build Coastguard Worker// Copyright 2012 The Chromium Authors
2*6777b538SAndroid Build Coastguard Worker// Use of this source code is governed by a BSD-style license that can be
3*6777b538SAndroid Build Coastguard Worker// found in the LICENSE file.
4*6777b538SAndroid Build Coastguard Worker
5*6777b538SAndroid Build Coastguard Worker#import <Foundation/Foundation.h>
6*6777b538SAndroid Build Coastguard Worker#include <getopt.h>
7*6777b538SAndroid Build Coastguard Worker
8*6777b538SAndroid Build Coastguard Worker#include <string>
9*6777b538SAndroid Build Coastguard Worker
10*6777b538SAndroid Build Coastguard Workernamespace {
11*6777b538SAndroid Build Coastguard Worker
12*6777b538SAndroid Build Coastguard Workervoid PrintUsage() {
13*6777b538SAndroid Build Coastguard Worker  fprintf(
14*6777b538SAndroid Build Coastguard Worker      stderr,
15*6777b538SAndroid Build Coastguard Worker      "Usage: iossim [-d device] [-s sdk_version] <app_path> <xctest_path>\n"
16*6777b538SAndroid Build Coastguard Worker      "  where <app_path> is the path to the .app directory and <xctest_path> "
17*6777b538SAndroid Build Coastguard Worker      "is the path to an optional xctest bundle.\n"
18*6777b538SAndroid Build Coastguard Worker      "Options:\n"
19*6777b538SAndroid Build Coastguard Worker      "  -u  Specifies the device udid to use. Will use -d, -s values to get "
20*6777b538SAndroid Build Coastguard Worker      "devices if not specified.\n"
21*6777b538SAndroid Build Coastguard Worker      "  -d  Specifies the device (must be one of the values from the iOS "
22*6777b538SAndroid Build Coastguard Worker      "Simulator's Hardware -> Device menu. Defaults to 'iPhone 6s'.\n"
23*6777b538SAndroid Build Coastguard Worker      "  -w  Wipe the device's contents and settings before running the "
24*6777b538SAndroid Build Coastguard Worker      "test.\n"
25*6777b538SAndroid Build Coastguard Worker      "  -e  Specifies an environment key=value pair that will be"
26*6777b538SAndroid Build Coastguard Worker      " set in the simulated application's environment.\n"
27*6777b538SAndroid Build Coastguard Worker      "  -t  Specifies a test or test suite that should be included in the "
28*6777b538SAndroid Build Coastguard Worker      "test run. All other tests will be excluded from this run. This is "
29*6777b538SAndroid Build Coastguard Worker      "incompatible with -i.\n"
30*6777b538SAndroid Build Coastguard Worker      "  -c  Specifies command line flags to pass to application.\n"
31*6777b538SAndroid Build Coastguard Worker      "  -p  Print the device's home directory, does not run a test.\n"
32*6777b538SAndroid Build Coastguard Worker      "  -s  Specifies the SDK version to use (e.g '9.3'). Will use system "
33*6777b538SAndroid Build Coastguard Worker      "default if not specified.\n"
34*6777b538SAndroid Build Coastguard Worker      "  -v  Be more verbose, showing all the xcrun commands we call\n"
35*6777b538SAndroid Build Coastguard Worker      "  -k  When to kill the iOS Simulator : before, after, both, never "
36*6777b538SAndroid Build Coastguard Worker      "(default: both)\n"
37*6777b538SAndroid Build Coastguard Worker      "  -i  Use iossim instead of xcodebuild (disables all xctest "
38*6777b538SAndroid Build Coastguard Worker      "features). This is incompatible with -t.\n");
39*6777b538SAndroid Build Coastguard Worker}
40*6777b538SAndroid Build Coastguard Worker
41*6777b538SAndroid Build Coastguard Worker// Exit status codes.
42*6777b538SAndroid Build Coastguard Workerconst int kExitSuccess = EXIT_SUCCESS;
43*6777b538SAndroid Build Coastguard Workerconst int kExitInvalidArguments = 2;
44*6777b538SAndroid Build Coastguard Worker
45*6777b538SAndroid Build Coastguard Workervoid LogError(NSString* format, ...) {
46*6777b538SAndroid Build Coastguard Worker  va_list list;
47*6777b538SAndroid Build Coastguard Worker  va_start(list, format);
48*6777b538SAndroid Build Coastguard Worker
49*6777b538SAndroid Build Coastguard Worker  NSString* message = [[NSString alloc] initWithFormat:format arguments:list];
50*6777b538SAndroid Build Coastguard Worker
51*6777b538SAndroid Build Coastguard Worker  NSLog(@"ERROR: %@", message);
52*6777b538SAndroid Build Coastguard Worker
53*6777b538SAndroid Build Coastguard Worker  va_end(list);
54*6777b538SAndroid Build Coastguard Worker}
55*6777b538SAndroid Build Coastguard Worker
56*6777b538SAndroid Build Coastguard Worker}
57*6777b538SAndroid Build Coastguard Worker
58*6777b538SAndroid Build Coastguard Workertypedef enum {
59*6777b538SAndroid Build Coastguard Worker  KILL_NEVER = 0,
60*6777b538SAndroid Build Coastguard Worker  KILL_BEFORE = 1 << 0,
61*6777b538SAndroid Build Coastguard Worker  KILL_AFTER = 1 << 1,
62*6777b538SAndroid Build Coastguard Worker  KILL_BOTH = KILL_BEFORE | KILL_AFTER,
63*6777b538SAndroid Build Coastguard Worker} SimulatorKill;
64*6777b538SAndroid Build Coastguard Worker
65*6777b538SAndroid Build Coastguard Worker// See https://stackoverflow.com/a/51895129 and
66*6777b538SAndroid Build Coastguard Worker// https://github.com/facebook/xctool/pull/159/files.
67*6777b538SAndroid Build Coastguard Worker@interface NSTask (PrivateAPI)
68*6777b538SAndroid Build Coastguard Worker- (void)setStartsNewProcessGroup:(BOOL)startsNewProcessGroup;
69*6777b538SAndroid Build Coastguard Worker@end
70*6777b538SAndroid Build Coastguard Worker
71*6777b538SAndroid Build Coastguard Worker// Wrap boiler plate calls to xcrun NSTasks.
72*6777b538SAndroid Build Coastguard Worker@interface XCRunTask : NSObject
73*6777b538SAndroid Build Coastguard Worker- (instancetype)initWithArguments:(NSArray*)arguments;
74*6777b538SAndroid Build Coastguard Worker- (void)run:(bool)verbose;
75*6777b538SAndroid Build Coastguard Worker- (void)launch:(bool)verbose;
76*6777b538SAndroid Build Coastguard Worker- (void)setStandardOutput:(id)output;
77*6777b538SAndroid Build Coastguard Worker- (void)setStandardError:(id)error;
78*6777b538SAndroid Build Coastguard Worker- (int)terminationStatus;
79*6777b538SAndroid Build Coastguard Worker@end
80*6777b538SAndroid Build Coastguard Worker
81*6777b538SAndroid Build Coastguard Worker@implementation XCRunTask {
82*6777b538SAndroid Build Coastguard Worker  NSTask* __strong _task;
83*6777b538SAndroid Build Coastguard Worker}
84*6777b538SAndroid Build Coastguard Worker
85*6777b538SAndroid Build Coastguard Worker- (instancetype)initWithArguments:(NSArray*)arguments {
86*6777b538SAndroid Build Coastguard Worker  self = [super init];
87*6777b538SAndroid Build Coastguard Worker  if (self) {
88*6777b538SAndroid Build Coastguard Worker    _task = [[NSTask alloc] init];
89*6777b538SAndroid Build Coastguard Worker    [_task setStartsNewProcessGroup:NO];
90*6777b538SAndroid Build Coastguard Worker    _task.launchPath = @"/usr/bin/xcrun";
91*6777b538SAndroid Build Coastguard Worker    _task.arguments = arguments;
92*6777b538SAndroid Build Coastguard Worker  }
93*6777b538SAndroid Build Coastguard Worker  return self;
94*6777b538SAndroid Build Coastguard Worker}
95*6777b538SAndroid Build Coastguard Worker
96*6777b538SAndroid Build Coastguard Worker- (void)setStandardOutput:(id)output {
97*6777b538SAndroid Build Coastguard Worker  _task.standardOutput = output;
98*6777b538SAndroid Build Coastguard Worker}
99*6777b538SAndroid Build Coastguard Worker
100*6777b538SAndroid Build Coastguard Worker- (void)setStandardError:(id)error {
101*6777b538SAndroid Build Coastguard Worker  _task.standardError = error;
102*6777b538SAndroid Build Coastguard Worker}
103*6777b538SAndroid Build Coastguard Worker
104*6777b538SAndroid Build Coastguard Worker- (int)terminationStatus {
105*6777b538SAndroid Build Coastguard Worker  return _task.terminationStatus;
106*6777b538SAndroid Build Coastguard Worker}
107*6777b538SAndroid Build Coastguard Worker
108*6777b538SAndroid Build Coastguard Worker- (void)run:(bool)verbose {
109*6777b538SAndroid Build Coastguard Worker  if (verbose) {
110*6777b538SAndroid Build Coastguard Worker    NSLog(@"Running xcrun %@", [_task.arguments componentsJoinedByString:@" "]);
111*6777b538SAndroid Build Coastguard Worker  }
112*6777b538SAndroid Build Coastguard Worker  [_task launch];
113*6777b538SAndroid Build Coastguard Worker  [_task waitUntilExit];
114*6777b538SAndroid Build Coastguard Worker}
115*6777b538SAndroid Build Coastguard Worker
116*6777b538SAndroid Build Coastguard Worker- (void)launch:(bool)verbose {
117*6777b538SAndroid Build Coastguard Worker  if (verbose) {
118*6777b538SAndroid Build Coastguard Worker    NSLog(@"Running xcrun %@", [_task.arguments componentsJoinedByString:@" "]);
119*6777b538SAndroid Build Coastguard Worker  }
120*6777b538SAndroid Build Coastguard Worker  [_task launch];
121*6777b538SAndroid Build Coastguard Worker}
122*6777b538SAndroid Build Coastguard Worker
123*6777b538SAndroid Build Coastguard Worker- (void)waitUntilExit {
124*6777b538SAndroid Build Coastguard Worker  [_task waitUntilExit];
125*6777b538SAndroid Build Coastguard Worker}
126*6777b538SAndroid Build Coastguard Worker
127*6777b538SAndroid Build Coastguard Worker@end
128*6777b538SAndroid Build Coastguard Worker
129*6777b538SAndroid Build Coastguard Worker// Return array of available iOS runtime dictionaries.  Unavailable (old Xcode
130*6777b538SAndroid Build Coastguard Worker// versions) or other runtimes (tvOS, watchOS) are removed.
131*6777b538SAndroid Build Coastguard WorkerNSArray* Runtimes(NSDictionary* simctl_list) {
132*6777b538SAndroid Build Coastguard Worker  NSMutableArray* runtimes = [simctl_list[@"runtimes"] mutableCopy];
133*6777b538SAndroid Build Coastguard Worker  for (NSDictionary* runtime in simctl_list[@"runtimes"]) {
134*6777b538SAndroid Build Coastguard Worker    BOOL available =
135*6777b538SAndroid Build Coastguard Worker        [runtime[@"availability"] isEqualToString:@"(available)"] ||
136*6777b538SAndroid Build Coastguard Worker        runtime[@"isAvailable"];
137*6777b538SAndroid Build Coastguard Worker
138*6777b538SAndroid Build Coastguard Worker    if (![runtime[@"identifier"]
139*6777b538SAndroid Build Coastguard Worker            hasPrefix:@"com.apple.CoreSimulator.SimRuntime.iOS"] ||
140*6777b538SAndroid Build Coastguard Worker        !available) {
141*6777b538SAndroid Build Coastguard Worker      [runtimes removeObject:runtime];
142*6777b538SAndroid Build Coastguard Worker    }
143*6777b538SAndroid Build Coastguard Worker  }
144*6777b538SAndroid Build Coastguard Worker  return runtimes;
145*6777b538SAndroid Build Coastguard Worker}
146*6777b538SAndroid Build Coastguard Worker
147*6777b538SAndroid Build Coastguard Worker// Return array of device dictionaries.
148*6777b538SAndroid Build Coastguard WorkerNSArray* Devices(NSDictionary* simctl_list) {
149*6777b538SAndroid Build Coastguard Worker  NSMutableArray* devicetypes = [simctl_list[@"devicetypes"] mutableCopy];
150*6777b538SAndroid Build Coastguard Worker  for (NSDictionary* devicetype in simctl_list[@"devicetypes"]) {
151*6777b538SAndroid Build Coastguard Worker    if (![devicetype[@"identifier"]
152*6777b538SAndroid Build Coastguard Worker            hasPrefix:@"com.apple.CoreSimulator.SimDeviceType.iPad"] &&
153*6777b538SAndroid Build Coastguard Worker        ![devicetype[@"identifier"]
154*6777b538SAndroid Build Coastguard Worker            hasPrefix:@"com.apple.CoreSimulator.SimDeviceType.iPhone"]) {
155*6777b538SAndroid Build Coastguard Worker      [devicetypes removeObject:devicetype];
156*6777b538SAndroid Build Coastguard Worker    }
157*6777b538SAndroid Build Coastguard Worker  }
158*6777b538SAndroid Build Coastguard Worker  return devicetypes;
159*6777b538SAndroid Build Coastguard Worker}
160*6777b538SAndroid Build Coastguard Worker
161*6777b538SAndroid Build Coastguard Worker// Get list of devices, runtimes, etc from sim_ctl.
162*6777b538SAndroid Build Coastguard WorkerNSDictionary* GetSimulatorList(bool verbose) {
163*6777b538SAndroid Build Coastguard Worker  XCRunTask* task =
164*6777b538SAndroid Build Coastguard Worker      [[XCRunTask alloc] initWithArguments:@[ @"simctl", @"list", @"-j" ]];
165*6777b538SAndroid Build Coastguard Worker  NSPipe* out = [NSPipe pipe];
166*6777b538SAndroid Build Coastguard Worker  task.standardOutput = out;
167*6777b538SAndroid Build Coastguard Worker
168*6777b538SAndroid Build Coastguard Worker  // In the rest of the this file we read from the pipe after -waitUntilExit
169*6777b538SAndroid Build Coastguard Worker  // (We normally wrap -launch and -waitUntilExit in one -run method).  However,
170*6777b538SAndroid Build Coastguard Worker  // on some swarming slaves this led to a hang on simctl's pipe.  Since the
171*6777b538SAndroid Build Coastguard Worker  // output of simctl is so instant, reading it before exit seems to work, and
172*6777b538SAndroid Build Coastguard Worker  // seems to avoid the hang.
173*6777b538SAndroid Build Coastguard Worker  [task launch:verbose];
174*6777b538SAndroid Build Coastguard Worker  NSData* data = [out.fileHandleForReading readDataToEndOfFile];
175*6777b538SAndroid Build Coastguard Worker  [task waitUntilExit];
176*6777b538SAndroid Build Coastguard Worker
177*6777b538SAndroid Build Coastguard Worker  NSError* error = nil;
178*6777b538SAndroid Build Coastguard Worker  return [NSJSONSerialization JSONObjectWithData:data
179*6777b538SAndroid Build Coastguard Worker                                         options:kNilOptions
180*6777b538SAndroid Build Coastguard Worker                                           error:&error];
181*6777b538SAndroid Build Coastguard Worker}
182*6777b538SAndroid Build Coastguard Worker
183*6777b538SAndroid Build Coastguard Worker// List supported runtimes and devices.
184*6777b538SAndroid Build Coastguard Workervoid PrintSupportedDevices(NSDictionary* simctl_list) {
185*6777b538SAndroid Build Coastguard Worker  printf("\niOS devices:\n");
186*6777b538SAndroid Build Coastguard Worker  for (NSDictionary* type in Devices(simctl_list)) {
187*6777b538SAndroid Build Coastguard Worker    printf("%s\n", [type[@"name"] UTF8String]);
188*6777b538SAndroid Build Coastguard Worker  }
189*6777b538SAndroid Build Coastguard Worker  printf("\nruntimes:\n");
190*6777b538SAndroid Build Coastguard Worker  for (NSDictionary* runtime in Runtimes(simctl_list)) {
191*6777b538SAndroid Build Coastguard Worker    printf("%s\n", [runtime[@"version"] UTF8String]);
192*6777b538SAndroid Build Coastguard Worker  }
193*6777b538SAndroid Build Coastguard Worker}
194*6777b538SAndroid Build Coastguard Worker
195*6777b538SAndroid Build Coastguard Worker// Expand path to absolute path.
196*6777b538SAndroid Build Coastguard WorkerNSString* ResolvePath(NSString* path) {
197*6777b538SAndroid Build Coastguard Worker  path = path.stringByExpandingTildeInPath;
198*6777b538SAndroid Build Coastguard Worker  path = path.stringByStandardizingPath;
199*6777b538SAndroid Build Coastguard Worker  const char* cpath = path.UTF8String;
200*6777b538SAndroid Build Coastguard Worker  char* resolved_name = nullptr;
201*6777b538SAndroid Build Coastguard Worker  char* abs_path = realpath(cpath, resolved_name);
202*6777b538SAndroid Build Coastguard Worker  if (abs_path == nullptr) {
203*6777b538SAndroid Build Coastguard Worker    return nil;
204*6777b538SAndroid Build Coastguard Worker  }
205*6777b538SAndroid Build Coastguard Worker  return @(abs_path);
206*6777b538SAndroid Build Coastguard Worker}
207*6777b538SAndroid Build Coastguard Worker
208*6777b538SAndroid Build Coastguard Worker// Search |simctl_list| for a udid matching |device_name| and |sdk_version|.
209*6777b538SAndroid Build Coastguard WorkerNSString* GetDeviceBySDKAndName(NSDictionary* simctl_list,
210*6777b538SAndroid Build Coastguard Worker                                NSString* device_name,
211*6777b538SAndroid Build Coastguard Worker                                NSString* sdk_version) {
212*6777b538SAndroid Build Coastguard Worker  NSString* sdk = nil;
213*6777b538SAndroid Build Coastguard Worker  NSString* name = nil;
214*6777b538SAndroid Build Coastguard Worker  // Get runtime identifier based on version property to handle
215*6777b538SAndroid Build Coastguard Worker  // cases when version and identifier are not the same,
216*6777b538SAndroid Build Coastguard Worker  // e.g. below identifer is *13-2 but version is 13.2.2
217*6777b538SAndroid Build Coastguard Worker  // {
218*6777b538SAndroid Build Coastguard Worker  //   "version" : "13.2.2",
219*6777b538SAndroid Build Coastguard Worker  //   "bundlePath" : "path"
220*6777b538SAndroid Build Coastguard Worker  //   "identifier" : "com.apple.CoreSimulator.SimRuntime.iOS-13-2",
221*6777b538SAndroid Build Coastguard Worker  //   "buildversion" : "17K90"
222*6777b538SAndroid Build Coastguard Worker  // }
223*6777b538SAndroid Build Coastguard Worker  for (NSDictionary* runtime in Runtimes(simctl_list)) {
224*6777b538SAndroid Build Coastguard Worker    if ([runtime[@"version"] isEqualToString:sdk_version]) {
225*6777b538SAndroid Build Coastguard Worker      sdk = runtime[@"identifier"];
226*6777b538SAndroid Build Coastguard Worker      name = runtime[@"name"];
227*6777b538SAndroid Build Coastguard Worker      break;
228*6777b538SAndroid Build Coastguard Worker    }
229*6777b538SAndroid Build Coastguard Worker  }
230*6777b538SAndroid Build Coastguard Worker  if (sdk == nil) {
231*6777b538SAndroid Build Coastguard Worker    printf("\nDid not find Runtime with specified version.\n");
232*6777b538SAndroid Build Coastguard Worker    PrintSupportedDevices(simctl_list);
233*6777b538SAndroid Build Coastguard Worker    exit(kExitInvalidArguments);
234*6777b538SAndroid Build Coastguard Worker  }
235*6777b538SAndroid Build Coastguard Worker  NSArray* devices = [simctl_list[@"devices"] objectForKey:sdk];
236*6777b538SAndroid Build Coastguard Worker  if (devices == nil || devices.count == 0) {
237*6777b538SAndroid Build Coastguard Worker    // Specific for XCode 10.1 and lower,
238*6777b538SAndroid Build Coastguard Worker    // where name from 'runtimes' uses as a key in 'devices'.
239*6777b538SAndroid Build Coastguard Worker    devices = [simctl_list[@"devices"] objectForKey:name];
240*6777b538SAndroid Build Coastguard Worker  }
241*6777b538SAndroid Build Coastguard Worker  for (NSDictionary* device in devices) {
242*6777b538SAndroid Build Coastguard Worker    if ([device[@"name"] isEqualToString:device_name]) {
243*6777b538SAndroid Build Coastguard Worker      return device[@"udid"];
244*6777b538SAndroid Build Coastguard Worker    }
245*6777b538SAndroid Build Coastguard Worker  }
246*6777b538SAndroid Build Coastguard Worker  return nil;
247*6777b538SAndroid Build Coastguard Worker}
248*6777b538SAndroid Build Coastguard Worker
249*6777b538SAndroid Build Coastguard Worker// Create and return a device udid of |device| and |sdk_version|.
250*6777b538SAndroid Build Coastguard WorkerNSString* CreateDeviceBySDKAndName(NSString* device,
251*6777b538SAndroid Build Coastguard Worker                                   NSString* sdk_version,
252*6777b538SAndroid Build Coastguard Worker                                   bool verbose) {
253*6777b538SAndroid Build Coastguard Worker  NSString* sdk = [@"iOS" stringByAppendingString:sdk_version];
254*6777b538SAndroid Build Coastguard Worker  XCRunTask* create = [[XCRunTask alloc]
255*6777b538SAndroid Build Coastguard Worker      initWithArguments:@[ @"simctl", @"create", device, device, sdk ]];
256*6777b538SAndroid Build Coastguard Worker  [create run:verbose];
257*6777b538SAndroid Build Coastguard Worker
258*6777b538SAndroid Build Coastguard Worker  NSDictionary* simctl_list = GetSimulatorList(verbose);
259*6777b538SAndroid Build Coastguard Worker  return GetDeviceBySDKAndName(simctl_list, device, sdk_version);
260*6777b538SAndroid Build Coastguard Worker}
261*6777b538SAndroid Build Coastguard Worker
262*6777b538SAndroid Build Coastguard Workerbool FindDeviceByUDID(NSDictionary* simctl_list, NSString* udid) {
263*6777b538SAndroid Build Coastguard Worker  NSDictionary* devices_table = simctl_list[@"devices"];
264*6777b538SAndroid Build Coastguard Worker  for (id runtimes in devices_table) {
265*6777b538SAndroid Build Coastguard Worker    NSArray* devices = devices_table[runtimes];
266*6777b538SAndroid Build Coastguard Worker    for (NSDictionary* device in devices) {
267*6777b538SAndroid Build Coastguard Worker      if ([device[@"udid"] isEqualToString:udid]) {
268*6777b538SAndroid Build Coastguard Worker        return true;
269*6777b538SAndroid Build Coastguard Worker      }
270*6777b538SAndroid Build Coastguard Worker    }
271*6777b538SAndroid Build Coastguard Worker  }
272*6777b538SAndroid Build Coastguard Worker  return false;
273*6777b538SAndroid Build Coastguard Worker}
274*6777b538SAndroid Build Coastguard Worker
275*6777b538SAndroid Build Coastguard Worker// Prints the HOME environment variable for a device.  Used by the bots to
276*6777b538SAndroid Build Coastguard Worker// package up all the test data.
277*6777b538SAndroid Build Coastguard Workervoid PrintDeviceHome(NSString* udid, bool verbose) {
278*6777b538SAndroid Build Coastguard Worker  XCRunTask* task = [[XCRunTask alloc]
279*6777b538SAndroid Build Coastguard Worker      initWithArguments:@[ @"simctl", @"getenv", udid, @"HOME" ]];
280*6777b538SAndroid Build Coastguard Worker  [task run:verbose];
281*6777b538SAndroid Build Coastguard Worker}
282*6777b538SAndroid Build Coastguard Worker
283*6777b538SAndroid Build Coastguard Worker// Erase a device, used by the bots before a clean test run.
284*6777b538SAndroid Build Coastguard Workervoid WipeDevice(NSString* udid, bool verbose) {
285*6777b538SAndroid Build Coastguard Worker  XCRunTask* shutdown =
286*6777b538SAndroid Build Coastguard Worker      [[XCRunTask alloc] initWithArguments:@[ @"simctl", @"shutdown", udid ]];
287*6777b538SAndroid Build Coastguard Worker  shutdown.standardOutput = nil;
288*6777b538SAndroid Build Coastguard Worker  shutdown.standardError = nil;
289*6777b538SAndroid Build Coastguard Worker  [shutdown run:verbose];
290*6777b538SAndroid Build Coastguard Worker
291*6777b538SAndroid Build Coastguard Worker  XCRunTask* erase =
292*6777b538SAndroid Build Coastguard Worker      [[XCRunTask alloc] initWithArguments:@[ @"simctl", @"erase", udid ]];
293*6777b538SAndroid Build Coastguard Worker  [erase run:verbose];
294*6777b538SAndroid Build Coastguard Worker}
295*6777b538SAndroid Build Coastguard Worker
296*6777b538SAndroid Build Coastguard Workervoid KillSimulator(bool verbose) {
297*6777b538SAndroid Build Coastguard Worker  XCRunTask* task =
298*6777b538SAndroid Build Coastguard Worker      [[XCRunTask alloc] initWithArguments:@[ @"killall", @"Simulator" ]];
299*6777b538SAndroid Build Coastguard Worker  task.standardOutput = nil;
300*6777b538SAndroid Build Coastguard Worker  task.standardError = nil;
301*6777b538SAndroid Build Coastguard Worker  [task run:verbose];
302*6777b538SAndroid Build Coastguard Worker}
303*6777b538SAndroid Build Coastguard Worker
304*6777b538SAndroid Build Coastguard WorkerNSString* GetBundleIdentifierFromPath(NSString* app_path) {
305*6777b538SAndroid Build Coastguard Worker  NSFileManager* file_manager = [NSFileManager defaultManager];
306*6777b538SAndroid Build Coastguard Worker  NSString* info_plist_path =
307*6777b538SAndroid Build Coastguard Worker      [app_path stringByAppendingPathComponent:@"Info.plist"];
308*6777b538SAndroid Build Coastguard Worker  if (![file_manager fileExistsAtPath:info_plist_path]) {
309*6777b538SAndroid Build Coastguard Worker    return nil;
310*6777b538SAndroid Build Coastguard Worker  }
311*6777b538SAndroid Build Coastguard Worker
312*6777b538SAndroid Build Coastguard Worker  NSDictionary* info_dictionary =
313*6777b538SAndroid Build Coastguard Worker      [NSDictionary dictionaryWithContentsOfFile:info_plist_path];
314*6777b538SAndroid Build Coastguard Worker  NSString* bundle_identifier = info_dictionary[@"CFBundleIdentifier"];
315*6777b538SAndroid Build Coastguard Worker  return bundle_identifier;
316*6777b538SAndroid Build Coastguard Worker}
317*6777b538SAndroid Build Coastguard Worker
318*6777b538SAndroid Build Coastguard Workerint RunSimCtl(NSArray* arguments, bool verbose) {
319*6777b538SAndroid Build Coastguard Worker  XCRunTask* task = [[XCRunTask alloc]
320*6777b538SAndroid Build Coastguard Worker      initWithArguments:[@[ @"simctl" ]
321*6777b538SAndroid Build Coastguard Worker                            arrayByAddingObjectsFromArray:arguments]];
322*6777b538SAndroid Build Coastguard Worker  [task run:verbose];
323*6777b538SAndroid Build Coastguard Worker  int ret = [task terminationStatus];
324*6777b538SAndroid Build Coastguard Worker  if (ret) {
325*6777b538SAndroid Build Coastguard Worker    NSLog(@"Warning: the following command failed: xcrun simctl %@",
326*6777b538SAndroid Build Coastguard Worker          [arguments componentsJoinedByString:@" "]);
327*6777b538SAndroid Build Coastguard Worker  }
328*6777b538SAndroid Build Coastguard Worker  return ret;
329*6777b538SAndroid Build Coastguard Worker}
330*6777b538SAndroid Build Coastguard Worker
331*6777b538SAndroid Build Coastguard Workervoid PrepareWebTests(NSString* udid, NSString* app_path, bool verbose) {
332*6777b538SAndroid Build Coastguard Worker  NSString* bundle_identifier = GetBundleIdentifierFromPath(app_path);
333*6777b538SAndroid Build Coastguard Worker
334*6777b538SAndroid Build Coastguard Worker  RunSimCtl(@[ @"uninstall", udid, bundle_identifier ], verbose);
335*6777b538SAndroid Build Coastguard Worker  RunSimCtl(@[ @"install", udid, app_path ], verbose);
336*6777b538SAndroid Build Coastguard Worker}
337*6777b538SAndroid Build Coastguard Worker
338*6777b538SAndroid Build Coastguard Workerint RunWebTest(NSString* app_path,
339*6777b538SAndroid Build Coastguard Worker               NSString* udid,
340*6777b538SAndroid Build Coastguard Worker               NSMutableArray* cmd_args,
341*6777b538SAndroid Build Coastguard Worker               bool verbose) {
342*6777b538SAndroid Build Coastguard Worker  NSMutableArray* arguments = [NSMutableArray array];
343*6777b538SAndroid Build Coastguard Worker  [arguments addObject:@"simctl"];
344*6777b538SAndroid Build Coastguard Worker  [arguments addObject:@"launch"];
345*6777b538SAndroid Build Coastguard Worker  [arguments addObject:@"--console"];
346*6777b538SAndroid Build Coastguard Worker  [arguments addObject:@"--terminate-running-process"];
347*6777b538SAndroid Build Coastguard Worker  [arguments addObject:udid];
348*6777b538SAndroid Build Coastguard Worker  [arguments addObject:GetBundleIdentifierFromPath(app_path)];
349*6777b538SAndroid Build Coastguard Worker  if (cmd_args.count == 1) {
350*6777b538SAndroid Build Coastguard Worker    for (NSString* arg in [cmd_args[0] componentsSeparatedByString:@" "]) {
351*6777b538SAndroid Build Coastguard Worker      [arguments addObject:arg];
352*6777b538SAndroid Build Coastguard Worker    }
353*6777b538SAndroid Build Coastguard Worker  }
354*6777b538SAndroid Build Coastguard Worker  [arguments addObject:@"-"];
355*6777b538SAndroid Build Coastguard Worker  XCRunTask* task = [[XCRunTask alloc] initWithArguments:arguments];
356*6777b538SAndroid Build Coastguard Worker
357*6777b538SAndroid Build Coastguard Worker  // The following stderr message causes a lot of test faiures on the web
358*6777b538SAndroid Build Coastguard Worker  // tests. Strip the message here.
359*6777b538SAndroid Build Coastguard Worker  NSArray* ignore_strings = @[ @"Class SwapLayerEAGL" ];
360*6777b538SAndroid Build Coastguard Worker  NSPipe* stderr_pipe = [NSPipe pipe];
361*6777b538SAndroid Build Coastguard Worker  stderr_pipe.fileHandleForReading.readabilityHandler =
362*6777b538SAndroid Build Coastguard Worker      ^(NSFileHandle* handle) {
363*6777b538SAndroid Build Coastguard Worker        NSString* log = [[NSString alloc] initWithData:handle.availableData
364*6777b538SAndroid Build Coastguard Worker                                              encoding:NSUTF8StringEncoding];
365*6777b538SAndroid Build Coastguard Worker        for (NSString* ignore_string in ignore_strings) {
366*6777b538SAndroid Build Coastguard Worker          if ([log rangeOfString:ignore_string].location != NSNotFound) {
367*6777b538SAndroid Build Coastguard Worker            return;
368*6777b538SAndroid Build Coastguard Worker          }
369*6777b538SAndroid Build Coastguard Worker        }
370*6777b538SAndroid Build Coastguard Worker        fprintf(stderr, "%s", log.UTF8String);
371*6777b538SAndroid Build Coastguard Worker      };
372*6777b538SAndroid Build Coastguard Worker  task.standardError = stderr_pipe;
373*6777b538SAndroid Build Coastguard Worker
374*6777b538SAndroid Build Coastguard Worker  [task run:verbose];
375*6777b538SAndroid Build Coastguard Worker  return [task terminationStatus];
376*6777b538SAndroid Build Coastguard Worker}
377*6777b538SAndroid Build Coastguard Worker
378*6777b538SAndroid Build Coastguard Workerbool isSimDeviceBooted(NSDictionary* simctl_list, NSString* udid) {
379*6777b538SAndroid Build Coastguard Worker  for (NSString* sdk in simctl_list[@"devices"]) {
380*6777b538SAndroid Build Coastguard Worker    for (NSDictionary* device in simctl_list[@"devices"][sdk]) {
381*6777b538SAndroid Build Coastguard Worker      if ([device[@"udid"] isEqualToString:udid]) {
382*6777b538SAndroid Build Coastguard Worker        if ([device[@"state"] isEqualToString:@"Booted"]) {
383*6777b538SAndroid Build Coastguard Worker          return true;
384*6777b538SAndroid Build Coastguard Worker        }
385*6777b538SAndroid Build Coastguard Worker      }
386*6777b538SAndroid Build Coastguard Worker    }
387*6777b538SAndroid Build Coastguard Worker  }
388*6777b538SAndroid Build Coastguard Worker  return false;
389*6777b538SAndroid Build Coastguard Worker}
390*6777b538SAndroid Build Coastguard Worker
391*6777b538SAndroid Build Coastguard Workerint SimpleRunApplication(NSString* app_path,
392*6777b538SAndroid Build Coastguard Worker                         NSString* udid,
393*6777b538SAndroid Build Coastguard Worker                         NSMutableArray* cmd_args,
394*6777b538SAndroid Build Coastguard Worker                         bool verbose) {
395*6777b538SAndroid Build Coastguard Worker  NSString* bundle_id = GetBundleIdentifierFromPath(app_path);
396*6777b538SAndroid Build Coastguard Worker
397*6777b538SAndroid Build Coastguard Worker  RunSimCtl(@[ @"uninstall", udid, bundle_id ], verbose);
398*6777b538SAndroid Build Coastguard Worker  RunSimCtl(@[ @"install", udid, app_path ], verbose);
399*6777b538SAndroid Build Coastguard Worker
400*6777b538SAndroid Build Coastguard Worker  NSArray* command = [@[
401*6777b538SAndroid Build Coastguard Worker    @"launch", @"--console", @"--terminate-running-process", udid, bundle_id
402*6777b538SAndroid Build Coastguard Worker  ] arrayByAddingObjectsFromArray:cmd_args];
403*6777b538SAndroid Build Coastguard Worker  return RunSimCtl(command, verbose);
404*6777b538SAndroid Build Coastguard Worker}
405*6777b538SAndroid Build Coastguard Worker
406*6777b538SAndroid Build Coastguard Workerint RunApplication(NSString* app_path,
407*6777b538SAndroid Build Coastguard Worker                   NSString* xctest_path,
408*6777b538SAndroid Build Coastguard Worker                   NSString* udid,
409*6777b538SAndroid Build Coastguard Worker                   NSMutableDictionary* app_env,
410*6777b538SAndroid Build Coastguard Worker                   NSMutableArray* cmd_args,
411*6777b538SAndroid Build Coastguard Worker                   NSMutableArray* tests_filter,
412*6777b538SAndroid Build Coastguard Worker                   bool verbose) {
413*6777b538SAndroid Build Coastguard Worker  NSString* filename =
414*6777b538SAndroid Build Coastguard Worker      [NSUUID.UUID.UUIDString stringByAppendingString:@".xctestrun"];
415*6777b538SAndroid Build Coastguard Worker  NSString* tempFilePath =
416*6777b538SAndroid Build Coastguard Worker      [NSTemporaryDirectory() stringByAppendingPathComponent:filename];
417*6777b538SAndroid Build Coastguard Worker  [NSFileManager.defaultManager createFileAtPath:tempFilePath
418*6777b538SAndroid Build Coastguard Worker                                        contents:nil
419*6777b538SAndroid Build Coastguard Worker                                      attributes:nil];
420*6777b538SAndroid Build Coastguard Worker
421*6777b538SAndroid Build Coastguard Worker  NSMutableDictionary* xctestrun = [NSMutableDictionary dictionary];
422*6777b538SAndroid Build Coastguard Worker  NSMutableDictionary* testTargetName = [NSMutableDictionary dictionary];
423*6777b538SAndroid Build Coastguard Worker
424*6777b538SAndroid Build Coastguard Worker  NSMutableDictionary* testingEnvironmentVariables =
425*6777b538SAndroid Build Coastguard Worker      [NSMutableDictionary dictionary];
426*6777b538SAndroid Build Coastguard Worker  testingEnvironmentVariables[@"IDEiPhoneInternalTestBundleName"] =
427*6777b538SAndroid Build Coastguard Worker      app_path.lastPathComponent;
428*6777b538SAndroid Build Coastguard Worker
429*6777b538SAndroid Build Coastguard Worker  testingEnvironmentVariables[@"DYLD_FRAMEWORK_PATH"] =
430*6777b538SAndroid Build Coastguard Worker      @"__TESTROOT__/Debug-iphonesimulator:__PLATFORMS__/"
431*6777b538SAndroid Build Coastguard Worker      @"iPhoneSimulator.platform/Developer/Library/Frameworks";
432*6777b538SAndroid Build Coastguard Worker  testingEnvironmentVariables[@"DYLD_LIBRARY_PATH"] =
433*6777b538SAndroid Build Coastguard Worker      @"__TESTROOT__/Debug-iphonesimulator:__PLATFORMS__/"
434*6777b538SAndroid Build Coastguard Worker      @"iPhoneSimulator.platform/Developer/Library";
435*6777b538SAndroid Build Coastguard Worker
436*6777b538SAndroid Build Coastguard Worker  if (xctest_path) {
437*6777b538SAndroid Build Coastguard Worker    testTargetName[@"TestBundlePath"] = xctest_path;
438*6777b538SAndroid Build Coastguard Worker    testingEnvironmentVariables[@"DYLD_INSERT_LIBRARIES"] =
439*6777b538SAndroid Build Coastguard Worker        @"__PLATFORMS__/iPhoneSimulator.platform/Developer/"
440*6777b538SAndroid Build Coastguard Worker        @"usr/lib/libXCTestBundleInject.dylib";
441*6777b538SAndroid Build Coastguard Worker    testingEnvironmentVariables[@"XCInjectBundleInto"] =
442*6777b538SAndroid Build Coastguard Worker        [NSString stringWithFormat:@"__TESTHOST__/%@",
443*6777b538SAndroid Build Coastguard Worker                                   app_path.lastPathComponent
444*6777b538SAndroid Build Coastguard Worker                                       .stringByDeletingPathExtension];
445*6777b538SAndroid Build Coastguard Worker  } else {
446*6777b538SAndroid Build Coastguard Worker    testTargetName[@"TestBundlePath"] = app_path;
447*6777b538SAndroid Build Coastguard Worker  }
448*6777b538SAndroid Build Coastguard Worker  testTargetName[@"TestHostPath"] = app_path;
449*6777b538SAndroid Build Coastguard Worker
450*6777b538SAndroid Build Coastguard Worker  if (app_env.count) {
451*6777b538SAndroid Build Coastguard Worker    testTargetName[@"EnvironmentVariables"] = app_env;
452*6777b538SAndroid Build Coastguard Worker  }
453*6777b538SAndroid Build Coastguard Worker
454*6777b538SAndroid Build Coastguard Worker  if (cmd_args.count > 0) {
455*6777b538SAndroid Build Coastguard Worker    testTargetName[@"CommandLineArguments"] = cmd_args;
456*6777b538SAndroid Build Coastguard Worker  }
457*6777b538SAndroid Build Coastguard Worker
458*6777b538SAndroid Build Coastguard Worker  if (tests_filter.count > 0) {
459*6777b538SAndroid Build Coastguard Worker    testTargetName[@"OnlyTestIdentifiers"] = tests_filter;
460*6777b538SAndroid Build Coastguard Worker  }
461*6777b538SAndroid Build Coastguard Worker
462*6777b538SAndroid Build Coastguard Worker  testTargetName[@"TestingEnvironmentVariables"] = testingEnvironmentVariables;
463*6777b538SAndroid Build Coastguard Worker  xctestrun[@"TestTargetName"] = testTargetName;
464*6777b538SAndroid Build Coastguard Worker
465*6777b538SAndroid Build Coastguard Worker  NSData* data = [NSPropertyListSerialization
466*6777b538SAndroid Build Coastguard Worker      dataWithPropertyList:xctestrun
467*6777b538SAndroid Build Coastguard Worker                    format:NSPropertyListXMLFormat_v1_0
468*6777b538SAndroid Build Coastguard Worker                   options:0
469*6777b538SAndroid Build Coastguard Worker                     error:nil];
470*6777b538SAndroid Build Coastguard Worker  [data writeToFile:tempFilePath atomically:YES];
471*6777b538SAndroid Build Coastguard Worker
472*6777b538SAndroid Build Coastguard Worker  XCRunTask* task = [[XCRunTask alloc] initWithArguments:@[
473*6777b538SAndroid Build Coastguard Worker    @"xcodebuild", @"-xctestrun", tempFilePath, @"-destination",
474*6777b538SAndroid Build Coastguard Worker    [@"platform=iOS Simulator,id=" stringByAppendingString:udid],
475*6777b538SAndroid Build Coastguard Worker    @"test-without-building"
476*6777b538SAndroid Build Coastguard Worker  ]];
477*6777b538SAndroid Build Coastguard Worker
478*6777b538SAndroid Build Coastguard Worker  if (!xctest_path) {
479*6777b538SAndroid Build Coastguard Worker    // The following stderr messages are meaningless on iossim when not running
480*6777b538SAndroid Build Coastguard Worker    // xctests and can be safely stripped.
481*6777b538SAndroid Build Coastguard Worker    NSArray* ignore_strings = @[
482*6777b538SAndroid Build Coastguard Worker      @"IDETestOperationsObserverErrorDomain", @"** TEST EXECUTE FAILED **"
483*6777b538SAndroid Build Coastguard Worker    ];
484*6777b538SAndroid Build Coastguard Worker    NSPipe* stderr_pipe = [NSPipe pipe];
485*6777b538SAndroid Build Coastguard Worker    stderr_pipe.fileHandleForReading.readabilityHandler =
486*6777b538SAndroid Build Coastguard Worker        ^(NSFileHandle* handle) {
487*6777b538SAndroid Build Coastguard Worker          NSString* log = [[NSString alloc] initWithData:handle.availableData
488*6777b538SAndroid Build Coastguard Worker                                                encoding:NSUTF8StringEncoding];
489*6777b538SAndroid Build Coastguard Worker          for (NSString* ignore_string in ignore_strings) {
490*6777b538SAndroid Build Coastguard Worker            if ([log rangeOfString:ignore_string].location != NSNotFound) {
491*6777b538SAndroid Build Coastguard Worker              return;
492*6777b538SAndroid Build Coastguard Worker            }
493*6777b538SAndroid Build Coastguard Worker          }
494*6777b538SAndroid Build Coastguard Worker          printf("%s", log.UTF8String);
495*6777b538SAndroid Build Coastguard Worker        };
496*6777b538SAndroid Build Coastguard Worker    task.standardError = stderr_pipe;
497*6777b538SAndroid Build Coastguard Worker  }
498*6777b538SAndroid Build Coastguard Worker  [task run:verbose];
499*6777b538SAndroid Build Coastguard Worker  return [task terminationStatus];
500*6777b538SAndroid Build Coastguard Worker}
501*6777b538SAndroid Build Coastguard Worker
502*6777b538SAndroid Build Coastguard Workerint main(int argc, char* const argv[]) {
503*6777b538SAndroid Build Coastguard Worker  NSString* app_path = nil;
504*6777b538SAndroid Build Coastguard Worker  NSString* xctest_path = nil;
505*6777b538SAndroid Build Coastguard Worker  NSString* udid = nil;
506*6777b538SAndroid Build Coastguard Worker  NSString* device_name = @"iPhone 6s";
507*6777b538SAndroid Build Coastguard Worker  bool wants_wipe = false;
508*6777b538SAndroid Build Coastguard Worker  bool wants_print_home = false;
509*6777b538SAndroid Build Coastguard Worker  bool wants_print_supported_devices = false;
510*6777b538SAndroid Build Coastguard Worker  bool run_web_test = false;
511*6777b538SAndroid Build Coastguard Worker  bool prepare_web_test = false;
512*6777b538SAndroid Build Coastguard Worker  NSString* sdk_version = nil;
513*6777b538SAndroid Build Coastguard Worker  NSMutableDictionary* app_env = [NSMutableDictionary dictionary];
514*6777b538SAndroid Build Coastguard Worker  NSMutableArray* cmd_args = [NSMutableArray array];
515*6777b538SAndroid Build Coastguard Worker  NSMutableArray* tests_filter = [NSMutableArray array];
516*6777b538SAndroid Build Coastguard Worker  bool verbose_commands = false;
517*6777b538SAndroid Build Coastguard Worker  SimulatorKill kill_simulator = KILL_BOTH;
518*6777b538SAndroid Build Coastguard Worker  bool wants_simple_iossim = false;
519*6777b538SAndroid Build Coastguard Worker
520*6777b538SAndroid Build Coastguard Worker  int c;
521*6777b538SAndroid Build Coastguard Worker  while ((c = getopt(argc, argv, "hs:d:u:t:e:c:pwlvk:i")) != -1) {
522*6777b538SAndroid Build Coastguard Worker    switch (c) {
523*6777b538SAndroid Build Coastguard Worker      case 's':
524*6777b538SAndroid Build Coastguard Worker        sdk_version = @(optarg);
525*6777b538SAndroid Build Coastguard Worker        break;
526*6777b538SAndroid Build Coastguard Worker      case 'd':
527*6777b538SAndroid Build Coastguard Worker        device_name = @(optarg);
528*6777b538SAndroid Build Coastguard Worker        break;
529*6777b538SAndroid Build Coastguard Worker      case 'u':
530*6777b538SAndroid Build Coastguard Worker        udid = @(optarg);
531*6777b538SAndroid Build Coastguard Worker        break;
532*6777b538SAndroid Build Coastguard Worker      case 'w':
533*6777b538SAndroid Build Coastguard Worker        wants_wipe = true;
534*6777b538SAndroid Build Coastguard Worker        break;
535*6777b538SAndroid Build Coastguard Worker      case 'c': {
536*6777b538SAndroid Build Coastguard Worker        NSString* cmd_arg = @(optarg);
537*6777b538SAndroid Build Coastguard Worker        [cmd_args addObject:cmd_arg];
538*6777b538SAndroid Build Coastguard Worker      } break;
539*6777b538SAndroid Build Coastguard Worker      case 't': {
540*6777b538SAndroid Build Coastguard Worker        NSString* test = @(optarg);
541*6777b538SAndroid Build Coastguard Worker        [tests_filter addObject:test];
542*6777b538SAndroid Build Coastguard Worker      } break;
543*6777b538SAndroid Build Coastguard Worker      case 'e': {
544*6777b538SAndroid Build Coastguard Worker        NSString* envLine = @(optarg);
545*6777b538SAndroid Build Coastguard Worker        NSRange range = [envLine rangeOfString:@"="];
546*6777b538SAndroid Build Coastguard Worker        if (range.location == NSNotFound) {
547*6777b538SAndroid Build Coastguard Worker          LogError(@"Invalid key=value argument for -e.");
548*6777b538SAndroid Build Coastguard Worker          PrintUsage();
549*6777b538SAndroid Build Coastguard Worker          exit(kExitInvalidArguments);
550*6777b538SAndroid Build Coastguard Worker        }
551*6777b538SAndroid Build Coastguard Worker        NSString* key = [envLine substringToIndex:range.location];
552*6777b538SAndroid Build Coastguard Worker        NSString* value = [envLine substringFromIndex:(range.location + 1)];
553*6777b538SAndroid Build Coastguard Worker        [app_env setObject:value forKey:key];
554*6777b538SAndroid Build Coastguard Worker      } break;
555*6777b538SAndroid Build Coastguard Worker      case 'p':
556*6777b538SAndroid Build Coastguard Worker        wants_print_home = true;
557*6777b538SAndroid Build Coastguard Worker        break;
558*6777b538SAndroid Build Coastguard Worker      case 'l':
559*6777b538SAndroid Build Coastguard Worker        wants_print_supported_devices = true;
560*6777b538SAndroid Build Coastguard Worker        break;
561*6777b538SAndroid Build Coastguard Worker      case 'v':
562*6777b538SAndroid Build Coastguard Worker        verbose_commands = true;
563*6777b538SAndroid Build Coastguard Worker        break;
564*6777b538SAndroid Build Coastguard Worker      case 'k': {
565*6777b538SAndroid Build Coastguard Worker        NSString* cmd_arg = @(optarg);
566*6777b538SAndroid Build Coastguard Worker        if ([cmd_arg isEqualToString:@"before"]) {
567*6777b538SAndroid Build Coastguard Worker          kill_simulator = KILL_BEFORE;
568*6777b538SAndroid Build Coastguard Worker        } else if ([cmd_arg isEqualToString:@"after"]) {
569*6777b538SAndroid Build Coastguard Worker          kill_simulator = KILL_AFTER;
570*6777b538SAndroid Build Coastguard Worker        } else if ([cmd_arg isEqualToString:@"both"]) {
571*6777b538SAndroid Build Coastguard Worker          kill_simulator = KILL_BOTH;
572*6777b538SAndroid Build Coastguard Worker        } else if ([cmd_arg isEqualToString:@"never"]) {
573*6777b538SAndroid Build Coastguard Worker          kill_simulator = KILL_NEVER;
574*6777b538SAndroid Build Coastguard Worker        } else {
575*6777b538SAndroid Build Coastguard Worker          PrintUsage();
576*6777b538SAndroid Build Coastguard Worker          exit(kExitInvalidArguments);
577*6777b538SAndroid Build Coastguard Worker        }
578*6777b538SAndroid Build Coastguard Worker      } break;
579*6777b538SAndroid Build Coastguard Worker      case 'i':
580*6777b538SAndroid Build Coastguard Worker        wants_simple_iossim = true;
581*6777b538SAndroid Build Coastguard Worker        break;
582*6777b538SAndroid Build Coastguard Worker      case 'h':
583*6777b538SAndroid Build Coastguard Worker        PrintUsage();
584*6777b538SAndroid Build Coastguard Worker        exit(kExitSuccess);
585*6777b538SAndroid Build Coastguard Worker      default:
586*6777b538SAndroid Build Coastguard Worker        PrintUsage();
587*6777b538SAndroid Build Coastguard Worker        exit(kExitInvalidArguments);
588*6777b538SAndroid Build Coastguard Worker    }
589*6777b538SAndroid Build Coastguard Worker  }
590*6777b538SAndroid Build Coastguard Worker
591*6777b538SAndroid Build Coastguard Worker  if (wants_simple_iossim && [tests_filter count]) {
592*6777b538SAndroid Build Coastguard Worker    LogError(@"Cannot specify tests with -t when using -i.");
593*6777b538SAndroid Build Coastguard Worker    exit(kExitInvalidArguments);
594*6777b538SAndroid Build Coastguard Worker  }
595*6777b538SAndroid Build Coastguard Worker
596*6777b538SAndroid Build Coastguard Worker  NSDictionary* simctl_list = GetSimulatorList(verbose_commands);
597*6777b538SAndroid Build Coastguard Worker
598*6777b538SAndroid Build Coastguard Worker  if (wants_print_supported_devices) {
599*6777b538SAndroid Build Coastguard Worker    PrintSupportedDevices(simctl_list);
600*6777b538SAndroid Build Coastguard Worker    exit(kExitSuccess);
601*6777b538SAndroid Build Coastguard Worker  }
602*6777b538SAndroid Build Coastguard Worker
603*6777b538SAndroid Build Coastguard Worker  if (!sdk_version) {
604*6777b538SAndroid Build Coastguard Worker    float sdk = 0;
605*6777b538SAndroid Build Coastguard Worker    for (NSDictionary* runtime in Runtimes(simctl_list)) {
606*6777b538SAndroid Build Coastguard Worker      sdk = fmax(sdk, [runtime[@"version"] floatValue]);
607*6777b538SAndroid Build Coastguard Worker    }
608*6777b538SAndroid Build Coastguard Worker    sdk_version = [NSString stringWithFormat:@"%0.1f", sdk];
609*6777b538SAndroid Build Coastguard Worker  }
610*6777b538SAndroid Build Coastguard Worker
611*6777b538SAndroid Build Coastguard Worker  NSRange range;
612*6777b538SAndroid Build Coastguard Worker  for (NSString* cmd_arg in cmd_args) {
613*6777b538SAndroid Build Coastguard Worker    range = [cmd_arg rangeOfString:@"--run-web-tests"];
614*6777b538SAndroid Build Coastguard Worker    if (range.location != NSNotFound) {
615*6777b538SAndroid Build Coastguard Worker      run_web_test = true;
616*6777b538SAndroid Build Coastguard Worker      break;
617*6777b538SAndroid Build Coastguard Worker    }
618*6777b538SAndroid Build Coastguard Worker  }
619*6777b538SAndroid Build Coastguard Worker
620*6777b538SAndroid Build Coastguard Worker  for (NSString* cmd_arg in cmd_args) {
621*6777b538SAndroid Build Coastguard Worker    range = [cmd_arg rangeOfString:@"--prepare-web-tests"];
622*6777b538SAndroid Build Coastguard Worker    if (range.location != NSNotFound) {
623*6777b538SAndroid Build Coastguard Worker      prepare_web_test = true;
624*6777b538SAndroid Build Coastguard Worker      break;
625*6777b538SAndroid Build Coastguard Worker    }
626*6777b538SAndroid Build Coastguard Worker  }
627*6777b538SAndroid Build Coastguard Worker
628*6777b538SAndroid Build Coastguard Worker  if (udid == nil) {
629*6777b538SAndroid Build Coastguard Worker    udid = GetDeviceBySDKAndName(simctl_list, device_name, sdk_version);
630*6777b538SAndroid Build Coastguard Worker    if (udid == nil) {
631*6777b538SAndroid Build Coastguard Worker      udid =
632*6777b538SAndroid Build Coastguard Worker          CreateDeviceBySDKAndName(device_name, sdk_version, verbose_commands);
633*6777b538SAndroid Build Coastguard Worker      if (udid == nil) {
634*6777b538SAndroid Build Coastguard Worker        LogError(@"Unable to find a device %@ with SDK %@.", device_name,
635*6777b538SAndroid Build Coastguard Worker                 sdk_version);
636*6777b538SAndroid Build Coastguard Worker        PrintSupportedDevices(simctl_list);
637*6777b538SAndroid Build Coastguard Worker        exit(kExitInvalidArguments);
638*6777b538SAndroid Build Coastguard Worker      }
639*6777b538SAndroid Build Coastguard Worker    }
640*6777b538SAndroid Build Coastguard Worker  } else {
641*6777b538SAndroid Build Coastguard Worker    if (!FindDeviceByUDID(simctl_list, udid)) {
642*6777b538SAndroid Build Coastguard Worker      LogError(
643*6777b538SAndroid Build Coastguard Worker          @"Unable to find a device with udid %@. Use 'xcrun simctl list' to "
644*6777b538SAndroid Build Coastguard Worker          @"see valid device udids.",
645*6777b538SAndroid Build Coastguard Worker          udid);
646*6777b538SAndroid Build Coastguard Worker      exit(kExitInvalidArguments);
647*6777b538SAndroid Build Coastguard Worker    }
648*6777b538SAndroid Build Coastguard Worker  }
649*6777b538SAndroid Build Coastguard Worker
650*6777b538SAndroid Build Coastguard Worker  if (wants_print_home) {
651*6777b538SAndroid Build Coastguard Worker    PrintDeviceHome(udid, verbose_commands);
652*6777b538SAndroid Build Coastguard Worker    exit(kExitSuccess);
653*6777b538SAndroid Build Coastguard Worker  }
654*6777b538SAndroid Build Coastguard Worker
655*6777b538SAndroid Build Coastguard Worker  if (kill_simulator & KILL_BEFORE) {
656*6777b538SAndroid Build Coastguard Worker    KillSimulator(verbose_commands);
657*6777b538SAndroid Build Coastguard Worker  }
658*6777b538SAndroid Build Coastguard Worker
659*6777b538SAndroid Build Coastguard Worker  if (wants_wipe) {
660*6777b538SAndroid Build Coastguard Worker    WipeDevice(udid, verbose_commands);
661*6777b538SAndroid Build Coastguard Worker    printf("Device wiped.\n");
662*6777b538SAndroid Build Coastguard Worker    exit(kExitSuccess);
663*6777b538SAndroid Build Coastguard Worker  }
664*6777b538SAndroid Build Coastguard Worker
665*6777b538SAndroid Build Coastguard Worker  // There should be at least one arg left, specifying the app path. Any
666*6777b538SAndroid Build Coastguard Worker  // additional args are passed as arguments to the app.
667*6777b538SAndroid Build Coastguard Worker  if (optind < argc) {
668*6777b538SAndroid Build Coastguard Worker    NSString* unresolved_app_path = [NSFileManager.defaultManager
669*6777b538SAndroid Build Coastguard Worker        stringWithFileSystemRepresentation:argv[optind]
670*6777b538SAndroid Build Coastguard Worker                                    length:strlen(argv[optind])];
671*6777b538SAndroid Build Coastguard Worker    app_path = ResolvePath(unresolved_app_path);
672*6777b538SAndroid Build Coastguard Worker    if (!app_path) {
673*6777b538SAndroid Build Coastguard Worker      LogError(@"Unable to resolve app_path %@", unresolved_app_path);
674*6777b538SAndroid Build Coastguard Worker      exit(kExitInvalidArguments);
675*6777b538SAndroid Build Coastguard Worker    }
676*6777b538SAndroid Build Coastguard Worker
677*6777b538SAndroid Build Coastguard Worker    if (++optind < argc) {
678*6777b538SAndroid Build Coastguard Worker      if (wants_simple_iossim) {
679*6777b538SAndroid Build Coastguard Worker        fprintf(stderr, "Warning: xctest_path ignored when using -i");
680*6777b538SAndroid Build Coastguard Worker      } else {
681*6777b538SAndroid Build Coastguard Worker        NSString* unresolved_xctest_path = [NSFileManager.defaultManager
682*6777b538SAndroid Build Coastguard Worker            stringWithFileSystemRepresentation:argv[optind]
683*6777b538SAndroid Build Coastguard Worker                                        length:strlen(argv[optind])];
684*6777b538SAndroid Build Coastguard Worker        xctest_path = ResolvePath(unresolved_xctest_path);
685*6777b538SAndroid Build Coastguard Worker        if (!xctest_path) {
686*6777b538SAndroid Build Coastguard Worker          LogError(@"Unable to resolve xctest_path %@", unresolved_xctest_path);
687*6777b538SAndroid Build Coastguard Worker          exit(kExitInvalidArguments);
688*6777b538SAndroid Build Coastguard Worker        }
689*6777b538SAndroid Build Coastguard Worker      }
690*6777b538SAndroid Build Coastguard Worker    }
691*6777b538SAndroid Build Coastguard Worker  } else {
692*6777b538SAndroid Build Coastguard Worker    LogError(@"Unable to parse command line arguments.");
693*6777b538SAndroid Build Coastguard Worker    PrintUsage();
694*6777b538SAndroid Build Coastguard Worker    exit(kExitInvalidArguments);
695*6777b538SAndroid Build Coastguard Worker  }
696*6777b538SAndroid Build Coastguard Worker
697*6777b538SAndroid Build Coastguard Worker  if ((prepare_web_test || run_web_test || wants_simple_iossim) &&
698*6777b538SAndroid Build Coastguard Worker      !isSimDeviceBooted(simctl_list, udid)) {
699*6777b538SAndroid Build Coastguard Worker    RunSimCtl(@[ @"boot", udid ], verbose_commands);
700*6777b538SAndroid Build Coastguard Worker  }
701*6777b538SAndroid Build Coastguard Worker
702*6777b538SAndroid Build Coastguard Worker  int return_code = -1;
703*6777b538SAndroid Build Coastguard Worker  if (prepare_web_test) {
704*6777b538SAndroid Build Coastguard Worker    PrepareWebTests(udid, app_path, verbose_commands);
705*6777b538SAndroid Build Coastguard Worker    return_code = kExitSuccess;
706*6777b538SAndroid Build Coastguard Worker  } else if (run_web_test) {
707*6777b538SAndroid Build Coastguard Worker    return_code = RunWebTest(app_path, udid, cmd_args, verbose_commands);
708*6777b538SAndroid Build Coastguard Worker  } else if (wants_simple_iossim) {
709*6777b538SAndroid Build Coastguard Worker    return_code =
710*6777b538SAndroid Build Coastguard Worker        SimpleRunApplication(app_path, udid, cmd_args, verbose_commands);
711*6777b538SAndroid Build Coastguard Worker  } else {
712*6777b538SAndroid Build Coastguard Worker    return_code = RunApplication(app_path, xctest_path, udid, app_env, cmd_args,
713*6777b538SAndroid Build Coastguard Worker                                 tests_filter, verbose_commands);
714*6777b538SAndroid Build Coastguard Worker  }
715*6777b538SAndroid Build Coastguard Worker
716*6777b538SAndroid Build Coastguard Worker  if (kill_simulator & KILL_AFTER) {
717*6777b538SAndroid Build Coastguard Worker    KillSimulator(verbose_commands);
718*6777b538SAndroid Build Coastguard Worker  }
719*6777b538SAndroid Build Coastguard Worker
720*6777b538SAndroid Build Coastguard Worker  return return_code;
721*6777b538SAndroid Build Coastguard Worker}
722