| 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:core"; | 7 import "dart:core"; |
| 8 import "dart:io"; | 8 import "dart:io"; |
| 9 | 9 |
| 10 import 'android.dart'; | 10 import 'android.dart'; |
| 11 import 'utils.dart'; | 11 import 'utils.dart'; |
| 12 | 12 |
| 13 /** Class describing the interface for communicating with browsers. */ | 13 /** Class describing the interface for communicating with browsers. */ |
| 14 abstract class Browser { | 14 abstract class Browser { |
| 15 StringBuffer _stdout = new StringBuffer(); | 15 StringBuffer _stdout = new StringBuffer(); |
| 16 StringBuffer _stderr = new StringBuffer(); | 16 StringBuffer _stderr = new StringBuffer(); |
| 17 StringBuffer _usageLog = new StringBuffer(); | 17 StringBuffer _usageLog = new StringBuffer(); |
| 18 // This function is called when the process is closed. | 18 // This is called after the process is closed, before the done future |
| 19 Completer _processClosedCompleter = new Completer(); | 19 // is completed. |
| 20 // This is called after the process is closed, after _processClosedCompleter | 20 // Subclasses can use this to cleanup any browser specific resources |
| 21 // has been called, but before onExit. Subclasses can use this to cleanup | 21 // (temp directories, profiles, etc). The function is expected to do |
| 22 // any browser specific resources (temp directories, profiles, etc) | 22 // it's work synchronously. |
| 23 // The function is expected to do it's work synchronously. | |
| 24 Function _cleanup; | 23 Function _cleanup; |
| 25 | 24 |
| 26 /** The version of the browser - normally set when starting a browser */ | 25 /** The version of the browser - normally set when starting a browser */ |
| 27 String version = ""; | 26 String version = ""; |
| 28 /** | 27 /** |
| 29 * The underlying process - don't mess directly with this if you don't | 28 * The underlying process - don't mess directly with this if you don't |
| 30 * know what you are doing (this is an interactive process that needs | 29 * know what you are doing (this is an interactive process that needs |
| 31 * special threatment to not leak). | 30 * special threatment to not leak). |
| 32 */ | 31 */ |
| 33 Process process; | 32 Process process; |
| 34 | 33 |
| 35 Function logger; | 34 Function logger; |
| 36 | 35 |
| 37 /** | 36 /** |
| 38 * Id of the browser | 37 * Id of the browser |
| 39 */ | 38 */ |
| 40 String id; | 39 String id; |
| 41 | 40 |
| 42 /** Print everything (stdout, stderr, usageLog) whenever we add to it */ | 41 /** Print everything (stdout, stderr, usageLog) whenever we add to it */ |
| 43 bool debugPrint = false; | 42 bool debugPrint = false; |
| 44 | 43 |
| 45 // This future will be lazily set when calling close() and will complete once | 44 // This future returns when the process exits. It is also the return value |
| 46 // the process did exit. | 45 // of close() |
| 47 Future browserTerminationFuture; | 46 Future done; |
| 48 | 47 |
| 49 Browser(); | 48 Browser(); |
| 50 | 49 |
| 51 factory Browser.byName(String name) { | 50 factory Browser.byName(String name) { |
| 52 if (name == 'ff' || name == 'firefox') { | 51 if (name == 'ff' || name == 'firefox') { |
| 53 return new Firefox(); | 52 return new Firefox(); |
| 54 } else if (name == 'chrome') { | 53 } else if (name == 'chrome') { |
| 55 return new Chrome(); | 54 return new Chrome(); |
| 56 } else if (name == 'safari') { | 55 } else if (name == 'safari') { |
| 57 return new Safari(); | 56 return new Safari(); |
| (...skipping 25 matching lines...) Expand all Loading... |
| 83 _stdout.write(output); | 82 _stdout.write(output); |
| 84 } | 83 } |
| 85 | 84 |
| 86 void _addStderr(String output) { | 85 void _addStderr(String output) { |
| 87 if (debugPrint) print("stderr: $output"); | 86 if (debugPrint) print("stderr: $output"); |
| 88 _stderr.write(output); | 87 _stderr.write(output); |
| 89 } | 88 } |
| 90 | 89 |
| 91 Future close() { | 90 Future close() { |
| 92 _logEvent("Close called on browser"); | 91 _logEvent("Close called on browser"); |
| 93 if (browserTerminationFuture == null) { | 92 if (process != null) { |
| 94 var completer = new Completer(); | 93 if (process.kill(ProcessSignal.SIGKILL)) { |
| 95 browserTerminationFuture = completer.future; | 94 _logEvent("Successfully sent kill signal to process."); |
| 96 | |
| 97 if (process != null) { | |
| 98 _processClosedCompleter.future.then((_) { | |
| 99 process = null; | |
| 100 completer.complete(true); | |
| 101 if (_cleanup != null) { | |
| 102 _cleanup(); | |
| 103 } | |
| 104 }); | |
| 105 | |
| 106 if (process.kill(ProcessSignal.SIGKILL)) { | |
| 107 _logEvent("Successfully sent kill signal to process."); | |
| 108 } else { | |
| 109 _logEvent("Sending kill signal failed."); | |
| 110 } | |
| 111 } else { | 95 } else { |
| 112 _logEvent("The process is already dead."); | 96 _logEvent("Sending kill signal failed."); |
| 113 completer.complete(true); | |
| 114 } | 97 } |
| 98 return done; |
| 99 } else { |
| 100 _logEvent("The process is already dead."); |
| 101 return new Future.immediate(true); |
| 115 } | 102 } |
| 116 return browserTerminationFuture; | |
| 117 } | 103 } |
| 118 | 104 |
| 119 /** | 105 /** |
| 120 * Start the browser using the supplied argument. | 106 * Start the browser using the supplied argument. |
| 121 * This sets up the error handling and usage logging. | 107 * This sets up the error handling and usage logging. |
| 122 */ | 108 */ |
| 123 Future<bool> startBrowser(String command, List<String> arguments) { | 109 Future<bool> startBrowser(String command, List<String> arguments) { |
| 124 return Process.start(command, arguments).then((startedProcess) { | 110 return Process.start(command, arguments).then((startedProcess) { |
| 125 process = startedProcess; | 111 process = startedProcess; |
| 112 // Used to notify when exiting, and as a return value on calls to |
| 113 // close(). |
| 114 var doneCompleter = new Completer(); |
| 115 done = doneCompleter.future; |
| 116 |
| 126 Completer stdoutDone = new Completer(); | 117 Completer stdoutDone = new Completer(); |
| 127 Completer stderrDone = new Completer(); | 118 Completer stderrDone = new Completer(); |
| 128 | 119 |
| 129 process.stdout.transform(new StringDecoder()).listen((data) { | 120 process.stdout.transform(new StringDecoder()).listen((data) { |
| 130 _addStdout(data); | 121 _addStdout(data); |
| 131 }, onError: (error) { | 122 }, onError: (error) { |
| 132 // This should _never_ happen, but we really want this in the log | 123 // This should _never_ happen, but we really want this in the log |
| 133 // if it actually does due to dart:io or vm bug. | 124 // if it actually does due to dart:io or vm bug. |
| 134 _logEvent("An error occured in the process stdout handling: $error"); | 125 _logEvent("An error occured in the process stdout handling: $error"); |
| 135 }, onDone: () { | 126 }, onDone: () { |
| 136 stdoutDone.complete(true); | 127 stdoutDone.complete(true); |
| 137 }); | 128 }); |
| 138 | 129 |
| 139 process.stderr.transform(new StringDecoder()).listen((data) { | 130 process.stderr.transform(new StringDecoder()).listen((data) { |
| 140 _addStderr(data); | 131 _addStderr(data); |
| 141 }, onError: (error) { | 132 }, onError: (error) { |
| 142 // This should _never_ happen, but we really want this in the log | 133 // This should _never_ happen, but we really want this in the log |
| 143 // if it actually does due to dart:io or vm bug. | 134 // if it actually does due to dart:io or vm bug. |
| 144 _logEvent("An error occured in the process stderr handling: $error"); | 135 _logEvent("An error occured in the process stderr handling: $error"); |
| 145 }, onDone: () { | 136 }, onDone: () { |
| 146 stderrDone.complete(true); | 137 stderrDone.complete(true); |
| 147 }); | 138 }); |
| 148 | 139 |
| 149 process.exitCode.then((exitCode) { | 140 process.exitCode.then((exitCode) { |
| 150 _logEvent("Browser closed with exitcode $exitCode"); | 141 _logEvent("Browser closed with exitcode $exitCode"); |
| 151 Future.wait([stdoutDone.future, stderrDone.future]).then((_) { | 142 Future.wait([stdoutDone.future, stderrDone.future]).then((_) { |
| 152 _processClosedCompleter.complete(exitCode); | 143 process = null; |
| 144 if (_cleanup != null) { |
| 145 _cleanup(); |
| 146 } |
| 147 doneCompleter.complete(exitCode); |
| 153 }); | 148 }); |
| 154 }); | 149 }); |
| 155 return true; | 150 return true; |
| 156 }).catchError((error) { | 151 }).catchError((error) { |
| 157 _logEvent("Running $command $arguments failed with $error"); | 152 _logEvent("Running $command $arguments failed with $error"); |
| 158 return false; | 153 return false; |
| 159 }); | 154 }); |
| 160 } | 155 } |
| 161 | 156 |
| 162 /** | 157 /** |
| (...skipping 487 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 650 } | 645 } |
| 651 } | 646 } |
| 652 | 647 |
| 653 void handleTimeout(BrowserTestingStatus status) { | 648 void handleTimeout(BrowserTestingStatus status) { |
| 654 // We simply kill the browser and starts up a new one! | 649 // We simply kill the browser and starts up a new one! |
| 655 // We could be smarter here, but it does not seems like it is worth it. | 650 // We could be smarter here, but it does not seems like it is worth it. |
| 656 DebugLogger.info("Handling timeout for browser ${status.browser.id}"); | 651 DebugLogger.info("Handling timeout for browser ${status.browser.id}"); |
| 657 status.timeout = true; | 652 status.timeout = true; |
| 658 timedOut.add(status.currentTest.url); | 653 timedOut.add(status.currentTest.url); |
| 659 var id = status.browser.id; | 654 var id = status.browser.id; |
| 660 status.browser.close().then((closed) { | 655 status.browser.close().then((_) { |
| 661 if (!closed) { | |
| 662 // Very bad, we could not kill the browser. | |
| 663 print("could not kill browser $id"); | |
| 664 return; | |
| 665 } | |
| 666 // We don't want to start a new browser if we are terminating. | 656 // We don't want to start a new browser if we are terminating. |
| 667 if (underTermination) return; | 657 if (underTermination) return; |
| 668 var browser; | 658 var browser; |
| 669 var new_id = id; | 659 var new_id = id; |
| 670 if (browserName == 'chromeOnAndroid') { | 660 if (browserName == 'chromeOnAndroid') { |
| 671 browser = new AndroidChrome(adbDeviceMapping[id]); | 661 browser = new AndroidChrome(adbDeviceMapping[id]); |
| 672 } else { | 662 } else { |
| 673 browserStatus.remove(id); | 663 browserStatus.remove(id); |
| 674 browser = getInstance(); | 664 browser = getInstance(); |
| 675 new_id = "BROWSER$browserIdCount"; | 665 new_id = "BROWSER$browserIdCount"; |
| (...skipping 379 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1055 </head> | 1045 </head> |
| 1056 <body onload="startTesting()"> | 1046 <body onload="startTesting()"> |
| 1057 Dart test driver, number of tests: <div id="number"></div> | 1047 Dart test driver, number of tests: <div id="number"></div> |
| 1058 <iframe id="embedded_iframe"></iframe> | 1048 <iframe id="embedded_iframe"></iframe> |
| 1059 </body> | 1049 </body> |
| 1060 </html> | 1050 </html> |
| 1061 """; | 1051 """; |
| 1062 return driverContent; | 1052 return driverContent; |
| 1063 } | 1053 } |
| 1064 } | 1054 } |
| OLD | NEW |