Chromium Code Reviews| 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 |