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

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: Address comments 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 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
835 final String lastKnownMessage; 835 final String lastKnownMessage;
836 836
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 /// Tests are added to the queue and the supplied callbacks are called
848 * are simply added to the queue and a the supplied callbacks are called 848 /// when a test completes.
849 * whenever a test completes. 849 /// BrowserTestRunner starts up to maxNumBrowser instances of the browser,
850 */ 850 /// to run the tests, starting them sequentially, as needed, so only
851 /// one is starting up at a time.
852 /// BrowserTestRunner starts a BrowserTestingServer, which serves a
853 /// driver page to the browsers, serves tests, and receives results and
854 /// requests back from the browsers.
851 class BrowserTestRunner { 855 class BrowserTestRunner {
852 static const int MAX_NEXT_TEST_TIMEOUTS = 10; 856 static const int MAX_NEXT_TEST_TIMEOUTS = 10;
853 static const Duration NEXT_TEST_TIMEOUT = const Duration(seconds: 60); 857 static const Duration NEXT_TEST_TIMEOUT = const Duration(seconds: 60);
854 static const Duration RESTART_BROWSER_INTERVAL = const Duration(seconds: 60); 858 static const Duration RESTART_BROWSER_INTERVAL = const Duration(seconds: 60);
855 859
860 /// If the queue was recently empty, don't start another browser.
861 static const Duration MIN_NONEMPTY_QUEUE_TIME = const Duration(seconds: 1);
862
856 final Map configuration; 863 final Map configuration;
864 BrowserTestingServer testingServer;
857 865
858 final String localIp; 866 final String localIp;
859 String browserName; 867 String browserName;
860 final int maxNumBrowsers; 868 int maxNumBrowsers;
861 bool checkedMode; 869 bool checkedMode;
870 int numBrowsers = 0;
862 // Used to send back logs from the browser (start, stop etc) 871 // Used to send back logs from the browser (start, stop etc)
863 Function logger; 872 Function logger;
864 int browserIdCount = 0;
865 873
874 int browserIdCounter = 1;
875
876 bool testingServerStarted = false;
866 bool underTermination = false; 877 bool underTermination = false;
867 int numBrowserGetTestTimeouts = 0; 878 int numBrowserGetTestTimeouts = 0;
868 879 DateTime lastEmptyTestQueueTime = new DateTime.now();
880 String _currentStartingBrowserId;
869 List<BrowserTest> testQueue = new List<BrowserTest>(); 881 List<BrowserTest> testQueue = new List<BrowserTest>();
870 Map<String, BrowserTestingStatus> browserStatus = 882 Map<String, BrowserStatus> browserStatus =
871 new Map<String, BrowserTestingStatus>(); 883 new Map<String, BrowserStatus>();
872 884
873 var adbDeviceMapping = new Map<String, AdbDevice>(); 885 var adbDeviceMapping = new Map<String, AdbDevice>();
886 List<AdbDevice> idleAdbDevices;
887
874 // This cache is used to guarantee that we never see double reporting. 888 // This cache is used to guarantee that we never see double reporting.
875 // If we do we need to provide developers with this information. 889 // 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. 890 // We don't add urls to the cache until we have run it.
877 Map<int, String> testCache = new Map<int, String>(); 891 Map<int, String> testCache = new Map<int, String>();
892
878 Map<int, String> doubleReportingOutputs = new Map<int, String>(); 893 Map<int, String> doubleReportingOutputs = new Map<int, String>();
894 List<String> timedOut = [];
879 895
880 BrowserTestingServer testingServer; 896 // We will start a new browser when the test queue hasn't been empty
897 // recently, we have fewer than maxNumBrowsers browsers, and there is
898 // no other browser instance currently starting up.
899 bool get queueWasEmptyRecently {
900 return testQueue.isEmpty ||
901 new DateTime.now().difference(lastEmptyTestQueueTime) <
902 MIN_NONEMPTY_QUEUE_TIME;
903 }
881 904
882 /** 905 // While a browser is starting, but has not requested its first test, its
883 * The TestRunner takes the testingServer in as a constructor parameter in 906 // browserId is stored in _currentStartingBrowserId.
884 * case we wish to have a testing server with different behavior (such as the 907 // When no browser is currently starting, _currentStartingBrowserId is null.
885 * case for performance testing. 908 bool get aBrowserIsCurrentlyStarting => _currentStartingBrowserId != null;
886 */ 909 void markCurrentlyStarting(String id) {
910 _currentStartingBrowserId = id;
911 }
912 void markNotCurrentlyStarting(String id) {
913 if (_currentStartingBrowserId == id) _currentStartingBrowserId = null;
914 }
915
916 // If [browserName] doesn't support opening new windows, we use new iframes
917 // instead.
918 bool get useIframe =>
919 !Browser.BROWSERS_WITH_WINDOW_SUPPORT.contains(browserName);
920
921 /// The optional testingServer parameter allows callers to pass in
922 /// a testing server with different behavior than the default
923 /// BrowserTestServer. The url handlers of the testingServer are
924 /// overwritten, so an existing handler can't be shared between instances.
887 BrowserTestRunner(this.configuration, 925 BrowserTestRunner(this.configuration,
888 this.localIp, 926 this.localIp,
889 this.browserName, 927 this.browserName,
890 this.maxNumBrowsers, 928 this.maxNumBrowsers,
891 {BrowserTestingServer this.testingServer}) { 929 {BrowserTestingServer this.testingServer}) {
892 checkedMode = configuration['checked']; 930 checkedMode = configuration['checked'];
931 if (browserName == 'ff') browserName = 'firefox';
893 } 932 }
894 933
895 Future<bool> start() { 934 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) { 935 if (testingServer == null) {
901 testingServer = new BrowserTestingServer( 936 testingServer = new BrowserTestingServer(
902 configuration, localIp, useIframe); 937 configuration, localIp, useIframe);
903 } 938 }
904 return testingServer.start().then((_) { 939 await testingServer.start();
905 testingServer.testDoneCallBack = handleResults; 940 testingServer
906 testingServer.testStatusUpdateCallBack = handleStatusUpdate; 941 ..testDoneCallBack = handleResults
907 testingServer.testStartedCallBack = handleStarted; 942 ..testStatusUpdateCallBack = handleStatusUpdate
908 testingServer.nextTestCallBack = getNextTest; 943 ..testStartedCallBack = handleStarted
909 return getBrowsers().then((browsers) { 944 ..nextTestCallBack = getNextTest;
910 var futures = []; 945 if (browserName == 'chromeOnAndroid') {
911 for (var browser in browsers) { 946 var idbNames = await AdbHelper.listDevices();
912 var url = testingServer.getDriverUrl(browser.id); 947 idleAdbDevices = new List.from(idbNames.map((id) => new AdbDevice(id)));
913 var future = browser.start(url).then((success) { 948 maxNumBrowsers = min(maxNumBrowsers, idleAdbDevices.length);
914 if (success) { 949 }
915 var status = new BrowserTestingStatus(browser); 950 testingServerStarted = true;
916 browserStatus[browser.id] = status; 951 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 } 952 }
930 953
931 Future<List<Browser>> getBrowsers() { 954 /// requestBrowser() is called whenever we might want to start an additional
932 // TODO(kustermann): This is a hackisch way to accomplish it and should 955 /// browser instance.
933 // be encapsulated 956 /// It is called when starting the BrowserTestRunner, and whenever a browser
934 var browsersCompleter = new Completer(); 957 /// is killed, whenever a new test is enqueued, or whenever a browser
935 var androidBrowserCreationMapping = { 958 /// finishes a test.
936 'chromeOnAndroid' : (AdbDevice device) => new AndroidChrome(device), 959 /// So we are guaranteed that this will always eventually be called, as long
937 'ContentShellOnAndroid' : (AdbDevice device) => new AndroidBrowser( 960 /// as the test queue isn't empty.
938 device, 961 void requestBrowser() {
939 contentShellOnAndroidConfig, 962 if (!testingServerStarted) return;
940 checkedMode, 963 if (underTermination) return;
941 configuration['drt']), 964 if (numBrowsers == maxNumBrowsers) return;
942 'DartiumOnAndroid' : (AdbDevice device) => new AndroidBrowser( 965 if (aBrowserIsCurrentlyStarting) return;
943 device, 966 if (numBrowsers > 0 && queueWasEmptyRecently) return;
944 dartiumOnAndroidConfig, 967 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 } 968 }
980 969
981 var timedOut = []; 970 String getNextBrowserId() => "BROWSER${browserIdCounter++}";
971
972 void createBrowser() {
973 final String id = getNextBrowserId();
974 final String url = testingServer.getDriverUrl(id);
975 Browser browser;
976 if (browserName == 'chromeOnAndroid') {
977 AdbDevice device = idleAdbDevices.removeLast();
978 adbDeviceMapping[id] = device;
979 browser = new AndroidChrome(device);
980 } else {
981 String path = Locations.getBrowserLocation(browserName, configuration);
982 browser = new Browser.byName(browserName, path, checkedMode);
983 browser.logger = logger;
984 }
985 browser.id = id;
986 markCurrentlyStarting(id);
987 final status = new BrowserStatus(browser);
988 browserStatus[id] = status;
989 numBrowsers++;
990 status.nextTestTimeout = createNextTestTimer(status);
991 browser.start(url);
992 }
982 993
983 void handleResults(String browserId, String output, int testId) { 994 void handleResults(String browserId, String output, int testId) {
984 var status = browserStatus[browserId]; 995 var status = browserStatus[browserId];
985 if (testCache.containsKey(testId)) { 996 if (testCache.containsKey(testId)) {
986 doubleReportingOutputs[testId] = output; 997 doubleReportingOutputs[testId] = output;
987 return; 998 return;
988 } 999 }
989 1000
990 if (status == null || status.timeout) { 1001 if (status == null || status.timeout) {
991 // We don't do anything, this browser is currently being killed and 1002 // 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 1054
1044 if (status != null && !status.timeout && status.currentTest != null) { 1055 if (status != null && !status.timeout && status.currentTest != null) {
1045 status.currentTest.timeoutTimer.cancel(); 1056 status.currentTest.timeoutTimer.cancel();
1046 status.currentTest.timeoutTimer = 1057 status.currentTest.timeoutTimer =
1047 createTimeoutTimer(status.currentTest, status); 1058 createTimeoutTimer(status.currentTest, status);
1048 status.currentTest.delayUntilTestStarted = 1059 status.currentTest.delayUntilTestStarted =
1049 status.currentTest.stopwatch.elapsed; 1060 status.currentTest.stopwatch.elapsed;
1050 } 1061 }
1051 } 1062 }
1052 1063
1053 void handleTimeout(BrowserTestingStatus status) { 1064 void handleTimeout(BrowserStatus status) {
1054 // We simply kill the browser and starts up a new one! 1065 // 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. 1066 // We could be smarter here, but it does not seems like it is worth it.
1056 if (status.timeout) { 1067 if (status.timeout) {
1057 DebugLogger.error( 1068 DebugLogger.error(
1058 "Got test timeout for an already restarting browser"); 1069 "Got test timeout for an already restarting browser");
1059 return; 1070 return;
1060 } 1071 }
1061 status.timeout = true; 1072 status.timeout = true;
1062 timedOut.add(status.currentTest.url); 1073 timedOut.add(status.currentTest.url);
1063 var id = status.browser.id; 1074 var id = status.browser.id;
(...skipping 13 matching lines...) Expand all
1077 status.currentTest.stopwatch.elapsed, 1088 status.currentTest.stopwatch.elapsed,
1078 lastKnownMessage, 1089 lastKnownMessage,
1079 status.browser.testBrowserOutput, 1090 status.browser.testBrowserOutput,
1080 didTimeout: true); 1091 didTimeout: true);
1081 status.currentTest.doneCallback(browserTestOutput); 1092 status.currentTest.doneCallback(browserTestOutput);
1082 status.lastTest = status.currentTest; 1093 status.lastTest = status.currentTest;
1083 status.currentTest = null; 1094 status.currentTest = null;
1084 1095
1085 // We don't want to start a new browser if we are terminating. 1096 // We don't want to start a new browser if we are terminating.
1086 if (underTermination) return; 1097 if (underTermination) return;
1087 restartBrowser(id); 1098 removeBrowser(id);
1099 requestBrowser();
1088 }); 1100 });
1089 } 1101 }
1090 1102
1091 void restartBrowser(String id) { 1103 /// Remove a browser that has closed from our data structures that track
1092 if (browserName.contains('OnAndroid')) { 1104 /// open browsers. Check if we want to replace it with a new browser.
1093 DebugLogger.info("Restarting browser $id"); 1105 void removeBrowser(String id) {
1106 if (browserName == 'chromeOnAndroid') {
1107 idleAdbDevices.add(adbDeviceMapping.remove(id));
1094 } 1108 }
1095 var browser; 1109 markNotCurrentlyStarting(id);
1096 var new_id = id; 1110 browserStatus.remove(id);
1097 if (browserName == 'chromeOnAndroid') { 1111 --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 } 1112 }
1142 1113
1143 BrowserTest getNextTest(String browserId) { 1114 BrowserTest getNextTest(String browserId) {
1115 markNotCurrentlyStarting(browserId);
1144 var status = browserStatus[browserId]; 1116 var status = browserStatus[browserId];
1145 if (status == null) return null; 1117 if (status == null) return null;
1146 if (status.nextTestTimeout != null) { 1118 if (status.nextTestTimeout != null) {
1147 status.nextTestTimeout.cancel(); 1119 status.nextTestTimeout.cancel();
1148 status.nextTestTimeout = null; 1120 status.nextTestTimeout = null;
1149 } 1121 }
1150 if (testQueue.isEmpty) return null; 1122 if (testQueue.isEmpty) return null;
1151 1123
1152 // We are currently terminating this browser, don't start a new test. 1124 // We are currently terminating this browser, don't start a new test.
1153 if (status.timeout) return null; 1125 if (status.timeout) return null;
1154 1126
1155 // Restart content_shell and dartium on Android if they have been 1127 // Restart Internet Explorer if it has been
1156 // running for longer than RESTART_BROWSER_INTERVAL. The tests have 1128 // running for longer than RESTART_BROWSER_INTERVAL. The tests have
1157 // had flaky timeouts, and this may help. 1129 // had flaky timeouts, and this may help.
1158 if ((browserName == 'ContentShellOnAndroid' || 1130 if ((browserName == 'ie10' ||
1159 browserName == 'DartiumOnAndroid' ||
1160 browserName == 'ie10' ||
1161 browserName == 'ie11') && 1131 browserName == 'ie11') &&
1162 status.timeSinceRestart.elapsed > RESTART_BROWSER_INTERVAL) { 1132 status.timeSinceRestart.elapsed > RESTART_BROWSER_INTERVAL) {
1163 var id = status.browser.id; 1133 var id = status.browser.id;
1164 // Reset stopwatch so we don't trigger again before restarting. 1134 // Reset stopwatch so we don't trigger again before restarting.
1165 status.timeout = true; 1135 status.timeout = true;
1166 status.browser.close().then((_) { 1136 status.browser.close().then((_) {
1167 // We don't want to start a new browser if we are terminating. 1137 // We don't want to start a new browser if we are terminating.
1168 if (underTermination) return; 1138 if (underTermination) return;
1169 restartBrowser(id); 1139 removeBrowser(id);
1140 requestBrowser();
1170 }); 1141 });
1171 // Don't send a test to the browser we are restarting. 1142 // Don't send a test to the browser we are restarting.
1172 return null; 1143 return null;
1173 } 1144 }
1174 1145
1175 BrowserTest test = testQueue.removeLast(); 1146 BrowserTest test = testQueue.removeLast();
1147 // If our queue isn't empty, try starting more browsers
1148 if (testQueue.isEmpty) {
1149 lastEmptyTestQueueTime = new DateTime.now();
1150 } else {
1151 requestBrowser();
1152 }
1176 if (status.currentTest == null) { 1153 if (status.currentTest == null) {
1177 status.currentTest = test; 1154 status.currentTest = test;
1178 status.currentTest.lastKnownMessage = ''; 1155 status.currentTest.lastKnownMessage = '';
1179 } else { 1156 } else {
1180 // TODO(ricow): Handle this better. 1157 // TODO(ricow): Handle this better.
1181 print("Browser requested next test before reporting previous result"); 1158 print("Browser requested next test before reporting previous result");
1182 print("This happened for browser $browserId"); 1159 print("This happened for browser $browserId");
1183 print("Old test was: ${status.currentTest.url}"); 1160 print("Old test was: ${status.currentTest.url}");
1184 print("The test before that was: ${status.lastTest.url}"); 1161 print("The test before that was: ${status.lastTest.url}");
1185 print("Timed out tests:"); 1162 print("Timed out tests:");
1186 for (var v in timedOut) { 1163 for (var v in timedOut) {
1187 print(" $v"); 1164 print(" $v");
1188 } 1165 }
1189 exit(1); 1166 exit(1);
1190 } 1167 }
1191 1168
1192 status.currentTest.timeoutTimer = createTimeoutTimer(test, status); 1169 status.currentTest.timeoutTimer = createTimeoutTimer(test, status);
1193 status.currentTest.stopwatch = new Stopwatch()..start(); 1170 status.currentTest.stopwatch = new Stopwatch()..start();
1194 1171
1195 // Reset the test specific output information (stdout, stderr) on the 1172 // Reset the test specific output information (stdout, stderr) on the
1196 // browser, since a new test is being started. 1173 // browser, since a new test is being started.
1197 status.browser.resetTestBrowserOutput(); 1174 status.browser.resetTestBrowserOutput();
1198 status.browser.logBrowserInfoToTestBrowserOutput(); 1175 status.browser.logBrowserInfoToTestBrowserOutput();
1199 if (browserName.contains('OnAndroid')) {
1200 DebugLogger.info("Browser $browserId getting test ${test.url}");
1201 }
1202
1203 return test; 1176 return test;
1204 } 1177 }
1205 1178
1206 Timer createTimeoutTimer(BrowserTest test, BrowserTestingStatus status) { 1179 /// Creates a timer that is active while a test is running on a browser.
1180 Timer createTimeoutTimer(BrowserTest test, BrowserStatus status) {
1207 return new Timer(new Duration(seconds: test.timeout), 1181 return new Timer(new Duration(seconds: test.timeout),
1208 () { handleTimeout(status); }); 1182 () { handleTimeout(status); });
1209 } 1183 }
1210 1184
1211 Timer createNextTestTimer(BrowserTestingStatus status) { 1185 /// Creates a timer that is active while no test is running on the
1186 /// browser. It has finished one test, and it has not requested a new test.
1187 Timer createNextTestTimer(BrowserStatus status) {
1212 return new Timer(BrowserTestRunner.NEXT_TEST_TIMEOUT, 1188 return new Timer(BrowserTestRunner.NEXT_TEST_TIMEOUT,
1213 () { handleNextTestTimeout(status); }); 1189 () { handleNextTestTimeout(status); });
1214 } 1190 }
1215 1191
1216 void handleNextTestTimeout(status) { 1192 void handleNextTestTimeout(status) {
1217 DebugLogger.warning( 1193 DebugLogger.warning(
1218 "Browser timed out before getting next test. Restarting"); 1194 "Browser timed out before getting next test. Restarting");
1219 if (status.timeout) return; 1195 if (status.timeout) return;
1220 numBrowserGetTestTimeouts++; 1196 numBrowserGetTestTimeouts++;
1221 if (numBrowserGetTestTimeouts >= MAX_NEXT_TEST_TIMEOUTS) { 1197 if (numBrowserGetTestTimeouts >= MAX_NEXT_TEST_TIMEOUTS) {
1222 DebugLogger.error( 1198 DebugLogger.error(
1223 "Too many browser timeouts before getting next test. Terminating"); 1199 "Too many browser timeouts before getting next test. Terminating");
1224 terminate().then((_) => exit(1)); 1200 terminate().then((_) => exit(1));
1225 } else { 1201 } else {
1226 status.timeout = true; 1202 status.timeout = true;
1227 status.browser.close().then((_) => restartBrowser(status.browser.id)); 1203 status.browser.close().then((_) {
1204 removeBrowser(status.browser.id);
1205 requestBrowser();
1206 });
1228 } 1207 }
1229 } 1208 }
1230 1209
1231 void queueTest(BrowserTest test) { 1210 void enqueueTest(BrowserTest test) {
1232 testQueue.add(test); 1211 testQueue.add(test);
1212 requestBrowser();
1233 } 1213 }
1234 1214
1235 void printDoubleReportingTests() { 1215 void printDoubleReportingTests() {
1236 if (doubleReportingOutputs.length == 0) return; 1216 if (doubleReportingOutputs.length == 0) return;
1237 // TODO(ricow): die on double reporting. 1217 // TODO(ricow): die on double reporting.
1238 // Currently we just report this here, we could have a callback to the 1218 // Currently we just report this here, we could have a callback to the
1239 // encapsulating environment. 1219 // encapsulating environment.
1240 print(""); 1220 print("");
1241 print("Double reporting tests"); 1221 print("Double reporting tests");
1242 for (var id in doubleReportingOutputs.keys) { 1222 for (var id in doubleReportingOutputs.keys) {
1243 print(" ${testCache[id]}"); 1223 print(" ${testCache[id]}");
1244 } 1224 }
1245 1225
1246 DebugLogger.warning("Double reporting tests:"); 1226 DebugLogger.warning("Double reporting tests:");
1247 for (var id in doubleReportingOutputs.keys) { 1227 for (var id in doubleReportingOutputs.keys) {
1248 DebugLogger.warning("${testCache[id]}, output: "); 1228 DebugLogger.warning("${testCache[id]}, output: ");
1249 DebugLogger.warning("${doubleReportingOutputs[id]}"); 1229 DebugLogger.warning("${doubleReportingOutputs[id]}");
1250 DebugLogger.warning(""); 1230 DebugLogger.warning("");
1251 DebugLogger.warning(""); 1231 DebugLogger.warning("");
1252 } 1232 }
1253 } 1233 }
1254 1234
1255 Future<bool> terminate() { 1235 // TODO(26191): Call a unified fatalError(), that shuts down all subprocesses.
1236 // This just kills the browsers in this BrowserTestRunner instance.
1237 Future terminate() async {
1256 var browsers = []; 1238 var browsers = [];
1257 underTermination = true; 1239 underTermination = true;
1258 testingServer.underTermination = true; 1240 testingServer.underTermination = true;
1259 for (BrowserTestingStatus status in browserStatus.values) { 1241 for (BrowserStatus status in browserStatus.values) {
1260 browsers.add(status.browser); 1242 browsers.add(status.browser);
1261 if (status.nextTestTimeout != null) { 1243 if (status.nextTestTimeout != null) {
1262 status.nextTestTimeout.cancel(); 1244 status.nextTestTimeout.cancel();
1263 status.nextTestTimeout = null; 1245 status.nextTestTimeout = null;
1264 } 1246 }
1265 } 1247 }
1266 // Success if all the browsers closed successfully. 1248 for (Browser b in browsers) {
1267 bool success = true; 1249 await b.close();
1268 Future closeBrowser(Browser b) {
1269 return b.close().then((bool closeSucceeded) {
1270 if (!closeSucceeded) {
1271 success = false;
1272 }
1273 });
1274 } 1250 }
1275 return Future.forEach(browsers, closeBrowser).then((_) { 1251 testingServer.errorReportingServer.close();
1276 testingServer.errorReportingServer.close(); 1252 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 } 1253 }
1289 } 1254 }
1290 1255
1291 class BrowserTestingServer { 1256 class BrowserTestingServer {
1292 final Map configuration; 1257 final Map configuration;
1293 /// Interface of the testing server: 1258 /// Interface of the testing server:
1294 /// 1259 ///
1295 /// GET /driver/BROWSER_ID -- This will get the driver page to fetch 1260 /// GET /driver/BROWSER_ID -- This will get the driver page to fetch
1296 /// and run tests ... 1261 /// and run tests ...
1297 /// GET /next_test/BROWSER_ID -- returns "WAIT" "TERMINATE" or "url#id" 1262 /// 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> 1761 </div>
1797 <div id="embedded_iframe_div" class="test box"> 1762 <div id="embedded_iframe_div" class="test box">
1798 <iframe style="width:100%;height:100%;" id="embedded_iframe"></iframe> 1763 <iframe style="width:100%;height:100%;" id="embedded_iframe"></iframe>
1799 </div> 1764 </div>
1800 </body> 1765 </body>
1801 </html> 1766 </html>
1802 """; 1767 """;
1803 return driverContent; 1768 return driverContent;
1804 } 1769 }
1805 } 1770 }
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