Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(76)

Side by Side Diff: testing/iossim/iossim.mm

Issue 10831111: - When reporting errors from the simulator framework always include the error (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 8 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #import <Foundation/Foundation.h> 5 #import <Foundation/Foundation.h>
6 #include <asl.h> 6 #include <asl.h>
7 #include <libgen.h> 7 #include <libgen.h>
8 #include <stdarg.h> 8 #include <stdarg.h>
9 #include <stdio.h> 9 #include <stdio.h>
10 10
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after
69 // is polled by iossim and written to iossim's stdout using the following 69 // is polled by iossim and written to iossim's stdout using the following
70 // polling interval. 70 // polling interval.
71 const NSTimeInterval kOutputPollIntervalSeconds = 0.1; 71 const NSTimeInterval kOutputPollIntervalSeconds = 0.1;
72 72
73 // The path within the developer dir of the private Simulator frameworks. 73 // The path within the developer dir of the private Simulator frameworks.
74 NSString* const kSimulatorFrameworkRelativePath = 74 NSString* const kSimulatorFrameworkRelativePath =
75 @"Platforms/iPhoneSimulator.platform/Developer/Library/PrivateFrameworks/" 75 @"Platforms/iPhoneSimulator.platform/Developer/Library/PrivateFrameworks/"
76 @"iPhoneSimulatorRemoteClient.framework"; 76 @"iPhoneSimulatorRemoteClient.framework";
77 NSString* const kDevToolsFoundationRelativePath = 77 NSString* const kDevToolsFoundationRelativePath =
78 @"../OtherFrameworks/DevToolsFoundation.framework"; 78 @"../OtherFrameworks/DevToolsFoundation.framework";
79 NSString* const kSimulatorRelativePath =
80 @"Platforms/iPhoneSimulator.platform/Developer/Applications/"
81 @"iPhone Simulator.app";
82
83 // Simulator Error String Key. This can be found by looking in the Simulator's
84 // Localizable.strings files.
85 NSString* const kSimulatorAppQuitErrorKey = @"The simulated application quit.";
79 86
80 const char* gToolName = "iossim"; 87 const char* gToolName = "iossim";
81 88
82 void LogError(NSString* format, ...) { 89 void LogError(NSString* format, ...) {
83 va_list list; 90 va_list list;
84 va_start(list, format); 91 va_start(list, format);
85 92
86 NSString* message = 93 NSString* message =
87 [[[NSString alloc] initWithFormat:format arguments:list] autorelease]; 94 [[[NSString alloc] initWithFormat:format arguments:list] autorelease];
88 95
(...skipping 15 matching lines...) Expand all
104 111
105 va_end(list); 112 va_end(list);
106 } 113 }
107 114
108 } // namespace 115 } // namespace
109 116
110 // A delegate that is called when the simulated app is started or ended in the 117 // A delegate that is called when the simulated app is started or ended in the
111 // simulator. 118 // simulator.
112 @interface SimulatorDelegate : NSObject <DTiPhoneSimulatorSessionDelegate> { 119 @interface SimulatorDelegate : NSObject <DTiPhoneSimulatorSessionDelegate> {
113 @private 120 @private
114 NSString* stdioPath_; // weak 121 NSString* stdioPath_;
122 NSString* developerDir_;
115 NSThread* outputThread_; 123 NSThread* outputThread_;
124 NSBundle* simulatorBundle_;
116 BOOL appRunning_; 125 BOOL appRunning_;
117 } 126 }
118 @end 127 @end
119 128
120 // An implementation that copies the simulated app's stdio to stdout of this 129 // An implementation that copies the simulated app's stdio to stdout of this
121 // executable. While it would be nice to get stdout and stderr independently 130 // executable. While it would be nice to get stdout and stderr independently
122 // from iOS Simulator, issues like I/O buffering and interleaved output 131 // from iOS Simulator, issues like I/O buffering and interleaved output
123 // between iOS Simulator and the app would cause iossim to display things out 132 // between iOS Simulator and the app would cause iossim to display things out
124 // of order here. Printing all output to a single file keeps the order correct. 133 // of order here. Printing all output to a single file keeps the order correct.
125 // Instances of this classe should be initialized with the location of the 134 // Instances of this classe should be initialized with the location of the
126 // simulated app's output file. When the simulated app starts, a thread is 135 // simulated app's output file. When the simulated app starts, a thread is
127 // started which handles copying data from the simulated app's output file to 136 // started which handles copying data from the simulated app's output file to
128 // the stdout of this executable. 137 // the stdout of this executable.
129 @implementation SimulatorDelegate 138 @implementation SimulatorDelegate
130 139
131 // Specifies the file locations of the simulated app's stdout and stderr. 140 // Specifies the file locations of the simulated app's stdout and stderr.
132 - (SimulatorDelegate*)initWithStdioPath:(NSString*)stdioPath { 141 - (SimulatorDelegate*)initWithStdioPath:(NSString*)stdioPath
142 developerDir:(NSString*)developerDir {
133 self = [super init]; 143 self = [super init];
134 if (self) 144 if (self) {
135 stdioPath_ = [stdioPath copy]; 145 stdioPath_ = [stdioPath copy];
146 developerDir_ = [developerDir copy];
147 }
136 148
137 return self; 149 return self;
138 } 150 }
139 151
140 - (void)dealloc { 152 - (void)dealloc {
141 [stdioPath_ release]; 153 [stdioPath_ release];
154 [developerDir_ release];
155 [simulatorBundle_ release];
142 [super dealloc]; 156 [super dealloc];
143 } 157 }
144 158
145 // Reads data from the simulated app's output and writes it to stdout. This 159 // Reads data from the simulated app's output and writes it to stdout. This
146 // method blocks, so it should be called in a separate thread. The iOS 160 // method blocks, so it should be called in a separate thread. The iOS
147 // Simulator takes a file path for the simulated app's stdout and stderr, but 161 // Simulator takes a file path for the simulated app's stdout and stderr, but
148 // this path isn't always available (e.g. when the stdout is Xcode's build 162 // this path isn't always available (e.g. when the stdout is Xcode's build
149 // window). As a workaround, iossim creates a temp file to hold output, which 163 // window). As a workaround, iossim creates a temp file to hold output, which
150 // this method reads and copies to stdout. 164 // this method reads and copies to stdout.
151 - (void)tailOutput { 165 - (void)tailOutput {
152 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; 166 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
153 167
154 // Copy data to stdout/stderr while the app is running. 168 // Copy data to stdout/stderr while the app is running.
155 NSFileHandle* simio = [NSFileHandle fileHandleForReadingAtPath:stdioPath_]; 169 NSFileHandle* simio = [NSFileHandle fileHandleForReadingAtPath:stdioPath_];
156 NSFileHandle* standardOutput = [NSFileHandle fileHandleWithStandardOutput]; 170 NSFileHandle* standardOutput = [NSFileHandle fileHandleWithStandardOutput];
157 while (appRunning_) { 171 while (appRunning_) {
158 NSAutoreleasePool* innerPool = [[NSAutoreleasePool alloc] init]; 172 NSAutoreleasePool* innerPool = [[NSAutoreleasePool alloc] init];
159 [standardOutput writeData:[simio readDataToEndOfFile]]; 173 [standardOutput writeData:[simio readDataToEndOfFile]];
160 [NSThread sleepForTimeInterval:kOutputPollIntervalSeconds]; 174 [NSThread sleepForTimeInterval:kOutputPollIntervalSeconds];
161 [innerPool drain]; 175 [innerPool drain];
162 } 176 }
163 177
164 // Once the app is no longer running, copy any data that was written during 178 // Once the app is no longer running, copy any data that was written during
165 // the last sleep cycle. 179 // the last sleep cycle.
166 [standardOutput writeData:[simio readDataToEndOfFile]]; 180 [standardOutput writeData:[simio readDataToEndOfFile]];
167 181
168 [pool drain]; 182 [pool drain];
169 } 183 }
170 184
185 // Fetches a localized error string from the Simulator.
186 - (NSString *)localizedSimulatorErrorString:(NSString*)stringKey {
187 // Lazy load of the simulator bundle.
188 if (simulatorBundle_ == nil) {
189 NSString* simulatorPath = [developerDir_
190 stringByAppendingPathComponent:kSimulatorRelativePath];
191 simulatorBundle_ = [NSBundle bundleWithPath:simulatorPath];
192 }
193 NSString *localizedStr =
194 [simulatorBundle_ localizedStringForKey:stringKey
195 value:nil
196 table:nil];
197 if ([localizedStr length])
198 return localizedStr;
199 // Failed to get a value, follow Cocoa conventions and use the key as the
200 // string.
201 return stringKey;
202 }
203
171 - (void)session:(DTiPhoneSimulatorSession*)session 204 - (void)session:(DTiPhoneSimulatorSession*)session
172 didStart:(BOOL)started 205 didStart:(BOOL)started
173 withError:(NSError*)error { 206 withError:(NSError*)error {
174 if (!started) { 207 if (!started) {
175 // If the test executes very quickly (<30ms), the SimulatorDelegate may not 208 // If the test executes very quickly (<30ms), the SimulatorDelegate may not
176 // get the initial session:started:withError: message indicating successful 209 // get the initial session:started:withError: message indicating successful
177 // startup of the simulated app. Instead the delegate will get a 210 // startup of the simulated app. Instead the delegate will get a
178 // session:started:withError: message after the timeout has elapsed. To 211 // session:started:withError: message after the timeout has elapsed. To
179 // account for this case, check if the simulated app's stdio file was 212 // account for this case, check if the simulated app's stdio file was
180 // ever created and if it exists dump it to stdout and return success. 213 // ever created and if it exists dump it to stdout and return success.
181 NSFileManager* fileManager = [NSFileManager defaultManager]; 214 NSFileManager* fileManager = [NSFileManager defaultManager];
182 if ([fileManager fileExistsAtPath:stdioPath_ isDirectory:NO]) { 215 if ([fileManager fileExistsAtPath:stdioPath_ isDirectory:NO]) {
183 appRunning_ = NO; 216 appRunning_ = NO;
184 [self tailOutput]; 217 [self tailOutput];
185 // Note that exiting in this state leaves a process running 218 // Note that exiting in this state leaves a process running
186 // (e.g. /.../iPhoneSimulator4.3.sdk/usr/libexec/installd -t 30) that will 219 // (e.g. /.../iPhoneSimulator4.3.sdk/usr/libexec/installd -t 30) that will
187 // prevent future simulator sessions from being started for 30 seconds 220 // prevent future simulator sessions from being started for 30 seconds
188 // unless the iOS Simulator application is killed altogether. 221 // unless the iOS Simulator application is killed altogether.
189 [self session:session didEndWithError:nil]; 222 [self session:session didEndWithError:nil];
190 223
191 // session:didEndWithError should not return (because it exits) so 224 // session:didEndWithError should not return (because it exits) so
192 // the execution path should never get here. 225 // the execution path should never get here.
193 exit(EXIT_FAILURE); 226 exit(EXIT_FAILURE);
194 } 227 }
195 228
196 LogError(@"Simulator failed to start: %@", [error localizedDescription]); 229 LogError(@"Simulator failed to start: \"%@\" (%@:%ld)",
230 [error localizedDescription],
231 [error domain], static_cast<long int>([error code]));
197 exit(EXIT_FAILURE); 232 exit(EXIT_FAILURE);
198 } 233 }
199 234
200 // Start a thread to write contents of outputPath to stdout. 235 // Start a thread to write contents of outputPath to stdout.
201 appRunning_ = YES; 236 appRunning_ = YES;
202 outputThread_ = [[NSThread alloc] initWithTarget:self 237 outputThread_ = [[NSThread alloc] initWithTarget:self
203 selector:@selector(tailOutput) 238 selector:@selector(tailOutput)
204 object:nil]; 239 object:nil];
205 [outputThread_ start]; 240 [outputThread_ start];
206 } 241 }
207 242
208 - (void)session:(DTiPhoneSimulatorSession*)session 243 - (void)session:(DTiPhoneSimulatorSession*)session
209 didEndWithError:(NSError*)error { 244 didEndWithError:(NSError*)error {
210 appRunning_ = NO; 245 appRunning_ = NO;
211 // Wait for the output thread to finish copying data to stdout. 246 // Wait for the output thread to finish copying data to stdout.
212 if (outputThread_) { 247 if (outputThread_) {
213 while (![outputThread_ isFinished]) { 248 while (![outputThread_ isFinished]) {
214 [NSThread sleepForTimeInterval:kOutputPollIntervalSeconds]; 249 [NSThread sleepForTimeInterval:kOutputPollIntervalSeconds];
215 } 250 }
216 [outputThread_ release]; 251 [outputThread_ release];
217 outputThread_ = nil; 252 outputThread_ = nil;
218 } 253 }
219 254
220 if (error) { 255 if (error) {
221 LogError(@"Simulator ended with error: %@", [error localizedDescription]); 256 // There appears to be a race condition where sometimes the simulator
222 exit(EXIT_FAILURE); 257 // framework will end with an error, but the error is that the simulated
258 // app cleanly shut down; try to trap this error and don't fail the
259 // simulator run.
260 NSString* localizedDescription = [error localizedDescription];
261 NSString* ignorableErrorStr =
262 [self localizedSimulatorErrorString:kSimulatorAppQuitErrorKey];
263 if ([ignorableErrorStr isEqual:localizedDescription]) {
264 LogWarning(@"Ignoring that Simulator ended with: \"%@\" (%@:%ld)",
265 localizedDescription, [error domain],
266 static_cast<long int>([error code]));
267 } else {
268 LogError(@"Simulator ended with error: \"%@\" (%@:%ld)",
269 localizedDescription, [error domain],
270 static_cast<long int>([error code]));
271 exit(EXIT_FAILURE);
272 }
223 } 273 }
224 274
225 // Check if the simulated app exited abnormally by looking for system log 275 // Check if the simulated app exited abnormally by looking for system log
226 // messages from launchd that refer to the simulated app's PID. Limit query 276 // messages from launchd that refer to the simulated app's PID. Limit query
227 // to messages in the last minute since PIDs are cyclical. 277 // to messages in the last minute since PIDs are cyclical.
228 aslmsg query = asl_new(ASL_TYPE_QUERY); 278 aslmsg query = asl_new(ASL_TYPE_QUERY);
229 asl_set_query(query, ASL_KEY_SENDER, "launchd", 279 asl_set_query(query, ASL_KEY_SENDER, "launchd",
230 ASL_QUERY_OP_EQUAL | ASL_QUERY_OP_SUBSTRING); 280 ASL_QUERY_OP_EQUAL | ASL_QUERY_OP_SUBSTRING);
231 asl_set_query(query, ASL_KEY_REF_PID, 281 asl_set_query(query, ASL_KEY_REF_PID,
232 [[[session simulatedApplicationPID] stringValue] UTF8String], 282 [[[session simulatedApplicationPID] stringValue] UTF8String],
(...skipping 363 matching lines...) Expand 10 before | Expand all | Expand 10 after
596 646
597 // Create the config and simulator session. 647 // Create the config and simulator session.
598 DTiPhoneSimulatorSessionConfig* config = BuildSessionConfig(appSpec, 648 DTiPhoneSimulatorSessionConfig* config = BuildSessionConfig(appSpec,
599 systemRoot, 649 systemRoot,
600 stdioPath, 650 stdioPath,
601 stdioPath, 651 stdioPath,
602 appArgs, 652 appArgs,
603 appEnv, 653 appEnv,
604 deviceFamily); 654 deviceFamily);
605 SimulatorDelegate* delegate = 655 SimulatorDelegate* delegate =
606 [[[SimulatorDelegate alloc] initWithStdioPath:stdioPath] autorelease]; 656 [[[SimulatorDelegate alloc] initWithStdioPath:stdioPath
657 developerDir:developerDir] autorelease];
607 DTiPhoneSimulatorSession* session = BuildSession(delegate); 658 DTiPhoneSimulatorSession* session = BuildSession(delegate);
608 659
609 // Start the simulator session. 660 // Start the simulator session.
610 NSError* error; 661 NSError* error;
611 BOOL started = [session requestStartWithConfig:config 662 BOOL started = [session requestStartWithConfig:config
612 timeout:kSessionStartTimeoutSeconds 663 timeout:kSessionStartTimeoutSeconds
613 error:&error]; 664 error:&error];
614 665
615 // Spin the runtime indefinitely. When the delegate gets the message that the 666 // Spin the runtime indefinitely. When the delegate gets the message that the
616 // app has quit it will exit this program. 667 // app has quit it will exit this program.
617 if (started) 668 if (started) {
618 [[NSRunLoop mainRunLoop] run]; 669 [[NSRunLoop mainRunLoop] run];
619 else 670 } else {
620 LogError(@"Simulator failed to start: %@", [error localizedDescription]); 671 LogError(@"Simulator failed to start: \"%@\" (%@:%ld)",
672 [error localizedDescription],
673 [error domain], static_cast<long int>([error code]));
674 }
621 675
622 // Note that this code is only executed if the simulator fails to start 676 // Note that this code is only executed if the simulator fails to start
623 // because once the main run loop is started, only the delegate calling 677 // because once the main run loop is started, only the delegate calling
624 // exit() will end the program. 678 // exit() will end the program.
625 [pool drain]; 679 [pool drain];
626 return EXIT_FAILURE; 680 return EXIT_FAILURE;
627 } 681 }
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698