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

Unified Diff: tools/testing/dart/browser_controller.dart

Issue 841193003: cleanup to tools/testing/dart (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: one last bit Created 5 years, 11 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « tools/testing/dart/android.dart ('k') | tools/testing/dart/browser_test.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tools/testing/dart/browser_controller.dart
diff --git a/tools/testing/dart/browser_controller.dart b/tools/testing/dart/browser_controller.dart
deleted file mode 100644
index 01c5aec7c6f3ed1c8b097f2f3f063c3b381441b2..0000000000000000000000000000000000000000
--- a/tools/testing/dart/browser_controller.dart
+++ /dev/null
@@ -1,1794 +0,0 @@
-// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-library browser;
-
-import "dart:async";
-import "dart:convert" show LineSplitter, UTF8, JSON;
-import "dart:core";
-import "dart:io";
-
-import 'android.dart';
-import 'http_server.dart';
-import 'path.dart';
-import 'utils.dart';
-
-class BrowserOutput {
- final StringBuffer stdout = new StringBuffer();
- final StringBuffer stderr = new StringBuffer();
- final StringBuffer eventLog = new StringBuffer();
-}
-
-/** Class describing the interface for communicating with browsers. */
-abstract class Browser {
- BrowserOutput _allBrowserOutput = new BrowserOutput();
- BrowserOutput _testBrowserOutput = new BrowserOutput();
-
- // This is called after the process is closed, before the done future
- // is completed.
- // Subclasses can use this to cleanup any browser specific resources
- // (temp directories, profiles, etc). The function is expected to do
- // it's work synchronously.
- Function _cleanup;
-
- /** The version of the browser - normally set when starting a browser */
- String version = "";
-
- // The path to the browser executable.
- String _binary;
-
- /**
- * The underlying process - don't mess directly with this if you don't
- * know what you are doing (this is an interactive process that needs
- * special treatment to not leak).
- */
- Process process;
-
- Function logger;
-
- /**
- * Id of the browser
- */
- String id;
-
- /**
- * Delete the browser specific caches on startup.
- * Browser specific implementations are free to ignore this.
- */
- static bool deleteCache = false;
-
- /** Print everything (stdout, stderr, usageLog) whenever we add to it */
- bool debugPrint = false;
-
- // This future returns when the process exits. It is also the return value
- // of close()
- Future done;
-
- Browser();
-
- factory Browser.byName(String name,
- String executablePath,
- [bool checkedMode = false]) {
- var browser;
- if (name == 'firefox') {
- browser = new Firefox();
- } else if (name == 'chrome') {
- browser = new Chrome();
- } else if (name == 'dartium') {
- browser = new Dartium(checkedMode);
- } else if (name == 'safari') {
- browser = new Safari();
- } else if (name == 'safarimobilesim') {
- browser = new SafariMobileSimulator();
- } else if (name.startsWith('ie')) {
- browser = new IE();
- } else {
- throw "Non supported browser";
- }
- browser._binary = executablePath;
- return browser;
- }
-
- static const List<String> SUPPORTED_BROWSERS =
- const ['safari', 'ff', 'firefox', 'chrome', 'ie9', 'ie10',
- 'ie11', 'dartium'];
-
- static const List<String> BROWSERS_WITH_WINDOW_SUPPORT =
- const ['ie11', 'ie10'];
-
- // TODO(kustermann): add standard support for chrome on android
- static bool supportedBrowser(String name) {
- return SUPPORTED_BROWSERS.contains(name);
- }
-
- void _logEvent(String event) {
- String toLog = "$this ($id) - $event \n";
- if (debugPrint) print("usageLog: $toLog");
- if (logger != null) logger(toLog);
-
- _allBrowserOutput.eventLog.write(toLog);
- _testBrowserOutput.eventLog.write(toLog);
- }
-
- void _addStdout(String output) {
- if (debugPrint) print("stdout: $output");
-
- _allBrowserOutput.stdout.write(output);
- _testBrowserOutput.stdout.write(output);
- }
-
- void _addStderr(String output) {
- if (debugPrint) print("stderr: $output");
-
- _allBrowserOutput.stderr.write(output);
- _testBrowserOutput.stderr.write(output);
- }
-
- Future close() {
- _logEvent("Close called on browser");
- if (process != null) {
- if (process.kill(ProcessSignal.SIGKILL)) {
- _logEvent("Successfully sent kill signal to process.");
- } else {
- _logEvent("Sending kill signal failed.");
- }
- return done;
- } else {
- _logEvent("The process is already dead.");
- return new Future.value(true);
- }
- }
-
- /**
- * Start the browser using the supplied argument.
- * This sets up the error handling and usage logging.
- */
- Future<bool> startBrowser(String command,
- List<String> arguments,
- {Map<String,String> environment}) {
- return Process.start(command, arguments, environment: environment)
- .then((startedProcess) {
- process = startedProcess;
- // Used to notify when exiting, and as a return value on calls to
- // close().
- var doneCompleter = new Completer();
- done = doneCompleter.future;
-
- Completer stdoutDone = new Completer();
- Completer stderrDone = new Completer();
-
- bool stdoutIsDone = false;
- bool stderrIsDone = false;
- StreamSubscription stdoutSubscription;
- StreamSubscription stderrSubscription;
-
- // This timer is used to close stdio to the subprocess once we got
- // the exitCode. Sometimes descendants of the subprocess keep stdio
- // handles alive even though the direct subprocess is dead.
- Timer watchdogTimer;
-
- void closeStdout([_]){
- if (!stdoutIsDone) {
- stdoutDone.complete();
- stdoutIsDone = true;
-
- if (stderrIsDone && watchdogTimer != null) {
- watchdogTimer.cancel();
- }
- }
- }
-
- void closeStderr([_]) {
- if (!stderrIsDone) {
- stderrDone.complete();
- stderrIsDone = true;
-
- if (stdoutIsDone && watchdogTimer != null) {
- watchdogTimer.cancel();
- }
- }
- }
-
- stdoutSubscription =
- process.stdout.transform(UTF8.decoder).listen((data) {
- _addStdout(data);
- }, onError: (error) {
- // This should _never_ happen, but we really want this in the log
- // if it actually does due to dart:io or vm bug.
- _logEvent("An error occured in the process stdout handling: $error");
- }, onDone: closeStdout);
-
- stderrSubscription =
- process.stderr.transform(UTF8.decoder).listen((data) {
- _addStderr(data);
- }, onError: (error) {
- // This should _never_ happen, but we really want this in the log
- // if it actually does due to dart:io or vm bug.
- _logEvent("An error occured in the process stderr handling: $error");
- }, onDone: closeStderr);
-
- process.exitCode.then((exitCode) {
- _logEvent("Browser closed with exitcode $exitCode");
-
- if (!stdoutIsDone || !stderrIsDone) {
- watchdogTimer = new Timer(MAX_STDIO_DELAY, () {
- DebugLogger.warning(
- "$MAX_STDIO_DELAY_PASSED_MESSAGE (browser: $this)");
- watchdogTimer = null;
- stdoutSubscription.cancel();
- stderrSubscription.cancel();
- closeStdout();
- closeStderr();
- });
- }
-
- Future.wait([stdoutDone.future, stderrDone.future]).then((_) {
- process = null;
- if (_cleanup != null) {
- _cleanup();
- }
- }).catchError((error) {
- _logEvent("Error closing browsers: $error");
- }).whenComplete(() => doneCompleter.complete(true));
- });
- return true;
- }).catchError((error) {
- _logEvent("Running $command $arguments failed with $error");
- return false;
- });
- }
-
- /**
- * Get the output that was written so far to stdout/stderr/eventLog.
- */
- BrowserOutput get allBrowserOutput => _allBrowserOutput;
- BrowserOutput get testBrowserOutput => _testBrowserOutput;
-
- void resetTestBrowserOutput() {
- _testBrowserOutput = new BrowserOutput();
- }
-
- /**
- * Add useful info about the browser to the _testBrowserOutput.stdout,
- * where it will be reported for failing tests. Used to report which
- * android device a failing test is running on.
- */
- void logBrowserInfoToTestBrowserOutput() { }
-
- String toString();
-
- /** Starts the browser loading the given url */
- Future<bool> start(String url);
-}
-
-class Safari extends Browser {
- /**
- * We get the safari version by parsing a version file
- */
- static const String versionFile =
- "/Applications/Safari.app/Contents/version.plist";
-
- /**
- * 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"];
-
-
- 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;
- });
- }
-
- 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 {
- _logEvent("${paths.current} is not present");
- return deleteIfExists(paths);
- }
- });
- }
-
- // 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() {
- /**
- * Example of the file:
- * <?xml version="1.0" encoding="UTF-8"?>
- * <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
- * <plist version="1.0">
- * <dict>
- * <key>BuildVersion</key>
- * <string>2</string>
- * <key>CFBundleShortVersionString</key>
- * <string>6.0.4</string>
- * <key>CFBundleVersion</key>
- * <string>8536.29.13</string>
- * <key>ProjectName</key>
- * <string>WebBrowser</string>
- * <key>SourceVersion</key>
- * <string>7536029013000000</string>
- * </dict>
- * </plist>
- */
- File f = new File(versionFile);
- return f.readAsLines().then((content) {
- bool versionOnNextLine = false;
- for (var line in content) {
- if (versionOnNextLine) return line;
- if (line.contains("CFBundleShortVersionString")) {
- versionOnNextLine = true;
- }
- }
- return null;
- });
- }
-
- void _createLaunchHTML(var path, var url) {
- var file = new File("${path}/launch.html");
- var randomFile = file.openSync(mode: FileMode.WRITE);
- var content = '<script language="JavaScript">location = "$url"</script>';
- randomFile.writeStringSync(content);
- randomFile.close();
- }
-
- Future<bool> start(String url) {
- _logEvent("Starting Safari browser on: $url");
- 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 startBrowser(_binary, args);
- });
- }).catchError((error) {
- _logEvent("Running $_binary --version failed with $error");
- return false;
- });
- });
- });
- }
-
- String toString() => "Safari";
-}
-
-
-class Chrome extends Browser {
- String _version = "Version not found yet";
-
- Map<String, String> _getEnvironment() => null;
-
- Future<bool> _getVersion() {
- if (Platform.isWindows) {
- // The version flag does not work on windows.
- // See issue:
- // https://code.google.com/p/chromium/issues/detail?id=158372
- // The registry hack does not seem to work.
- _version = "Can't get version on windows";
- // We still validate that the binary exists so that we can give good
- // feedback.
- return new File(_binary).exists().then((exists) {
- if (!exists) {
- _logEvent("Chrome binary not available.");
- _logEvent("Make sure $_binary is a valid program for running chrome");
- }
- return exists;
- });
- }
- return Process.run(_binary, ["--version"]).then((var versionResult) {
- if (versionResult.exitCode != 0) {
- _logEvent("Failed to chrome get version");
- _logEvent("Make sure $_binary is a valid program for running chrome");
- return false;
- }
- _version = versionResult.stdout;
- return true;
- });
- }
-
-
- Future<bool> start(String url) {
- _logEvent("Starting chrome browser on: $url");
- // Get the version and log that.
- return _getVersion().then((success) {
- if (!success) return false;
- _logEvent("Got version: $_version");
-
- return Directory.systemTemp.createTemp().then((userDir) {
- _cleanup = () { userDir.deleteSync(recursive: true); };
- var args = ["--user-data-dir=${userDir.path}", url,
- "--disable-extensions", "--disable-popup-blocking",
- "--bwsi", "--no-first-run"];
- return startBrowser(_binary, args, environment: _getEnvironment());
- });
- }).catchError((e) {
- _logEvent("Running $_binary --version failed with $e");
- return false;
- });
- }
-
- String toString() => "Chrome";
-}
-
-
-class SafariMobileSimulator extends Safari {
- /**
- * Directories where safari simulator stores state. We delete these if the
- * deleteCache is set
- */
- static const List<String> CACHE_DIRECTORIES =
- const ["Library/Application Support/iPhone Simulator/7.1/Applications"];
-
- // 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<bool> start(String url) {
- _logEvent("Starting safari mobile simulator browser on: $url");
- return clearCache().then((success) {
- if (!success) {
- _logEvent("Could not clear cache, exiting");
- return false;
- }
- var args = ["-SimulateApplication",
- "/Applications/Xcode.app/Contents/Developer/Platforms/"
- "iPhoneSimulator.platform/Developer/SDKs/"
- "iPhoneSimulator7.1.sdk/Applications/MobileSafari.app/"
- "MobileSafari",
- "-u", url];
- return startBrowser(_binary, args)
- .catchError((e) {
- _logEvent("Running $_binary --version failed with $e");
- return false;
- });
- });
- }
-
- String toString() => "SafariMobileSimulator";
-}
-
-
-class Dartium extends Chrome {
- final bool checkedMode;
-
- Dartium(this.checkedMode);
-
- Map<String, String> _getEnvironment() {
- var environment = new Map<String,String>.from(Platform.environment);
- // By setting this environment variable, dartium will forward "print()"
- // calls in dart to the top-level javascript function "dartPrint()" if
- // available.
- environment['DART_FORWARDING_PRINT'] = '1';
- if (checkedMode) {
- environment['DART_FLAGS'] = '--checked';
- }
- return environment;
- }
-
- String toString() => "Dartium";
-}
-
-class IE extends Browser {
- Future<String> getVersion() {
- var args = ["query",
- "HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Internet Explorer",
- "/v",
- "svcVersion"];
- return Process.run("reg", args).then((result) {
- if (result.exitCode == 0) {
- // The string we get back looks like this:
- // HKEY_LOCAL_MACHINE\Software\Microsoft\Internet Explorer
- // version REG_SZ 9.0.8112.16421
- var findString = "REG_SZ";
- var index = result.stdout.indexOf(findString);
- if (index > 0) {
- return result.stdout.substring(index + findString.length).trim();
- }
- }
- return "Could not get the version of internet explorer";
- });
- }
-
- // 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\\"
- "Internet Explorer\\Recovery");
- return dir.delete(recursive: true)
- .then((_) { return true; })
- .catchError((error) {
- _logEvent("Deleting recovery dir failed with $error");
- return false;
- });
- }
-
- Future<bool> start(String url) {
- _logEvent("Starting ie browser on: $url");
- return clearCache().then((_) => getVersion()).then((version) {
- _logEvent("Got version: $version");
- return startBrowser(_binary, [url]);
- });
- }
- String toString() => "IE";
-
-}
-
-
-class AndroidBrowserConfig {
- final String name;
- final String package;
- final String activity;
- final String action;
- AndroidBrowserConfig(this.name, this.package, this.activity, this.action);
-}
-
-
-final contentShellOnAndroidConfig = new AndroidBrowserConfig(
- 'ContentShellOnAndroid',
- 'org.chromium.content_shell_apk',
- '.ContentShellActivity',
- 'android.intent.action.VIEW');
-
-
-final dartiumOnAndroidConfig = new AndroidBrowserConfig(
- 'DartiumOnAndroid',
- 'com.google.android.apps.chrome',
- '.Main',
- 'android.intent.action.VIEW');
-
-
-class AndroidBrowser extends Browser {
- final bool checkedMode;
- AdbDevice _adbDevice;
- AndroidBrowserConfig _config;
-
- AndroidBrowser(this._adbDevice, this._config, this.checkedMode, apkPath) {
- _binary = apkPath;
- }
-
- Future<bool> start(String url) {
- var intent = new Intent(
- _config.action, _config.package, _config.activity, url);
- return _adbDevice.waitForBootCompleted().then((_) {
- return _adbDevice.forceStop(_config.package);
- }).then((_) {
- return _adbDevice.killAll();
- }).then((_) {
- return _adbDevice.adbRoot();
- }).then((_) {
- return _adbDevice.setProp("DART_FORWARDING_PRINT", "1");
- }).then((_) {
- if (checkedMode) {
- return _adbDevice.setProp("DART_FLAGS", "--checked");
- } else {
- return _adbDevice.setProp("DART_FLAGS", "");
- }
- }).then((_) {
- return _adbDevice.installApk(new Path(_binary));
- }).then((_) {
- return _adbDevice.startActivity(intent).then((_) => true);
- });
- }
-
- Future<bool> close() {
- if (_adbDevice != null) {
- return _adbDevice.forceStop(_config.package).then((_) {
- return _adbDevice.killAll().then((_) => true);
- });
- }
- return new Future.value(true);
- }
-
- void logBrowserInfoToTestBrowserOutput() {
- _testBrowserOutput.stdout.write(
- 'Android device id: ${_adbDevice.deviceId}\n');
- }
-
- String toString() => _config.name;
-}
-
-
-class AndroidChrome extends Browser {
- static const String viewAction = 'android.intent.action.VIEW';
- static const String mainAction = 'android.intent.action.MAIN';
- static const String chromePackage = 'com.android.chrome';
- static const String browserPackage = 'com.android.browser';
- static const String firefoxPackage = 'org.mozilla.firefox';
- static const String turnScreenOnPackage = 'com.google.dart.turnscreenon';
-
- AndroidEmulator _emulator;
- AdbDevice _adbDevice;
-
- AndroidChrome(this._adbDevice);
-
- Future<bool> start(String url) {
- var browserIntent = new Intent(
- viewAction, browserPackage, '.BrowserActivity', url);
- var chromeIntent = new Intent(viewAction, chromePackage, '.Main', url);
- var firefoxIntent = new Intent(viewAction, firefoxPackage, '.App', url);
- var turnScreenOnIntent =
- new Intent(mainAction, turnScreenOnPackage, '.Main');
-
- var testing_resources_dir =
- new Path('third_party/android_testing_resources');
- if (!new Directory(testing_resources_dir.toNativePath()).existsSync()) {
- DebugLogger.error("$testing_resources_dir doesn't exist. Exiting now.");
- exit(1);
- }
-
- var chromeAPK = testing_resources_dir.append('com.android.chrome-1.apk');
- var turnScreenOnAPK = testing_resources_dir.append('TurnScreenOn.apk');
- var chromeConfDir = testing_resources_dir.append('chrome_configuration');
- var chromeConfDirRemote = new Path('/data/user/0/com.android.chrome/');
-
- return _adbDevice.waitForBootCompleted().then((_) {
- return _adbDevice.forceStop(chromeIntent.package);
- }).then((_) {
- return _adbDevice.killAll();
- }).then((_) {
- return _adbDevice.adbRoot();
- }).then((_) {
- return _adbDevice.installApk(turnScreenOnAPK);
- }).then((_) {
- return _adbDevice.installApk(chromeAPK);
- }).then((_) {
- return _adbDevice.pushData(chromeConfDir, chromeConfDirRemote);
- }).then((_) {
- return _adbDevice.chmod('777', chromeConfDirRemote);
- }).then((_) {
- return _adbDevice.startActivity(turnScreenOnIntent).then((_) => true);
- }).then((_) {
- return _adbDevice.startActivity(chromeIntent).then((_) => true);
- });
- }
-
- Future<bool> close() {
- if (_adbDevice != null) {
- return _adbDevice.forceStop(chromePackage).then((_) {
- return _adbDevice.killAll().then((_) => true);
- });
- }
- return new Future.value(true);
- }
-
- void logBrowserInfoToTestBrowserOutput() {
- _testBrowserOutput.stdout.write(
- 'Android device id: ${_adbDevice.deviceId}\n');
- }
-
- String toString() => "chromeOnAndroid";
-}
-
-
-class Firefox extends Browser {
- static const String enablePopUp =
- 'user_pref("dom.disable_open_during_load", false);';
- static const String disableDefaultCheck =
- 'user_pref("browser.shell.checkDefaultBrowser", false);';
- static const String disableScriptTimeLimit =
- 'user_pref("dom.max_script_run_time", 0);';
-
- void _createPreferenceFile(var path) {
- var file = new File("${path.toString()}/user.js");
- var randomFile = file.openSync(mode: FileMode.WRITE);
- randomFile.writeStringSync(enablePopUp);
- randomFile.writeStringSync(disableDefaultCheck);
- randomFile.writeStringSync(disableScriptTimeLimit);
- randomFile.close();
- }
-
- Future<bool> start(String url) {
- _logEvent("Starting firefox browser on: $url");
- // Get the version and log that.
- return Process.run(_binary, ["--version"]).then((var versionResult) {
- if (versionResult.exitCode != 0) {
- _logEvent("Failed to firefox get version");
- _logEvent("Make sure $_binary is a valid program for running firefox");
- return new Future.value(false);
- }
- version = versionResult.stdout;
- _logEvent("Got version: $version");
-
- return Directory.systemTemp.createTemp().then((userDir) {
- _createPreferenceFile(userDir.path);
- _cleanup = () { userDir.deleteSync(recursive: true); };
- var args = ["-profile", "${userDir.path}",
- "-no-remote", "-new-instance", url];
- return startBrowser(_binary, args);
-
- });
- }).catchError((e) {
- _logEvent("Running $_binary --version failed with $e");
- return false;
- });
- }
-
- String toString() => "Firefox";
-}
-
-
-/**
- * Describes the current state of a browser used for testing.
- */
-class BrowserTestingStatus {
- Browser browser;
- BrowserTest currentTest;
-
- // This is currently not used for anything except for error reporting.
- // Given the usefulness of this in debugging issues this should not be
- // removed even when we have a really stable system.
- BrowserTest lastTest;
- bool timeout = false;
- Timer nextTestTimeout;
- Stopwatch timeSinceRestart = new Stopwatch();
-
- BrowserTestingStatus(Browser this.browser);
-}
-
-
-/**
- * Describes a single test to be run in the browser.
- */
-class BrowserTest {
- // TODO(ricow): Add timeout callback instead of the string passing hack.
- Function doneCallback;
- String url;
- int timeout;
- String lastKnownMessage = '';
- Stopwatch stopwatch;
-
- // This might be null
- Duration delayUntilTestStarted;
-
- // We store this here for easy access when tests time out (instead of
- // capturing this in a closure)
- Timer timeoutTimer;
-
- // Used for debugging, this is simply a unique identifier assigned to each
- // test.
- int id;
- static int _idCounter = 0;
-
- BrowserTest(this.url, this.doneCallback, this.timeout) {
- id = _idCounter++;
- }
-
- String toJSON() => JSON.encode({'url': url,
- 'id': id,
- 'isHtmlTest': false});
-}
-
-
-/**
- * Describes a test with a custom HTML page to be run in the browser.
- */
-class HtmlTest extends BrowserTest {
- List<String> expectedMessages;
-
- HtmlTest(url, doneCallback, timeout, this.expectedMessages)
- : super(url, doneCallback, timeout) { }
-
- String toJSON() => JSON.encode({'url': url,
- 'id': id,
- 'isHtmlTest': true,
- 'expectedMessages': expectedMessages});
-}
-
-
-/* Describes the output of running the test in a browser */
-class BrowserTestOutput {
- final Duration delayUntilTestStarted;
- final Duration duration;
-
- final String lastKnownMessage;
-
- final BrowserOutput browserOutput;
- final bool didTimeout;
-
- BrowserTestOutput(
- this.delayUntilTestStarted, this.duration, this.lastKnownMessage,
- this.browserOutput, {this.didTimeout: false});
-}
-
-/**
- * Encapsulates all the functionality for running tests in browsers.
- * The interface is rather simple. After starting, the runner tests
- * are simply added to the queue and a the supplied callbacks are called
- * whenever a test completes.
- */
-class BrowserTestRunner {
- static const int MAX_NEXT_TEST_TIMEOUTS = 10;
- static const Duration NEXT_TEST_TIMEOUT = const Duration(seconds: 60);
- static const Duration RESTART_BROWSER_INTERVAL = const Duration(seconds: 60);
-
- final Map configuration;
-
- final String localIp;
- String browserName;
- final int maxNumBrowsers;
- bool checkedMode;
- // Used to send back logs from the browser (start, stop etc)
- Function logger;
- int browserIdCount = 0;
-
- bool underTermination = false;
- int numBrowserGetTestTimeouts = 0;
-
- List<BrowserTest> testQueue = new List<BrowserTest>();
- Map<String, BrowserTestingStatus> browserStatus =
- new Map<String, BrowserTestingStatus>();
-
- var adbDeviceMapping = new Map<String, AdbDevice>();
- // This cache is used to guarantee that we never see double reporting.
- // If we do we need to provide developers with this information.
- // We don't add urls to the cache until we have run it.
- Map<int, String> testCache = new Map<int, String>();
- Map<int, String> doubleReportingOutputs = new Map<int, String>();
-
- BrowserTestingServer testingServer;
-
- /**
- * The TestRunner takes the testingServer in as a constructor parameter in
- * case we wish to have a testing server with different behavior (such as the
- * case for performance testing.
- */
- BrowserTestRunner(this.configuration,
- this.localIp,
- this.browserName,
- this.maxNumBrowsers,
- {BrowserTestingServer this.testingServer}) {
- checkedMode = configuration['checked'];
- }
-
- Future<bool> start() {
- // If [browserName] doesn't support opening new windows, we use new iframes
- // instead.
- bool useIframe =
- !Browser.BROWSERS_WITH_WINDOW_SUPPORT.contains(browserName);
- if (testingServer == null) {
- testingServer = new BrowserTestingServer(
- configuration, localIp, useIframe);
- }
- return testingServer.start().then((_) {
- testingServer.testDoneCallBack = handleResults;
- testingServer.testStatusUpdateCallBack = handleStatusUpdate;
- testingServer.testStartedCallBack = handleStarted;
- testingServer.nextTestCallBack = getNextTest;
- return getBrowsers().then((browsers) {
- var futures = [];
- for (var browser in browsers) {
- var url = testingServer.getDriverUrl(browser.id);
- var future = browser.start(url).then((success) {
- if (success) {
- var status = new BrowserTestingStatus(browser);
- browserStatus[browser.id] = status;
- status.nextTestTimeout = createNextTestTimer(status);
- status.timeSinceRestart.start();
- }
- return success;
- });
- futures.add(future);
- }
- return Future.wait(futures).then((values) {
- return !values.contains(false);
- });
- });
- });
- }
-
- Future<List<Browser>> getBrowsers() {
- // TODO(kustermann): This is a hackisch way to accomplish it and should
- // be encapsulated
- var browsersCompleter = new Completer();
- var androidBrowserCreationMapping = {
- 'chromeOnAndroid' : (AdbDevice device) => new AndroidChrome(device),
- 'ContentShellOnAndroid' : (AdbDevice device) => new AndroidBrowser(
- device,
- contentShellOnAndroidConfig,
- checkedMode,
- configuration['drt']),
- 'DartiumOnAndroid' : (AdbDevice device) => new AndroidBrowser(
- device,
- dartiumOnAndroidConfig,
- checkedMode,
- configuration['dartium']),
- };
- if (androidBrowserCreationMapping.containsKey(browserName)) {
- AdbHelper.listDevices().then((deviceIds) {
- if (deviceIds.length > 0) {
- var browsers = [];
- for (int i = 0; i < deviceIds.length; i++) {
- var id = "BROWSER$i";
- var device = new AdbDevice(deviceIds[i]);
- adbDeviceMapping[id] = device;
- var browser = androidBrowserCreationMapping[browserName](device);
- browsers.add(browser);
- // We store this in case we need to kill the browser.
- browser.id = id;
- }
- browsersCompleter.complete(browsers);
- } else {
- throw new StateError("No android devices found.");
- }
- });
- } else {
- var browsers = [];
- for (int i = 0; i < maxNumBrowsers; i++) {
- var id = "BROWSER$browserIdCount";
- browserIdCount++;
- var browser = getInstance();
- browsers.add(browser);
- // We store this in case we need to kill the browser.
- browser.id = id;
- }
- browsersCompleter.complete(browsers);
- }
- return browsersCompleter.future;
- }
-
- var timedOut = [];
-
- void handleResults(String browserId, String output, int testId) {
- var status = browserStatus[browserId];
- if (testCache.containsKey(testId)) {
- doubleReportingOutputs[testId] = output;
- return;
- }
-
- if (status == null || status.timeout) {
- // We don't do anything, this browser is currently being killed and
- // replaced. The browser here can be null if we decided to kill the
- // browser.
- } else if (status.currentTest != null) {
- status.currentTest.timeoutTimer.cancel();
- status.currentTest.stopwatch.stop();
-
- if (status.currentTest.id != testId) {
- print("Expected test id ${status.currentTest.id} for"
- "${status.currentTest.url}");
- print("Got test id ${testId}");
- print("Last test id was ${status.lastTest.id} for "
- "${status.currentTest.url}");
- throw("This should never happen, wrong test id");
- }
- testCache[testId] = status.currentTest.url;
-
- // Report that the test is finished now
- var browserTestOutput = new BrowserTestOutput(
- status.currentTest.delayUntilTestStarted,
- status.currentTest.stopwatch.elapsed,
- output,
- status.browser.testBrowserOutput);
- status.currentTest.doneCallback(browserTestOutput);
-
- status.lastTest = status.currentTest;
- status.currentTest = null;
- status.nextTestTimeout = createNextTestTimer(status);
- } else {
- print("\nThis is bad, should never happen, handleResult no test");
- print("URL: ${status.lastTest.url}");
- print(output);
- terminate().then((_) {
- exit(1);
- });
- }
- }
-
- void handleStatusUpdate(String browserId, String output, int testId) {
- var status = browserStatus[browserId];
-
- if (status == null || status.timeout) {
- // We don't do anything, this browser is currently being killed and
- // replaced. The browser here can be null if we decided to kill the
- // browser.
- } else if (status.currentTest != null && status.currentTest.id == testId) {
- status.currentTest.lastKnownMessage = output;
- }
- }
-
- void handleStarted(String browserId, String output, int testId) {
- var status = browserStatus[browserId];
-
- if (status != null && !status.timeout && status.currentTest != null) {
- status.currentTest.timeoutTimer.cancel();
- status.currentTest.timeoutTimer =
- createTimeoutTimer(status.currentTest, status);
- status.currentTest.delayUntilTestStarted =
- status.currentTest.stopwatch.elapsed;
- }
- }
-
- void handleTimeout(BrowserTestingStatus status) {
- // We simply kill the browser and starts up a new one!
- // We could be smarter here, but it does not seems like it is worth it.
- if (status.timeout) {
- DebugLogger.error(
- "Got test timeout for an already restarting browser");
- return;
- }
- status.timeout = true;
- timedOut.add(status.currentTest.url);
- var id = status.browser.id;
-
- status.currentTest.stopwatch.stop();
- status.browser.close().then((_) {
- var lastKnownMessage =
- 'Dom could not be fetched, since the test timed out.';
- if (status.currentTest.lastKnownMessage.length > 0) {
- lastKnownMessage = status.currentTest.lastKnownMessage;
- }
- // Wait until the browser is closed before reporting the test as timeout.
- // This will enable us to capture stdout/stderr from the browser
- // (which might provide us with information about what went wrong).
- var browserTestOutput = new BrowserTestOutput(
- status.currentTest.delayUntilTestStarted,
- status.currentTest.stopwatch.elapsed,
- lastKnownMessage,
- status.browser.testBrowserOutput,
- didTimeout: true);
- status.currentTest.doneCallback(browserTestOutput);
- status.lastTest = status.currentTest;
- status.currentTest = null;
-
- // We don't want to start a new browser if we are terminating.
- if (underTermination) return;
- restartBrowser(id);
- });
- }
-
- void restartBrowser(String id) {
- var browser;
- var new_id = id;
- if (browserName == 'chromeOnAndroid') {
- browser = new AndroidChrome(adbDeviceMapping[id]);
- } else if (browserName == 'ContentShellOnAndroid') {
- browser = new AndroidBrowser(adbDeviceMapping[id],
- contentShellOnAndroidConfig,
- checkedMode,
- configuration['drt']);
- } else if (browserName == 'DartiumOnAndroid') {
- browser = new AndroidBrowser(adbDeviceMapping[id],
- dartiumOnAndroidConfig,
- checkedMode,
- configuration['dartium']);
- } else {
- browserStatus.remove(id);
- browser = getInstance();
- new_id = "BROWSER$browserIdCount";
- browserIdCount++;
- }
- browser.id = new_id;
- var status = new BrowserTestingStatus(browser);
- browserStatus[new_id] = status;
- status.nextTestTimeout = createNextTestTimer(status);
- status.timeSinceRestart.start();
- browser.start(testingServer.getDriverUrl(new_id)).then((success) {
- // We may have started terminating in the mean time.
- if (underTermination) {
- if (status.nextTestTimeout != null) {
- status.nextTestTimeout.cancel();
- status.nextTestTimeout = null;
- }
- browser.close().then((success) {
- // We should never hit this, print it out.
- if (!success) {
- print("Could not kill browser ($id) started due to timeout");
- }
- });
- return;
- }
- if (!success) {
- // TODO(ricow): Handle this better.
- print("This is bad, should never happen, could not start browser");
- exit(1);
- }
- });
- }
-
- BrowserTest getNextTest(String browserId) {
- var status = browserStatus[browserId];
- if (status == null) return null;
- if (status.nextTestTimeout != null) {
- status.nextTestTimeout.cancel();
- status.nextTestTimeout = null;
- }
- if (testQueue.isEmpty) return null;
-
- // We are currently terminating this browser, don't start a new test.
- if (status.timeout) return null;
-
- // Restart content_shell and dartium on Android if they have been
- // running for longer than RESTART_BROWSER_INTERVAL. The tests have
- // had flaky timeouts, and this may help.
- if ((browserName == 'ContentShellOnAndroid' ||
- browserName == 'DartiumOnAndroid' ) &&
- status.timeSinceRestart.elapsed > RESTART_BROWSER_INTERVAL) {
- var id = status.browser.id;
- // Reset stopwatch so we don't trigger again before restarting.
- status.timeout = true;
- status.browser.close().then((_) {
- // We don't want to start a new browser if we are terminating.
- if (underTermination) return;
- restartBrowser(id);
- });
- // Don't send a test to the browser we are restarting.
- return null;
- }
-
- BrowserTest test = testQueue.removeLast();
- if (status.currentTest == null) {
- status.currentTest = test;
- status.currentTest.lastKnownMessage = '';
- } else {
- // TODO(ricow): Handle this better.
- print("Browser requested next test before reporting previous result");
- print("This happened for browser $browserId");
- print("Old test was: ${status.currentTest.url}");
- print("The test before that was: ${status.lastTest.url}");
- print("Timed out tests:");
- for (var v in timedOut) {
- print(" $v");
- }
- exit(1);
- }
-
- status.currentTest.timeoutTimer = createTimeoutTimer(test, status);
- status.currentTest.stopwatch = new Stopwatch()..start();
-
- // Reset the test specific output information (stdout, stderr) on the
- // browser, since a new test is being started.
- status.browser.resetTestBrowserOutput();
- status.browser.logBrowserInfoToTestBrowserOutput();
-
- return test;
- }
-
- Timer createTimeoutTimer(BrowserTest test, BrowserTestingStatus status) {
- return new Timer(new Duration(seconds: test.timeout),
- () { handleTimeout(status); });
- }
-
- Timer createNextTestTimer(BrowserTestingStatus status) {
- return new Timer(BrowserTestRunner.NEXT_TEST_TIMEOUT,
- () { handleNextTestTimeout(status); });
- }
-
- void handleNextTestTimeout(status) {
- DebugLogger.warning(
- "Browser timed out before getting next test. Restarting");
- if (status.timeout) return;
- numBrowserGetTestTimeouts++;
- if (numBrowserGetTestTimeouts >= MAX_NEXT_TEST_TIMEOUTS) {
- DebugLogger.error(
- "Too many browser timeouts before getting next test. Terminating");
- terminate().then((_) => exit(1));
- } else {
- status.timeout = true;
- status.browser.close().then((_) => restartBrowser(status.browser.id));
- }
- }
-
- void queueTest(BrowserTest test) {
- testQueue.add(test);
- }
-
- void printDoubleReportingTests() {
- if (doubleReportingOutputs.length == 0) return;
- // TODO(ricow): die on double reporting.
- // Currently we just report this here, we could have a callback to the
- // encapsulating environment.
- print("");
- print("Double reporting tests");
- for (var id in doubleReportingOutputs.keys) {
- print(" ${testCache[id]}");
- }
-
- DebugLogger.warning("Double reporting tests:");
- for (var id in doubleReportingOutputs.keys) {
- DebugLogger.warning("${testCache[id]}, output: ");
- DebugLogger.warning("${doubleReportingOutputs[id]}");
- DebugLogger.warning("");
- DebugLogger.warning("");
- }
- }
-
- Future<bool> terminate() {
- var browsers = [];
- underTermination = true;
- testingServer.underTermination = true;
- for (BrowserTestingStatus status in browserStatus.values) {
- browsers.add(status.browser);
- if (status.nextTestTimeout != null) {
- status.nextTestTimeout.cancel();
- status.nextTestTimeout = null;
- }
- }
- // Success if all the browsers closed successfully.
- bool success = true;
- Future closeBrowser(Browser b) {
- return b.close().then((bool closeSucceeded) {
- if (!closeSucceeded) {
- success = false;
- }
- });
- }
- return Future.forEach(browsers, closeBrowser).then((_) {
- testingServer.errorReportingServer.close();
- printDoubleReportingTests();
- return success;
- });
- }
-
- Browser getInstance() {
- if (browserName == 'ff') browserName = 'firefox';
- var path = Locations.getBrowserLocation(browserName, configuration);
- var browser = new Browser.byName(browserName, path, checkedMode);
- browser.logger = logger;
- return browser;
- }
-}
-
-class BrowserTestingServer {
- final Map configuration;
- /// Interface of the testing server:
- ///
- /// GET /driver/BROWSER_ID -- This will get the driver page to fetch
- /// and run tests ...
- /// GET /next_test/BROWSER_ID -- returns "WAIT" "TERMINATE" or "url#id"
- /// where url is the test to run, and id is the id of the test.
- /// If there are currently no available tests the waitSignal is send
- /// back. If we are in the process of terminating the terminateSignal
- /// is send back and the browser will stop requesting new tasks.
- /// POST /report/BROWSER_ID?id=NUM -- sends back the dom of the executed
- /// test
-
- final String localIp;
-
- static const String driverPath = "/driver";
- static const String nextTestPath = "/next_test";
- static const String reportPath = "/report";
- static const String statusUpdatePath = "/status_update";
- static const String startedPath = "/started";
- static const String waitSignal = "WAIT";
- static const String terminateSignal = "TERMINATE";
-
- 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);
-
- Future start() {
- var test_driver_error_port = configuration['test_driver_error_port'];
- return HttpServer.bind(localIp, test_driver_error_port)
- .then(setupErrorServer)
- .then(setupDispatchingServer);
- }
-
- void setupErrorServer(HttpServer server) {
- errorReportingServer = server;
- void errorReportingHandler(HttpRequest request) {
- StringBuffer buffer = new StringBuffer();
- request.transform(UTF8.decoder).listen((data) {
- buffer.write(data);
- }, onDone: () {
- String back = buffer.toString();
- request.response.headers.set("Access-Control-Allow-Origin", "*");
- request.response.done.catchError((error) {
- DebugLogger.error("Error getting error from browser"
- "on uri ${request.uri.path}: $error");
- });
- request.response.close();
- DebugLogger.error("Error from browser on : "
- "${request.uri.path}, data: $back");
- }, onError: (error) { print(error); });
- }
- void errorHandler(e) {
- if (!underTermination) print("Error occured in httpserver: $e");
- }
- errorReportingServer.listen(errorReportingHandler, onError: errorHandler);
- }
-
- void setupDispatchingServer(_) {
- DispatchingServer server = configuration['_servers_'].server;
- void noCache(request) {
- request.response.headers.set("Cache-Control",
- "no-cache, no-store, must-revalidate");
- }
- int testId(request) =>
- int.parse(request.uri.queryParameters["id"]);
- String browserId(request, prefix) =>
- request.uri.path.substring(prefix.length + 1);
-
-
- server.addHandler(reportPath, (HttpRequest request) {
- noCache(request);
- handleReport(request, browserId(request, reportPath),
- testId(request), isStatusUpdate: false);
- });
- server.addHandler(statusUpdatePath, (HttpRequest request) {
- noCache(request);
- handleReport(request, browserId(request, statusUpdatePath),
- testId(request), isStatusUpdate: true);
- });
- server.addHandler(startedPath, (HttpRequest request) {
- noCache(request);
- handleStarted(request, browserId(request, startedPath),
- testId(request));
- });
-
- 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";
- }
- });
- };
- server.addHandler(driverPath, makeSendPageHandler(driverPath));
- server.addHandler(nextTestPath, makeSendPageHandler(nextTestPath));
- }
-
- void handleReport(HttpRequest request, String browserId, var testId,
- {bool isStatusUpdate}) {
- StringBuffer buffer = new StringBuffer();
- request.transform(UTF8.decoder).listen((data) {
- buffer.write(data);
- }, onDone: () {
- String back = buffer.toString();
- request.response.close();
- if (isStatusUpdate) {
- testStatusUpdateCallBack(browserId, back, testId);
- } else {
- testDoneCallBack(browserId, back, testId);
- }
- // TODO(ricow): We should do something smart if we get an error here.
- }, onError: (error) { DebugLogger.error("$error"); });
- }
-
- void handleStarted(HttpRequest request, String browserId, var testId) {
- StringBuffer buffer = new StringBuffer();
- // If an error occurs while receiving the data from the request stream,
- // we don't handle it specially. We can safely ignore it, since the started
- // events are not crucial.
- request.transform(UTF8.decoder).listen((data) {
- buffer.write(data);
- }, onDone: () {
- String back = buffer.toString();
- request.response.close();
- testStartedCallBack(browserId, back, testId);
- }, onError: (error) { DebugLogger.error("$error"); });
- }
-
- String getNextTest(String browserId) {
- var nextTest = nextTestCallBack(browserId);
- if (underTermination) {
- // Browsers will be killed shortly, send them a terminate signal so
- // that they stop pulling.
- return terminateSignal;
- }
- return nextTest == null ? waitSignal : nextTest.toJSON();
- }
-
- String getDriverUrl(String browserId) {
- if (errorReportingServer == null) {
- print("Bad browser testing server, you are not started yet. Can't "
- "produce driver url");
- exit(1);
- // This should never happen - exit immediately;
- }
- var port = configuration['_servers_'].port;
- return "http://$localIp:$port/driver/$browserId";
- }
-
-
- String getDriverPage(String browserId) {
- var errorReportingUrl =
- "http://$localIp:${errorReportingServer.port}/$browserId";
- String driverContent = """
-<!DOCTYPE html><html>
-<head>
- <style>
- 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;
-
- function startTesting() {
- var number_of_tests = 0;
- var current_id;
- var next_id;
-
- // Has the test in the current iframe reported that it is done?
- var test_completed = true;
- // Has the test in the current iframe reported that it is started?
- var test_started = false;
- var testing_window;
-
- var embedded_iframe_div = document.getElementById('embedded_iframe_div');
- var embedded_iframe = document.getElementById('embedded_iframe');
- var number_div = document.getElementById('number');
- var executing_div = document.getElementById('currently_executing');
- var error_div = document.getElementById('unhandled_error');
- var use_iframe = ${useIframe};
- var start = new Date();
-
- // Object that holds the state of an HTML test
- var html_test;
-
- function newTaskHandler() {
- if (this.readyState == this.DONE) {
- if (this.status == 200) {
- if (this.responseText == '$waitSignal') {
- setTimeout(getNextTask, 500);
- } else if (this.responseText == '$terminateSignal') {
- // Don't do anything, we will be killed shortly.
- } else {
- var elapsed = new Date() - start;
- var nextTask = JSON.parse(this.responseText);
- var url = nextTask.url;
- next_id = nextTask.id;
- if (nextTask.isHtmlTest) {
- html_test = {
- expected_messages: nextTask.expectedMessages,
- found_message_count: 0,
- double_received_messages: [],
- unexpected_messages: [],
- found_messages: {}
- };
- for (var i = 0; i < html_test.expected_messages.length; ++i) {
- html_test.found_messages[html_test.expected_messages[i]] = 0;
- }
- } else {
- html_test = null;
- }
- run(url);
- }
- } else {
- reportError('Could not contact the server and get a new task');
- }
- }
- }
-
- function contactBrowserController(method,
- path,
- callback,
- msg,
- isUrlEncoded) {
- var client = new XMLHttpRequest();
- client.onreadystatechange = callback;
- client.open(method, path);
- if (isUrlEncoded) {
- client.setRequestHeader('Content-type',
- 'application/x-www-form-urlencoded');
- }
- client.send(msg);
- }
-
- function getNextTask() {
- // Until we have the next task we set the current_id to a specific
- // negative value.
- contactBrowserController(
- 'GET', '$nextTestPath/$browserId', newTaskHandler, "", false);
- }
-
- function childError(message, filename, lineno, colno, error) {
- sendStatusUpdate();
- if (error) {
- reportMessage('FAIL:' + filename + ':' + lineno +
- ':' + colno + ':' + message + '\\n' + error.stack, false, false);
- } else if (filename) {
- reportMessage('FAIL:' + filename + ':' + lineno +
- ':' + colno + ':' + message, false, false);
- } else {
- reportMessage('FAIL: ' + message, false, false);
- }
- return true;
- }
-
- function setChildHandlers(e) {
- embedded_iframe.contentWindow.addEventListener('message',
- childMessageHandler,
- false);
- embedded_iframe.contentWindow.onerror = childError;
- reportMessage("First message from html test", true, false);
- html_test.handlers_installed = true;
- sendRepeatingStatusUpdate();
- }
-
- function checkChildHandlersInstalled() {
- if (!html_test.handlers_installed) {
- reportMessage("First message from html test", true, false);
- reportMessage(
- 'FAIL: Html test did not call ' +
- 'window.parent.dispatchEvent(new Event("detect_errors")) ' +
- 'as its first action', false, false);
- }
- }
-
- function run(url) {
- number_of_tests++;
- number_div.innerHTML = number_of_tests;
- executing_div.innerHTML = url;
- if (use_iframe) {
- if (html_test) {
- window.addEventListener('detect_errors', setChildHandlers, false);
- embedded_iframe.onload = checkChildHandlersInstalled;
- } else {
- embedded_iframe.onload = null;
- }
- embedded_iframe_div.removeChild(embedded_iframe);
- embedded_iframe = document.createElement('iframe');
- embedded_iframe.id = "embedded_iframe";
- embedded_iframe.style="width:100%;height:100%";
- embedded_iframe_div.appendChild(embedded_iframe);
- embedded_iframe.src = url;
- } else {
- if (typeof testing_window != 'undefined') {
- testing_window.close();
- }
- testing_window = window.open(url);
- }
- test_started = false;
- test_completed = false;
- }
-
- window.onerror = function (message, url, lineNumber) {
- if (url) {
- reportError(url + ':' + lineNumber + ':' + message);
- } else {
- reportError(message);
- }
- }
-
- function reportError(msg) {
- function handleReady() {
- if (this.readyState == this.DONE && this.status != 200) {
- var error = 'Sending back error did not succeeed: ' + this.status;
- error = error + '. Failed to send msg: ' + msg;
- error_div.innerHTML = error;
- }
- }
- contactBrowserController(
- 'POST', '$errorReportingUrl?test=1', handleReady, msg, true);
- }
-
- function reportMessage(msg, isFirstMessage, isStatusUpdate) {
- if (isFirstMessage) {
- if (test_started) {
- reportMessage(
- "FAIL: test started more than once (test reloads itself) " +
- msg, false, false);
- return;
- }
- current_id = next_id;
- test_started = true;
- contactBrowserController(
- 'POST', '$startedPath/${browserId}?id=' + current_id,
- function () {}, msg, true);
- } else if (isStatusUpdate) {
- contactBrowserController(
- 'POST', '$statusUpdatePath/${browserId}?id=' + current_id,
- function() {}, msg, true);
- } else {
- var is_double_report = test_completed;
- var retry = 0;
- test_completed = true;
-
- function reportDoneMessage() {
- contactBrowserController(
- 'POST', '$reportPath/${browserId}?id=' + current_id,
- handleReady, msg, true);
- }
-
- function handleReady() {
- if (this.readyState == this.DONE) {
- if (this.status == 200) {
- if (!is_double_report) {
- getNextTask();
- }
- } else {
- reportError('Error sending result to server. Status: ' +
- this.status + ' Retry: ' + retry);
- retry++;
- if (retry < 3) {
- setTimeout(reportDoneMessage, 1000);
- }
- }
- }
- }
-
- reportDoneMessage();
- }
- }
-
- function parseResult(result) {
- var parsedData = null;
- try {
- parsedData = JSON.parse(result);
- } catch(error) { }
- return parsedData;
- }
-
- // Browser tests send JSON messages to the driver window, handled here.
- function messageHandler(e) {
- var msg = e.data;
- if (typeof msg != 'string') return;
- var expectedSource =
- use_iframe ? embedded_iframe.contentWindow : testing_window;
- if (e.source != expectedSource) {
- reportError("Message received from old test window: " + msg);
- return;
- }
- var parsedData = parseResult(msg);
- if (parsedData) {
- // Only if the JSON message contains all required parameters,
- // will we handle it and post it back to the test controller.
- if ('message' in parsedData &&
- 'is_first_message' in parsedData &&
- 'is_status_update' in parsedData &&
- 'is_done' in parsedData) {
- var message = parsedData['message'];
- var isFirstMessage = parsedData['is_first_message'];
- var isStatusUpdate = parsedData['is_status_update'];
- var isDone = parsedData['is_done'];
- if (!isFirstMessage && !isStatusUpdate) {
- if (!isDone) {
- alert("Bug in test_controller.js: " +
- "isFirstMessage/isStatusUpdate/isDone were all false");
- }
- }
- reportMessage(message, isFirstMessage, isStatusUpdate);
- }
- }
- }
-
- function sendStatusUpdate () {
- var dom =
- embedded_iframe.contentWindow.document.documentElement.innerHTML;
- reportMessage('Status:\\n Messages received multiple times:\\n ' +
- html_test.double_received_messages +
- '\\n Unexpected messages:\\n ' +
- html_test.unexpected_messages +
- '\\n DOM:\\n ' + dom, false, true);
- }
-
- function sendRepeatingStatusUpdate() {
- sendStatusUpdate();
- setTimeout(sendRepeatingStatusUpdate, STATUS_UPDATE_INTERVAL);
- }
-
- // HTML tests post messages to their own window, handled by this handler.
- // This handler is installed on the child window when it sends the
- // 'detect_errors' event. Every HTML test must send 'detect_errors' to
- // its parent window as its first action, so all errors will be caught.
- function childMessageHandler(e) {
- var msg = e.data;
- if (typeof msg != 'string') return;
- if (msg in html_test.found_messages) {
- html_test.found_messages[msg]++;
- if (html_test.found_messages[msg] == 1) {
- html_test.found_message_count++;
- } else {
- html_test.double_received_messages.push(msg);
- sendStatusUpdate();
- }
- } else {
- html_test.unexpected_messages.push(msg);
- sendStatusUpdate();
- }
- if (html_test.found_message_count ==
- html_test.expected_messages.length) {
- reportMessage('Test done: PASS', false, false);
- }
- }
-
- if (!html_test) {
- window.addEventListener('message', messageHandler, false);
- waitForDone = false;
- }
- getNextTask();
- }
- </script>
-</head>
- <body onload="startTesting()">
- <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 style="width:100%;height:100%;" id="embedded_iframe"></iframe>
- </div>
- </body>
-</html>
-""";
- return driverContent;
- }
-}
« no previous file with comments | « tools/testing/dart/android.dart ('k') | tools/testing/dart/browser_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698