Version 2.18.0-70.0.dev
Merge commit '38438fb165f4b2c16118d76059c842589c6332f4' into 'dev'
diff --git a/pkg/test_runner/bin/launch_browser.dart b/pkg/test_runner/bin/launch_browser.dart
index a92be18..4b81a69 100644
--- a/pkg/test_runner/bin/launch_browser.dart
+++ b/pkg/test_runner/bin/launch_browser.dart
@@ -35,7 +35,6 @@
var configuration = TestConfiguration(
configuration: Configuration(
"dummy-configuration", null, null, null, runtime, null));
- var executable = configuration.browserLocation;
- var browser = Browser.byRuntime(runtime, executable);
+ var browser = Browser.fromConfiguration(configuration);
browser.start(arguments[1]);
}
diff --git a/pkg/test_runner/lib/src/browser_controller.dart b/pkg/test_runner/lib/src/browser_controller.dart
index 607c5f1..9466097 100644
--- a/pkg/test_runner/lib/src/browser_controller.dart
+++ b/pkg/test_runner/lib/src/browser_controller.dart
@@ -7,10 +7,11 @@
import 'dart:io';
import 'dart:math';
+import 'package:webdriver/io.dart';
+
import 'android.dart';
import 'configuration.dart';
import 'path.dart';
-import 'reset_safari.dart';
import 'utils.dart';
typedef BrowserDoneCallback = void Function(BrowserTestOutput output);
@@ -39,10 +40,7 @@
Function _cleanup;
/// The version of the browser - normally set when starting a browser
- String version = "";
-
- /// The path to the browser executable.
- String _binary;
+ Future<String> get version;
/// 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
@@ -63,18 +61,18 @@
/// This future returns when the process exits. It is also the return value
/// of close()
- Future done;
+ Future<bool> done;
Browser();
- factory Browser.byRuntime(Runtime runtime, String executablePath) {
+ static Browser fromConfiguration(TestConfiguration configuration) {
Browser browser;
- switch (runtime) {
+ switch (configuration.runtime) {
case Runtime.firefox:
- browser = Firefox();
+ browser = Firefox(configuration.browserLocation);
break;
case Runtime.chrome:
- browser = Chrome();
+ browser = Chrome(configuration.browserLocation);
break;
case Runtime.safari:
browser = Safari();
@@ -82,13 +80,12 @@
case Runtime.ie9:
case Runtime.ie10:
case Runtime.ie11:
- browser = IE();
+ browser = IE(configuration.browserLocation);
break;
default:
throw "unreachable";
}
- browser._binary = executablePath;
return browser;
}
@@ -131,7 +128,7 @@
_testBrowserOutput.stderr.write(output);
}
- Future close() {
+ Future<bool> close() {
_logEvent("Close called on browser");
if (process != null) {
if (process.kill(ProcessSignal.sigkill)) {
@@ -258,58 +255,87 @@
///
/// This is used by [Safari] to ensure the browser window has focus.
Future<Null> onDriverPageRequested() => Future.value();
+
+ @override
+ String toString() => '$runtimeType';
}
-class Safari extends Browser {
- /// We get the safari version by parsing a version file
- static const String versionFile =
- "/Applications/Safari.app/Contents/version.plist";
+abstract class WebDriverBrowser extends Browser {
+ static int _nextPort = 4444;
+ final int _port = _nextPort++;
+ WebDriver _driver;
- static const String safariBundleLocation = "/Applications/Safari.app/";
+ String get driverExecutable;
+ List<String> get driverArguments;
+ Map<String, dynamic> get desiredCapabilities;
- // 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;
-
- var completer = Completer<Null>();
- handleUncaughtError(error, StackTrace stackTrace) {
- if (!completer.isCompleted) {
- completer.completeError(error, stackTrace);
- } else {
- throw AsyncError(error, stackTrace);
- }
- }
-
- var parent = Zone.current;
- var specification = ZoneSpecification(
- print: (Zone self, ZoneDelegate delegate, Zone zone, String line) {
- delegate.run(parent, () {
- _logEvent(line);
- });
- });
- Future zoneWrapper() {
- var safariUri = Uri.base.resolve(safariBundleLocation);
- return 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.
- runZonedGuarded(zoneWrapper, handleUncaughtError,
- zoneSpecification: specification);
-
- try {
- await completer.future;
- return true;
- } catch (error, st) {
- _logEvent("Unable to reset Safari: $error$st");
+ @override
+ Future<bool> start(String url) async {
+ _logEvent('Starting $this browser on: $url');
+ if (!await startBrowserProcess(
+ driverExecutable, ['--port', '$_port', ...driverArguments])) {
return false;
}
+ await _createDriver();
+ await _driver.get(url);
+ try {
+ _logEvent('Got version: ${await version}');
+ } catch (error) {
+ _logEvent('Failed to get version.\nError: $error');
+ return false;
+ }
+ return true;
+ }
+
+ Future<void> _createDriver() async {
+ for (var i = 5; i >= 0; i--) {
+ // Give the driver process some time to be ready to accept connections.
+ await Future.delayed(const Duration(seconds: 1));
+ try {
+ _driver = await createDriver(
+ uri: Uri.parse('http://localhost:$_port/'),
+ desired: desiredCapabilities);
+ } catch (error) {
+ if (i > 0) {
+ _logEvent(
+ 'Failed to create driver ($i retries left).\nError: $error');
+ } else {
+ _logEvent('Failed to create driver.\nError: $error');
+ await close();
+ rethrow;
+ }
+ }
+ if (_driver != null) break;
+ }
}
- Future<String> getVersion() {
+ @override
+ Future<bool> close() async {
+ try {
+ await _driver?.quit();
+ // Give the driver process some time to be quit the browser.
+ await Future.delayed(const Duration(seconds: 1));
+ } finally {
+ process?.kill();
+ }
+ return true;
+ }
+}
+
+class Safari extends WebDriverBrowser {
+ /// We get the safari version by parsing a version file
+ static const versionFile = '/Applications/Safari.app/Contents/version.plist';
+
+ @override
+ final driverExecutable = '/usr/bin/safaridriver';
+ @override
+ final driverArguments = <String>[];
+ @override
+ final desiredCapabilities = <String, dynamic>{
+ 'browserName': 'safari',
+ };
+ @override
+ Future<String> get version async {
// 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">
@@ -327,189 +353,119 @@
// <string>7536029013000000</string>
// </dict>
// </plist>
- return File(versionFile).readAsLines().then((content) {
- var versionOnNextLine = false;
- for (var line in content) {
- if (versionOnNextLine) return line;
- if (line.contains("CFBundleShortVersionString")) {
- versionOnNextLine = true;
- }
- }
- return null;
- });
+ final versionLine = (await File(versionFile).readAsLines())
+ .skipWhile((line) => !line.contains("CFBundleShortVersionString"))
+ .skip(1)
+ .take(1);
+ return versionLine.isEmpty ? 'unknown' : versionLine.first;
}
-
- Future<Null> _createLaunchHTML(String path, String url) async {
- var file = File("$path/launch.html");
- var randomFile = await file.open(mode: FileMode.write);
- var content = '<script language="JavaScript">location = "$url"</script>';
- await randomFile.writeString(content);
- await randomFile.close();
- }
-
- Future<bool> start(String url) async {
- _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']);
- }
-
- String toString() => "Safari";
}
class Chrome extends Browser {
- String _version = "Version not found yet";
+ Chrome(this._binary);
+
+ final String _binary;
Map<String, String> _getEnvironment() => null;
- Future<bool> _getVersion() {
+ @override
+ Future<String> get version async {
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 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 "unknown on windows";
}
- 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 as String;
- return true;
- });
+ final result = await Process.run(_binary, ["--version"]);
+ if (result.exitCode != 0) {
+ _logEvent("Failed to get chrome version");
+ _logEvent("Make sure $_binary is a valid program for running chrome");
+ throw StateError(
+ "Failed to get chrome version.\nExit code: ${result.exitCode}");
+ }
+ return result.stdout as String;
}
- Future<bool> start(String url) {
+ @override
+ Future<bool> start(String url) async {
_logEvent("Starting chrome browser on: $url");
- // Get the version and log that.
- return _getVersion().then<bool>((success) {
- if (!success) return false;
- _logEvent("Got version: $_version");
-
- return Directory.systemTemp.createTemp().then((userDir) {
- _cleanup = () {
- try {
- userDir.deleteSync(recursive: true);
- } catch (e) {
- _logEvent(
- "Error: failed to delete Chrome user-data-dir ${userDir.path}"
- ", will try again in 40 seconds: $e");
- Timer(const Duration(seconds: 40), () {
- try {
- userDir.deleteSync(recursive: true);
- } catch (e) {
- _logEvent("Error: failed on second attempt to delete Chrome "
- "user-data-dir ${userDir.path}: $e");
- }
- });
- }
- };
- var args = [
- "--bwsi",
- "--disable-component-update",
- "--disable-extensions",
- "--disable-popup-blocking",
- "--no-first-run",
- "--use-mock-keychain",
- "--user-data-dir=${userDir.path}",
- url,
- ];
-
- // TODO(rnystrom): Uncomment this to open the dev tools tab when Chrome
- // is spawned. Handy for debugging tests.
- // args.add("--auto-open-devtools-for-tabs");
-
- return startBrowserProcess(_binary, args,
- environment: _getEnvironment());
- });
- }).catchError((e) {
- _logEvent("Running $_binary --version failed with $e");
+ if (!await File(_binary).exists()) {
+ _logEvent("Chrome binary not available.");
+ _logEvent("Make sure $_binary is a valid program for running chrome");
return false;
- });
- }
+ }
+ try {
+ _logEvent("Got version: ${await version}");
+ final userDir = await Directory.systemTemp.createTemp();
+ _cleanup = () {
+ try {
+ userDir.deleteSync(recursive: true);
+ } catch (e) {
+ _logEvent(
+ "Error: failed to delete Chrome user-data-dir ${userDir.path}, "
+ "will try again in 40 seconds: $e");
+ Timer(const Duration(seconds: 40), () {
+ try {
+ userDir.deleteSync(recursive: true);
+ } catch (e) {
+ _logEvent("Error: failed on second attempt to delete Chrome "
+ "user-data-dir ${userDir.path}: $e");
+ }
+ });
+ }
+ };
+ var args = [
+ "--bwsi",
+ "--disable-component-update",
+ "--disable-extensions",
+ "--disable-popup-blocking",
+ "--no-first-run",
+ "--use-mock-keychain",
+ "--user-data-dir=${userDir.path}",
+ url,
+ ];
- String toString() => "Chrome";
+ // TODO(rnystrom): Uncomment this to open the dev tools tab when Chrome
+ // is spawned. Handy for debugging tests.
+ // args.add("--auto-open-devtools-for-tabs");
+
+ return startBrowserProcess(_binary, args, environment: _getEnvironment());
+ } catch (e) {
+ _logEvent("Starting chrome failed with $e");
+ return false;
+ }
+ }
}
class IE extends Browser {
- Future<String> getVersion() {
+ IE(this._binary);
+
+ final String _binary;
+
+ @override
+ Future<String> get version async {
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 as String).indexOf(findString);
- if (index > 0) {
- return (result.stdout as String)
- .substring(index + findString.length)
- .trim();
- }
- }
- return "Could not get the version of internet explorer";
- });
+ final result = await Process.run("reg", args);
+ if (result.exitCode != 0) {
+ throw StateError("Could not get the version of internet explorer");
+ }
+ // 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 as String).indexOf(findString);
+ if (index > 0) {
+ return (result.stdout as String)
+ .substring(index + findString.length)
+ .trim();
+ }
+ throw StateError("Could not get the version of internet explorer");
}
// Clears the recovery cache and allows popups on localhost if the static
@@ -529,24 +485,23 @@
var localAppData = Platform.environment['LOCALAPPDATA'];
var dir = Directory("$localAppData\\Microsoft\\"
"Internet Explorer\\Recovery");
- return dir.delete(recursive: true).then((_) {
+ try {
+ dir.delete(recursive: true);
return true;
- }).catchError((error) {
+ } catch (error) {
_logEvent("Deleting recovery dir failed with $error");
return false;
- });
+ }
}
- Future<bool> start(String url) {
+ @override
+ Future<bool> start(String url) async {
_logEvent("Starting ie browser on: $url");
- return resetConfiguration().then((_) => getVersion()).then((version) {
- _logEvent("Got version: $version");
- return startBrowserProcess(_binary, [url]);
- });
+ await resetConfiguration();
+ _logEvent("Got version: ${await version}");
+ return startBrowserProcess(_binary, [url]);
}
- String toString() => "IE";
-
Future<void> _setRegistryKey(String key, String value,
{String data, String type}) async {
var args = <String>[
@@ -580,6 +535,7 @@
AndroidChrome(this._adbDevice);
+ @override
Future<bool> start(String url) {
var chromeIntent = Intent(viewAction, chromePackage, chromeActivity, url);
var turnScreenOnIntent =
@@ -617,6 +573,7 @@
});
}
+ @override
Future<bool> close() {
if (_adbDevice != null) {
return _adbDevice.forceStop(chromePackage).then((_) {
@@ -631,10 +588,18 @@
.write('Android device id: ${_adbDevice.deviceId}\n');
}
+ @override
+ final Future<String> version = Future.value('unknown');
+
+ @override
String toString() => "chromeOnAndroid";
}
class Firefox extends Browser {
+ Firefox(this._binary);
+
+ final String _binary;
+
static const String enablePopUp =
'user_pref("dom.disable_open_during_load", false);';
static const String disableDefaultCheck =
@@ -651,41 +616,43 @@
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 Future.value(false);
- }
- version = versionResult.stdout as String;
- _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
- ];
- var environment = Map<String, String>.from(Platform.environment);
- environment["MOZ_CRASHREPORTER_DISABLE"] = "1";
- return startBrowserProcess(_binary, args, environment: environment);
- });
- }).catchError((e) {
- _logEvent("Running $_binary --version failed with $e");
- return false;
- });
+ @override
+ Future<String> get version async {
+ final result = await Process.run(_binary, ["--version"]);
+ if (result.exitCode != 0) {
+ _logEvent("Failed to get firefox version");
+ _logEvent("Make sure $_binary is a valid program for running firefox");
+ throw StateError(
+ "Failed to get firefox version.\nExit code: ${result.exitCode}");
+ }
+ return result.stdout as String;
}
- String toString() => "Firefox";
+ @override
+ Future<bool> start(String url) async {
+ _logEvent("Starting firefox browser on: $url");
+ try {
+ _logEvent("Got version: ${await version}");
+ final userDir = await Directory.systemTemp.createTemp();
+ _createPreferenceFile(userDir.path);
+ _cleanup = () {
+ userDir.deleteSync(recursive: true);
+ };
+ var args = [
+ "-profile",
+ "${userDir.path}",
+ "-no-remote",
+ "-new-instance",
+ url
+ ];
+ var environment = Map<String, String>.from(Platform.environment);
+ environment["MOZ_CRASHREPORTER_DISABLE"] = "1";
+ return startBrowserProcess(_binary, args, environment: environment);
+ } catch (e) {
+ _logEvent("Starting firefox failed with $e");
+ return false;
+ }
+ }
}
/// Describes the current state of a browser used for testing.
@@ -766,6 +733,7 @@
final TestConfiguration configuration;
final BrowserTestingServer testingServer;
+ final Browser Function(TestConfiguration configuration) browserFactory;
final String localIp;
int maxNumBrowsers;
@@ -774,7 +742,7 @@
/// Used to send back logs from the browser (start, stop etc.).
Function logger;
- int browserIdCounter = 1;
+ static int browserIdCounter = 1;
bool testingServerStarted = false;
bool underTermination = false;
@@ -816,13 +784,14 @@
if (_currentStartingBrowserId == id) _currentStartingBrowserId = null;
}
- BrowserTestRunner(this.configuration, this.localIp, this.maxNumBrowsers)
+ BrowserTestRunner(this.configuration, this.localIp, this.maxNumBrowsers,
+ [this.browserFactory = Browser.fromConfiguration])
: testingServer = BrowserTestingServer(configuration, localIp,
Browser.requiresFocus(configuration.runtime.name)) {
testingServer.testRunner = this;
}
- Future start() async {
+ Future<bool> start() async {
await testingServer.start();
testingServer
..testDoneCallBack = handleResults
@@ -835,7 +804,7 @@
maxNumBrowsers = min(maxNumBrowsers, idleAdbDevices.length);
}
testingServerStarted = true;
- requestBrowser();
+ return requestBrowser();
}
/// requestBrowser() is called whenever we might want to start an additional
@@ -846,18 +815,18 @@
/// finishes a test.
/// So we are guaranteed that this will always eventually be called, as long
/// as the test queue isn't empty.
- void requestBrowser() {
- if (!testingServerStarted) return;
- if (underTermination) return;
- if (numBrowsers == maxNumBrowsers) return;
- if (aBrowserIsCurrentlyStarting) return;
- if (numBrowsers > 0 && queueWasEmptyRecently) return;
- createBrowser();
+ Future<bool> requestBrowser() async {
+ if (!testingServerStarted) return false;
+ if (underTermination) return false;
+ if (numBrowsers == maxNumBrowsers) return false;
+ if (aBrowserIsCurrentlyStarting) return false;
+ if (numBrowsers > 0 && queueWasEmptyRecently) return false;
+ return createBrowser();
}
- String getNextBrowserId() => "BROWSER${browserIdCounter++}";
+ static String getNextBrowserId() => "BROWSER${browserIdCounter++}";
- void createBrowser() {
+ Future<bool> createBrowser() {
var id = getNextBrowserId();
var url = testingServer.getDriverUrl(id);
@@ -867,8 +836,7 @@
adbDeviceMapping[id] = device;
browser = AndroidChrome(device);
} else {
- var path = configuration.browserLocation;
- browser = Browser.byRuntime(configuration.runtime, path);
+ browser = browserFactory(configuration);
browser.logger = logger;
}
@@ -878,7 +846,7 @@
browserStatus[id] = status;
numBrowsers++;
status.nextTestTimeout = createNextTestTimer(status);
- browser.start(url);
+ return browser.start(url);
}
void handleResults(String browserId, String output, int testId) {
@@ -1049,7 +1017,7 @@
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("The test before that was: ${status.lastTest?.url}");
print("Timed out tests:");
for (var v in timedOut) {
print(" $v");
@@ -1182,10 +1150,11 @@
BrowserTestingServer(this.configuration, this.localIp, this.requiresFocus);
- Future start() {
- return HttpServer.bind(localIp, configuration.testDriverErrorPort)
- .then(setupErrorServer)
- .then(setupDispatchingServer);
+ Future start() async {
+ var server =
+ await HttpServer.bind(localIp, configuration.testDriverErrorPort);
+ setupErrorServer(server);
+ setupDispatchingServer(server);
}
void setupErrorServer(HttpServer server) {
@@ -1335,7 +1304,7 @@
}
Future<String> getDriverPage(String browserId) async {
- await testRunner.browserStatus[browserId].browser.onDriverPageRequested();
+ await testRunner.browserStatus[browserId]?.browser?.onDriverPageRequested();
var errorReportingUrl =
"http://$localIp:${errorReportingServer.port}/$browserId";
var driverContent = """
diff --git a/pkg/test_runner/lib/src/configuration.dart b/pkg/test_runner/lib/src/configuration.dart
index d98b8b8..cf71c99 100644
--- a/pkg/test_runner/lib/src/configuration.dart
+++ b/pkg/test_runner/lib/src/configuration.dart
@@ -297,9 +297,6 @@
case Runtime.firefox:
location = firefoxPath;
break;
- case Runtime.safari:
- location = safariPath;
- break;
}
if (location != null) return location;
@@ -317,9 +314,6 @@
'/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
System.linux: 'google-chrome'
},
- Runtime.safari: {
- System.mac: '/Applications/Safari.app/Contents/MacOS/Safari'
- },
Runtime.ie9: {
System.win: 'C:\\Program Files\\Internet Explorer\\iexplore.exe'
},
diff --git a/pkg/test_runner/lib/src/options.dart b/pkg/test_runner/lib/src/options.dart
index 34a00ff..d0a67e1 100644
--- a/pkg/test_runner/lib/src/options.dart
+++ b/pkg/test_runner/lib/src/options.dart
@@ -302,8 +302,6 @@
_Option.int('test_server_cross_origin_port',
'Port for test http server cross origin.',
defaultsTo: 0, hide: true),
- _Option.int('test_driver_port', 'Port for http test driver server.',
- defaultsTo: 0, hide: true),
_Option.int(
'test_driver_error_port', 'Port for http test driver server errors.',
defaultsTo: 0, hide: true),
diff --git a/pkg/test_runner/lib/src/process_queue.dart b/pkg/test_runner/lib/src/process_queue.dart
index 2965f44..bfce029 100644
--- a/pkg/test_runner/lib/src/process_queue.dart
+++ b/pkg/test_runner/lib/src/process_queue.dart
@@ -787,7 +787,7 @@
}
Future<CommandOutput> _startBrowserControllerTest(
- BrowserTestCommand browserCommand, int timeout) {
+ BrowserTestCommand browserCommand, int timeout) async {
var completer = Completer<CommandOutput>();
callback(BrowserTestOutput output) {
@@ -795,11 +795,16 @@
}
var browserTest = BrowserTest(browserCommand.url, callback, timeout);
- _getBrowserTestRunner(browserCommand.configuration).then((testRunner) {
- testRunner.enqueueTest(browserTest);
- });
-
- return completer.future;
+ for (var failures = 0; failures < 10; failures++) {
+ var testRunner =
+ await _getBrowserTestRunner(browserCommand.configuration);
+ if (testRunner != null) {
+ testRunner.enqueueTest(browserTest);
+ return completer.future;
+ }
+ }
+ print('FATAL: Failed to get a browser test runner 10 times in a row.');
+ io.exit(1);
}
Future<BrowserTestRunner> _getBrowserTestRunner(
@@ -811,7 +816,11 @@
testRunner.logger = DebugLogger.info;
}
_browserTestRunners[configuration] = testRunner;
- await testRunner.start();
+ if (!await testRunner.start()) {
+ DebugLogger.error('Failed to start browser test runner.');
+ _browserTestRunners.remove(configuration);
+ await testRunner.terminate();
+ }
}
return _browserTestRunners[configuration];
}
diff --git a/pkg/test_runner/lib/src/reset_safari.dart b/pkg/test_runner/lib/src/reset_safari.dart
deleted file mode 100644
index 212de21..0000000
--- a/pkg/test_runner/lib/src/reset_safari.dart
+++ /dev/null
@@ -1,212 +0,0 @@
-// Copyright (c) 2016, 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.
-
-/// Helper program for killing and resetting all Safari settings to a known
-/// state that works well for testing dart2js output in Safari.
-///
-/// Warning: this will delete all your Safari settings and bookmarks.
-library testing.reset_safari;
-
-import 'dart:async' show Future, Timer;
-
-import 'dart:io' show Directory, File, Platform, Process;
-
-const String defaultSafariBundleLocation = "/Applications/Safari.app/";
-
-const String relativeSafariLocation = "Contents/MacOS/Safari";
-
-const String lsofLocation = "/usr/sbin/lsof";
-
-const String killLocation = "/bin/kill";
-
-const String pkillLocation = "/usr/bin/pkill";
-
-const String safari = "com.apple.Safari";
-
-const String defaultsLocation = "/usr/bin/defaults";
-
-final List<String> safariSettings = <String>[
- "Library/Caches/$safari",
- "Library/Safari",
- "Library/Saved Application State/$safari.savedState",
- "Library/Caches/Metadata/Safari",
- "Library/Preferences/$safari.plist",
-];
-
-const Duration defaultPollDelay = Duration(milliseconds: 1);
-
-final String cpgi = "$safari.ContentPageGroupIdentifier";
-
-final String knownSafariPreference = '''
-{
- DefaultBrowserPromptingState2 = 2;
- StartPageViewControllerMode = 0;
- TestDriveOriginBrowser = 1;
- TestDriveUserDecision = 2;
- TestDriveState = 3;
- AlwaysRestoreSessionAtLaunch = 0;
- NewTabBehavior = 1;
- NewWindowBehavior = 1;
- LastSafariVersionWithWelcomePage = "9.0";
- OpenNewTabsInFront = 0;
- TabCreationPolicy = 0;
-
- IncludeDevelopMenu = 1;
- WebKitDeveloperExtrasEnabledPreferenceKey = 1;
- "$cpgi.WebKit2DeveloperExtrasEnabled" = 1;
-
- AutoFillCreditCardData = 0;
- AutoFillMiscellaneousForms = 0;
- AutoFillPasswords = 0;
-
- SuppressSearchSuggestions = 1;
-
- PreloadTopHit = 0;
- ShowFavoritesUnderSmartSearchField = 0;
- WebsiteSpecificSearchEnabled = 0;
-
- WarnAboutFraudulentWebsites = 0;
-
-
- WebKitJavaScriptEnabled = 1;
- "$cpgi.WebKit2JavaScriptEnabled" = 1;
-
- WebKitJavaScriptCanOpenWindowsAutomatically = 1;
- "$cpgi.WebKit2JavaScriptCanOpenWindowsAutomatically" = 1;
-
- "$cpgi.WebKit2WebGLEnabled" = 1;
- WebGLDefaultLoadPolicy = WebGLPolicyAllowNoSecurityRestrictions;
-
- "$cpgi.WebKit2PluginsEnabled" = 0;
-
- BlockStoragePolicy = 1;
- WebKitStorageBlockingPolicy = 0;
- "$cpgi.WebKit2StorageBlockingPolicy" = 0;
-
-
- SafariGeolocationPermissionPolicy = 0;
-
- CanPromptForPushNotifications = 0;
-
- InstallExtensionUpdatesAutomatically = 0;
-
- ShowFullURLInSmartSearchField = 1;
-
- "$cpgi.WebKit2PlugInSnapshottingEnabled" = 0;
-}
-''';
-
-Future<Null> get pollDelay => Future.delayed(defaultPollDelay);
-
-String signalArgument(String defaultSignal,
- {bool force = false, bool testOnly = false}) {
- if (force && testOnly) {
- throw ArgumentError("[force] and [testOnly] can't both be true.");
- }
- if (force) return "-KILL";
- if (testOnly) return "-0";
- return defaultSignal;
-}
-
-Future<int> kill(List<String> pids,
- {bool force = false, bool testOnly = false}) async {
- var arguments = [signalArgument("-TERM", force: force, testOnly: testOnly)]
- ..addAll(pids);
- var result = await Process.run(killLocation, arguments);
- return result.exitCode;
-}
-
-Future<int> pkill(String pattern,
- {bool force = false, bool testOnly = false}) async {
- var arguments = [
- signalArgument("-HUP", force: force, testOnly: testOnly),
- pattern
- ];
- var result = await Process.run(pkillLocation, arguments);
- return result.exitCode;
-}
-
-Uri validatedBundleName(Uri bundle) {
- if (bundle == null) return Uri.base.resolve(defaultSafariBundleLocation);
- if (!bundle.path.endsWith("/")) {
- throw ArgumentError("Bundle ('$bundle') must end with a slash ('/').");
- }
- return bundle;
-}
-
-Future<Null> killSafari({Uri bundle}) async {
- bundle = validatedBundleName(bundle);
- var safariBinary = bundle.resolve(relativeSafariLocation);
- var result =
- await Process.run(lsofLocation, ["-t", safariBinary.toFilePath()]);
- if (result.exitCode == 0) {
- var stdout = result.stdout as String;
- var pids =
- stdout.split("\n").where((String line) => line.isNotEmpty).toList();
- var timer = Timer(const Duration(seconds: 10), () {
- print("Kill -9 Safari $pids");
- kill(pids, force: true);
- });
- var exitCode = await kill(pids);
- while (exitCode == 0) {
- await pollDelay;
- print("Polling Safari $pids");
- exitCode = await kill(pids, testOnly: true);
- }
- timer.cancel();
- }
- var timer = Timer(const Duration(seconds: 10), () {
- print("Kill -9 $safari");
- pkill(safari, force: true);
- });
- var exitCode = await pkill(safari);
- while (exitCode == 0) {
- await pollDelay;
- print("Polling $safari");
- exitCode = await pkill(safari, testOnly: true);
- }
- timer.cancel();
-}
-
-Future<Null> deleteIfExists(Uri uri) async {
- var directory = Directory.fromUri(uri);
- if (await directory.exists()) {
- print("Deleting directory '$uri'.");
- await directory.delete(recursive: true);
- } else {
- var file = File.fromUri(uri);
- if (await file.exists()) {
- print("Deleting file '$uri'.");
- await file.delete();
- } else {
- print("File '$uri' not found.");
- }
- }
-}
-
-Future<Null> resetSafariSettings() async {
- var home = Platform.environment["HOME"];
- if (!home.endsWith("/")) {
- home = "$home/";
- }
- var homeDirectory = Uri.base.resolve(home);
- for (var setting in safariSettings) {
- await deleteIfExists(homeDirectory.resolve(setting));
- }
- var result = await Process.run(
- defaultsLocation, <String>["write", safari, knownSafariPreference]);
- if (result.exitCode != 0) {
- throw "Unable to reset Safari settings: ${result.stdout}${result.stderr}";
- }
-}
-
-Future<Null> killAndResetSafari({Uri bundle}) async {
- bundle = validatedBundleName(bundle);
- await killSafari(bundle: bundle);
- await resetSafariSettings();
-}
-
-Future<Null> main() async {
- await killAndResetSafari();
-}
diff --git a/pkg/test_runner/pubspec.yaml b/pkg/test_runner/pubspec.yaml
index 506b9b2..e78bc08 100644
--- a/pkg/test_runner/pubspec.yaml
+++ b/pkg/test_runner/pubspec.yaml
@@ -19,6 +19,8 @@
path: ../smith
status_file:
path: ../status_file
+ webdriver:
+ path: ../../third_party/pkg/webdriver
dev_dependencies:
analyzer:
path: ../../pkg/analyzer
diff --git a/pkg/test_runner/test/browser_controller_test.dart b/pkg/test_runner/test/browser_controller_test.dart
new file mode 100644
index 0000000..c801de2
--- /dev/null
+++ b/pkg/test_runner/test/browser_controller_test.dart
@@ -0,0 +1,52 @@
+// Copyright (c) 2022, 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.
+
+import 'dart:io';
+
+import 'package:expect/expect.dart';
+
+import 'package:test_runner/src/browser_controller.dart';
+
+void main() async {
+ if (Platform.environment.containsKey('CHROME_PATH')) {
+ print('Testing Chrome');
+ await testChrome();
+ }
+ if (Platform.environment.containsKey('FIREFOX_PATH')) {
+ print('Testing Firefox');
+ await testFirefox();
+ }
+ if (Platform.isMacOS) {
+ print('Testing Safari');
+ await testSafari();
+ }
+}
+
+Future<void> testChrome() {
+ return testBrowser(Chrome(Platform.environment['CHROME_PATH']));
+}
+
+Future<void> testFirefox() {
+ return testBrowser(Firefox(Platform.environment['FIREFOX_PATH']));
+}
+
+Future<void> testSafari() {
+ return testBrowser(Safari());
+}
+
+Future<void> testBrowser(Browser browser) async {
+ browser.debugPrint = true;
+ await browser.version;
+ await testStartStop(browser);
+}
+
+Future<void> testStartStop(Browser browser) async {
+ var closed = false;
+ try {
+ Expect.isTrue(await browser.start('about:blank'));
+ } finally {
+ closed = await browser.close();
+ }
+ Expect.isTrue(closed);
+}
diff --git a/pkg/test_runner/test/browser_test_runner_test.dart b/pkg/test_runner/test/browser_test_runner_test.dart
new file mode 100644
index 0000000..e9bf1a6
--- /dev/null
+++ b/pkg/test_runner/test/browser_test_runner_test.dart
@@ -0,0 +1,40 @@
+// Copyright (c) 2022, 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.
+
+import 'package:expect/expect.dart';
+import 'package:test_runner/src/browser_controller.dart';
+import 'package:test_runner/src/configuration.dart';
+
+void main() async {
+ var configuration = TestConfiguration(
+ configuration: Configuration.parse(
+ const String.fromEnvironment("test_runner.configuration"),
+ {'runtime': 'vm'}),
+ isVerbose: false,
+ localIP: '127.0.0.1',
+ testDriverErrorPort: 0,
+ testServerPort: 0,
+ testServerCrossOriginPort: 0,
+ );
+ await configuration.startServers();
+ try {
+ var testRunner =
+ BrowserTestRunner(configuration, '127.0.0.1', 1, (_) => FakeBrowser());
+ await testRunner.start();
+ try {
+ Expect.isTrue(testRunner.testingServerStarted);
+ Expect.equals(1, testRunner.numBrowsers);
+ } finally {
+ await testRunner.terminate();
+ }
+ } finally {
+ configuration.stopServers();
+ }
+}
+
+class FakeBrowser extends Browser {
+ Future<bool> start(String url) => Future.value(true);
+ Future<bool> close() => Future.value(true);
+ Future<String> version = Future.value('fake version');
+}
diff --git a/runtime/tests/vm/vm.status b/runtime/tests/vm/vm.status
index 7c963b6..7db7cad 100644
--- a/runtime/tests/vm/vm.status
+++ b/runtime/tests/vm/vm.status
@@ -53,11 +53,6 @@
dart/transferable_throws_oom_test: SkipByDesign # This test tries to allocate too much memory on purpose. Still dartbug.com/37188
dart_2/transferable_throws_oom_test: SkipByDesign # This test tries to allocate too much memory on purpose. Still dartbug.com/37188
-[ $builder_tag == obfuscated ]
-dart_2/causal_stacks/async_throws_stack_lazy_test: SkipByDesign # Asserts exact stacktrace output.
-dart_2/causal_stacks/async_throws_stack_no_causal_test: SkipByDesign # Asserts exact stacktrace output.
-dart_2/causal_stacks/sync_async_start_pkg_test_test: SkipByDesign # Asserts exact stacktrace output.
-
[ $builder_tag == optimization_counter_threshold ]
cc/*: Skip # Many tests want see unoptimized code running
dart/appjit*: SkipByDesign # Test needs to a particular opt-counter value
@@ -427,6 +422,16 @@
dart/run_appended_aot_snapshot_test: SkipByDesign # Tests the precompiled runtime.
dart_2/run_appended_aot_snapshot_test: SkipByDesign # Tests the precompiled runtime.
+[ $builder_tag == dwarf || $builder_tag == obfuscated ]
+dart/causal_stacks/async_throws_stack_lazy_test: SkipByDesign # Asserts exact stacktrace output.
+dart/causal_stacks/async_throws_stack_no_causal_test: SkipByDesign # Asserts exact stacktrace output.
+dart/causal_stacks/flutter_regress_100441_test: SkipByDesign # Asserts exact stacktrace output.
+dart/causal_stacks/sync_async_start_pkg_test_test: SkipByDesign # Asserts exact stacktrace output.
+dart_2/causal_stacks/async_throws_stack_lazy_test: SkipByDesign # Asserts exact stacktrace output.
+dart_2/causal_stacks/async_throws_stack_no_causal_test: SkipByDesign # Asserts exact stacktrace output.
+dart_2/causal_stacks/flutter_regress_100441_test: SkipByDesign # Asserts exact stacktrace output.
+dart_2/causal_stacks/sync_async_start_pkg_test_test: SkipByDesign # Asserts exact stacktrace output.
+
[ $compiler == dart2analyzer || $compiler == dart2js ]
dart/data_uri*test: Skip # Data uri's not supported by dart2js or the analyzer.
dart_2/data_uri*test: Skip # Data uri's not supported by dart2js or the analyzer.
diff --git a/tools/VERSION b/tools/VERSION
index 3e99c74..448f620 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 18
PATCH 0
-PRERELEASE 69
+PRERELEASE 70
PRERELEASE_PATCH 0
\ No newline at end of file
diff --git a/tools/bots/try_benchmarks.sh b/tools/bots/try_benchmarks.sh
index 9d2db12..93003aa 100755
--- a/tools/bots/try_benchmarks.sh
+++ b/tools/bots/try_benchmarks.sh
@@ -331,6 +331,7 @@
out/ReleaseX64/dart-sdk/bin/dart compile js --sound-null-safety --out=out.js -m hello.dart
third_party/d8/linux/x64/d8 --stack_size=1024 sdk/lib/_internal/js_runtime/lib/preambles/d8.js out.js
out/ReleaseX64/dart-sdk/bin/dart compile js --out=out.js -m hello.dart
+ out/ReleaseX64/dart-sdk/bin/dart --print_metrics compile js --out=out.js -m hello.dart
LD_LIBRARY_PATH=third_party/firefox_jsshell/ third_party/firefox_jsshell/js -f sdk/lib/_internal/js_runtime/lib/preambles/jsshell.js -f out.js
out/ReleaseX64/dart-sdk/bin/dart compile js --sound-null-safety --out=out.js -m hello.dart
LD_LIBRARY_PATH=third_party/firefox_jsshell/ third_party/firefox_jsshell/js -f sdk/lib/_internal/js_runtime/lib/preambles/jsshell.js -f out.js