Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 library browser; | 4 library browser; |
| 5 | 5 |
| 6 import "dart:async"; | 6 import "dart:async"; |
| 7 import "dart:convert" show LineSplitter, UTF8; | 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 } |
| OLD | NEW |