| Index: tools/testing/dart/browser_controller.dart
|
| diff --git a/tools/testing/dart/browser_controller.dart b/tools/testing/dart/browser_controller.dart
|
| index 7094120661ad9d45ee880021dce68a5be28e1d92..27765697ea3b74db45fe86f46381cd51199c2df6 100644
|
| --- a/tools/testing/dart/browser_controller.dart
|
| +++ b/tools/testing/dart/browser_controller.dart
|
| @@ -14,9 +14,6 @@ import 'http_server.dart';
|
| import 'path.dart';
|
| import 'utils.dart';
|
|
|
| -import 'reset_safari.dart' show
|
| - killAndResetSafari;
|
| -
|
| class BrowserOutput {
|
| final StringBuffer stdout = new StringBuffer();
|
| final StringBuffer stderr = new StringBuffer();
|
| @@ -56,10 +53,10 @@ abstract class Browser {
|
| String id;
|
|
|
| /**
|
| - * Reset the browser to a known configuration on start-up.
|
| + * Delete the browser specific caches on startup.
|
| * Browser specific implementations are free to ignore this.
|
| */
|
| - static bool resetBrowserConfiguration = false;
|
| + static bool deleteCache = false;
|
|
|
| /** Print everything (stdout, stderr, usageLog) whenever we add to it */
|
| bool debugPrint = false;
|
| @@ -108,15 +105,6 @@ abstract class Browser {
|
| 'ie10'
|
| ];
|
|
|
| - /// If [browserName] doesn't support Window.open, we use iframes instead.
|
| - static bool requiresIframe(String browserName) {
|
| - return !BROWSERS_WITH_WINDOW_SUPPORT.contains(browserName);
|
| - }
|
| -
|
| - static bool requiresFocus(String browserName) {
|
| - return browserName == "safari";
|
| - }
|
| -
|
| // TODO(kustermann): add standard support for chrome on android
|
| static bool supportedBrowser(String name) {
|
| return SUPPORTED_BROWSERS.contains(name);
|
| @@ -281,13 +269,6 @@ abstract class Browser {
|
|
|
| /** Starts the browser loading the given url */
|
| Future<bool> start(String url);
|
| -
|
| - /// Called when the driver page is requested, that is, when the browser first
|
| - /// contacts the test server. At this time, it's safe to assume that the
|
| - /// browser process has started and opened its first window.
|
| - ///
|
| - /// This is used by [Safari] to ensure the browser window has focus.
|
| - Future<Null> onDriverPageRequested() => new Future<Null>.value();
|
| }
|
|
|
| class Safari extends Browser {
|
| @@ -297,48 +278,62 @@ class Safari extends Browser {
|
| static const String versionFile =
|
| "/Applications/Safari.app/Contents/version.plist";
|
|
|
| - static const String safariBundleLocation = "/Applications/Safari.app/";
|
| + /**
|
| + * Directories where safari stores state. We delete these if the deleteCache
|
| + * is set
|
| + */
|
| + static const List<String> CACHE_DIRECTORIES = const [
|
| + "Library/Caches/com.apple.Safari",
|
| + "Library/Safari",
|
| + "Library/Saved Application State/com.apple.Safari.savedState",
|
| + "Library/Caches/Metadata/Safari"
|
| + ];
|
|
|
| - // Clears the cache if the static resetBrowserConfiguration flag is set.
|
| - // Returns false if the command to actually clear the cache did not complete.
|
| - Future<bool> resetConfiguration() async {
|
| - if (!Browser.resetBrowserConfiguration) return true;
|
| + Future<bool> allowPopUps() {
|
| + var command = "defaults";
|
| + var args = [
|
| + "write",
|
| + "com.apple.safari",
|
| + "com.apple.Safari.ContentPageGroupIdentifier."
|
| + "WebKit2JavaScriptCanOpenWindowsAutomatically",
|
| + "1"
|
| + ];
|
| + return Process.run(command, args).then((result) {
|
| + if (result.exitCode != 0) {
|
| + _logEvent("Could not disable pop-up blocking for safari");
|
| + return false;
|
| + }
|
| + return true;
|
| + });
|
| + }
|
|
|
| - Completer completer = new Completer();
|
| - handleUncaughtError(error, StackTrace stackTrace) {
|
| - if (!completer.isCompleted) {
|
| - completer.completeError(error, stackTrace);
|
| + Future<bool> deleteIfExists(Iterator<String> paths) {
|
| + if (!paths.moveNext()) return new Future.value(true);
|
| + Directory directory = new Directory(paths.current);
|
| + return directory.exists().then((exists) {
|
| + if (exists) {
|
| + _logEvent("Deleting ${paths.current}");
|
| + return directory
|
| + .delete(recursive: true)
|
| + .then((_) => deleteIfExists(paths))
|
| + .catchError((error) {
|
| + _logEvent("Failure trying to delete ${paths.current}: $error");
|
| + return false;
|
| + });
|
| } else {
|
| - throw new AsyncError(error, stackTrace);
|
| + _logEvent("${paths.current} is not present");
|
| + return deleteIfExists(paths);
|
| }
|
| - }
|
| - Zone parent = Zone.current;
|
| - ZoneSpecification specification = new ZoneSpecification(
|
| - print: (Zone self, ZoneDelegate delegate, Zone zone, String line) {
|
| - delegate.run(parent, () {
|
| - _logEvent(line);
|
| - });
|
| - });
|
| - Future zoneWrapper() {
|
| - Uri safariUri = Uri.base.resolve(safariBundleLocation);
|
| - return new Future(() => killAndResetSafari(bundle: safariUri))
|
| - .then(completer.complete);
|
| - }
|
| -
|
| - // We run killAndResetSafari in a Zone as opposed to running an external
|
| - // process. The Zone allows us to collect its output, and protect the rest
|
| - // of the test infrastructure against errors in it.
|
| - runZoned(
|
| - zoneWrapper, zoneSpecification: specification,
|
| - onError: handleUncaughtError);
|
| + });
|
| + }
|
|
|
| - try {
|
| - await completer.future;
|
| - return true;
|
| - } catch (error, st) {
|
| - _logEvent("Unable to reset Safari: $error$st");
|
| - return false;
|
| - }
|
| + // Clears the cache if the static deleteCache flag is set.
|
| + // Returns false if the command to actually clear the cache did not complete.
|
| + Future<bool> clearCache() {
|
| + if (!Browser.deleteCache) return new Future.value(true);
|
| + var home = Platform.environment['HOME'];
|
| + Iterator iterator = CACHE_DIRECTORIES.map((s) => "$home/$s").iterator;
|
| + return deleteIfExists(iterator);
|
| }
|
|
|
| Future<String> getVersion() {
|
| @@ -374,58 +369,42 @@ class Safari extends Browser {
|
| });
|
| }
|
|
|
| - Future<Null> _createLaunchHTML(var path, var url) async {
|
| + void _createLaunchHTML(var path, var url) {
|
| var file = new File("${path}/launch.html");
|
| - var randomFile = await file.open(mode: FileMode.WRITE);
|
| + var randomFile = file.openSync(mode: FileMode.WRITE);
|
| var content = '<script language="JavaScript">location = "$url"</script>';
|
| - await randomFile.writeString(content);
|
| - await randomFile.close();
|
| + randomFile.writeStringSync(content);
|
| + randomFile.close();
|
| }
|
|
|
| - Future<bool> start(String url) async {
|
| + Future<bool> start(String url) {
|
| _logEvent("Starting Safari browser on: $url");
|
| - if (!await resetConfiguration()) {
|
| - _logEvent("Could not clear cache");
|
| - return false;
|
| - }
|
| - String version;
|
| - try {
|
| - version = await getVersion();
|
| - } catch (error) {
|
| - _logEvent("Running $_binary --version failed with $error");
|
| - return false;
|
| - }
|
| - _logEvent("Got version: $version");
|
| - Directory userDir;
|
| - try {
|
| - userDir = await Directory.systemTemp.createTemp();
|
| - } catch (error) {
|
| - _logEvent("Error creating temporary directory: $error");
|
| - return false;
|
| - }
|
| - _cleanup = () {
|
| - userDir.deleteSync(recursive: true);
|
| - };
|
| - try {
|
| - await _createLaunchHTML(userDir.path, url);
|
| - } catch (error) {
|
| - _logEvent("Error creating launch HTML: $error");
|
| - return false;
|
| - }
|
| - var args = [
|
| - "-d", "-i", "-m", "-s", "-u", _binary,
|
| - "${userDir.path}/launch.html"];
|
| - try {
|
| - return startBrowserProcess("/usr/bin/caffeinate", args);
|
| - } catch (error) {
|
| - _logEvent("Error starting browser process: $error");
|
| - return false;
|
| - }
|
| - }
|
| -
|
| - Future<Null> onDriverPageRequested() async {
|
| - await Process.run("/usr/bin/osascript",
|
| - ['-e', 'tell application "Safari" to activate']);
|
| + return allowPopUps().then((success) {
|
| + if (!success) {
|
| + return false;
|
| + }
|
| + return clearCache().then((cleared) {
|
| + if (!cleared) {
|
| + _logEvent("Could not clear cache");
|
| + return false;
|
| + }
|
| + // Get the version and log that.
|
| + return getVersion().then((version) {
|
| + _logEvent("Got version: $version");
|
| + return Directory.systemTemp.createTemp().then((userDir) {
|
| + _cleanup = () {
|
| + userDir.deleteSync(recursive: true);
|
| + };
|
| + _createLaunchHTML(userDir.path, url);
|
| + var args = ["${userDir.path}/launch.html"];
|
| + return startBrowserProcess(_binary, args);
|
| + });
|
| + }).catchError((error) {
|
| + _logEvent("Running $_binary --version failed with $error");
|
| + return false;
|
| + });
|
| + });
|
| + });
|
| }
|
|
|
| String toString() => "Safari";
|
| @@ -498,16 +477,16 @@ class Chrome extends Browser {
|
| class SafariMobileSimulator extends Safari {
|
| /**
|
| * Directories where safari simulator stores state. We delete these if the
|
| - * resetBrowserConfiguration is set
|
| + * deleteCache is set
|
| */
|
| static const List<String> CACHE_DIRECTORIES = const [
|
| "Library/Application Support/iPhone Simulator/7.1/Applications"
|
| ];
|
|
|
| - // Clears the cache if the static resetBrowserConfiguration flag is set.
|
| + // Clears the cache if the static deleteCache flag is set.
|
| // Returns false if the command to actually clear the cache did not complete.
|
| - Future<bool> resetConfiguration() {
|
| - if (!Browser.resetBrowserConfiguration) return new Future.value(true);
|
| + Future<bool> clearCache() {
|
| + if (!Browser.deleteCache) return new Future.value(true);
|
| var home = Platform.environment['HOME'];
|
| Iterator iterator = CACHE_DIRECTORIES.map((s) => "$home/$s").iterator;
|
| return deleteIfExists(iterator);
|
| @@ -515,7 +494,7 @@ class SafariMobileSimulator extends Safari {
|
|
|
| Future<bool> start(String url) {
|
| _logEvent("Starting safari mobile simulator browser on: $url");
|
| - return resetConfiguration().then((success) {
|
| + return clearCache().then((success) {
|
| if (!success) {
|
| _logEvent("Could not clear cache, exiting");
|
| return false;
|
| @@ -582,10 +561,9 @@ class IE extends Browser {
|
| });
|
| }
|
|
|
| - // Clears the recovery cache if the static resetBrowserConfiguration flag is
|
| - // set.
|
| - Future<bool> resetConfiguration() {
|
| - if (!Browser.resetBrowserConfiguration) return new Future.value(true);
|
| + // Clears the recovery cache if the static deleteCache flag is set.
|
| + Future<bool> clearCache() {
|
| + if (!Browser.deleteCache) return new Future.value(true);
|
| var localAppData = Platform.environment['LOCALAPPDATA'];
|
|
|
| Directory dir = new Directory("$localAppData\\Microsoft\\"
|
| @@ -600,7 +578,7 @@ class IE extends Browser {
|
|
|
| Future<bool> start(String url) {
|
| _logEvent("Starting ie browser on: $url");
|
| - return resetConfiguration().then((_) => getVersion()).then((version) {
|
| + return clearCache().then((_) => getVersion()).then((version) {
|
| _logEvent("Got version: $version");
|
| return startBrowserProcess(_binary, [url]);
|
| });
|
| @@ -898,12 +876,12 @@ class BrowserTestRunner {
|
| static const Duration MIN_NONEMPTY_QUEUE_TIME = const Duration(seconds: 1);
|
|
|
| final Map configuration;
|
| - final BrowserTestingServer testingServer;
|
| + BrowserTestingServer testingServer;
|
|
|
| final String localIp;
|
| - final String browserName;
|
| - final int maxNumBrowsers;
|
| - final bool checkedMode;
|
| + String browserName;
|
| + int maxNumBrowsers;
|
| + bool checkedMode;
|
| int numBrowsers = 0;
|
| // Used to send back logs from the browser (start, stop etc)
|
| Function logger;
|
| @@ -950,23 +928,27 @@ class BrowserTestRunner {
|
| if (_currentStartingBrowserId == id) _currentStartingBrowserId = null;
|
| }
|
|
|
| + // If [browserName] doesn't support opening new windows, we use new iframes
|
| + // instead.
|
| + bool get useIframe =>
|
| + !Browser.BROWSERS_WITH_WINDOW_SUPPORT.contains(browserName);
|
| +
|
| + /// The optional testingServer parameter allows callers to pass in
|
| + /// a testing server with different behavior than the default
|
| + /// BrowserTestServer. The url handlers of the testingServer are
|
| + /// overwritten, so an existing handler can't be shared between instances.
|
| BrowserTestRunner(
|
| - Map configuration,
|
| - String localIp,
|
| - String browserName,
|
| - this.maxNumBrowsers)
|
| - : configuration = configuration,
|
| - localIp = localIp,
|
| - browserName = (browserName == 'ff') ? 'firefox' : browserName,
|
| - checkedMode = configuration['checked'],
|
| - testingServer = new BrowserTestingServer(
|
| - configuration, localIp,
|
| - Browser.requiresIframe(browserName),
|
| - Browser.requiresFocus(browserName)) {
|
| - testingServer.testRunner = this;
|
| + this.configuration, this.localIp, this.browserName, this.maxNumBrowsers,
|
| + {BrowserTestingServer this.testingServer}) {
|
| + checkedMode = configuration['checked'];
|
| + if (browserName == 'ff') browserName = 'firefox';
|
| }
|
|
|
| Future start() async {
|
| + if (testingServer == null) {
|
| + testingServer =
|
| + new BrowserTestingServer(configuration, localIp, useIframe);
|
| + }
|
| await testingServer.start();
|
| testingServer
|
| ..testDoneCallBack = handleResults
|
| @@ -1303,9 +1285,6 @@ class BrowserTestingServer {
|
| /// test
|
|
|
| final String localIp;
|
| - final bool useIframe;
|
| - final bool requiresFocus;
|
| - BrowserTestRunner testRunner;
|
|
|
| static const String driverPath = "/driver";
|
| static const String nextTestPath = "/next_test";
|
| @@ -1318,14 +1297,14 @@ class BrowserTestingServer {
|
| var testCount = 0;
|
| var errorReportingServer;
|
| bool underTermination = false;
|
| + bool useIframe = false;
|
|
|
| Function testDoneCallBack;
|
| Function testStatusUpdateCallBack;
|
| Function testStartedCallBack;
|
| Function nextTestCallBack;
|
|
|
| - BrowserTestingServer(
|
| - this.configuration, this.localIp, this.useIframe, this.requiresFocus);
|
| + BrowserTestingServer(this.configuration, this.localIp, this.useIframe);
|
|
|
| Future start() {
|
| var test_driver_error_port = configuration['test_driver_error_port'];
|
| @@ -1387,41 +1366,29 @@ class BrowserTestingServer {
|
| handleStarted(request, browserId(request, startedPath), testId(request));
|
| });
|
|
|
| - void sendPageHandler(HttpRequest request) {
|
| - // Do NOT make this method async. We need to call catchError below
|
| - // synchronously to avoid unhandled asynchronous errors.
|
| - noCache(request);
|
| - Future<String> textResponse;
|
| - if (request.uri.path.startsWith(driverPath)) {
|
| - textResponse = getDriverPage(browserId(request, driverPath));
|
| - request.response.headers.set('Content-Type', 'text/html');
|
| - } else if (request.uri.path.startsWith(nextTestPath)) {
|
| - textResponse = new Future<String>.value(
|
| - getNextTest(browserId(request, nextTestPath)));
|
| - request.response.headers.set('Content-Type', 'text/plain');
|
| - } else {
|
| - textResponse = new Future<String>.value("");
|
| - }
|
| - request.response.done.catchError((error) {
|
| - if (!underTermination) {
|
| - return textResponse.then((String text) {
|
| - print("URI ${request.uri}");
|
| - print("textResponse $textResponse");
|
| - throw "Error returning content to browser: $error";
|
| + makeSendPageHandler(String prefix) => (HttpRequest request) {
|
| + noCache(request);
|
| + var textResponse = "";
|
| + if (prefix == driverPath) {
|
| + textResponse = getDriverPage(browserId(request, prefix));
|
| + request.response.headers.set('Content-Type', 'text/html');
|
| + }
|
| + if (prefix == nextTestPath) {
|
| + textResponse = getNextTest(browserId(request, prefix));
|
| + request.response.headers.set('Content-Type', 'text/plain');
|
| + }
|
| + request.response.write(textResponse);
|
| + request.listen((_) {}, onDone: request.response.close);
|
| + request.response.done.catchError((error) {
|
| + if (!underTermination) {
|
| + print("URI ${request.uri}");
|
| + print("Textresponse $textResponse");
|
| + throw "Error returning content to browser: $error";
|
| + }
|
| });
|
| - }
|
| - });
|
| - textResponse.then((String text) async {
|
| - request.response.write(text);
|
| - await request.listen(null).asFuture();
|
| - // Ignoring the returned closure as it returns the 'done' future
|
| - // which alread has catchError installed above.
|
| - request.response.close();
|
| - });
|
| - }
|
| -
|
| - server.addHandler(driverPath, sendPageHandler);
|
| - server.addHandler(nextTestPath, sendPageHandler);
|
| + };
|
| + server.addHandler(driverPath, makeSendPageHandler(driverPath));
|
| + server.addHandler(nextTestPath, makeSendPageHandler(nextTestPath));
|
| }
|
|
|
| void handleReport(HttpRequest request, String browserId, var testId,
|
| @@ -1480,34 +1447,33 @@ class BrowserTestingServer {
|
| return "http://$localIp:$port/driver/$browserId";
|
| }
|
|
|
| - Future<String> getDriverPage(String browserId) async {
|
| - await testRunner.browserStatus[browserId].browser.onDriverPageRequested();
|
| + String getDriverPage(String browserId) {
|
| var errorReportingUrl =
|
| "http://$localIp:${errorReportingServer.port}/$browserId";
|
| String driverContent = """
|
| <!DOCTYPE html><html>
|
| <head>
|
| - <title>Driving page</title>
|
| <style>
|
| -.big-notice {
|
| - background-color: red;
|
| - color: white;
|
| - font-weight: bold;
|
| - font-size: xx-large;
|
| - text-align: center;
|
| -}
|
| -.controller.box {
|
| - white-space: nowrap;
|
| - overflow: scroll;
|
| - height: 6em;
|
| -}
|
| -body {
|
| - font-family: sans-serif;
|
| -}
|
| -body div {
|
| - padding-top: 10px;
|
| -}
|
| + body {
|
| + margin: 0;
|
| + }
|
| + .box {
|
| + overflow: hidden;
|
| + overflow-y: auto;
|
| + position: absolute;
|
| + left: 0;
|
| + right: 0;
|
| + }
|
| + .controller.box {
|
| + height: 75px;
|
| + top: 0;
|
| + }
|
| + .test.box {
|
| + top: 75px;
|
| + bottom: 0;
|
| + }
|
| </style>
|
| + <title>Driving page</title>
|
| <script type='text/javascript'>
|
| var STATUS_UPDATE_INTERVAL = 10000;
|
|
|
| @@ -1637,8 +1603,7 @@ body div {
|
| embedded_iframe_div.removeChild(embedded_iframe);
|
| embedded_iframe = document.createElement('iframe');
|
| embedded_iframe.id = "embedded_iframe";
|
| - embedded_iframe.width='800px';
|
| - embedded_iframe.height='600px';
|
| + embedded_iframe.style="width:100%;height:100%";
|
| embedded_iframe_div.appendChild(embedded_iframe);
|
| embedded_iframe.src = url;
|
| } else {
|
| @@ -1810,26 +1775,13 @@ body div {
|
| </script>
|
| </head>
|
| <body onload="startTesting()">
|
| -
|
| - <div class='big-notice'>
|
| - Please keep this window in focus at all times.
|
| - </div>
|
| -
|
| - <div>
|
| - Some browsers, Safari, in particular, may pause JavaScript when not
|
| - visible to conserve power consumption and CPU resources. In addition,
|
| - some tests of focus events will not work correctly if this window doesn't
|
| - have focus. It's also advisable to close any other programs that may open
|
| - modal dialogs, for example, Chrome with Calendar open.
|
| - </div>
|
| -
|
| <div class="controller box">
|
| Dart test driver, number of tests: <span id="number"></span><br>
|
| Currently executing: <span id="currently_executing"></span><br>
|
| Unhandled error: <span id="unhandled_error"></span>
|
| </div>
|
| <div id="embedded_iframe_div" class="test box">
|
| - <iframe id="embedded_iframe"></iframe>
|
| + <iframe style="width:100%;height:100%;" id="embedded_iframe"></iframe>
|
| </div>
|
| </body>
|
| </html>
|
|
|