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

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
stuartmorgan 2012/08/01 14:26:17 Explain in the comment where this comes from?
TVL 2012/08/01 14:35:16 Done.
84 NSString* const kSimulatorAppQuitErrorKey = @"The simulated application quit.";
79 85
80 const char* gToolName = "iossim"; 86 const char* gToolName = "iossim";
81 87
82 void LogError(NSString* format, ...) { 88 void LogError(NSString* format, ...) {
83 va_list list; 89 va_list list;
84 va_start(list, format); 90 va_start(list, format);
85 91
86 NSString* message = 92 NSString* message =
87 [[[NSString alloc] initWithFormat:format arguments:list] autorelease]; 93 [[[NSString alloc] initWithFormat:format arguments:list] autorelease];
88 94
(...skipping 15 matching lines...) Expand all
104 110
105 va_end(list); 111 va_end(list);
106 } 112 }
107 113
108 } // namespace 114 } // namespace
109 115
110 // A delegate that is called when the simulated app is started or ended in the 116 // A delegate that is called when the simulated app is started or ended in the
111 // simulator. 117 // simulator.
112 @interface SimulatorDelegate : NSObject <DTiPhoneSimulatorSessionDelegate> { 118 @interface SimulatorDelegate : NSObject <DTiPhoneSimulatorSessionDelegate> {
113 @private 119 @private
114 NSString* stdioPath_; // weak 120 NSString* stdioPath_;
121 NSString* developerDir_;
115 NSThread* outputThread_; 122 NSThread* outputThread_;
123 NSBundle* simulatorBundle_;
116 BOOL appRunning_; 124 BOOL appRunning_;
117 } 125 }
118 @end 126 @end
119 127
120 // An implementation that copies the simulated app's stdio to stdout of this 128 // 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 129 // executable. While it would be nice to get stdout and stderr independently
122 // from iOS Simulator, issues like I/O buffering and interleaved output 130 // 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 131 // 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. 132 // 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 133 // 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 134 // 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 135 // started which handles copying data from the simulated app's output file to
128 // the stdout of this executable. 136 // the stdout of this executable.
129 @implementation SimulatorDelegate 137 @implementation SimulatorDelegate
130 138
131 // Specifies the file locations of the simulated app's stdout and stderr. 139 // Specifies the file locations of the simulated app's stdout and stderr.
132 - (SimulatorDelegate*)initWithStdioPath:(NSString*)stdioPath { 140 - (SimulatorDelegate*)initWithStdioPath:(NSString*)stdioPath
141 developerDir:(NSString*)developerDir {
133 self = [super init]; 142 self = [super init];
134 if (self) 143 if (self) {
135 stdioPath_ = [stdioPath copy]; 144 stdioPath_ = [stdioPath copy];
145 developerDir_ = [developerDir copy];
146 }
136 147
137 return self; 148 return self;
138 } 149 }
139 150
140 - (void)dealloc { 151 - (void)dealloc {
141 [stdioPath_ release]; 152 [stdioPath_ release];
153 [developerDir_ release];
154 [simulatorBundle_ release];
142 [super dealloc]; 155 [super dealloc];
143 } 156 }
144 157
145 // Reads data from the simulated app's output and writes it to stdout. This 158 // 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 159 // 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 160 // 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 161 // 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 162 // window). As a workaround, iossim creates a temp file to hold output, which
150 // this method reads and copies to stdout. 163 // this method reads and copies to stdout.
151 - (void)tailOutput { 164 - (void)tailOutput {
152 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; 165 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
153 166
154 // Copy data to stdout/stderr while the app is running. 167 // Copy data to stdout/stderr while the app is running.
155 NSFileHandle* simio = [NSFileHandle fileHandleForReadingAtPath:stdioPath_]; 168 NSFileHandle* simio = [NSFileHandle fileHandleForReadingAtPath:stdioPath_];
156 NSFileHandle* standardOutput = [NSFileHandle fileHandleWithStandardOutput]; 169 NSFileHandle* standardOutput = [NSFileHandle fileHandleWithStandardOutput];
157 while (appRunning_) { 170 while (appRunning_) {
158 NSAutoreleasePool* innerPool = [[NSAutoreleasePool alloc] init]; 171 NSAutoreleasePool* innerPool = [[NSAutoreleasePool alloc] init];
159 [standardOutput writeData:[simio readDataToEndOfFile]]; 172 [standardOutput writeData:[simio readDataToEndOfFile]];
160 [NSThread sleepForTimeInterval:kOutputPollIntervalSeconds]; 173 [NSThread sleepForTimeInterval:kOutputPollIntervalSeconds];
161 [innerPool drain]; 174 [innerPool drain];
162 } 175 }
163 176
164 // Once the app is no longer running, copy any data that was written during 177 // Once the app is no longer running, copy any data that was written during
165 // the last sleep cycle. 178 // the last sleep cycle.
166 [standardOutput writeData:[simio readDataToEndOfFile]]; 179 [standardOutput writeData:[simio readDataToEndOfFile]];
167 180
168 [pool drain]; 181 [pool drain];
169 } 182 }
170 183
184 // Fetches a localized error string from the Simulator.
185 - (NSString *)localizedSimulatorErrorString:(NSString*)stringKey {
186 // Lazy load of the simulator bundle.
187 if (simulatorBundle_ == nil) {
188 NSString* simulatorPath = [developerDir_
189 stringByAppendingPathComponent:kSimulatorRelativePath];
190 simulatorBundle_ = [NSBundle bundleWithPath:simulatorPath];
191 }
192 NSString *localizedStr =
193 [simulatorBundle_ localizedStringForKey:stringKey
194 value:nil
195 table:nil];
196 if ([localizedStr length])
197 return localizedStr;
198 // Failed to get a value, follow Cocoa conventions and use the key as the
199 // string.
200 return stringKey;
201 }
202
171 - (void)session:(DTiPhoneSimulatorSession*)session 203 - (void)session:(DTiPhoneSimulatorSession*)session
172 didStart:(BOOL)started 204 didStart:(BOOL)started
173 withError:(NSError*)error { 205 withError:(NSError*)error {
174 if (!started) { 206 if (!started) {
175 // If the test executes very quickly (<30ms), the SimulatorDelegate may not 207 // If the test executes very quickly (<30ms), the SimulatorDelegate may not
176 // get the initial session:started:withError: message indicating successful 208 // get the initial session:started:withError: message indicating successful
177 // startup of the simulated app. Instead the delegate will get a 209 // startup of the simulated app. Instead the delegate will get a
178 // session:started:withError: message after the timeout has elapsed. To 210 // session:started:withError: message after the timeout has elapsed. To
179 // account for this case, check if the simulated app's stdio file was 211 // 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. 212 // ever created and if it exists dump it to stdout and return success.
181 NSFileManager* fileManager = [NSFileManager defaultManager]; 213 NSFileManager* fileManager = [NSFileManager defaultManager];
182 if ([fileManager fileExistsAtPath:stdioPath_ isDirectory:NO]) { 214 if ([fileManager fileExistsAtPath:stdioPath_ isDirectory:NO]) {
183 appRunning_ = NO; 215 appRunning_ = NO;
184 [self tailOutput]; 216 [self tailOutput];
185 // Note that exiting in this state leaves a process running 217 // Note that exiting in this state leaves a process running
186 // (e.g. /.../iPhoneSimulator4.3.sdk/usr/libexec/installd -t 30) that will 218 // (e.g. /.../iPhoneSimulator4.3.sdk/usr/libexec/installd -t 30) that will
187 // prevent future simulator sessions from being started for 30 seconds 219 // prevent future simulator sessions from being started for 30 seconds
188 // unless the iOS Simulator application is killed altogether. 220 // unless the iOS Simulator application is killed altogether.
189 [self session:session didEndWithError:nil]; 221 [self session:session didEndWithError:nil];
190 222
191 // session:didEndWithError should not return (because it exits) so 223 // session:didEndWithError should not return (because it exits) so
192 // the execution path should never get here. 224 // the execution path should never get here.
193 exit(EXIT_FAILURE); 225 exit(EXIT_FAILURE);
194 } 226 }
195 227
196 LogError(@"Simulator failed to start: %@", [error localizedDescription]); 228 LogError(@"Simulator failed to start: \"%@\" (%@:%ld)",
229 [error localizedDescription],
230 [error domain], (long)[error code]);
197 exit(EXIT_FAILURE); 231 exit(EXIT_FAILURE);
198 } 232 }
199 233
200 // Start a thread to write contents of outputPath to stdout. 234 // Start a thread to write contents of outputPath to stdout.
201 appRunning_ = YES; 235 appRunning_ = YES;
202 outputThread_ = [[NSThread alloc] initWithTarget:self 236 outputThread_ = [[NSThread alloc] initWithTarget:self
203 selector:@selector(tailOutput) 237 selector:@selector(tailOutput)
204 object:nil]; 238 object:nil];
205 [outputThread_ start]; 239 [outputThread_ start];
206 } 240 }
207 241
208 - (void)session:(DTiPhoneSimulatorSession*)session 242 - (void)session:(DTiPhoneSimulatorSession*)session
209 didEndWithError:(NSError*)error { 243 didEndWithError:(NSError*)error {
210 appRunning_ = NO; 244 appRunning_ = NO;
211 // Wait for the output thread to finish copying data to stdout. 245 // Wait for the output thread to finish copying data to stdout.
212 if (outputThread_) { 246 if (outputThread_) {
213 while (![outputThread_ isFinished]) { 247 while (![outputThread_ isFinished]) {
214 [NSThread sleepForTimeInterval:kOutputPollIntervalSeconds]; 248 [NSThread sleepForTimeInterval:kOutputPollIntervalSeconds];
215 } 249 }
216 [outputThread_ release]; 250 [outputThread_ release];
217 outputThread_ = nil; 251 outputThread_ = nil;
218 } 252 }
219 253
220 if (error) { 254 if (error) {
221 LogError(@"Simulator ended with error: %@", [error localizedDescription]); 255 // There appears to be a race condition where sometimes the simulator
222 exit(EXIT_FAILURE); 256 // framework will end with an error, but the error is that the simulated
257 // app cleanly shut down, try trap this error and don't fail the simulator
stuartmorgan 2012/08/01 14:26:17 s/,/;/ s/try trap/try to trap/
TVL 2012/08/01 14:35:16 Done. Also fixed in the cl description.
258 // run.
259 NSString* localizedDescription = [error localizedDescription];
260 NSString* ignorableErrorStr =
261 [self localizedSimulatorErrorString:kSimulatorAppQuitErrorKey];
262 if ([ignorableErrorStr isEqual:localizedDescription]) {
263 LogWarning(@"Ignoring that Simulator ended with: \"%@\" (%@:%ld)",
264 localizedDescription, [error domain], (long)[error code]);
stuartmorgan 2012/08/01 14:26:17 C++ cast s/long/long int/
TVL 2012/08/01 14:35:16 Done.
265 } else {
266 LogError(@"Simulator ended with error: \"%@\" (%@:%ld)",
267 localizedDescription, [error domain], (long)[error code]);
stuartmorgan 2012/08/01 14:26:17 Same
TVL 2012/08/01 14:35:16 Done.
268 exit(EXIT_FAILURE);
269 }
223 } 270 }
224 271
225 // Check if the simulated app exited abnormally by looking for system log 272 // 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 273 // messages from launchd that refer to the simulated app's PID. Limit query
227 // to messages in the last minute since PIDs are cyclical. 274 // to messages in the last minute since PIDs are cyclical.
228 aslmsg query = asl_new(ASL_TYPE_QUERY); 275 aslmsg query = asl_new(ASL_TYPE_QUERY);
229 asl_set_query(query, ASL_KEY_SENDER, "launchd", 276 asl_set_query(query, ASL_KEY_SENDER, "launchd",
230 ASL_QUERY_OP_EQUAL | ASL_QUERY_OP_SUBSTRING); 277 ASL_QUERY_OP_EQUAL | ASL_QUERY_OP_SUBSTRING);
231 asl_set_query(query, ASL_KEY_REF_PID, 278 asl_set_query(query, ASL_KEY_REF_PID,
232 [[[session simulatedApplicationPID] stringValue] UTF8String], 279 [[[session simulatedApplicationPID] stringValue] UTF8String],
(...skipping 363 matching lines...) Expand 10 before | Expand all | Expand 10 after
596 643
597 // Create the config and simulator session. 644 // Create the config and simulator session.
598 DTiPhoneSimulatorSessionConfig* config = BuildSessionConfig(appSpec, 645 DTiPhoneSimulatorSessionConfig* config = BuildSessionConfig(appSpec,
599 systemRoot, 646 systemRoot,
600 stdioPath, 647 stdioPath,
601 stdioPath, 648 stdioPath,
602 appArgs, 649 appArgs,
603 appEnv, 650 appEnv,
604 deviceFamily); 651 deviceFamily);
605 SimulatorDelegate* delegate = 652 SimulatorDelegate* delegate =
606 [[[SimulatorDelegate alloc] initWithStdioPath:stdioPath] autorelease]; 653 [[[SimulatorDelegate alloc] initWithStdioPath:stdioPath
654 developerDir:developerDir] autorelease];
607 DTiPhoneSimulatorSession* session = BuildSession(delegate); 655 DTiPhoneSimulatorSession* session = BuildSession(delegate);
608 656
609 // Start the simulator session. 657 // Start the simulator session.
610 NSError* error; 658 NSError* error;
611 BOOL started = [session requestStartWithConfig:config 659 BOOL started = [session requestStartWithConfig:config
612 timeout:kSessionStartTimeoutSeconds 660 timeout:kSessionStartTimeoutSeconds
613 error:&error]; 661 error:&error];
614 662
615 // Spin the runtime indefinitely. When the delegate gets the message that the 663 // Spin the runtime indefinitely. When the delegate gets the message that the
616 // app has quit it will exit this program. 664 // app has quit it will exit this program.
617 if (started) 665 if (started) {
618 [[NSRunLoop mainRunLoop] run]; 666 [[NSRunLoop mainRunLoop] run];
619 else 667 } else {
620 LogError(@"Simulator failed to start: %@", [error localizedDescription]); 668 LogError(@"Simulator failed to start: \"%@\" (%@:%ld)",
669 [error localizedDescription],
670 [error domain], (long)[error code]);
stuartmorgan 2012/08/01 14:26:17 Same
TVL 2012/08/01 14:35:16 Done.
671 }
621 672
622 // Note that this code is only executed if the simulator fails to start 673 // 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 674 // because once the main run loop is started, only the delegate calling
624 // exit() will end the program. 675 // exit() will end the program.
625 [pool drain]; 676 [pool drain];
626 return EXIT_FAILURE; 677 return EXIT_FAILURE;
627 } 678 }
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