Index: testing/iossim/iossim.mm |
diff --git a/testing/iossim/iossim.mm b/testing/iossim/iossim.mm |
new file mode 100644 |
index 0000000000000000000000000000000000000000..009355c66cf9d091df3808e4bf0e2acd5a52d26f |
--- /dev/null |
+++ b/testing/iossim/iossim.mm |
@@ -0,0 +1,511 @@ |
+// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#import <Foundation/Foundation.h> |
+#include <asl.h> |
+#include <libgen.h> |
+#include <stdarg.h> |
+#include <stdio.h> |
+ |
+// An executable (iossim) that runs an app in the iOS Simulator. |
+// Run 'iossim -h' for usage information. |
+// |
+// For best results, the iOS Simulator application should not be running when |
+// iossim is invoked. |
+// |
+// Headers for the iPhoneSimulatorRemoteClient framework used in this tool are |
+// generated by class-dump, via GYP. |
+// (class-dump is available at http://www.codethecode.com/projects/class-dump/) |
+// |
+// However, there are some forward declarations required to get things to |
+// compile. Also, the DTiPhoneSimulatorSessionDelegate protocol is referenced |
+// by the iPhoneSimulatorRemoteClient framework, but not defined in the object |
+// file, so it must be defined here before we import the generated |
stuartmorgan
2012/07/20 07:45:44
s/we import/importing/
lliabraa
2012/07/26 19:14:11
Done.
|
+// iPhoneSimulatorRemoteClient.h file. |
+ |
+@class DTiPhoneSimulatorSession; |
+@class DTiPhoneSimulatorSessionConfig; |
+@class DTiPhoneSimulatorSystemRoot; |
+@class DTiPhoneSimulatorApplicationSpecifier; |
stuartmorgan
2012/07/20 07:45:44
Move this up to the top so it's alphabetical.
lliabraa
2012/07/26 19:14:11
Done.
|
+@protocol DTiPhoneSimulatorSessionDelegate |
stuartmorgan
2012/07/20 07:45:44
Add a blank line before this.
lliabraa
2012/07/26 19:14:11
Done.
|
+- (void)session:(DTiPhoneSimulatorSession*)session |
+ didEndWithError:(NSError*)error; |
+- (void)session:(DTiPhoneSimulatorSession*)session |
+ didStart:(BOOL)started |
+ withError:(NSError*)error; |
+@end |
+ |
+#import "iPhoneSimulatorRemoteClient.h" |
+ |
+// Name of environment variables that control the user's home directory in the |
+// simulator. |
+#define kUserHomeEnvVariable "CFFIXED_USER_HOME" |
+#define kHomeEnvVariable "HOME" |
stuartmorgan
2012/07/20 07:45:44
Let's change these to const char* const, and the o
lliabraa
2012/07/26 19:14:11
Done.
|
+ |
+// Device family codes for iPhone and iPad. |
+#define kIPhoneFamily 1 |
+#define kIPadFamily 2 |
+ |
+// Max number of seconds to wait for the simulator session to start. |
+// This timeout must allow time to start up iOS Simulator, install the app |
+// and perform any other black magic that is encoded in the |
+// iPhoneSimulatorRemoteClient framework to kick things off. Normal start up |
+// time is only a couple seconds but machine load, disk caches, etc., can all |
+// affect startup time in the wild so the timeout needs to be fairly generous. |
+// If this timeout occurs iossim will likely exit with non-zero status; the |
+// exception being if the app is invoked and completes execution before the |
+// session is started (this case is handled in session:didStart:withError). |
+#define kSessionStartTimeout 30 // seconds |
stuartmorgan
2012/07/20 07:45:44
This one would be const NSTimeInterval I guess (si
lliabraa
2012/07/26 19:14:11
Done.
|
+ |
+// An undocumented system log key included in messages from launchd. The value |
+// is the PID of the process the message is about (as opposed to launchd's PID). |
+#define ASL_KEY_REF_PID "RefPID" |
stuartmorgan
2012/07/20 07:45:44
This one we can leave as-is for consistency with t
lliabraa
2012/07/26 19:14:11
Done.
|
+ |
+namespace { |
+ |
+static const char* gToolName = "iossim"; |
stuartmorgan
2012/07/20 07:45:44
static is redundant.
lliabraa
2012/07/26 19:14:11
Done.
|
+ |
+void LogError(NSString* format, ...) { |
+ va_list list; |
+ va_start(list, format); |
+ |
+ NSString* str = |
stuartmorgan
2012/07/20 07:45:44
s/str/message/ in these functions
lliabraa
2012/07/26 19:14:11
Done.
|
+ [[[NSString alloc] initWithFormat:format arguments:list] autorelease]; |
+ |
+ fprintf(stderr, "%s: ERROR: %s\n", gToolName, [str UTF8String]); |
+ fflush(stderr); |
+ |
+ va_end(list); |
+} |
+ |
+void LogWarning(NSString* format, ...) { |
+ va_list list; |
+ va_start(list, format); |
+ |
+ NSString* str = |
+ [[[NSString alloc] initWithFormat:format arguments:list] autorelease]; |
+ |
+ fprintf(stderr, "%s: WARNING: %s\n", gToolName, [str UTF8String]); |
+ fflush(stderr); |
+ |
+ va_end(list); |
+} |
+ |
+} // namespace |
+ |
+// A delegate that is called when the simulated app is started or ended in the |
+// simulator. |
+@interface SimulatorDelegate : NSObject <DTiPhoneSimulatorSessionDelegate> { |
+ @private |
+ NSString* stdioPath_; // weak |
stuartmorgan
2012/07/20 07:45:44
Two spaces before // rather than one.
lliabraa
2012/07/26 19:14:11
Done.
|
+ NSThread* outputThread_; |
+ BOOL appRunning_; |
+} |
+@end |
+ |
+// An implementation that copies the simulated app's stdio to stdout of this |
+// executable. While it would be nice to get stdout and stderr independently |
+// from the Simulator; anything from io buffering to output interleaved |
stuartmorgan
2012/07/20 07:45:44
s/;/,/
lliabraa
2012/07/26 19:14:11
Done.
|
+// between the two mean they will show up out of onder here. Putting them |
stuartmorgan
2012/07/20 07:45:44
s/onder/order/
lliabraa
2012/07/26 19:14:11
Done.
|
+// together keeps the order correct. The class should be initialized with the |
+// location of the simulated app's output file. When the simulated app starts, |
+// a thread is started which handles copying data from the simulated app's |
+// output file to the stdout of this executable. |
+@implementation SimulatorDelegate |
+ |
+// Specifies the file locations of the simulated app's stdout and stderr. |
+- (SimulatorDelegate*)initWithStdioPath:(NSString*)stdioPath { |
+ self = [super init]; |
+ if (self) { |
+ stdioPath_ = stdioPath; |
stuartmorgan
2012/07/20 07:45:44
The only reason this doesn't eventually crash is t
lliabraa
2012/07/26 19:14:11
Done.
|
+ } |
stuartmorgan
2012/07/20 07:45:44
No {} for the one-line condition
lliabraa
2012/07/26 19:14:11
Done.
|
+ return self; |
+} |
+ |
+// Reads data from the simulated app's output and writes it to stdout. This |
+// method blocks, so it should be called in a separate thread. The iOS |
+// Simulator takes a file path for the simulated app's stdout and stderr, but |
+// this path isn't always available (e.g. when the stdout is Xcode's build |
+// window). As a workaround, iossim creates temp file to hold output, which |
stuartmorgan
2012/07/20 07:45:44
s/temp file/a temp file/
lliabraa
2012/07/26 19:14:11
Done.
|
+// this method reads and copies to stdout. |
+- (void)tailOutput { |
+ NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; |
+ |
+ // Copy data to stdout/stderr while the app is running. |
+ NSFileHandle* simio = [NSFileHandle fileHandleForReadingAtPath:stdioPath_]; |
+ NSFileHandle* standardOutput = [NSFileHandle fileHandleWithStandardOutput]; |
+ while (appRunning_) { |
+ NSAutoreleasePool* innerPool = [[NSAutoreleasePool alloc] init]; |
+ [standardOutput writeData:[simio readDataToEndOfFile]]; |
+ [NSThread sleepForTimeInterval:0.1]; // seconds |
stuartmorgan
2012/07/20 07:45:44
Remove the // seconds since TimeInterval is always
lliabraa
2012/07/26 19:14:11
Is this a hard convention? I don't see how it hurt
stuartmorgan
2012/07/26 19:47:05
Yes, see
https://developer.apple.com/library/mac/#
lliabraa
2012/07/27 13:08:58
I meant is it a hard convention not to document un
|
+ [innerPool drain]; |
+ } |
+ |
+ // Once the app is no longer running, copy any data that was written during |
+ // the last sleep cycle. |
stuartmorgan
2012/07/20 07:45:44
Could we just swap the sleep and the print in the
lliabraa
2012/07/26 19:14:11
I think it is better to leave this final read/writ
|
+ [standardOutput writeData:[simio readDataToEndOfFile]]; |
+ |
+ [pool drain]; |
+} |
+ |
+- (void)session:(DTiPhoneSimulatorSession*)session |
+ didStart:(BOOL)started |
+ withError:(NSError*)error { |
+ if (!started) { |
+ // If the test executes very quickly (<30ms), the SimulatorDelegate may not |
+ // get the initial session:started:withError: message indicating successful |
+ // startup of the simulated app. Instead the delegate will get a |
+ // session:started:withError: message after the timeout has elapsed. To |
+ // account for this case, check if the simulated app's stdio file was |
+ // ever created and if it exists dump it to stdout and return success. |
+ NSFileManager* fileManager = [NSFileManager defaultManager]; |
+ if ([fileManager fileExistsAtPath:stdioPath_ isDirectory:NO]) { |
+ appRunning_ = NO; |
+ [self tailOutput]; |
+ // Note that exiting in this state leaves a process running |
+ // (i.e. /.../iPhoneSimulator4.3.sdk/usr/libexec/installd -t 30) that will |
+ // prevent future simulator sessions from being started for 30 seconds |
+ // unless the iOS Simulator application is killed altogether. |
+ [self session:session didEndWithError:nil]; |
+ |
+ // session:didEndWithError should not return (because it exits) so |
+ // the execution path should never get here. |
+ exit(EXIT_FAILURE); |
+ } |
+ |
+ LogError(@"Simulator failed to start: %@", [error localizedDescription]); |
+ exit(EXIT_FAILURE); |
+ } |
+ |
+ // Start a thread to write contents of outputPath to stdout. |
+ appRunning_ = YES; |
+ outputThread_ = [[NSThread alloc] initWithTarget:self |
+ selector:@selector(tailOutput) |
+ object:nil]; |
+ [outputThread_ start]; |
+} |
+ |
+- (void)session:(DTiPhoneSimulatorSession*)session |
+ didEndWithError:(NSError*)error { |
+ appRunning_ = NO; |
+ // Wait for the output thread to finish copying data to stdout. |
+ if (outputThread_) { |
+ while (![outputThread_ isFinished]) { |
+ [NSThread sleepForTimeInterval:0.1]; // seconds |
stuartmorgan
2012/07/20 07:45:44
Remove the comment.
lliabraa
2012/07/26 19:14:11
See above.
|
+ } |
+ [outputThread_ release]; |
+ outputThread_ = nil; |
+ } |
+ |
+ if (error) { |
+ LogError(@"Simulator ended with error: %@", [error localizedDescription]); |
+ exit(EXIT_FAILURE); |
+ } |
+ |
+ // Check if the simulated app exited abnormally by looking for system log |
+ // messages from launchd that refer to the simulated app's PID. Limit query |
+ // to messages in the last minute since PIDs are cyclical. |
+ aslmsg query = asl_new(ASL_TYPE_QUERY); |
+ asl_set_query(query, ASL_KEY_SENDER, "launchd", |
+ ASL_QUERY_OP_EQUAL | ASL_QUERY_OP_SUBSTRING); |
+ asl_set_query(query, ASL_KEY_REF_PID, |
+ [[[session simulatedApplicationPID] stringValue] UTF8String], |
+ ASL_QUERY_OP_EQUAL); |
+ asl_set_query(query, ASL_KEY_TIME, "-1m", ASL_QUERY_OP_GREATER_EQUAL); |
+ |
+ // Log any messages found. |
+ aslresponse response = asl_search(NULL, query); |
+ BOOL entryFound = NO; |
+ aslmsg entry; |
+ while ((entry = aslresponse_next(response)) != NULL) { |
+ entryFound = YES; |
+ LogWarning(@"Console message: %s", asl_get(entry, ASL_KEY_MSG)); |
+ } |
+ |
+ // launchd only sends messages if the process crashed or exits with a |
+ // non-zero status, so if the query returned any results iossim should exit |
+ // with non-zero status. |
+ if (entryFound) { |
+ LogError(@"Simulated app crashed or exited with non-zero status"); |
+ exit(EXIT_FAILURE); |
+ } |
+ exit(EXIT_SUCCESS); |
+} |
+@end |
+ |
+namespace { |
+ |
+// Converts the given app path to an application spec, which requires an |
+// absolute path. |
+DTiPhoneSimulatorApplicationSpecifier* BuildAppSpec(NSString* appPath) { |
+ if (![appPath isAbsolutePath]) { |
+ NSString* cwd = [[NSFileManager defaultManager] currentDirectoryPath]; |
+ appPath = [cwd stringByAppendingPathComponent:appPath]; |
+ } |
+ appPath = [appPath stringByStandardizingPath]; |
+ return [DTiPhoneSimulatorApplicationSpecifier |
+ specifierWithApplicationPath:appPath]; |
+} |
+ |
+// Returns the system root for the given SDK version. If sdkVersion is nil, the |
+// default system root is returned. Will return nil if the sdkVersion is not |
+// valid. |
+DTiPhoneSimulatorSystemRoot* BuildSystemRoot(NSString* sdkVersion) { |
+ DTiPhoneSimulatorSystemRoot* systemRoot = |
+ [DTiPhoneSimulatorSystemRoot defaultRoot]; |
+ if (sdkVersion) { |
+ systemRoot = [DTiPhoneSimulatorSystemRoot rootWithSDKVersion:sdkVersion]; |
+ } |
stuartmorgan
2012/07/20 07:45:44
No {}
lliabraa
2012/07/26 19:14:11
Done.
|
+ return systemRoot; |
+} |
+ |
+// Builds a config object for starting the specified app. |
+DTiPhoneSimulatorSessionConfig* BuildSessionConfig( |
+ DTiPhoneSimulatorApplicationSpecifier* appSpec, |
+ DTiPhoneSimulatorSystemRoot* systemRoot, |
+ NSString* stdoutPath, |
+ NSString* stderrPath, |
+ NSArray* appArgs, |
+ NSNumber* deviceFamily) { |
+ DTiPhoneSimulatorSessionConfig* sessionConfig = |
+ [[[DTiPhoneSimulatorSessionConfig alloc] init] autorelease]; |
+ sessionConfig.applicationToSimulateOnStart = appSpec; |
+ sessionConfig.simulatedSystemRoot = systemRoot; |
+ sessionConfig.localizedClientName = @"chromium"; |
+ sessionConfig.simulatedApplicationStdErrPath = stderrPath; |
+ sessionConfig.simulatedApplicationStdOutPath = stdoutPath; |
+ sessionConfig.simulatedApplicationLaunchArgs = appArgs; |
+ // Note: This environment doesn't work for setting the app's user home |
+ // directory via CFFIXED_USER_HOME. Instead the user home directory is set via |
+ // the environment that the simuator runs in. |
+ // sessionConfig.simulatedApplicationLaunchEnvironment = |
+ // [NSDictionary dictionary]; |
stuartmorgan
2012/07/20 07:45:44
If this doesn't work, let's not leave the code her
lliabraa
2012/07/26 19:14:11
I changed this to a more generic TODO for supporti
|
+ sessionConfig.simulatedDeviceFamily = deviceFamily; |
+ return sessionConfig; |
+} |
+ |
+// Builds a simulator session that will use the given delegate. |
+DTiPhoneSimulatorSession* BuildSession(SimulatorDelegate* delegate) { |
+ DTiPhoneSimulatorSession* session = |
+ [[[DTiPhoneSimulatorSession alloc] init] autorelease]; |
+ session.delegate = delegate; |
+ return session; |
+} |
+ |
+// Creates a temporary directory with a unique name based on the provided |
+// template. The template should not contain any path separators and be suffixed |
+// with X's, which will be substituted with a unique alphanumeric string (see |
+// 'man mkdtemp' for details). The directory will be created as a subdirectory |
+// of NSTemporaryDirectory(). For example, if dirNameTemplate is 'test-XXX', |
+// this method would return something like '/path/to/tempdir/test-3n2'. |
+// |
+// Returns the absolute path of the newly-created directory, or nill if unable |
+// to create a unique directory. |
+NSString* CreateTempDirectory(NSString* dirNameTemplate) { |
+ NSString* fullPathTemplate = [NSTemporaryDirectory() |
+ stringByAppendingPathComponent:dirNameTemplate]; |
stuartmorgan
2012/07/20 07:45:44
Just indent 4 from the start of the previous line.
lliabraa
2012/07/26 19:14:11
I've changed the formatting to put the entire call
stuartmorgan
2012/07/26 19:47:05
Nope, both are fine. I tend toward what you did as
lliabraa
2012/07/27 13:08:58
Ack.
|
+ char* fullPath = mkdtemp(const_cast<char*>([fullPathTemplate UTF8String])); |
+ if (fullPath == NULL) { |
+ return nil; |
+ } |
stuartmorgan
2012/07/20 07:45:44
No {}
lliabraa
2012/07/26 19:14:11
Done.
|
+ return [NSString stringWithUTF8String:fullPath]; |
+} |
+ |
+// Creates the necessary directory structure under the given user home directory |
+// path. |
+// Returns YES if successful, NO if unable to create the directories. |
+BOOL CreateHomeDirSubDirs(NSString* userHomePath) { |
+ NSFileManager* fileManager = [NSFileManager defaultManager]; |
+ |
+ // Create user home and subdirectories. |
+ NSArray* subDirsToCreate = [NSArray arrayWithObjects: |
+ @"Documents", |
+ @"Library/Caches", |
+ nil]; |
+ for (NSString* subDir in subDirsToCreate) { |
+ NSString* path = [userHomePath stringByAppendingPathComponent:subDir]; |
+ NSError* error; |
+ if (![fileManager createDirectoryAtPath:path |
+ withIntermediateDirectories:YES |
+ attributes:nil |
+ error:&error]) { |
+ LogError(@"Unable to create directory: %@. Error: %@", |
+ path, [error localizedDescription]); |
+ return NO; |
+ } |
+ } |
+ |
+ return YES; |
+} |
+ |
+// Creates the necessary directory structure under the given user home directory |
+// path, then sets the path in the appropriate environment variable. |
+// Returns YES if successful, NO if unable to create or initialize the given |
+// directory. |
+BOOL InitializeSimulatorUserHome(NSString* userHomePath) { |
+ if (!CreateHomeDirSubDirs(userHomePath)) { |
+ return NO; |
+ } |
stuartmorgan
2012/07/20 07:45:44
No {}
lliabraa
2012/07/26 19:14:11
Done.
|
+ |
+ // Update the environment to use the specified directory as the user home |
+ // directory. |
+ // Note: the third param of setenv specifies whether or not to overwrite the |
+ // variable's value if it has already been set. |
+ if ((setenv(kUserHomeEnvVariable, [userHomePath UTF8String], YES) == -1) || |
+ (setenv(kHomeEnvVariable, [userHomePath UTF8String], YES) == -1)) { |
+ LogError(@"Unable to set %s environment variables."); |
stuartmorgan
2012/07/20 07:45:44
This will crash if it's ever reached; there's no a
lliabraa
2012/07/26 19:14:11
Done.
|
+ return NO; |
+ } |
+ |
+ return YES; |
+} |
+ |
+// Prints the usage information to stderr. |
+void PrintUsage() { |
+ fprintf(stderr, "Usage: iossim [-d device] [-s sdkVersion] [-u homeDir] " |
+ "<appPath> [<appArgs>]\n"); |
+ fprintf(stderr, " where <appPath> is the path to the .app directory and " |
stuartmorgan
2012/07/20 07:45:44
Why all the separate fprintf statements instead of
lliabraa
2012/07/26 19:14:11
No idea. Changed to string continuations
|
+ "appArgs are any arguments to send the simulated app.\n"); |
+ fprintf(stderr, "\n"); |
+ fprintf(stderr, "Options:\n"); |
+ fprintf(stderr, " -d Specifies the device (either 'iPhone' or 'iPad')." |
+ " Defaults to 'iPhone'.\n"); |
+ fprintf(stderr, " -s Specifies the SDK version to use (e.g '4.3')." |
+ " Will use system default if not specified.\n"); |
+ fprintf(stderr, " -u Specifies a user home directory for the simulator." |
+ " Will create a new directory if not specified.\n"); |
+} |
+ |
+} // namespace |
stuartmorgan
2012/07/20 07:45:44
One more space before //
lliabraa
2012/07/26 19:14:11
Done.
|
+ |
+ |
+int main(int argc, char* const argv[]) { |
+ NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; |
+ |
+ char* toolName = basename(argv[0]); |
+ if (toolName != NULL) |
+ gToolName = toolName; |
+ |
+ NSString* appPath = nil; |
+ NSString* appName = nil; |
+ NSString* sdkVersion = nil; |
+ NSString* deviceName = @"iPhone"; |
+ NSString* simHomePath = nil; |
+ NSMutableArray* appArgs = [NSMutableArray array]; |
+ |
+ // Parse the optional arguments |
+ int c; |
+ while ((c = getopt(argc, argv, "hs:d:u:")) != -1) { |
+ switch (c) { |
+ case 's': |
+ sdkVersion = [NSString stringWithUTF8String:optarg]; |
+ break; |
+ case 'd': |
+ deviceName = [NSString stringWithUTF8String:optarg]; |
+ break; |
+ case 'u': |
+ simHomePath = [NSString stringWithUTF8String:optarg]; |
stuartmorgan
2012/07/20 07:45:44
This should be using NSFileManager's stringWithFil
lliabraa
2012/07/26 19:14:11
Done.
|
+ break; |
+ case 'h': |
+ PrintUsage(); |
+ exit(EXIT_SUCCESS); |
+ default: |
+ PrintUsage(); |
+ exit(EXIT_FAILURE); |
+ } |
+ } |
+ |
+ // There should be at least one arg left, specifying the app path. Any |
+ // additional args are passed as arguments to the app. |
+ if (optind < argc) { |
+ appPath = [NSString stringWithUTF8String:argv[optind++]]; |
stuartmorgan
2012/07/20 07:45:44
As should this.
lliabraa
2012/07/26 19:14:11
Done.
|
+ appName = [appPath lastPathComponent]; |
+ while (optind < argc) { |
+ [appArgs addObject:[NSString stringWithUTF8String:argv[optind++]]]; |
+ } |
+ } else { |
+ LogError(@"Unable to parse command line arguments."); |
+ PrintUsage(); |
+ exit(EXIT_FAILURE); |
+ } |
+ |
+ // Make sure the app path provided is legit. |
+ DTiPhoneSimulatorApplicationSpecifier* appSpec = BuildAppSpec(appPath); |
+ if (!appSpec) { |
+ LogError(@"Invalid app path: %@", appPath); |
+ exit(EXIT_FAILURE); |
+ } |
+ |
+ // Make sure the SDK path provided is legit (or nil). |
+ DTiPhoneSimulatorSystemRoot* systemRoot = BuildSystemRoot(sdkVersion); |
+ if (!systemRoot) { |
+ LogError(@"Invalid SDK version: %@", sdkVersion); |
+ exit(EXIT_FAILURE); |
+ } |
+ |
+ // Get the paths for stdout and stderr so the simulated app's output will show |
+ // up in the caller's stdout/stderr. |
+ NSString* outputDir = CreateTempDirectory(@"iossim-XXXXXX"); |
+ NSString* stdioPath = [outputDir stringByAppendingPathComponent:@"stdio.txt"]; |
+ |
+ // Make sure the device name is legit. |
+ NSNumber* deviceFamily = nil; |
+ if (!deviceName || |
+ [@"iPhone" caseInsensitiveCompare:deviceName] == NSOrderedSame) { |
+ deviceFamily = [NSNumber numberWithInt:kIPhoneFamily]; |
+ } else if ([@"iPad" caseInsensitiveCompare:deviceName] == NSOrderedSame) { |
+ deviceFamily = [NSNumber numberWithInt:kIPadFamily]; |
+ } else { |
+ LogError(@"Invalid device name: %@", deviceName); |
+ exit(EXIT_FAILURE); |
+ } |
+ |
+ // Set up the user home directory for the simulator |
+ if (!simHomePath) { |
+ NSString* dirNameTemplate = |
+ [NSString stringWithFormat:@"iossim-%@-%@-XXXXXX", appName, deviceName]; |
+ simHomePath = CreateTempDirectory(dirNameTemplate); |
+ if (!simHomePath) { |
+ LogError(@"Unable to create unique directory for template %@", |
+ dirNameTemplate); |
stuartmorgan
2012/07/20 07:45:44
Indent 1 more
lliabraa
2012/07/26 19:14:11
Done.
|
+ exit(EXIT_FAILURE); |
+ } |
+ } |
+ if (!InitializeSimulatorUserHome(simHomePath)) { |
+ LogError(@"Unable to initialize home directory for simulator: %@", |
+ simHomePath); |
stuartmorgan
2012/07/20 07:45:44
Indent 1 more
lliabraa
2012/07/26 19:14:11
Done.
|
+ exit(EXIT_FAILURE); |
+ } |
+ |
+ // Create the config and simulator session. |
+ DTiPhoneSimulatorSessionConfig* config = BuildSessionConfig(appSpec, |
+ systemRoot, |
+ stdioPath, |
+ stdioPath, |
+ appArgs, |
+ deviceFamily); |
+ SimulatorDelegate* delegate = |
+ [[[SimulatorDelegate alloc] initWithStdioPath:stdioPath] autorelease]; |
+ DTiPhoneSimulatorSession* session = BuildSession(delegate); |
+ |
+ // Start the simulator session. |
+ NSError* error; |
+ BOOL started = [session requestStartWithConfig:config |
+ timeout:kSessionStartTimeout |
+ error:&error]; |
+ |
+ // Spin the runtime indefinitely. When the delegate gets the message that the |
+ // app has quit it will exit this program. |
+ if (started) { |
+ [[NSRunLoop mainRunLoop] run]; |
+ } else { |
+ LogError(@"Simulator failed to start: %@", [error localizedDescription]); |
+ } |
stuartmorgan
2012/07/20 07:45:44
Remove {}s on both
lliabraa
2012/07/26 19:14:11
Done.
|
+ |
+ // Note that this code is only executed if the simulator fails to start |
+ // because once the main run loop is started, only the delegate calling |
+ // exit() will end the program. |
+ [pool drain]; |
+ return EXIT_FAILURE; |
+} |