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

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

Issue 28533003: Capture and report stdout/stderr of the browser while running a test (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 2 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 | Annotate | Revision Log
« no previous file with comments | « no previous file | tools/testing/dart/test_runner.dart » ('j') | tools/testing/dart/test_runner.dart » ('J')
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; 7 import "dart:convert" show LineSplitter, UTF8;
8 import "dart:core"; 8 import "dart:core";
9 import "dart:io"; 9 import "dart:io";
10 10
11 import 'android.dart'; 11 import 'android.dart';
12 import 'utils.dart'; 12 import 'utils.dart';
13 13
14 class BrowserOutput {
15 final StringBuffer stdout = new StringBuffer();
16 final StringBuffer stderr = new StringBuffer();
17 final StringBuffer eventLog = new StringBuffer();
18 }
19
14 /** Class describing the interface for communicating with browsers. */ 20 /** Class describing the interface for communicating with browsers. */
15 abstract class Browser { 21 abstract class Browser {
16 StringBuffer _stdout = new StringBuffer(); 22 BrowserOutput _allBrowserOutput = new BrowserOutput();
17 StringBuffer _stderr = new StringBuffer(); 23 BrowserOutput _testBrowserOutput = new BrowserOutput();
18 StringBuffer _usageLog = new StringBuffer(); 24
19 // This is called after the process is closed, before the done future 25 // This is called after the process is closed, before the done future
20 // is completed. 26 // is completed.
21 // Subclasses can use this to cleanup any browser specific resources 27 // Subclasses can use this to cleanup any browser specific resources
22 // (temp directories, profiles, etc). The function is expected to do 28 // (temp directories, profiles, etc). The function is expected to do
23 // it's work synchronously. 29 // it's work synchronously.
24 Function _cleanup; 30 Function _cleanup;
25 31
26 /** The version of the browser - normally set when starting a browser */ 32 /** The version of the browser - normally set when starting a browser */
27 String version = ""; 33 String version = "";
28 /** 34 /**
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
72 78
73 // TODO(kustermann): add standard support for chrome on android 79 // TODO(kustermann): add standard support for chrome on android
74 static bool supportedBrowser(String name) { 80 static bool supportedBrowser(String name) {
75 return SUPPORTED_BROWSERS.contains(name); 81 return SUPPORTED_BROWSERS.contains(name);
76 } 82 }
77 83
78 void _logEvent(String event) { 84 void _logEvent(String event) {
79 String toLog = "$this ($id) - $event \n"; 85 String toLog = "$this ($id) - $event \n";
80 if (debugPrint) print("usageLog: $toLog"); 86 if (debugPrint) print("usageLog: $toLog");
81 if (logger != null) logger(toLog); 87 if (logger != null) logger(toLog);
82 _usageLog.write(toLog); 88
89 _allBrowserOutput.eventLog.write(toLog);
90 _testBrowserOutput.eventLog.write(toLog);
83 } 91 }
84 92
85 void _addStdout(String output) { 93 void _addStdout(String output) {
86 if (debugPrint) print("stdout: $output"); 94 if (debugPrint) print("stdout: $output");
87 _stdout.write(output); 95
96 _allBrowserOutput.stdout.write(output);
97 _testBrowserOutput.stdout.write(output);
88 } 98 }
89 99
90 void _addStderr(String output) { 100 void _addStderr(String output) {
91 if (debugPrint) print("stderr: $output"); 101 if (debugPrint) print("stderr: $output");
92 _stderr.write(output); 102
103 _allBrowserOutput.stderr.write(output);
104 _testBrowserOutput.stderr.write(output);
93 } 105 }
94 106
95 Future close() { 107 Future close() {
96 _logEvent("Close called on browser"); 108 _logEvent("Close called on browser");
97 if (process != null) { 109 if (process != null) {
98 if (process.kill(ProcessSignal.SIGKILL)) { 110 if (process.kill(ProcessSignal.SIGKILL)) {
99 _logEvent("Successfully sent kill signal to process."); 111 _logEvent("Successfully sent kill signal to process.");
100 } else { 112 } else {
101 _logEvent("Sending kill signal failed."); 113 _logEvent("Sending kill signal failed.");
102 } 114 }
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
157 }).whenComplete(() => doneCompleter.complete(true)); 169 }).whenComplete(() => doneCompleter.complete(true));
158 }); 170 });
159 return true; 171 return true;
160 }).catchError((error) { 172 }).catchError((error) {
161 _logEvent("Running $command $arguments failed with $error"); 173 _logEvent("Running $command $arguments failed with $error");
162 return false; 174 return false;
163 }); 175 });
164 } 176 }
165 177
166 /** 178 /**
167 * Get any stdout that the browser wrote during execution. 179 * Get the output that was written so far to stdout/stderr/eventLog.
168 */ 180 */
169 String get stdout => _stdout.toString(); 181 BrowserOutput get allBrowserOutput => _allBrowserOutput;
170 String get stderr => _stderr.toString(); 182 BrowserOutput get testBrowserOutput => _testBrowserOutput;
171 String get usageLog => _usageLog.toString(); 183
184 void resetTestBrowserOutput() {
185 _testBrowserOutput = new BrowserOutput();
186 }
172 187
173 String toString(); 188 String toString();
189
174 /** Starts the browser loading the given url */ 190 /** Starts the browser loading the given url */
175 Future<bool> start(String url); 191 Future<bool> start(String url);
176 } 192 }
177 193
178 class Safari extends Browser { 194 class Safari extends Browser {
179 /** 195 /**
180 * The binary used to run safari - changing this can be nececcary for 196 * The binary used to run safari - changing this can be nececcary for
181 * testing or using non standard safari installation. 197 * testing or using non standard safari installation.
182 */ 198 */
183 static const String binary = "/Applications/Safari.app/Contents/MacOS/Safari"; 199 static const String binary = "/Applications/Safari.app/Contents/MacOS/Safari";
(...skipping 332 matching lines...) Expand 10 before | Expand all | Expand 10 after
516 } 532 }
517 533
518 class Firefox extends Browser { 534 class Firefox extends Browser {
519 static const String enablePopUp = 535 static const String enablePopUp =
520 'user_pref("dom.disable_open_during_load", false);'; 536 'user_pref("dom.disable_open_during_load", false);';
521 static const String disableDefaultCheck = 537 static const String disableDefaultCheck =
522 'user_pref("browser.shell.checkDefaultBrowser", false);'; 538 'user_pref("browser.shell.checkDefaultBrowser", false);';
523 static const String disableScriptTimeLimit = 539 static const String disableScriptTimeLimit =
524 'user_pref("dom.max_script_run_time", 0);'; 540 'user_pref("dom.max_script_run_time", 0);';
525 541
526 static string _binary = _getBinary(); 542 static String _binary = _getBinary();
527 543
528 Future _createPreferenceFile(var path) { 544 Future _createPreferenceFile(var path) {
529 var file = new File("${path.toString()}/user.js"); 545 var file = new File("${path.toString()}/user.js");
530 var randomFile = file.openSync(mode: FileMode.WRITE); 546 var randomFile = file.openSync(mode: FileMode.WRITE);
531 randomFile.writeStringSync(enablePopUp); 547 randomFile.writeStringSync(enablePopUp);
532 randomFile.writeStringSync(disableDefaultCheck); 548 randomFile.writeStringSync(disableDefaultCheck);
533 randomFile.writeStringSync(disableScriptTimeLimit); 549 randomFile.writeStringSync(disableScriptTimeLimit);
534 randomFile.close(); 550 randomFile.close();
535 } 551 }
536 552
537 // This is extracted to a function since we may need to support several 553 // This is extracted to a function since we may need to support several
538 // locations. 554 // locations.
539 static String _getWindowsBinary() { 555 static String _getWindowsBinary() {
540 return "C:\\Program Files (x86)\\Mozilla Firefox\\firefox.exe"; 556 return "C:\\Program Files (x86)\\Mozilla Firefox\\firefox.exe";
541 } 557 }
542 558
543 static String _getBinary() { 559 static String _getBinary() {
544 if (Platform.isWindows) return _getWindowsBinary(); 560 if (Platform.isWindows) return _getWindowsBinary();
545 if (Platform.isLinux) return 'firefox'; 561 if (Platform.isLinux) return 'firefox';
546 } 562 }
547 563
548 Future<bool> start(String url) { 564 Future<bool> start(String url) {
549 _logEvent("Starting firefox browser on: $url"); 565 _logEvent("Starting firefox browser on: $url");
550 // Get the version and log that. 566 // Get the version and log that.
551 return Process.run(_binary, ["--version"]).then((var versionResult) { 567 return Process.run(_binary, ["--version"]).then((var versionResult) {
552 if (versionResult.exitCode != 0) { 568 if (versionResult.exitCode != 0) {
553 _logEvent("Failed to firefox get version"); 569 _logEvent("Failed to firefox get version");
554 _logEvent("Make sure $binary is a valid program for running firefox"); 570 _logEvent("Make sure $_binary is a valid program for running firefox");
555 return new Future.value(false); 571 return new Future.value(false);
556 } 572 }
557 version = versionResult.stdout; 573 version = versionResult.stdout;
558 _logEvent("Got version: $version"); 574 _logEvent("Got version: $version");
559 575
560 return new Directory('').createTemp().then((userDir) { 576 return new Directory('').createTemp().then((userDir) {
561 _createPreferenceFile(userDir.path); 577 _createPreferenceFile(userDir.path);
562 _cleanup = () { userDir.deleteSync(recursive: true); }; 578 _cleanup = () { userDir.deleteSync(recursive: true); };
563 var args = ["-profile", "${userDir.path}", 579 var args = ["-profile", "${userDir.path}",
564 "-no-remote", "-new-instance", url]; 580 "-no-remote", "-new-instance", url];
565 return startBrowser(_binary, args); 581 return startBrowser(_binary, args);
566 582
567 }); 583 });
568 }).catchError((e) { 584 }).catchError((e) {
569 _logEvent("Running $binary --version failed with $e"); 585 _logEvent("Running $_binary --version failed with $e");
570 return false; 586 return false;
571 }); 587 });
572 } 588 }
573 589
574 String toString() => "Firefox"; 590 String toString() => "Firefox";
575 } 591 }
576 592
577 593
578 /** 594 /**
579 * Describes the current state of a browser used for testing. 595 * Describes the current state of a browser used for testing.
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
613 // Used for debugging, this is simply a unique identifier assigned to each 629 // Used for debugging, this is simply a unique identifier assigned to each
614 // test. 630 // test.
615 int id; 631 int id;
616 static int _idCounter = 0; 632 static int _idCounter = 0;
617 633
618 BrowserTest(this.url, this.doneCallback, this.timeout) { 634 BrowserTest(this.url, this.doneCallback, this.timeout) {
619 id = _idCounter++; 635 id = _idCounter++;
620 } 636 }
621 } 637 }
622 638
639 /* Describes the output of running the test in a browser */
640 class BrowserTestOutput {
641 final bool didTimeout;
642 final Duration delayUntilTestStarted;
643 final Duration duration;
644 final BrowserOutput browserOutput;
645 final String dom;
646
647 BrowserTestOutput(
648 this.delayUntilTestStarted, this.duration, this.dom,
649 this.browserOutput, {this.didTimeout: false});
650 }
623 651
624 /** 652 /**
625 * Encapsulates all the functionality for running tests in browsers. 653 * Encapsulates all the functionality for running tests in browsers.
626 * The interface is rather simple. After starting the runner tests 654 * The interface is rather simple. After starting the runner tests
627 * are simply added to the queue and a the supplied callbacks are called 655 * are simply added to the queue and a the supplied callbacks are called
628 * whenever a test completes. 656 * whenever a test completes.
629 */ 657 */
630 class BrowserTestRunner { 658 class BrowserTestRunner {
631 final Map globalConfiguration; 659 final Map globalConfiguration;
632 660
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after
743 if (status.currentTest.id != testId) { 771 if (status.currentTest.id != testId) {
744 print("Expected test id ${status.currentTest.id} for" 772 print("Expected test id ${status.currentTest.id} for"
745 "${status.currentTest.url}"); 773 "${status.currentTest.url}");
746 print("Got test id ${testId}"); 774 print("Got test id ${testId}");
747 print("Last test id was ${status.lastTest.id} for " 775 print("Last test id was ${status.lastTest.id} for "
748 "${status.currentTest.url}"); 776 "${status.currentTest.url}");
749 throw("This should never happen, wrong test id"); 777 throw("This should never happen, wrong test id");
750 } 778 }
751 testCache[testId] = status.currentTest.url; 779 testCache[testId] = status.currentTest.url;
752 Stopwatch watch = new Stopwatch()..start(); 780 Stopwatch watch = new Stopwatch()..start();
753 status.currentTest.doneCallback(output, 781
754 status.currentTest.delayUntilTestStarted, 782 // Report that the test is finished now
755 status.currentTest.stopwatch.elapsed); 783 var browserTestOutput = new BrowserTestOutput(
784 status.currentTest.delayUntilTestStarted,
785 status.currentTest.stopwatch.elapsed,
786 output,
787 status.browser.testBrowserOutput);
788 status.currentTest.doneCallback(browserTestOutput);
789
756 watch.stop(); 790 watch.stop();
757 status.lastTest = status.currentTest; 791 status.lastTest = status.currentTest;
758 status.currentTest = null; 792 status.currentTest = null;
759 } else { 793 } else {
760 print("\nThis is bad, should never happen, handleResult no test"); 794 print("\nThis is bad, should never happen, handleResult no test");
761 print("URL: ${status.lastTest.url}"); 795 print("URL: ${status.lastTest.url}");
762 print(output); 796 print(output);
763 terminate().then((_) { 797 terminate().then((_) {
764 exit(1); 798 exit(1);
765 }); 799 });
(...skipping 11 matching lines...) Expand all
777 status.currentTest.stopwatch.elapsed; 811 status.currentTest.stopwatch.elapsed;
778 } 812 }
779 } 813 }
780 814
781 void handleTimeout(BrowserTestingStatus status) { 815 void handleTimeout(BrowserTestingStatus status) {
782 // We simply kill the browser and starts up a new one! 816 // We simply kill the browser and starts up a new one!
783 // We could be smarter here, but it does not seems like it is worth it. 817 // We could be smarter here, but it does not seems like it is worth it.
784 status.timeout = true; 818 status.timeout = true;
785 timedOut.add(status.currentTest.url); 819 timedOut.add(status.currentTest.url);
786 var id = status.browser.id; 820 var id = status.browser.id;
821
822 status.currentTest.stopwatch.stop();
787 status.browser.close().then((_) { 823 status.browser.close().then((_) {
824 // Wait until the browser is closed before reporting the test as timeout.
825 // This will enable us to capture stdout/stderr from the browser
826 // (which might provide us with information about what went wrong).
827 var browserTestOutput = new BrowserTestOutput(
828 status.currentTest.delayUntilTestStarted,
829 status.currentTest.stopwatch.elapsed,
830 'Dom could not be fetched, since the test timed out.',
831 status.browser.testBrowserOutput,
832 didTimeout: true);
833 status.currentTest.doneCallback(browserTestOutput);
834 status.currentTest = null;
ricow1 2013/10/18 14:00:42 shouldn't we do this right away instead of waiting
kustermann 2013/10/18 15:28:27 I don't think so. We should not set the current te
835
788 // We don't want to start a new browser if we are terminating. 836 // We don't want to start a new browser if we are terminating.
789 if (underTermination) return; 837 if (underTermination) return;
790 var browser; 838 var browser;
791 var new_id = id; 839 var new_id = id;
792 if (browserName == 'chromeOnAndroid') { 840 if (browserName == 'chromeOnAndroid') {
793 browser = new AndroidChrome(adbDeviceMapping[id]); 841 browser = new AndroidChrome(adbDeviceMapping[id]);
794 } else { 842 } else {
795 browserStatus.remove(id); 843 browserStatus.remove(id);
796 browser = getInstance(); 844 browser = getInstance();
797 new_id = "BROWSER$browserIdCount"; 845 new_id = "BROWSER$browserIdCount";
(...skipping 14 matching lines...) Expand all
812 } 860 }
813 if (success) { 861 if (success) {
814 browserStatus[browser.id] = new BrowserTestingStatus(browser); 862 browserStatus[browser.id] = new BrowserTestingStatus(browser);
815 } else { 863 } else {
816 // TODO(ricow): Handle this better. 864 // TODO(ricow): Handle this better.
817 print("This is bad, should never happen, could not start browser"); 865 print("This is bad, should never happen, could not start browser");
818 exit(1); 866 exit(1);
819 } 867 }
820 }); 868 });
821 }); 869 });
822 status.currentTest.stopwatch.stop();
823 status.currentTest.doneCallback("TIMEOUT",
824 status.currentTest.delayUntilTestStarted,
825 status.currentTest.stopwatch.elapsed);
826 status.currentTest = null;
827 } 870 }
828 871
829 BrowserTest getNextTest(String browserId) { 872 BrowserTest getNextTest(String browserId) {
830 if (testQueue.isEmpty) return null; 873 if (testQueue.isEmpty) return null;
831 var status = browserStatus[browserId]; 874 var status = browserStatus[browserId];
832 if (status == null) return null; 875 if (status == null) return null;
833 876
834 // We are currently terminating this browser, don't start a new test. 877 // We are currently terminating this browser, don't start a new test.
835 if (status.timeout) return null; 878 if (status.timeout) return null;
879
836 BrowserTest test = testQueue.removeLast(); 880 BrowserTest test = testQueue.removeLast();
837 if (status.currentTest == null) { 881 if (status.currentTest == null) {
838 status.currentTest = test; 882 status.currentTest = test;
839 } else { 883 } else {
840 // TODO(ricow): Handle this better. 884 // TODO(ricow): Handle this better.
841 print("This is bad, should never happen, getNextTest all full"); 885 print("This is bad, should never happen, getNextTest all full");
842 print("This happened for browser $browserId"); 886 print("This happened for browser $browserId");
843 print("Old test was: ${status.currentTest.url}"); 887 print("Old test was: ${status.currentTest.url}");
844 print("Timed out tests:"); 888 print("Timed out tests:");
845 for (var v in timedOut) { 889 for (var v in timedOut) {
846 print(" $v"); 890 print(" $v");
847 } 891 }
848 exit(1); 892 exit(1);
849 } 893 }
850 894
851 status.currentTest.timeoutTimer = createTimeoutTimer(test, status); 895 status.currentTest.timeoutTimer = createTimeoutTimer(test, status);
852 status.currentTest.stopwatch = new Stopwatch()..start(); 896 status.currentTest.stopwatch = new Stopwatch()..start();
897
898 // Reset the test specific output information (stdout, stderr) on the
899 // browser since a new test is begin started.
900 status.browser.resetTestBrowserOutput();
901
853 return test; 902 return test;
854 } 903 }
855 904
856 Timer createTimeoutTimer(BrowserTest test, BrowserTestingStatus status) { 905 Timer createTimeoutTimer(BrowserTest test, BrowserTestingStatus status) {
857 return new Timer( 906 return new Timer(
858 new Duration(seconds: test.timeout), () { handleTimeout(status); }); 907 new Duration(seconds: test.timeout), () { handleTimeout(status); });
859 } 908 }
860 909
861 void queueTest(BrowserTest test) { 910 void queueTest(BrowserTest test) {
862 testQueue.add(test); 911 testQueue.add(test);
(...skipping 354 matching lines...) Expand 10 before | Expand all | Expand 10 after
1217 Dart test driver, number of tests: <div id="number"></div><br> 1266 Dart test driver, number of tests: <div id="number"></div><br>
1218 Currently executing: <div id="currently_executing"></div><br> 1267 Currently executing: <div id="currently_executing"></div><br>
1219 Unhandled error: <div id="unhandled_error"></div> 1268 Unhandled error: <div id="unhandled_error"></div>
1220 <iframe id="embedded_iframe"></iframe> 1269 <iframe id="embedded_iframe"></iframe>
1221 </body> 1270 </body>
1222 </html> 1271 </html>
1223 """; 1272 """;
1224 return driverContent; 1273 return driverContent;
1225 } 1274 }
1226 } 1275 }
OLDNEW
« no previous file with comments | « no previous file | tools/testing/dart/test_runner.dart » ('j') | tools/testing/dart/test_runner.dart » ('J')

Powered by Google App Engine
This is Rietveld 408576698