Skip to content

Commit

Permalink
Add firefox support.
Browse files Browse the repository at this point in the history
See #31

R=kevmoo@google.com

Review URL: https://codereview.chromium.org//1070313002
  • Loading branch information
nex3 committed Apr 10, 2015
1 parent f0d5c2f commit 45c6248
Show file tree
Hide file tree
Showing 10 changed files with 417 additions and 67 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
of concurrent test suites will be run equal to half the machine's processors;
this can be controlled with the `--concurrency` flag.

* Add support for running tests on Firefox.

### 0.12.0-beta.5

* Add a `--pub-serve` flag that runs tests against a `pub serve` instance.
Expand Down
11 changes: 8 additions & 3 deletions lib/src/backend/test_platform.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,19 @@ class TestPlatform {
// variable tests in test/backend/platform_selector/evaluate_test.

/// The command-line Dart VM.
static const vm = const TestPlatform._("VM", "vm", isDartVm: true);
static const TestPlatform vm =
const TestPlatform._("VM", "vm", isDartVm: true);

/// Google Chrome.
static const chrome = const TestPlatform._("Chrome", "chrome",
static const TestPlatform chrome = const TestPlatform._("Chrome", "chrome",
isBrowser: true, isJS: true, isBlink: true);

/// Mozilla Firefox.
static const TestPlatform firefox = const TestPlatform._("Firefox", "firefox",
isBrowser: true, isJS: true);

/// A list of all instances of [TestPlatform].
static const all = const [vm, chrome];
static const List<TestPlatform> all = const [vm, chrome, firefox];

/// Finds a platform by its identifier string.
///
Expand Down
29 changes: 29 additions & 0 deletions lib/src/runner/browser/browser.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright (c) 2015, 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 test.runner.browser.browser;

import 'dart:async';

/// An interface for running browser instances.
///
/// This is intentionally coarse-grained: browsers are controlled primary from
/// inside a single tab. Thus this interface only provides support for closing
/// the browser and seeing if it closes itself.
///
/// Any errors starting or running the browser process are reported through
/// [onExit].
abstract class Browser {
/// A future that completes when the browser exits.
///
/// If there's a problem starting or running the browser, this will complete
/// with an error.
Future get onExit;

/// Kills the browser process.
///
/// Returns the same [Future] as [onExit], except that it won't emit
/// exceptions.
Future close();
}
11 changes: 2 additions & 9 deletions lib/src/runner/browser/chrome.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import 'dart:io';
import 'package:path/path.dart' as p;

import '../../util/io.dart';
import 'browser.dart';

// TODO(nweiz): move this into its own package?
// TODO(nweiz): support other browsers.
Expand All @@ -20,7 +21,7 @@ import '../../util/io.dart';
/// constructed, and is killed when [close] is called.
///
/// Any errors starting or running the process are reported through [onExit].
class Chrome {
class Chrome implements Browser {
/// The underlying process.
Process _process;

Expand All @@ -30,10 +31,6 @@ class Chrome {
/// well-isolated.
String _dir;

/// A future that completes when the browser exits.
///
/// If there's a problem starting or running the browser, this will complete
/// with an error.
Future get onExit => _onExitCompleter.future;
final _onExitCompleter = new Completer();

Expand Down Expand Up @@ -80,10 +77,6 @@ class Chrome {
.catchError(_onExitCompleter.completeError);
}

/// Kills the browser process.
///
/// Returns the same [Future] as [onExit], except that it won't emit
/// exceptions.
Future close() {
_onProcessStarted.then((_) => _process.kill());

Expand Down
115 changes: 115 additions & 0 deletions lib/src/runner/browser/firefox.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// Copyright (c) 2015, 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 test.runner.browser.firefox;

import 'dart:async';
import 'dart:io';

import 'package:path/path.dart' as p;

import '../../util/io.dart';
import 'browser.dart';

final _preferences = '''
user_pref("browser.shell.checkDefaultBrowser", false);
user_pref("dom.disable_open_during_load", false);
user_pref("dom.max_script_run_time", 0);
''';

/// A class for running an instance of Firefox.
///
/// Most of the communication with the browser is expected to happen via HTTP,
/// so this exposes a bare-bones API. The browser starts as soon as the class is
/// constructed, and is killed when [close] is called.
///
/// Any errors starting or running the process are reported through [onExit].
class Firefox implements Browser {
/// The underlying process.
Process _process;

/// The temporary directory used as the browser's user data dir.
///
/// A new data dir is created for each run to ensure that they're
/// well-isolated.
String _dir;

Future get onExit => _onExitCompleter.future;
final _onExitCompleter = new Completer();

/// A future that completes when the browser process has started.
///
/// This is used to ensure that [close] works regardless of when it's called.
Future get _onProcessStarted => _onProcessStartedCompleter.future;
final _onProcessStartedCompleter = new Completer();

/// Starts a new instance of Firefox open to the given [url], which may be a
/// [Uri] or a [String].
///
/// If [executable] is passed, it's used as the Firefox executable. Otherwise
/// the default executable name for the current OS will be used.
Firefox(url, {String executable}) {
if (executable == null) executable = _defaultExecutable();

// Don't return a Future here because there's no need for the caller to wait
// for the process to actually start. They should just wait for the HTTP
// request instead.
withTempDir((dir) {
_dir = dir;

new File(p.join(dir, 'prefs.js')).writeAsStringSync(_preferences);

return Process.start(executable, [
"--profile",
"$_dir",
url.toString(),
"--no-remote"
]).then((process) {
_process = process;
_onProcessStartedCompleter.complete();

// TODO(nweiz): the browser's standard output is almost always useless
// noise, but we should allow the user to opt in to seeing it.
return _process.exitCode;
});
}).then((exitCode) {
if (exitCode != 0) throw "Firefox failed with exit code $exitCode.";
}).then(_onExitCompleter.complete)
.catchError(_onExitCompleter.completeError);
}

Future close() {
_onProcessStarted.then((_) => _process.kill());

// Swallow exceptions. The user should explicitly use [onExit] for these.
return onExit.catchError((_) {});
}

/// Return the default executable for the current operating system.
String _defaultExecutable() {
if (Platform.isMacOS) {
return '/Applications/Firefox.app/Contents/MacOS/firefox-bin';
}
if (!Platform.isWindows) return 'firefox';

// Firefox could be installed in several places on Windows. The only way to
// find it is to check.
var prefixes = [
Platform.environment['PROGRAMFILES'],
Platform.environment['PROGRAMFILES(X86)']
];
var suffix = r'Mozilla Firefox\firefox.exe';

for (var prefix in prefixes) {
if (prefix == null) continue;

var path = p.join(prefix, suffix);
if (new File(p.join(prefix, suffix)).existsSync()) return path;
}

// Fall back on looking it up on the path. This probably won't work, but at
// least it will fail with a useful error message.
return "firefox.exe";
}
}
Loading

0 comments on commit 45c6248

Please sign in to comment.