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

Side by Side Diff: tools/testing/dart/browser_controller.dart

Issue 1871883002: Make Safari tests more robust. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Address comments. Created 4 years, 6 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
OLDNEW
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
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
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
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
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
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
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
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
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
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
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
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
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698