OLD | NEW |
---|---|
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 library browser; | 4 library browser; |
5 | 5 |
6 import "dart:async"; | 6 import "dart:async"; |
7 import "dart:convert" show LineSplitter, UTF8, JSON; | 7 import "dart:convert" show LineSplitter, UTF8, JSON; |
8 import "dart:core"; | 8 import "dart:core"; |
9 import "dart:io"; | 9 import "dart:io"; |
10 import "dart:math" show max, min; | 10 import "dart:math" show max, min; |
11 | 11 |
12 import 'android.dart'; | 12 import 'android.dart'; |
13 import 'http_server.dart'; | 13 import 'http_server.dart'; |
14 import 'path.dart'; | 14 import 'path.dart'; |
15 import 'utils.dart'; | 15 import 'utils.dart'; |
16 | 16 |
17 import 'reset_safari.dart' show | |
18 killAndResetSafari; | |
19 | |
17 class BrowserOutput { | 20 class BrowserOutput { |
18 final StringBuffer stdout = new StringBuffer(); | 21 final StringBuffer stdout = new StringBuffer(); |
19 final StringBuffer stderr = new StringBuffer(); | 22 final StringBuffer stderr = new StringBuffer(); |
20 final StringBuffer eventLog = new StringBuffer(); | 23 final StringBuffer eventLog = new StringBuffer(); |
21 } | 24 } |
22 | 25 |
23 /** Class describing the interface for communicating with browsers. */ | 26 /** Class describing the interface for communicating with browsers. */ |
24 abstract class Browser { | 27 abstract class Browser { |
25 BrowserOutput _allBrowserOutput = new BrowserOutput(); | 28 BrowserOutput _allBrowserOutput = new BrowserOutput(); |
26 BrowserOutput _testBrowserOutput = new BrowserOutput(); | 29 BrowserOutput _testBrowserOutput = new BrowserOutput(); |
(...skipping 19 matching lines...) Expand all Loading... | |
46 Process process; | 49 Process process; |
47 | 50 |
48 Function logger; | 51 Function logger; |
49 | 52 |
50 /** | 53 /** |
51 * Id of the browser | 54 * Id of the browser |
52 */ | 55 */ |
53 String id; | 56 String id; |
54 | 57 |
55 /** | 58 /** |
56 * Delete the browser specific caches on startup. | 59 * Reset the browser to a known configuration on start-up. |
57 * Browser specific implementations are free to ignore this. | 60 * Browser specific implementations are free to ignore this. |
58 */ | 61 */ |
59 static bool deleteCache = false; | 62 static bool resetBrowserConfiguration = false; |
60 | 63 |
61 /** Print everything (stdout, stderr, usageLog) whenever we add to it */ | 64 /** Print everything (stdout, stderr, usageLog) whenever we add to it */ |
62 bool debugPrint = false; | 65 bool debugPrint = false; |
63 | 66 |
64 // This future returns when the process exits. It is also the return value | 67 // This future returns when the process exits. It is also the return value |
65 // of close() | 68 // of close() |
66 Future done; | 69 Future done; |
67 | 70 |
68 Browser(); | 71 Browser(); |
69 | 72 |
(...skipping 28 matching lines...) Expand all Loading... | |
98 'ie10', | 101 'ie10', |
99 'ie11', | 102 'ie11', |
100 'dartium' | 103 'dartium' |
101 ]; | 104 ]; |
102 | 105 |
103 static const List<String> BROWSERS_WITH_WINDOW_SUPPORT = const [ | 106 static const List<String> BROWSERS_WITH_WINDOW_SUPPORT = const [ |
104 'ie11', | 107 'ie11', |
105 'ie10' | 108 'ie10' |
106 ]; | 109 ]; |
107 | 110 |
111 /// If [browserName] doesn't support Window.open, we use iframes instead. | |
112 static bool requiresIframe(String browserName) { | |
113 return !BROWSERS_WITH_WINDOW_SUPPORT.contains(browserName); | |
114 } | |
115 | |
116 static bool requiresFocus(String browserName) { | |
117 return browserName == "safari"; | |
118 } | |
119 | |
108 // TODO(kustermann): add standard support for chrome on android | 120 // TODO(kustermann): add standard support for chrome on android |
109 static bool supportedBrowser(String name) { | 121 static bool supportedBrowser(String name) { |
110 return SUPPORTED_BROWSERS.contains(name); | 122 return SUPPORTED_BROWSERS.contains(name); |
111 } | 123 } |
112 | 124 |
113 void _logEvent(String event) { | 125 void _logEvent(String event) { |
114 String toLog = "$this ($id) - $event \n"; | 126 String toLog = "$this ($id) - $event \n"; |
115 if (debugPrint) print("usageLog: $toLog"); | 127 if (debugPrint) print("usageLog: $toLog"); |
116 if (logger != null) logger(toLog); | 128 if (logger != null) logger(toLog); |
117 | 129 |
(...skipping 144 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
262 * Add useful info about the browser to the _testBrowserOutput.stdout, | 274 * Add useful info about the browser to the _testBrowserOutput.stdout, |
263 * where it will be reported for failing tests. Used to report which | 275 * where it will be reported for failing tests. Used to report which |
264 * android device a failing test is running on. | 276 * android device a failing test is running on. |
265 */ | 277 */ |
266 void logBrowserInfoToTestBrowserOutput() {} | 278 void logBrowserInfoToTestBrowserOutput() {} |
267 | 279 |
268 String toString(); | 280 String toString(); |
269 | 281 |
270 /** Starts the browser loading the given url */ | 282 /** Starts the browser loading the given url */ |
271 Future<bool> start(String url); | 283 Future<bool> start(String url); |
284 | |
285 Future<Null> notifyGetDriverPage() => new Future<Null>.value(); | |
Bill Hesse
2016/06/09 17:35:42
This could use a comment as explanation, that this
ahe
2016/06/15 08:16:54
Done.
| |
272 } | 286 } |
273 | 287 |
274 class Safari extends Browser { | 288 class Safari extends Browser { |
275 /** | 289 /** |
276 * We get the safari version by parsing a version file | 290 * We get the safari version by parsing a version file |
277 */ | 291 */ |
278 static const String versionFile = | 292 static const String versionFile = |
279 "/Applications/Safari.app/Contents/version.plist"; | 293 "/Applications/Safari.app/Contents/version.plist"; |
280 | 294 |
281 /** | 295 static const String safariBundleLocation = "/Applications/Safari.app/"; |
282 * Directories where safari stores state. We delete these if the deleteCache | |
283 * is set | |
284 */ | |
285 static const List<String> CACHE_DIRECTORIES = const [ | |
286 "Library/Caches/com.apple.Safari", | |
287 "Library/Safari", | |
288 "Library/Saved Application State/com.apple.Safari.savedState", | |
289 "Library/Caches/Metadata/Safari" | |
290 ]; | |
291 | 296 |
292 Future<bool> allowPopUps() { | 297 // Clears the cache if the static resetBrowserConfiguration flag is set. |
293 var command = "defaults"; | 298 // Returns false if the command to actually clear the cache did not complete. |
294 var args = [ | 299 Future<bool> resetConfiguration() async { |
295 "write", | 300 if (!Browser.resetBrowserConfiguration) return true; |
296 "com.apple.safari", | 301 |
297 "com.apple.Safari.ContentPageGroupIdentifier." | 302 Completer completer = new Completer(); |
298 "WebKit2JavaScriptCanOpenWindowsAutomatically", | 303 handleUncaughtError(error, StackTrace stackTrace) { |
299 "1" | 304 if (!completer.isCompleted) { |
300 ]; | 305 completer.completeError(error, stackTrace); |
301 return Process.run(command, args).then((result) { | 306 } else { |
302 if (result.exitCode != 0) { | 307 throw new AsyncError(error, stackTrace); |
303 _logEvent("Could not disable pop-up blocking for safari"); | |
304 return false; | |
305 } | 308 } |
309 } | |
310 Zone parent = Zone.current; | |
311 ZoneSpecification specification = new ZoneSpecification( | |
312 print: (Zone self, ZoneDelegate delegate, Zone zone, String line) { | |
313 delegate.run(parent, () { | |
314 _logEvent(line); | |
315 }); | |
316 }); | |
317 Future zoneWrapper() { | |
318 Uri safariUri = Uri.base.resolve(safariBundleLocation); | |
319 return new Future(() => killAndResetSafari(bundle: safariUri)) | |
320 .then(completer.complete); | |
321 } | |
322 | |
323 // We run killAndResetSafari in a Zone as opposed to running an external | |
324 // process. The Zone allows us to collect its output, and protect the rest | |
325 // of the test infrastructure against errors in it. | |
326 runZoned( | |
327 zoneWrapper, zoneSpecification: specification, | |
328 onError: handleUncaughtError); | |
329 | |
330 try { | |
331 await completer.future; | |
306 return true; | 332 return true; |
307 }); | 333 } catch (error, st) { |
308 } | 334 _logEvent("Unable to reset Safari: $error$st"); |
309 | 335 return false; |
310 Future<bool> deleteIfExists(Iterator<String> paths) { | 336 } |
311 if (!paths.moveNext()) return new Future.value(true); | |
312 Directory directory = new Directory(paths.current); | |
313 return directory.exists().then((exists) { | |
314 if (exists) { | |
315 _logEvent("Deleting ${paths.current}"); | |
316 return directory | |
317 .delete(recursive: true) | |
318 .then((_) => deleteIfExists(paths)) | |
319 .catchError((error) { | |
320 _logEvent("Failure trying to delete ${paths.current}: $error"); | |
321 return false; | |
322 }); | |
323 } else { | |
324 _logEvent("${paths.current} is not present"); | |
325 return deleteIfExists(paths); | |
326 } | |
327 }); | |
328 } | |
329 | |
330 // Clears the cache if the static deleteCache flag is set. | |
331 // Returns false if the command to actually clear the cache did not complete. | |
332 Future<bool> clearCache() { | |
333 if (!Browser.deleteCache) return new Future.value(true); | |
334 var home = Platform.environment['HOME']; | |
335 Iterator iterator = CACHE_DIRECTORIES.map((s) => "$home/$s").iterator; | |
336 return deleteIfExists(iterator); | |
337 } | 337 } |
338 | 338 |
339 Future<String> getVersion() { | 339 Future<String> getVersion() { |
340 /** | 340 /** |
341 * Example of the file: | 341 * Example of the file: |
342 * <?xml version="1.0" encoding="UTF-8"?> | 342 * <?xml version="1.0" encoding="UTF-8"?> |
343 * <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.co m/DTDs/PropertyList-1.0.dtd"> | 343 * <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.co m/DTDs/PropertyList-1.0.dtd"> |
344 * <plist version="1.0"> | 344 * <plist version="1.0"> |
345 * <dict> | 345 * <dict> |
346 * <key>BuildVersion</key> | 346 * <key>BuildVersion</key> |
(...skipping 15 matching lines...) Expand all Loading... | |
362 for (var line in content) { | 362 for (var line in content) { |
363 if (versionOnNextLine) return line; | 363 if (versionOnNextLine) return line; |
364 if (line.contains("CFBundleShortVersionString")) { | 364 if (line.contains("CFBundleShortVersionString")) { |
365 versionOnNextLine = true; | 365 versionOnNextLine = true; |
366 } | 366 } |
367 } | 367 } |
368 return null; | 368 return null; |
369 }); | 369 }); |
370 } | 370 } |
371 | 371 |
372 void _createLaunchHTML(var path, var url) { | 372 Future<Null> _createLaunchHTML(var path, var url) async { |
373 var file = new File("${path}/launch.html"); | 373 var file = new File("${path}/launch.html"); |
374 var randomFile = file.openSync(mode: FileMode.WRITE); | 374 var randomFile = await file.open(mode: FileMode.WRITE); |
375 var content = '<script language="JavaScript">location = "$url"</script>'; | 375 var content = '<script language="JavaScript">location = "$url"</script>'; |
376 randomFile.writeStringSync(content); | 376 await randomFile.writeString(content); |
377 randomFile.close(); | 377 await randomFile.close(); |
378 } | 378 } |
379 | 379 |
380 Future<bool> start(String url) { | 380 Future<bool> start(String url) async { |
381 _logEvent("Starting Safari browser on: $url"); | 381 _logEvent("Starting Safari browser on: $url"); |
382 return allowPopUps().then((success) { | 382 if (!await resetConfiguration()) { |
383 if (!success) { | 383 _logEvent("Could not clear cache"); |
384 return false; | 384 return false; |
385 } | 385 } |
386 return clearCache().then((cleared) { | 386 String version; |
387 if (!cleared) { | 387 try { |
388 _logEvent("Could not clear cache"); | 388 version = await getVersion(); |
389 return false; | 389 } catch (error) { |
390 } | 390 _logEvent("Running $_binary --version failed with $error"); |
391 // Get the version and log that. | 391 return false; |
392 return getVersion().then((version) { | 392 } |
393 _logEvent("Got version: $version"); | 393 _logEvent("Got version: $version"); |
394 return Directory.systemTemp.createTemp().then((userDir) { | 394 Directory userDir; |
395 _cleanup = () { | 395 try { |
396 userDir.deleteSync(recursive: true); | 396 userDir = await Directory.systemTemp.createTemp(); |
397 }; | 397 } catch (error) { |
398 _createLaunchHTML(userDir.path, url); | 398 _logEvent("Error creating temporary directory: $error"); |
399 var args = ["${userDir.path}/launch.html"]; | 399 return false; |
400 return startBrowserProcess(_binary, args); | 400 } |
401 }); | 401 _cleanup = () { |
402 }).catchError((error) { | 402 userDir.deleteSync(recursive: true); |
403 _logEvent("Running $_binary --version failed with $error"); | 403 }; |
404 return false; | 404 try { |
405 }); | 405 await _createLaunchHTML(userDir.path, url); |
406 }); | 406 } catch (error) { |
407 }); | 407 _logEvent("Error creating launch HTML: $error"); |
408 return false; | |
409 } | |
410 var args = [ | |
411 "-d", "-i", "-m", "-s", "-u", _binary, | |
412 "${userDir.path}/launch.html"]; | |
413 try { | |
414 return startBrowserProcess("/usr/bin/caffeinate", args); | |
415 } catch (error) { | |
416 _logEvent("Error starting browser process: $error"); | |
417 return false; | |
418 } | |
419 } | |
420 | |
421 Future<Null> notifyGetDriverPage() async { | |
422 await Process.run("/usr/bin/osascript", | |
423 ['-e', 'tell application "Safari" to activate']); | |
408 } | 424 } |
409 | 425 |
410 String toString() => "Safari"; | 426 String toString() => "Safari"; |
411 } | 427 } |
412 | 428 |
413 class Chrome extends Browser { | 429 class Chrome extends Browser { |
414 String _version = "Version not found yet"; | 430 String _version = "Version not found yet"; |
415 | 431 |
416 Map<String, String> _getEnvironment() => null; | 432 Map<String, String> _getEnvironment() => null; |
417 | 433 |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
470 return false; | 486 return false; |
471 }); | 487 }); |
472 } | 488 } |
473 | 489 |
474 String toString() => "Chrome"; | 490 String toString() => "Chrome"; |
475 } | 491 } |
476 | 492 |
477 class SafariMobileSimulator extends Safari { | 493 class SafariMobileSimulator extends Safari { |
478 /** | 494 /** |
479 * Directories where safari simulator stores state. We delete these if the | 495 * Directories where safari simulator stores state. We delete these if the |
480 * deleteCache is set | 496 * resetBrowserConfiguration is set |
481 */ | 497 */ |
482 static const List<String> CACHE_DIRECTORIES = const [ | 498 static const List<String> CACHE_DIRECTORIES = const [ |
483 "Library/Application Support/iPhone Simulator/7.1/Applications" | 499 "Library/Application Support/iPhone Simulator/7.1/Applications" |
484 ]; | 500 ]; |
485 | 501 |
486 // Clears the cache if the static deleteCache flag is set. | 502 // Clears the cache if the static resetBrowserConfiguration flag is set. |
487 // Returns false if the command to actually clear the cache did not complete. | 503 // Returns false if the command to actually clear the cache did not complete. |
488 Future<bool> clearCache() { | 504 Future<bool> resetConfiguration() { |
489 if (!Browser.deleteCache) return new Future.value(true); | 505 if (!Browser.resetBrowserConfiguration) return new Future.value(true); |
490 var home = Platform.environment['HOME']; | 506 var home = Platform.environment['HOME']; |
491 Iterator iterator = CACHE_DIRECTORIES.map((s) => "$home/$s").iterator; | 507 Iterator iterator = CACHE_DIRECTORIES.map((s) => "$home/$s").iterator; |
492 return deleteIfExists(iterator); | 508 return deleteIfExists(iterator); |
493 } | 509 } |
494 | 510 |
495 Future<bool> start(String url) { | 511 Future<bool> start(String url) { |
496 _logEvent("Starting safari mobile simulator browser on: $url"); | 512 _logEvent("Starting safari mobile simulator browser on: $url"); |
497 return clearCache().then((success) { | 513 return resetConfiguration().then((success) { |
498 if (!success) { | 514 if (!success) { |
499 _logEvent("Could not clear cache, exiting"); | 515 _logEvent("Could not clear cache, exiting"); |
500 return false; | 516 return false; |
501 } | 517 } |
502 var args = [ | 518 var args = [ |
503 "-SimulateApplication", | 519 "-SimulateApplication", |
504 "/Applications/Xcode.app/Contents/Developer/Platforms/" | 520 "/Applications/Xcode.app/Contents/Developer/Platforms/" |
505 "iPhoneSimulator.platform/Developer/SDKs/" | 521 "iPhoneSimulator.platform/Developer/SDKs/" |
506 "iPhoneSimulator7.1.sdk/Applications/MobileSafari.app/" | 522 "iPhoneSimulator7.1.sdk/Applications/MobileSafari.app/" |
507 "MobileSafari", | 523 "MobileSafari", |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
554 var findString = "REG_SZ"; | 570 var findString = "REG_SZ"; |
555 var index = result.stdout.indexOf(findString); | 571 var index = result.stdout.indexOf(findString); |
556 if (index > 0) { | 572 if (index > 0) { |
557 return result.stdout.substring(index + findString.length).trim(); | 573 return result.stdout.substring(index + findString.length).trim(); |
558 } | 574 } |
559 } | 575 } |
560 return "Could not get the version of internet explorer"; | 576 return "Could not get the version of internet explorer"; |
561 }); | 577 }); |
562 } | 578 } |
563 | 579 |
564 // Clears the recovery cache if the static deleteCache flag is set. | 580 // Clears the recovery cache if the static resetBrowserConfiguration flag is |
565 Future<bool> clearCache() { | 581 // set. |
566 if (!Browser.deleteCache) return new Future.value(true); | 582 Future<bool> resetConfiguration() { |
583 if (!Browser.resetBrowserConfiguration) return new Future.value(true); | |
567 var localAppData = Platform.environment['LOCALAPPDATA']; | 584 var localAppData = Platform.environment['LOCALAPPDATA']; |
568 | 585 |
569 Directory dir = new Directory("$localAppData\\Microsoft\\" | 586 Directory dir = new Directory("$localAppData\\Microsoft\\" |
570 "Internet Explorer\\Recovery"); | 587 "Internet Explorer\\Recovery"); |
571 return dir.delete(recursive: true).then((_) { | 588 return dir.delete(recursive: true).then((_) { |
572 return true; | 589 return true; |
573 }).catchError((error) { | 590 }).catchError((error) { |
574 _logEvent("Deleting recovery dir failed with $error"); | 591 _logEvent("Deleting recovery dir failed with $error"); |
575 return false; | 592 return false; |
576 }); | 593 }); |
577 } | 594 } |
578 | 595 |
579 Future<bool> start(String url) { | 596 Future<bool> start(String url) { |
580 _logEvent("Starting ie browser on: $url"); | 597 _logEvent("Starting ie browser on: $url"); |
581 return clearCache().then((_) => getVersion()).then((version) { | 598 return resetConfiguration().then((_) => getVersion()).then((version) { |
582 _logEvent("Got version: $version"); | 599 _logEvent("Got version: $version"); |
583 return startBrowserProcess(_binary, [url]); | 600 return startBrowserProcess(_binary, [url]); |
584 }); | 601 }); |
585 } | 602 } |
586 | 603 |
587 String toString() => "IE"; | 604 String toString() => "IE"; |
588 } | 605 } |
589 | 606 |
590 class AndroidBrowserConfig { | 607 class AndroidBrowserConfig { |
591 final String name; | 608 final String name; |
(...skipping 277 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
869 /// requests back from the browsers. | 886 /// requests back from the browsers. |
870 class BrowserTestRunner { | 887 class BrowserTestRunner { |
871 static const int MAX_NEXT_TEST_TIMEOUTS = 10; | 888 static const int MAX_NEXT_TEST_TIMEOUTS = 10; |
872 static const Duration NEXT_TEST_TIMEOUT = const Duration(seconds: 60); | 889 static const Duration NEXT_TEST_TIMEOUT = const Duration(seconds: 60); |
873 static const Duration RESTART_BROWSER_INTERVAL = const Duration(seconds: 60); | 890 static const Duration RESTART_BROWSER_INTERVAL = const Duration(seconds: 60); |
874 | 891 |
875 /// If the queue was recently empty, don't start another browser. | 892 /// If the queue was recently empty, don't start another browser. |
876 static const Duration MIN_NONEMPTY_QUEUE_TIME = const Duration(seconds: 1); | 893 static const Duration MIN_NONEMPTY_QUEUE_TIME = const Duration(seconds: 1); |
877 | 894 |
878 final Map configuration; | 895 final Map configuration; |
879 BrowserTestingServer testingServer; | 896 final BrowserTestingServer testingServer; |
880 | 897 |
881 final String localIp; | 898 final String localIp; |
882 String browserName; | 899 final String browserName; |
883 int maxNumBrowsers; | 900 final int maxNumBrowsers; |
884 bool checkedMode; | 901 final bool checkedMode; |
885 int numBrowsers = 0; | 902 int numBrowsers = 0; |
886 // Used to send back logs from the browser (start, stop etc) | 903 // Used to send back logs from the browser (start, stop etc) |
887 Function logger; | 904 Function logger; |
888 | 905 |
889 int browserIdCounter = 1; | 906 int browserIdCounter = 1; |
890 | 907 |
891 bool testingServerStarted = false; | 908 bool testingServerStarted = false; |
892 bool underTermination = false; | 909 bool underTermination = false; |
893 int numBrowserGetTestTimeouts = 0; | 910 int numBrowserGetTestTimeouts = 0; |
894 DateTime lastEmptyTestQueueTime = new DateTime.now(); | 911 DateTime lastEmptyTestQueueTime = new DateTime.now(); |
(...skipping 26 matching lines...) Expand all Loading... | |
921 // When no browser is currently starting, _currentStartingBrowserId is null. | 938 // When no browser is currently starting, _currentStartingBrowserId is null. |
922 bool get aBrowserIsCurrentlyStarting => _currentStartingBrowserId != null; | 939 bool get aBrowserIsCurrentlyStarting => _currentStartingBrowserId != null; |
923 void markCurrentlyStarting(String id) { | 940 void markCurrentlyStarting(String id) { |
924 _currentStartingBrowserId = id; | 941 _currentStartingBrowserId = id; |
925 } | 942 } |
926 | 943 |
927 void markNotCurrentlyStarting(String id) { | 944 void markNotCurrentlyStarting(String id) { |
928 if (_currentStartingBrowserId == id) _currentStartingBrowserId = null; | 945 if (_currentStartingBrowserId == id) _currentStartingBrowserId = null; |
929 } | 946 } |
930 | 947 |
931 // If [browserName] doesn't support opening new windows, we use new iframes | |
932 // instead. | |
933 bool get useIframe => | |
934 !Browser.BROWSERS_WITH_WINDOW_SUPPORT.contains(browserName); | |
935 | |
936 /// The optional testingServer parameter allows callers to pass in | |
937 /// a testing server with different behavior than the default | |
938 /// BrowserTestServer. The url handlers of the testingServer are | |
939 /// overwritten, so an existing handler can't be shared between instances. | |
940 BrowserTestRunner( | 948 BrowserTestRunner( |
941 this.configuration, this.localIp, this.browserName, this.maxNumBrowsers, | 949 Map configuration, |
942 {BrowserTestingServer this.testingServer}) { | 950 String localIp, |
943 checkedMode = configuration['checked']; | 951 String browserName, |
944 if (browserName == 'ff') browserName = 'firefox'; | 952 this.maxNumBrowsers) |
953 : configuration = configuration, | |
954 localIp = localIp, | |
955 browserName = (browserName == 'ff') ? 'firefox' : browserName, | |
956 checkedMode = configuration['checked'], | |
957 testingServer = new BrowserTestingServer( | |
958 configuration, localIp, | |
959 Browser.requiresIframe(browserName), | |
960 Browser.requiresFocus(browserName)) { | |
961 testingServer.testRunner = this; | |
945 } | 962 } |
946 | 963 |
947 Future start() async { | 964 Future start() async { |
948 if (testingServer == null) { | |
949 testingServer = | |
950 new BrowserTestingServer(configuration, localIp, useIframe); | |
951 } | |
952 await testingServer.start(); | 965 await testingServer.start(); |
953 testingServer | 966 testingServer |
954 ..testDoneCallBack = handleResults | 967 ..testDoneCallBack = handleResults |
955 ..testStatusUpdateCallBack = handleStatusUpdate | 968 ..testStatusUpdateCallBack = handleStatusUpdate |
956 ..testStartedCallBack = handleStarted | 969 ..testStartedCallBack = handleStarted |
957 ..nextTestCallBack = getNextTest; | 970 ..nextTestCallBack = getNextTest; |
958 if (browserName == 'chromeOnAndroid') { | 971 if (browserName == 'chromeOnAndroid') { |
959 var idbNames = await AdbHelper.listDevices(); | 972 var idbNames = await AdbHelper.listDevices(); |
960 idleAdbDevices = new List.from(idbNames.map((id) => new AdbDevice(id))); | 973 idleAdbDevices = new List.from(idbNames.map((id) => new AdbDevice(id))); |
961 maxNumBrowsers = min(maxNumBrowsers, idleAdbDevices.length); | 974 maxNumBrowsers = min(maxNumBrowsers, idleAdbDevices.length); |
(...skipping 316 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1278 /// and run tests ... | 1291 /// and run tests ... |
1279 /// GET /next_test/BROWSER_ID -- returns "WAIT" "TERMINATE" or "url#id" | 1292 /// GET /next_test/BROWSER_ID -- returns "WAIT" "TERMINATE" or "url#id" |
1280 /// where url is the test to run, and id is the id of the test. | 1293 /// where url is the test to run, and id is the id of the test. |
1281 /// If there are currently no available tests the waitSignal is send | 1294 /// If there are currently no available tests the waitSignal is send |
1282 /// back. If we are in the process of terminating the terminateSignal | 1295 /// back. If we are in the process of terminating the terminateSignal |
1283 /// is send back and the browser will stop requesting new tasks. | 1296 /// is send back and the browser will stop requesting new tasks. |
1284 /// POST /report/BROWSER_ID?id=NUM -- sends back the dom of the executed | 1297 /// POST /report/BROWSER_ID?id=NUM -- sends back the dom of the executed |
1285 /// test | 1298 /// test |
1286 | 1299 |
1287 final String localIp; | 1300 final String localIp; |
1301 final bool useIframe; | |
1302 final bool requiresFocus; | |
1303 BrowserTestRunner testRunner; | |
1288 | 1304 |
1289 static const String driverPath = "/driver"; | 1305 static const String driverPath = "/driver"; |
1290 static const String nextTestPath = "/next_test"; | 1306 static const String nextTestPath = "/next_test"; |
1291 static const String reportPath = "/report"; | 1307 static const String reportPath = "/report"; |
1292 static const String statusUpdatePath = "/status_update"; | 1308 static const String statusUpdatePath = "/status_update"; |
1293 static const String startedPath = "/started"; | 1309 static const String startedPath = "/started"; |
1294 static const String waitSignal = "WAIT"; | 1310 static const String waitSignal = "WAIT"; |
1295 static const String terminateSignal = "TERMINATE"; | 1311 static const String terminateSignal = "TERMINATE"; |
1296 | 1312 |
1297 var testCount = 0; | 1313 var testCount = 0; |
1298 var errorReportingServer; | 1314 var errorReportingServer; |
1299 bool underTermination = false; | 1315 bool underTermination = false; |
1300 bool useIframe = false; | |
1301 | 1316 |
1302 Function testDoneCallBack; | 1317 Function testDoneCallBack; |
1303 Function testStatusUpdateCallBack; | 1318 Function testStatusUpdateCallBack; |
1304 Function testStartedCallBack; | 1319 Function testStartedCallBack; |
1305 Function nextTestCallBack; | 1320 Function nextTestCallBack; |
1306 | 1321 |
1307 BrowserTestingServer(this.configuration, this.localIp, this.useIframe); | 1322 BrowserTestingServer( |
1323 this.configuration, this.localIp, this.useIframe, this.requiresFocus); | |
1308 | 1324 |
1309 Future start() { | 1325 Future start() { |
1310 var test_driver_error_port = configuration['test_driver_error_port']; | 1326 var test_driver_error_port = configuration['test_driver_error_port']; |
1311 return HttpServer | 1327 return HttpServer |
1312 .bind(localIp, test_driver_error_port) | 1328 .bind(localIp, test_driver_error_port) |
1313 .then(setupErrorServer) | 1329 .then(setupErrorServer) |
1314 .then(setupDispatchingServer); | 1330 .then(setupDispatchingServer); |
1315 } | 1331 } |
1316 | 1332 |
1317 void setupErrorServer(HttpServer server) { | 1333 void setupErrorServer(HttpServer server) { |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1359 noCache(request); | 1375 noCache(request); |
1360 handleReport( | 1376 handleReport( |
1361 request, browserId(request, statusUpdatePath), testId(request), | 1377 request, browserId(request, statusUpdatePath), testId(request), |
1362 isStatusUpdate: true); | 1378 isStatusUpdate: true); |
1363 }); | 1379 }); |
1364 server.addHandler(startedPath, (HttpRequest request) { | 1380 server.addHandler(startedPath, (HttpRequest request) { |
1365 noCache(request); | 1381 noCache(request); |
1366 handleStarted(request, browserId(request, startedPath), testId(request)); | 1382 handleStarted(request, browserId(request, startedPath), testId(request)); |
1367 }); | 1383 }); |
1368 | 1384 |
1369 makeSendPageHandler(String prefix) => (HttpRequest request) { | 1385 void sendPageHandler(HttpRequest request) { |
1370 noCache(request); | 1386 // Do NOT make this method async. We need to call catchError below |
1371 var textResponse = ""; | 1387 // synchronously to avoid unhandled asynchronous errors. |
1372 if (prefix == driverPath) { | 1388 noCache(request); |
1373 textResponse = getDriverPage(browserId(request, prefix)); | 1389 Future<String> textResponse; |
1374 request.response.headers.set('Content-Type', 'text/html'); | 1390 if (request.uri.path.startsWith(driverPath)) { |
1375 } | 1391 textResponse = getDriverPage(browserId(request, driverPath)); |
1376 if (prefix == nextTestPath) { | 1392 request.response.headers.set('Content-Type', 'text/html'); |
1377 textResponse = getNextTest(browserId(request, prefix)); | 1393 } else if (request.uri.path.startsWith(nextTestPath)) { |
1378 request.response.headers.set('Content-Type', 'text/plain'); | 1394 textResponse = new Future<String>.value( |
1379 } | 1395 getNextTest(browserId(request, nextTestPath))); |
1380 request.response.write(textResponse); | 1396 request.response.headers.set('Content-Type', 'text/plain'); |
1381 request.listen((_) {}, onDone: request.response.close); | 1397 } else { |
1382 request.response.done.catchError((error) { | 1398 textResponse = new Future<String>.value(""); |
1383 if (!underTermination) { | 1399 } |
1384 print("URI ${request.uri}"); | 1400 request.response.done.catchError((error) { |
1385 print("Textresponse $textResponse"); | 1401 if (!underTermination) { |
1386 throw "Error returning content to browser: $error"; | 1402 return textResponse.then((String text) { |
1387 } | 1403 print("URI ${request.uri}"); |
1404 print("textResponse $textResponse"); | |
1405 throw "Error returning content to browser: $error"; | |
1388 }); | 1406 }); |
1389 }; | 1407 } |
1390 server.addHandler(driverPath, makeSendPageHandler(driverPath)); | 1408 }); |
1391 server.addHandler(nextTestPath, makeSendPageHandler(nextTestPath)); | 1409 textResponse.then((String text) async { |
1410 request.response.write(text); | |
1411 await request.listen(null).asFuture(); | |
1412 // Ignoring the returned closure as it returns the 'done' future | |
1413 // which alread has catchError installed above. | |
1414 request.response.close(); | |
1415 }); | |
1416 } | |
1417 | |
1418 server.addHandler(driverPath, sendPageHandler); | |
1419 server.addHandler(nextTestPath, sendPageHandler); | |
1392 } | 1420 } |
1393 | 1421 |
1394 void handleReport(HttpRequest request, String browserId, var testId, | 1422 void handleReport(HttpRequest request, String browserId, var testId, |
1395 {bool isStatusUpdate}) { | 1423 {bool isStatusUpdate}) { |
1396 StringBuffer buffer = new StringBuffer(); | 1424 StringBuffer buffer = new StringBuffer(); |
1397 request.transform(UTF8.decoder).listen((data) { | 1425 request.transform(UTF8.decoder).listen((data) { |
1398 buffer.write(data); | 1426 buffer.write(data); |
1399 }, onDone: () { | 1427 }, onDone: () { |
1400 String back = buffer.toString(); | 1428 String back = buffer.toString(); |
1401 request.response.close(); | 1429 request.response.close(); |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1440 if (errorReportingServer == null) { | 1468 if (errorReportingServer == null) { |
1441 print("Bad browser testing server, you are not started yet. Can't " | 1469 print("Bad browser testing server, you are not started yet. Can't " |
1442 "produce driver url"); | 1470 "produce driver url"); |
1443 exit(1); | 1471 exit(1); |
1444 // This should never happen - exit immediately; | 1472 // This should never happen - exit immediately; |
1445 } | 1473 } |
1446 var port = configuration['_servers_'].port; | 1474 var port = configuration['_servers_'].port; |
1447 return "http://$localIp:$port/driver/$browserId"; | 1475 return "http://$localIp:$port/driver/$browserId"; |
1448 } | 1476 } |
1449 | 1477 |
1450 String getDriverPage(String browserId) { | 1478 Future<String> getDriverPage(String browserId) async { |
1479 await testRunner.browserStatus[browserId].browser.notifyGetDriverPage(); | |
1451 var errorReportingUrl = | 1480 var errorReportingUrl = |
1452 "http://$localIp:${errorReportingServer.port}/$browserId"; | 1481 "http://$localIp:${errorReportingServer.port}/$browserId"; |
1453 String driverContent = """ | 1482 String driverContent = """ |
1454 <!DOCTYPE html><html> | 1483 <!DOCTYPE html><html> |
1455 <head> | 1484 <head> |
1485 <title>Driving page</title> | |
1456 <style> | 1486 <style> |
1457 body { | 1487 .big-notice { |
1458 margin: 0; | 1488 background-color: red; |
1459 } | 1489 color: white; |
1460 .box { | 1490 font-weight: bold; |
1461 overflow: hidden; | 1491 font-size: xx-large; |
1462 overflow-y: auto; | 1492 text-align: center; |
1463 position: absolute; | 1493 } |
1464 left: 0; | 1494 .controller.box { |
1465 right: 0; | 1495 white-space: nowrap; |
1466 } | 1496 overflow: scroll; |
1467 .controller.box { | 1497 height: 6em; |
1468 height: 75px; | 1498 } |
1469 top: 0; | 1499 body { |
1470 } | 1500 font-family: sans-serif; |
1471 .test.box { | 1501 } |
1472 top: 75px; | 1502 body div { |
1473 bottom: 0; | 1503 padding-top: 10px; |
1474 } | 1504 } |
1475 </style> | 1505 </style> |
1476 <title>Driving page</title> | |
1477 <script type='text/javascript'> | 1506 <script type='text/javascript'> |
1478 var STATUS_UPDATE_INTERVAL = 10000; | 1507 var STATUS_UPDATE_INTERVAL = 10000; |
1479 | 1508 |
1480 function startTesting() { | 1509 function startTesting() { |
1481 var number_of_tests = 0; | 1510 var number_of_tests = 0; |
1482 var current_id; | 1511 var current_id; |
1483 var next_id; | 1512 var next_id; |
1484 | 1513 |
1485 // Has the test in the current iframe reported that it is done? | 1514 // Has the test in the current iframe reported that it is done? |
1486 var test_completed = true; | 1515 var test_completed = true; |
(...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1596 if (use_iframe) { | 1625 if (use_iframe) { |
1597 if (html_test) { | 1626 if (html_test) { |
1598 window.addEventListener('detect_errors', setChildHandlers, false); | 1627 window.addEventListener('detect_errors', setChildHandlers, false); |
1599 embedded_iframe.onload = checkChildHandlersInstalled; | 1628 embedded_iframe.onload = checkChildHandlersInstalled; |
1600 } else { | 1629 } else { |
1601 embedded_iframe.onload = null; | 1630 embedded_iframe.onload = null; |
1602 } | 1631 } |
1603 embedded_iframe_div.removeChild(embedded_iframe); | 1632 embedded_iframe_div.removeChild(embedded_iframe); |
1604 embedded_iframe = document.createElement('iframe'); | 1633 embedded_iframe = document.createElement('iframe'); |
1605 embedded_iframe.id = "embedded_iframe"; | 1634 embedded_iframe.id = "embedded_iframe"; |
1606 embedded_iframe.style="width:100%;height:100%"; | 1635 embedded_iframe.width='800px'; |
1636 embedded_iframe.height='600px'; | |
1607 embedded_iframe_div.appendChild(embedded_iframe); | 1637 embedded_iframe_div.appendChild(embedded_iframe); |
1608 embedded_iframe.src = url; | 1638 embedded_iframe.src = url; |
1609 } else { | 1639 } else { |
1610 if (typeof testing_window != 'undefined') { | 1640 if (typeof testing_window != 'undefined') { |
1611 testing_window.close(); | 1641 testing_window.close(); |
1612 } | 1642 } |
1613 testing_window = window.open(url); | 1643 testing_window = window.open(url); |
1614 } | 1644 } |
1615 test_started = false; | 1645 test_started = false; |
1616 test_completed = false; | 1646 test_completed = false; |
(...skipping 151 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1768 | 1798 |
1769 if (!html_test) { | 1799 if (!html_test) { |
1770 window.addEventListener('message', messageHandler, false); | 1800 window.addEventListener('message', messageHandler, false); |
1771 waitForDone = false; | 1801 waitForDone = false; |
1772 } | 1802 } |
1773 getNextTask(); | 1803 getNextTask(); |
1774 } | 1804 } |
1775 </script> | 1805 </script> |
1776 </head> | 1806 </head> |
1777 <body onload="startTesting()"> | 1807 <body onload="startTesting()"> |
1808 | |
1809 <div class='big-notice'> | |
1810 Please keep this window in focus at all times. | |
1811 </div> | |
1812 | |
1813 <div> | |
1814 Some browsers, Safari, in particular, may pause JavaScript when not | |
1815 visible to conserve power consumption and CPU resources. In addition, | |
1816 some tests of focus events will not work correctly if this window doesn't | |
1817 have focus. It's also advisable to close any other programs that may open | |
1818 modal dialogs, for example, Chrome with Calendar open. | |
1819 </div> | |
1820 | |
1778 <div class="controller box"> | 1821 <div class="controller box"> |
1779 Dart test driver, number of tests: <span id="number"></span><br> | 1822 Dart test driver, number of tests: <span id="number"></span><br> |
1780 Currently executing: <span id="currently_executing"></span><br> | 1823 Currently executing: <span id="currently_executing"></span><br> |
1781 Unhandled error: <span id="unhandled_error"></span> | 1824 Unhandled error: <span id="unhandled_error"></span> |
1782 </div> | 1825 </div> |
1783 <div id="embedded_iframe_div" class="test box"> | 1826 <div id="embedded_iframe_div" class="test box"> |
1784 <iframe style="width:100%;height:100%;" id="embedded_iframe"></iframe> | 1827 <iframe id="embedded_iframe"></iframe> |
1785 </div> | 1828 </div> |
1786 </body> | 1829 </body> |
1787 </html> | 1830 </html> |
1788 """; | 1831 """; |
1789 return driverContent; | 1832 return driverContent; |
1790 } | 1833 } |
1791 } | 1834 } |
OLD | NEW |