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

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

Issue 1859523002: Start browsers sequentially in testing scripts, as needed. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Remove "requestBrowser" call from removeBrowser. Created 4 years, 8 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
« no previous file with comments | « no previous file | tools/testing/dart/test_runner.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 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 11
11 import 'android.dart'; 12 import 'android.dart';
12 import 'http_server.dart'; 13 import 'http_server.dart';
13 import 'path.dart'; 14 import 'path.dart';
14 import 'utils.dart'; 15 import 'utils.dart';
15 16
16 class BrowserOutput { 17 class BrowserOutput {
17 final StringBuffer stdout = new StringBuffer(); 18 final StringBuffer stdout = new StringBuffer();
18 final StringBuffer stderr = new StringBuffer(); 19 final StringBuffer stderr = new StringBuffer();
19 final StringBuffer eventLog = new StringBuffer(); 20 final StringBuffer eventLog = new StringBuffer();
(...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after
136 } else { 137 } else {
137 _logEvent("The process is already dead."); 138 _logEvent("The process is already dead.");
138 return new Future.value(true); 139 return new Future.value(true);
139 } 140 }
140 } 141 }
141 142
142 /** 143 /**
143 * Start the browser using the supplied argument. 144 * Start the browser using the supplied argument.
144 * This sets up the error handling and usage logging. 145 * This sets up the error handling and usage logging.
145 */ 146 */
146 Future<bool> startBrowser(String command, 147 Future<bool> startBrowserProcess(String command,
147 List<String> arguments, 148 List<String> arguments,
148 {Map<String,String> environment}) { 149 {Map<String,String> environment}) {
149 return Process.start(command, arguments, environment: environment) 150 return Process.start(command, arguments, environment: environment)
150 .then((startedProcess) { 151 .then((startedProcess) {
151 _logEvent("Started browser using $command ${arguments.join(' ')}"); 152 _logEvent("Started browser using $command ${arguments.join(' ')}");
152 process = startedProcess; 153 process = startedProcess;
153 // Used to notify when exiting, and as a return value on calls to 154 // Used to notify when exiting, and as a return value on calls to
154 // close(). 155 // close().
155 var doneCompleter = new Completer(); 156 var doneCompleter = new Completer();
156 done = doneCompleter.future; 157 done = doneCompleter.future;
(...skipping 218 matching lines...) Expand 10 before | Expand all | Expand 10 after
375 _logEvent("Could not clear cache"); 376 _logEvent("Could not clear cache");
376 return false; 377 return false;
377 } 378 }
378 // Get the version and log that. 379 // Get the version and log that.
379 return getVersion().then((version) { 380 return getVersion().then((version) {
380 _logEvent("Got version: $version"); 381 _logEvent("Got version: $version");
381 return Directory.systemTemp.createTemp().then((userDir) { 382 return Directory.systemTemp.createTemp().then((userDir) {
382 _cleanup = () { userDir.deleteSync(recursive: true); }; 383 _cleanup = () { userDir.deleteSync(recursive: true); };
383 _createLaunchHTML(userDir.path, url); 384 _createLaunchHTML(userDir.path, url);
384 var args = ["${userDir.path}/launch.html"]; 385 var args = ["${userDir.path}/launch.html"];
385 return startBrowser(_binary, args); 386 return startBrowserProcess(_binary, args);
386 }); 387 });
387 }).catchError((error) { 388 }).catchError((error) {
388 _logEvent("Running $_binary --version failed with $error"); 389 _logEvent("Running $_binary --version failed with $error");
389 return false; 390 return false;
390 }); 391 });
391 }); 392 });
392 }); 393 });
393 } 394 }
394 395
395 String toString() => "Safari"; 396 String toString() => "Safari";
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
435 // Get the version and log that. 436 // Get the version and log that.
436 return _getVersion().then((success) { 437 return _getVersion().then((success) {
437 if (!success) return false; 438 if (!success) return false;
438 _logEvent("Got version: $_version"); 439 _logEvent("Got version: $_version");
439 440
440 return Directory.systemTemp.createTemp().then((userDir) { 441 return Directory.systemTemp.createTemp().then((userDir) {
441 _cleanup = () { userDir.deleteSync(recursive: true); }; 442 _cleanup = () { userDir.deleteSync(recursive: true); };
442 var args = ["--user-data-dir=${userDir.path}", url, 443 var args = ["--user-data-dir=${userDir.path}", url,
443 "--disable-extensions", "--disable-popup-blocking", 444 "--disable-extensions", "--disable-popup-blocking",
444 "--bwsi", "--no-first-run"]; 445 "--bwsi", "--no-first-run"];
445 return startBrowser(_binary, args, environment: _getEnvironment()); 446 return startBrowserProcess(_binary, args, environment: _getEnvironment() );
446 }); 447 });
447 }).catchError((e) { 448 }).catchError((e) {
448 _logEvent("Running $_binary --version failed with $e"); 449 _logEvent("Running $_binary --version failed with $e");
449 return false; 450 return false;
450 }); 451 });
451 } 452 }
452 453
453 String toString() => "Chrome"; 454 String toString() => "Chrome";
454 } 455 }
455 456
(...skipping 21 matching lines...) Expand all
477 if (!success) { 478 if (!success) {
478 _logEvent("Could not clear cache, exiting"); 479 _logEvent("Could not clear cache, exiting");
479 return false; 480 return false;
480 } 481 }
481 var args = ["-SimulateApplication", 482 var args = ["-SimulateApplication",
482 "/Applications/Xcode.app/Contents/Developer/Platforms/" 483 "/Applications/Xcode.app/Contents/Developer/Platforms/"
483 "iPhoneSimulator.platform/Developer/SDKs/" 484 "iPhoneSimulator.platform/Developer/SDKs/"
484 "iPhoneSimulator7.1.sdk/Applications/MobileSafari.app/" 485 "iPhoneSimulator7.1.sdk/Applications/MobileSafari.app/"
485 "MobileSafari", 486 "MobileSafari",
486 "-u", url]; 487 "-u", url];
487 return startBrowser(_binary, args) 488 return startBrowserProcess(_binary, args)
488 .catchError((e) { 489 .catchError((e) {
489 _logEvent("Running $_binary --version failed with $e"); 490 _logEvent("Running $_binary --version failed with $e");
490 return false; 491 return false;
491 }); 492 });
492 }); 493 });
493 } 494 }
494 495
495 String toString() => "SafariMobileSimulator"; 496 String toString() => "SafariMobileSimulator";
496 } 497 }
497 498
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
549 .catchError((error) { 550 .catchError((error) {
550 _logEvent("Deleting recovery dir failed with $error"); 551 _logEvent("Deleting recovery dir failed with $error");
551 return false; 552 return false;
552 }); 553 });
553 } 554 }
554 555
555 Future<bool> start(String url) { 556 Future<bool> start(String url) {
556 _logEvent("Starting ie browser on: $url"); 557 _logEvent("Starting ie browser on: $url");
557 return clearCache().then((_) => getVersion()).then((version) { 558 return clearCache().then((_) => getVersion()).then((version) {
558 _logEvent("Got version: $version"); 559 _logEvent("Got version: $version");
559 return startBrowser(_binary, [url]); 560 return startBrowserProcess(_binary, [url]);
560 }); 561 });
561 } 562 }
562 String toString() => "IE"; 563 String toString() => "IE";
563 564
564 } 565 }
565 566
566 567
567 class AndroidBrowserConfig { 568 class AndroidBrowserConfig {
568 final String name; 569 final String name;
569 final String package; 570 final String package;
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after
639 640
640 641
641 class AndroidChrome extends Browser { 642 class AndroidChrome extends Browser {
642 static const String viewAction = 'android.intent.action.VIEW'; 643 static const String viewAction = 'android.intent.action.VIEW';
643 static const String mainAction = 'android.intent.action.MAIN'; 644 static const String mainAction = 'android.intent.action.MAIN';
644 static const String chromePackage = 'com.android.chrome'; 645 static const String chromePackage = 'com.android.chrome';
645 static const String browserPackage = 'com.android.browser'; 646 static const String browserPackage = 'com.android.browser';
646 static const String firefoxPackage = 'org.mozilla.firefox'; 647 static const String firefoxPackage = 'org.mozilla.firefox';
647 static const String turnScreenOnPackage = 'com.google.dart.turnscreenon'; 648 static const String turnScreenOnPackage = 'com.google.dart.turnscreenon';
648 649
649 AndroidEmulator _emulator;
650 AdbDevice _adbDevice; 650 AdbDevice _adbDevice;
651 651
652 AndroidChrome(this._adbDevice); 652 AndroidChrome(this._adbDevice);
653 653
654 Future<bool> start(String url) { 654 Future<bool> start(String url) {
655 var browserIntent = new Intent( 655 var browserIntent = new Intent(
656 viewAction, browserPackage, '.BrowserActivity', url); 656 viewAction, browserPackage, '.BrowserActivity', url);
657 var chromeIntent = new Intent(viewAction, chromePackage, '.Main', url); 657 var chromeIntent = new Intent(viewAction, chromePackage, '.Main', url);
658 var firefoxIntent = new Intent(viewAction, firefoxPackage, '.App', url); 658 var firefoxIntent = new Intent(viewAction, firefoxPackage, '.App', url);
659 var turnScreenOnIntent = 659 var turnScreenOnIntent =
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after
739 version = versionResult.stdout; 739 version = versionResult.stdout;
740 _logEvent("Got version: $version"); 740 _logEvent("Got version: $version");
741 741
742 return Directory.systemTemp.createTemp().then((userDir) { 742 return Directory.systemTemp.createTemp().then((userDir) {
743 _createPreferenceFile(userDir.path); 743 _createPreferenceFile(userDir.path);
744 _cleanup = () { userDir.deleteSync(recursive: true); }; 744 _cleanup = () { userDir.deleteSync(recursive: true); };
745 var args = ["-profile", "${userDir.path}", 745 var args = ["-profile", "${userDir.path}",
746 "-no-remote", "-new-instance", url]; 746 "-no-remote", "-new-instance", url];
747 var environment = new Map<String,String>.from(Platform.environment); 747 var environment = new Map<String,String>.from(Platform.environment);
748 environment["MOZ_CRASHREPORTER_DISABLE"] = "1"; 748 environment["MOZ_CRASHREPORTER_DISABLE"] = "1";
749 return startBrowser(_binary, args, environment: environment); 749 return startBrowserProcess(_binary, args, environment: environment);
750 750
751 }); 751 });
752 }).catchError((e) { 752 }).catchError((e) {
753 _logEvent("Running $_binary --version failed with $e"); 753 _logEvent("Running $_binary --version failed with $e");
754 return false; 754 return false;
755 }); 755 });
756 } 756 }
757 757
758 String toString() => "Firefox"; 758 String toString() => "Firefox";
759 } 759 }
760 760
761 761
762 /** 762 /**
763 * Describes the current state of a browser used for testing. 763 * Describes the current state of a browser used for testing.
764 */ 764 */
765 class BrowserTestingStatus { 765 class BrowserStatus {
766 Browser browser; 766 Browser browser;
767 BrowserTest currentTest; 767 BrowserTest currentTest;
768 768
769 // This is currently not used for anything except for error reporting. 769 // This is currently not used for anything except for error reporting.
770 // Given the usefulness of this in debugging issues this should not be 770 // Given the usefulness of this in debugging issues this should not be
771 // removed even when we have a really stable system. 771 // removed even when we have a really stable system.
772 BrowserTest lastTest; 772 BrowserTest lastTest;
773 bool timeout = false; 773 bool timeout = false;
774 Timer nextTestTimeout; 774 Timer nextTestTimeout;
775 Stopwatch timeSinceRestart = new Stopwatch(); 775 Stopwatch timeSinceRestart = new Stopwatch()..start();
776 776
777 BrowserTestingStatus(Browser this.browser); 777 BrowserStatus(Browser this.browser);
778 } 778 }
779 779
780 780
781 /** 781 /**
782 * Describes a single test to be run in the browser. 782 * Describes a single test to be run in the browser.
783 */ 783 */
784 class BrowserTest { 784 class BrowserTest {
785 // TODO(ricow): Add timeout callback instead of the string passing hack. 785 // TODO(ricow): Add timeout callback instead of the string passing hack.
786 Function doneCallback; 786 Function doneCallback;
787 String url; 787 String url;
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
837 final BrowserOutput browserOutput; 837 final BrowserOutput browserOutput;
838 final bool didTimeout; 838 final bool didTimeout;
839 839
840 BrowserTestOutput( 840 BrowserTestOutput(
841 this.delayUntilTestStarted, this.duration, this.lastKnownMessage, 841 this.delayUntilTestStarted, this.duration, this.lastKnownMessage,
842 this.browserOutput, {this.didTimeout: false}); 842 this.browserOutput, {this.didTimeout: false});
843 } 843 }
844 844
845 /** 845 /**
846 * Encapsulates all the functionality for running tests in browsers. 846 * Encapsulates all the functionality for running tests in browsers.
847 * The interface is rather simple. After starting, the runner tests 847 * The interface is rather simple. After starting, the runner tests
ahe 2016/04/05 16:14:08 Remove "The interface is rather simple."
Bill Hesse 2016/04/07 15:24:10 Done. Added info about starting browsers and star
848 * are simply added to the queue and a the supplied callbacks are called 848 * are simply added to the queue and a the supplied callbacks are called
849 * whenever a test completes. 849 * whenever a test completes.
850 */ 850 */
851 class BrowserTestRunner { 851 class BrowserTestRunner {
852 static const int MAX_NEXT_TEST_TIMEOUTS = 10; 852 static const int MAX_NEXT_TEST_TIMEOUTS = 10;
853 static const Duration NEXT_TEST_TIMEOUT = const Duration(seconds: 60); 853 static const Duration NEXT_TEST_TIMEOUT = const Duration(seconds: 60);
854 static const Duration RESTART_BROWSER_INTERVAL = const Duration(seconds: 60); 854 static const Duration RESTART_BROWSER_INTERVAL = const Duration(seconds: 60);
855 855
856 /// If the queue was recently empty, don't start another browser.
857 static const Duration MIN_NONEMPTY_QUEUE_TIME = const Duration(seconds: 1);
858
856 final Map configuration; 859 final Map configuration;
860 BrowserTestingServer testingServer;
ahe 2016/04/05 16:14:07 What is a testingServer?
Bill Hesse 2016/04/07 15:24:10 Added to class documentation.
857 861
858 final String localIp; 862 final String localIp;
859 String browserName; 863 String browserName;
860 final int maxNumBrowsers; 864 int maxNumBrowsers;
861 bool checkedMode; 865 bool checkedMode;
866 int numBrowsers = 0;
862 // Used to send back logs from the browser (start, stop etc) 867 // Used to send back logs from the browser (start, stop etc)
863 Function logger; 868 Function logger;
864 int browserIdCount = 0;
865 869
870 int browserIdCounter = 1;
871
872 bool testServerStarted = false;
ahe 2016/04/05 16:14:08 What is a testServer? Is it the same as a testingS
Bill Hesse 2016/04/07 15:24:10 Changed to testingServerStarted.
866 bool underTermination = false; 873 bool underTermination = false;
867 int numBrowserGetTestTimeouts = 0; 874 int numBrowserGetTestTimeouts = 0;
868 875 DateTime lastEmptyTestQueueTime = new DateTime.now();
876 String _currentStartingBrowserId;
869 List<BrowserTest> testQueue = new List<BrowserTest>(); 877 List<BrowserTest> testQueue = new List<BrowserTest>();
870 Map<String, BrowserTestingStatus> browserStatus = 878 Map<String, BrowserStatus> browserStatus =
871 new Map<String, BrowserTestingStatus>(); 879 new Map<String, BrowserStatus>();
872 880
873 var adbDeviceMapping = new Map<String, AdbDevice>(); 881 var adbDeviceMapping = new Map<String, AdbDevice>();
882 List<AdbDevice> idleAdbDevices;
883
874 // This cache is used to guarantee that we never see double reporting. 884 // This cache is used to guarantee that we never see double reporting.
875 // If we do we need to provide developers with this information. 885 // If we do we need to provide developers with this information.
876 // We don't add urls to the cache until we have run it. 886 // We don't add urls to the cache until we have run it.
877 Map<int, String> testCache = new Map<int, String>(); 887 Map<int, String> testCache = new Map<int, String>();
888
878 Map<int, String> doubleReportingOutputs = new Map<int, String>(); 889 Map<int, String> doubleReportingOutputs = new Map<int, String>();
890 List<String> timedOut = [];
879 891
880 BrowserTestingServer testingServer; 892 // We will start a new browser when the test queue hasn't been empty
893 // recently, we have fewer than maxNumBrowsers browsers, and there is
894 // no other browser instance currently starting up.
ahe 2016/04/05 16:14:08 Let's say that the queue hasn't been empty recentl
Bill Hesse 2016/04/07 15:24:10 No, except that a new browser shouldn't start unti
895 bool get queueWasEmptyRecently {
896 return testQueue.isEmpty ||
897 new DateTime.now().difference(lastEmptyTestQueueTime) <
898 MIN_NONEMPTY_QUEUE_TIME;
899 }
900
901 // While a browser is starting, but has not requested its first test, its
902 // browserId is stored in _currentStartingBrowserId.
903 // When no browser is currently starting, _currentStartingBrowserId is null.
904 bool get aBrowserIsCurrentlyStarting => _currentStartingBrowserId != null;
905 void markCurrentlyStarting(String id) {
906 _currentStartingBrowserId = id;
907 }
908 void markNotCurrentlyStarting(String id) {
909 if (_currentStartingBrowserId == id) _currentStartingBrowserId = null;
910 }
911
912 // If [browserName] doesn't support opening new windows, we use new iframes
913 // instead.
914 bool get useIframe =>
915 !Browser.BROWSERS_WITH_WINDOW_SUPPORT.contains(browserName);
881 916
882 /** 917 /**
883 * The TestRunner takes the testingServer in as a constructor parameter in 918 * The TestRunner takes the testingServer in as a constructor parameter in
884 * case we wish to have a testing server with different behavior (such as the 919 * case we wish to have a testing server with different behavior (such as the
885 * case for performance testing. 920 * case for performance testing. Note that the testingServer handlers are
921 * overwritten with handlers for this specific configuration, in that case.
ahe 2016/04/05 16:14:07 This comment is confusing and not balanced (missin
Bill Hesse 2016/04/07 15:24:10 Rewritten
886 */ 922 */
887 BrowserTestRunner(this.configuration, 923 BrowserTestRunner(this.configuration,
888 this.localIp, 924 this.localIp,
889 this.browserName, 925 this.browserName,
890 this.maxNumBrowsers, 926 this.maxNumBrowsers,
891 {BrowserTestingServer this.testingServer}) { 927 {BrowserTestingServer this.testingServer}) {
892 checkedMode = configuration['checked']; 928 checkedMode = configuration['checked'];
929 if (browserName == 'ff') browserName = 'firefox';
ahe 2016/04/05 16:14:07 This looks like a hack.
Bill Hesse 2016/04/07 15:24:10 It is. I recall that there was a failure if we nor
893 } 930 }
894 931
895 Future<bool> start() { 932 Future start() async {
896 // If [browserName] doesn't support opening new windows, we use new iframes
897 // instead.
898 bool useIframe =
899 !Browser.BROWSERS_WITH_WINDOW_SUPPORT.contains(browserName);
900 if (testingServer == null) { 933 if (testingServer == null) {
901 testingServer = new BrowserTestingServer( 934 testingServer = new BrowserTestingServer(
902 configuration, localIp, useIframe); 935 configuration, localIp, useIframe);
903 } 936 }
904 return testingServer.start().then((_) { 937 await testingServer.start();
905 testingServer.testDoneCallBack = handleResults; 938 testingServer
906 testingServer.testStatusUpdateCallBack = handleStatusUpdate; 939 ..testDoneCallBack = handleResults
907 testingServer.testStartedCallBack = handleStarted; 940 ..testStatusUpdateCallBack = handleStatusUpdate
908 testingServer.nextTestCallBack = getNextTest; 941 ..testStartedCallBack = handleStarted
909 return getBrowsers().then((browsers) { 942 ..nextTestCallBack = getNextTest;
ahe 2016/04/05 16:14:07 Indent all .. lines by four.
Bill Hesse 2016/04/07 15:24:10 Done. Will be reformatted by the follow-up autofo
910 var futures = []; 943 if (browserName == 'chromeOnAndroid') {
911 for (var browser in browsers) { 944 var idbNames = await AdbHelper.listDevices();
912 var url = testingServer.getDriverUrl(browser.id); 945 idleAdbDevices = new List.from(idbNames.map((id) => new AdbDevice(id)));
913 var future = browser.start(url).then((success) { 946 maxNumBrowsers = min(maxNumBrowsers, idleAdbDevices.length);
914 if (success) { 947 }
915 var status = new BrowserTestingStatus(browser); 948 testServerStarted = true;
916 browserStatus[browser.id] = status; 949 requestBrowser();
917 status.nextTestTimeout = createNextTestTimer(status);
918 status.timeSinceRestart.start();
919 }
920 return success;
921 });
922 futures.add(future);
923 }
924 return Future.wait(futures).then((values) {
925 return !values.contains(false);
926 });
927 });
928 });
929 } 950 }
930 951
931 Future<List<Browser>> getBrowsers() { 952 /// requestBrowser() is called whenever we might want to start an additional
932 // TODO(kustermann): This is a hackisch way to accomplish it and should 953 /// browser instance.
933 // be encapsulated 954 /// It is called when starting the BrowserTestRunner, and whenever a browser
934 var browsersCompleter = new Completer(); 955 /// is killed, whenever a new test is enqueued, or whenever a browser
935 var androidBrowserCreationMapping = { 956 /// finishes a test.
936 'chromeOnAndroid' : (AdbDevice device) => new AndroidChrome(device), 957 /// So we are guaranteed that this will always eventually be called, as long
937 'ContentShellOnAndroid' : (AdbDevice device) => new AndroidBrowser( 958 /// as the test queue isn't empty.
938 device, 959 void requestBrowser() {
939 contentShellOnAndroidConfig, 960 if (!testServerStarted) return;
940 checkedMode, 961 if (underTermination) return;
941 configuration['drt']), 962 if (numBrowsers == maxNumBrowsers) return;
942 'DartiumOnAndroid' : (AdbDevice device) => new AndroidBrowser( 963 if (aBrowserIsCurrentlyStarting) return;
943 device, 964 if (numBrowsers > 0 && queueWasEmptyRecently) return;
944 dartiumOnAndroidConfig, 965 createBrowser();
945 checkedMode,
946 configuration['dartium']),
947 };
948 if (androidBrowserCreationMapping.containsKey(browserName)) {
949 AdbHelper.listDevices().then((deviceIds) {
950 if (deviceIds.length > 0) {
951 var browsers = [];
952 for (int i = 0; i < deviceIds.length; i++) {
953 var id = "BROWSER$i";
954 var device = new AdbDevice(deviceIds[i]);
955 adbDeviceMapping[id] = device;
956 var browser = androidBrowserCreationMapping[browserName](device);
957 browsers.add(browser);
958 // We store this in case we need to kill the browser.
959 browser.id = id;
960 }
961 browsersCompleter.complete(browsers);
962 } else {
963 throw new StateError("No android devices found.");
964 }
965 });
966 } else {
967 var browsers = [];
968 for (int i = 0; i < maxNumBrowsers; i++) {
969 var id = "BROWSER$browserIdCount";
970 browserIdCount++;
971 var browser = getInstance();
972 browsers.add(browser);
973 // We store this in case we need to kill the browser.
974 browser.id = id;
975 }
976 browsersCompleter.complete(browsers);
977 }
978 return browsersCompleter.future;
979 } 966 }
980 967
981 var timedOut = []; 968 void createBrowser() {
969 final id = "BROWSER$browserIdCounter";
970 ++browserIdCounter;
ahe 2016/04/05 16:14:08 Convert to method: String getNextBrowserId() => "
971 final url = testingServer.getDriverUrl(id);
ahe 2016/04/05 16:14:08 Is this a String or a Uri? Add type to make it cle
Bill Hesse 2016/04/07 15:24:10 Done.
972 Browser browser;
973 if (browserName == 'chromeOnAndroid') {
974 AdbDevice device = idleAdbDevices.removeLast();
975 adbDeviceMapping[id] = device;
976 browser = new AndroidChrome(device);
977 } else {
978 var path = Locations.getBrowserLocation(browserName, configuration);
979 browser = new Browser.byName(browserName, path, checkedMode);
980 browser.logger = logger;
981 }
982 browser.id = id;
983 markCurrentlyStarting(id);
984 final status = new BrowserStatus(browser);
985 browserStatus[id] = status;
986 numBrowsers++;
987 status.nextTestTimeout = createNextTestTimer(status);
988 browser.start(url);
989 }
982 990
983 void handleResults(String browserId, String output, int testId) { 991 void handleResults(String browserId, String output, int testId) {
984 var status = browserStatus[browserId]; 992 var status = browserStatus[browserId];
985 if (testCache.containsKey(testId)) { 993 if (testCache.containsKey(testId)) {
986 doubleReportingOutputs[testId] = output; 994 doubleReportingOutputs[testId] = output;
987 return; 995 return;
988 } 996 }
989 997
990 if (status == null || status.timeout) { 998 if (status == null || status.timeout) {
991 // We don't do anything, this browser is currently being killed and 999 // We don't do anything, this browser is currently being killed and
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
1043 1051
1044 if (status != null && !status.timeout && status.currentTest != null) { 1052 if (status != null && !status.timeout && status.currentTest != null) {
1045 status.currentTest.timeoutTimer.cancel(); 1053 status.currentTest.timeoutTimer.cancel();
1046 status.currentTest.timeoutTimer = 1054 status.currentTest.timeoutTimer =
1047 createTimeoutTimer(status.currentTest, status); 1055 createTimeoutTimer(status.currentTest, status);
1048 status.currentTest.delayUntilTestStarted = 1056 status.currentTest.delayUntilTestStarted =
1049 status.currentTest.stopwatch.elapsed; 1057 status.currentTest.stopwatch.elapsed;
1050 } 1058 }
1051 } 1059 }
1052 1060
1053 void handleTimeout(BrowserTestingStatus status) { 1061 void handleTimeout(BrowserStatus status) {
1054 // We simply kill the browser and starts up a new one! 1062 // We simply kill the browser and starts up a new one!
1055 // We could be smarter here, but it does not seems like it is worth it. 1063 // We could be smarter here, but it does not seems like it is worth it.
1056 if (status.timeout) { 1064 if (status.timeout) {
1057 DebugLogger.error( 1065 DebugLogger.error(
1058 "Got test timeout for an already restarting browser"); 1066 "Got test timeout for an already restarting browser");
1059 return; 1067 return;
1060 } 1068 }
1061 status.timeout = true; 1069 status.timeout = true;
1062 timedOut.add(status.currentTest.url); 1070 timedOut.add(status.currentTest.url);
1063 var id = status.browser.id; 1071 var id = status.browser.id;
(...skipping 13 matching lines...) Expand all
1077 status.currentTest.stopwatch.elapsed, 1085 status.currentTest.stopwatch.elapsed,
1078 lastKnownMessage, 1086 lastKnownMessage,
1079 status.browser.testBrowserOutput, 1087 status.browser.testBrowserOutput,
1080 didTimeout: true); 1088 didTimeout: true);
1081 status.currentTest.doneCallback(browserTestOutput); 1089 status.currentTest.doneCallback(browserTestOutput);
1082 status.lastTest = status.currentTest; 1090 status.lastTest = status.currentTest;
1083 status.currentTest = null; 1091 status.currentTest = null;
1084 1092
1085 // We don't want to start a new browser if we are terminating. 1093 // We don't want to start a new browser if we are terminating.
1086 if (underTermination) return; 1094 if (underTermination) return;
1087 restartBrowser(id); 1095 removeBrowser(id);
1096 requestBrowser();
1088 }); 1097 });
1089 } 1098 }
1090 1099
1091 void restartBrowser(String id) { 1100 /// Remove a browser that has closed from our data structures that track
1092 if (browserName.contains('OnAndroid')) { 1101 /// open browsers. Check if we want to replace it with a new browser.
1093 DebugLogger.info("Restarting browser $id"); 1102 void removeBrowser(String id) {
1103 if (browserName == 'chromeOnAndroid') {
1104 idleAdbDevices.add(adbDeviceMapping.remove(id));
1094 } 1105 }
1095 var browser; 1106 markNotCurrentlyStarting(id);
1096 var new_id = id; 1107 browserStatus.remove(id);
1097 if (browserName == 'chromeOnAndroid') { 1108 --numBrowsers;
1098 browser = new AndroidChrome(adbDeviceMapping[id]);
1099 } else if (browserName == 'ContentShellOnAndroid') {
1100 browser = new AndroidBrowser(adbDeviceMapping[id],
1101 contentShellOnAndroidConfig,
1102 checkedMode,
1103 configuration['drt']);
1104 } else if (browserName == 'DartiumOnAndroid') {
1105 browser = new AndroidBrowser(adbDeviceMapping[id],
1106 dartiumOnAndroidConfig,
1107 checkedMode,
1108 configuration['dartium']);
1109 } else {
1110 browserStatus.remove(id);
1111 browser = getInstance();
1112 new_id = "BROWSER$browserIdCount";
1113 browserIdCount++;
1114 }
1115 browser.id = new_id;
1116 var status = new BrowserTestingStatus(browser);
1117 browserStatus[new_id] = status;
1118 status.nextTestTimeout = createNextTestTimer(status);
1119 status.timeSinceRestart.start();
1120 browser.start(testingServer.getDriverUrl(new_id)).then((success) {
1121 // We may have started terminating in the mean time.
1122 if (underTermination) {
1123 if (status.nextTestTimeout != null) {
1124 status.nextTestTimeout.cancel();
1125 status.nextTestTimeout = null;
1126 }
1127 browser.close().then((success) {
1128 // We should never hit this, print it out.
1129 if (!success) {
1130 print("Could not kill browser ($id) started due to timeout");
1131 }
1132 });
1133 return;
1134 }
1135 if (!success) {
1136 // TODO(ricow): Handle this better.
1137 print("This is bad, should never happen, could not start browser");
1138 exit(1);
1139 }
1140 });
1141 } 1109 }
1142 1110
1143 BrowserTest getNextTest(String browserId) { 1111 BrowserTest getNextTest(String browserId) {
1112 markNotCurrentlyStarting(browserId);
1144 var status = browserStatus[browserId]; 1113 var status = browserStatus[browserId];
1145 if (status == null) return null; 1114 if (status == null) return null;
1146 if (status.nextTestTimeout != null) { 1115 if (status.nextTestTimeout != null) {
1147 status.nextTestTimeout.cancel(); 1116 status.nextTestTimeout.cancel();
1148 status.nextTestTimeout = null; 1117 status.nextTestTimeout = null;
1149 } 1118 }
1150 if (testQueue.isEmpty) return null; 1119 if (testQueue.isEmpty) return null;
1151 1120
1152 // We are currently terminating this browser, don't start a new test. 1121 // We are currently terminating this browser, don't start a new test.
1153 if (status.timeout) return null; 1122 if (status.timeout) return null;
1154 1123
1155 // Restart content_shell and dartium on Android if they have been 1124 // Restart Internet Explorer if it has been
1156 // running for longer than RESTART_BROWSER_INTERVAL. The tests have 1125 // running for longer than RESTART_BROWSER_INTERVAL. The tests have
1157 // had flaky timeouts, and this may help. 1126 // had flaky timeouts, and this may help.
1158 if ((browserName == 'ContentShellOnAndroid' || 1127 if ((browserName == 'ie10' ||
1159 browserName == 'DartiumOnAndroid' ||
1160 browserName == 'ie10' ||
1161 browserName == 'ie11') && 1128 browserName == 'ie11') &&
1162 status.timeSinceRestart.elapsed > RESTART_BROWSER_INTERVAL) { 1129 status.timeSinceRestart.elapsed > RESTART_BROWSER_INTERVAL) {
1163 var id = status.browser.id; 1130 var id = status.browser.id;
1164 // Reset stopwatch so we don't trigger again before restarting. 1131 // Reset stopwatch so we don't trigger again before restarting.
1165 status.timeout = true; 1132 status.timeout = true;
1166 status.browser.close().then((_) { 1133 status.browser.close().then((_) {
1167 // We don't want to start a new browser if we are terminating. 1134 // We don't want to start a new browser if we are terminating.
1168 if (underTermination) return; 1135 if (underTermination) return;
1169 restartBrowser(id); 1136 removeBrowser(id);
1137 requestBrowser();
1170 }); 1138 });
1171 // Don't send a test to the browser we are restarting. 1139 // Don't send a test to the browser we are restarting.
1172 return null; 1140 return null;
1173 } 1141 }
1174 1142
1175 BrowserTest test = testQueue.removeLast(); 1143 BrowserTest test = testQueue.removeLast();
ahe 2016/04/05 16:14:08 There's an opportunity to create a class that repr
1144 // If our queue isn't empty, try starting more browsers
1145 if (testQueue.isEmpty) {
1146 lastEmptyTestQueueTime = new DateTime.now();
1147 } else {
1148 requestBrowser();
1149 }
1176 if (status.currentTest == null) { 1150 if (status.currentTest == null) {
1177 status.currentTest = test; 1151 status.currentTest = test;
1178 status.currentTest.lastKnownMessage = ''; 1152 status.currentTest.lastKnownMessage = '';
1179 } else { 1153 } else {
1180 // TODO(ricow): Handle this better. 1154 // TODO(ricow): Handle this better.
1181 print("Browser requested next test before reporting previous result"); 1155 print("Browser requested next test before reporting previous result");
1182 print("This happened for browser $browserId"); 1156 print("This happened for browser $browserId");
1183 print("Old test was: ${status.currentTest.url}"); 1157 print("Old test was: ${status.currentTest.url}");
1184 print("The test before that was: ${status.lastTest.url}"); 1158 print("The test before that was: ${status.lastTest.url}");
1185 print("Timed out tests:"); 1159 print("Timed out tests:");
1186 for (var v in timedOut) { 1160 for (var v in timedOut) {
1187 print(" $v"); 1161 print(" $v");
1188 } 1162 }
1189 exit(1); 1163 exit(1);
1190 } 1164 }
1191 1165
1192 status.currentTest.timeoutTimer = createTimeoutTimer(test, status); 1166 status.currentTest.timeoutTimer = createTimeoutTimer(test, status);
1193 status.currentTest.stopwatch = new Stopwatch()..start(); 1167 status.currentTest.stopwatch = new Stopwatch()..start();
1194 1168
1195 // Reset the test specific output information (stdout, stderr) on the 1169 // Reset the test specific output information (stdout, stderr) on the
1196 // browser, since a new test is being started. 1170 // browser, since a new test is being started.
1197 status.browser.resetTestBrowserOutput(); 1171 status.browser.resetTestBrowserOutput();
1198 status.browser.logBrowserInfoToTestBrowserOutput(); 1172 status.browser.logBrowserInfoToTestBrowserOutput();
1199 if (browserName.contains('OnAndroid')) {
1200 DebugLogger.info("Browser $browserId getting test ${test.url}");
1201 }
1202
1203 return test; 1173 return test;
1204 } 1174 }
1205 1175
1206 Timer createTimeoutTimer(BrowserTest test, BrowserTestingStatus status) { 1176 /// This creates a timer that is active while a test is running on a browser.
ahe 2016/04/05 16:14:07 Remove "this" which is implied.
Bill Hesse 2016/04/07 15:24:10 Done.
1177 Timer createTimeoutTimer(BrowserTest test, BrowserStatus status) {
1207 return new Timer(new Duration(seconds: test.timeout), 1178 return new Timer(new Duration(seconds: test.timeout),
1208 () { handleTimeout(status); }); 1179 () { handleTimeout(status); });
1209 } 1180 }
1210 1181
1211 Timer createNextTestTimer(BrowserTestingStatus status) { 1182 /// This creates a timer that is active while no test is running on the
ahe 2016/04/05 16:14:08 Ditto.
Bill Hesse 2016/04/07 15:24:10 Done.
1183 /// browser. It has finished one test, and it has not requested a new test.
1184 Timer createNextTestTimer(BrowserStatus status) {
1212 return new Timer(BrowserTestRunner.NEXT_TEST_TIMEOUT, 1185 return new Timer(BrowserTestRunner.NEXT_TEST_TIMEOUT,
1213 () { handleNextTestTimeout(status); }); 1186 () { handleNextTestTimeout(status); });
1214 } 1187 }
1215 1188
1216 void handleNextTestTimeout(status) { 1189 void handleNextTestTimeout(status) {
1217 DebugLogger.warning( 1190 DebugLogger.warning(
1218 "Browser timed out before getting next test. Restarting"); 1191 "Browser timed out before getting next test. Restarting");
1219 if (status.timeout) return; 1192 if (status.timeout) return;
1220 numBrowserGetTestTimeouts++; 1193 numBrowserGetTestTimeouts++;
1221 if (numBrowserGetTestTimeouts >= MAX_NEXT_TEST_TIMEOUTS) { 1194 if (numBrowserGetTestTimeouts >= MAX_NEXT_TEST_TIMEOUTS) {
1222 DebugLogger.error( 1195 DebugLogger.error(
1223 "Too many browser timeouts before getting next test. Terminating"); 1196 "Too many browser timeouts before getting next test. Terminating");
1224 terminate().then((_) => exit(1)); 1197 terminate().then((_) => exit(1));
1225 } else { 1198 } else {
1226 status.timeout = true; 1199 status.timeout = true;
1227 status.browser.close().then((_) => restartBrowser(status.browser.id)); 1200 status.browser.close().then((_) {
1201 removeBrowser(status.browser.id);
1202 requestBrowser();
1203 });
1228 } 1204 }
1229 } 1205 }
1230 1206
1231 void queueTest(BrowserTest test) { 1207 void queueTest(BrowserTest test) {
ahe 2016/04/05 16:14:08 enqueueTest?
Bill Hesse 2016/04/07 15:24:10 Done.
1232 testQueue.add(test); 1208 testQueue.add(test);
1209 requestBrowser();
1233 } 1210 }
1234 1211
1235 void printDoubleReportingTests() { 1212 void printDoubleReportingTests() {
1236 if (doubleReportingOutputs.length == 0) return; 1213 if (doubleReportingOutputs.length == 0) return;
1237 // TODO(ricow): die on double reporting. 1214 // TODO(ricow): die on double reporting.
1238 // Currently we just report this here, we could have a callback to the 1215 // Currently we just report this here, we could have a callback to the
1239 // encapsulating environment. 1216 // encapsulating environment.
1240 print(""); 1217 print("");
1241 print("Double reporting tests"); 1218 print("Double reporting tests");
1242 for (var id in doubleReportingOutputs.keys) { 1219 for (var id in doubleReportingOutputs.keys) {
1243 print(" ${testCache[id]}"); 1220 print(" ${testCache[id]}");
1244 } 1221 }
1245 1222
1246 DebugLogger.warning("Double reporting tests:"); 1223 DebugLogger.warning("Double reporting tests:");
1247 for (var id in doubleReportingOutputs.keys) { 1224 for (var id in doubleReportingOutputs.keys) {
1248 DebugLogger.warning("${testCache[id]}, output: "); 1225 DebugLogger.warning("${testCache[id]}, output: ");
1249 DebugLogger.warning("${doubleReportingOutputs[id]}"); 1226 DebugLogger.warning("${doubleReportingOutputs[id]}");
1250 DebugLogger.warning(""); 1227 DebugLogger.warning("");
1251 DebugLogger.warning(""); 1228 DebugLogger.warning("");
1252 } 1229 }
1253 } 1230 }
1254 1231
1255 Future<bool> terminate() { 1232 // TODO(26191): Call a unified fatalError(), that shuts down all subprocesses.
1233 // This just kills the browsers in this BrowserTestRunner instance.
1234 Future terminate() async {
1256 var browsers = []; 1235 var browsers = [];
1257 underTermination = true; 1236 underTermination = true;
1258 testingServer.underTermination = true; 1237 testingServer.underTermination = true;
1259 for (BrowserTestingStatus status in browserStatus.values) { 1238 for (BrowserStatus status in browserStatus.values) {
1260 browsers.add(status.browser); 1239 browsers.add(status.browser);
1261 if (status.nextTestTimeout != null) { 1240 if (status.nextTestTimeout != null) {
1262 status.nextTestTimeout.cancel(); 1241 status.nextTestTimeout.cancel();
1263 status.nextTestTimeout = null; 1242 status.nextTestTimeout = null;
1264 } 1243 }
1265 } 1244 }
1266 // Success if all the browsers closed successfully. 1245 for (Browser b in browsers) {
1267 bool success = true; 1246 await b.close();
1268 Future closeBrowser(Browser b) {
1269 return b.close().then((bool closeSucceeded) {
1270 if (!closeSucceeded) {
1271 success = false;
1272 }
1273 });
1274 } 1247 }
1275 return Future.forEach(browsers, closeBrowser).then((_) { 1248 testingServer.errorReportingServer.close();
1276 testingServer.errorReportingServer.close(); 1249 printDoubleReportingTests();
1277 printDoubleReportingTests();
1278 return success;
1279 });
1280 }
1281
1282 Browser getInstance() {
1283 if (browserName == 'ff') browserName = 'firefox';
1284 var path = Locations.getBrowserLocation(browserName, configuration);
1285 var browser = new Browser.byName(browserName, path, checkedMode);
1286 browser.logger = logger;
1287 return browser;
1288 } 1250 }
1289 } 1251 }
1290 1252
1291 class BrowserTestingServer { 1253 class BrowserTestingServer {
1292 final Map configuration; 1254 final Map configuration;
1293 /// Interface of the testing server: 1255 /// Interface of the testing server:
1294 /// 1256 ///
1295 /// GET /driver/BROWSER_ID -- This will get the driver page to fetch 1257 /// GET /driver/BROWSER_ID -- This will get the driver page to fetch
1296 /// and run tests ... 1258 /// and run tests ...
1297 /// GET /next_test/BROWSER_ID -- returns "WAIT" "TERMINATE" or "url#id" 1259 /// GET /next_test/BROWSER_ID -- returns "WAIT" "TERMINATE" or "url#id"
(...skipping 498 matching lines...) Expand 10 before | Expand all | Expand 10 after
1796 </div> 1758 </div>
1797 <div id="embedded_iframe_div" class="test box"> 1759 <div id="embedded_iframe_div" class="test box">
1798 <iframe style="width:100%;height:100%;" id="embedded_iframe"></iframe> 1760 <iframe style="width:100%;height:100%;" id="embedded_iframe"></iframe>
1799 </div> 1761 </div>
1800 </body> 1762 </body>
1801 </html> 1763 </html>
1802 """; 1764 """;
1803 return driverContent; 1765 return driverContent;
1804 } 1766 }
1805 } 1767 }
OLDNEW
« no previous file with comments | « no previous file | tools/testing/dart/test_runner.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698