Revert "Add RunnerSuite.{on,is}Debugging properties."
This reverts commit 71bfbdcd1b89ab62df9bf3a1a6f48bffe8c63974. This
commit was causing intermittent stack overflows after running tests. I'm
reverting so we can get 0.12.6 out immediately; I'll re-apply once I
have time to fix the issues.
R=kevmoo@google.com
Review URL: https://codereview.chromium.org//1465403002 .
diff --git a/lib/src/runner/browser/browser_manager.dart b/lib/src/runner/browser/browser_manager.dart
index a6222be..1b1f4c9 100644
--- a/lib/src/runner/browser/browser_manager.dart
+++ b/lib/src/runner/browser/browser_manager.dart
@@ -11,23 +11,27 @@
import 'package:http_parser/http_parser.dart';
import 'package:pool/pool.dart';
+import '../../backend/group.dart';
import '../../backend/metadata.dart';
+import '../../backend/test.dart';
import '../../backend/test_platform.dart';
import '../../util/multi_channel.dart';
+import '../../util/remote_exception.dart';
import '../../util/stack_trace_mapper.dart';
import '../../utils.dart';
import '../application_exception.dart';
import '../environment.dart';
+import '../load_exception.dart';
import '../runner_suite.dart';
import 'browser.dart';
import 'chrome.dart';
import 'content_shell.dart';
import 'dartium.dart';
import 'firefox.dart';
+import 'iframe_test.dart';
import 'internet_explorer.dart';
import 'phantom_js.dart';
import 'safari.dart';
-import 'suite.dart';
/// A class that manages the connection to a single running browser.
///
@@ -176,16 +180,18 @@
// prematurely (e.g. via Control-C).
var suiteVirtualChannel = _channel.virtualChannel();
var suiteId = _suiteId++;
+ var suiteChannel;
closeIframe() {
if (_closed) return;
+ suiteChannel.sink.close();
_channel.sink.add({
"command": "closeSuite",
"id": suiteId
});
}
- return await _pool.withResource(() async {
+ var response = await _pool.withResource(() {
_channel.sink.add({
"command": "loadSuite",
"url": url.toString(),
@@ -193,15 +199,85 @@
"channel": suiteVirtualChannel.id
});
- try {
- return await loadBrowserSuite(
- suiteVirtualChannel, await _environment, path,
- mapper: mapper, platform: _platform, onClose: () => closeIframe());
- } catch (_) {
- closeIframe();
- rethrow;
- }
+ // Create a nested MultiChannel because the iframe will be using a channel
+ // wrapped within the host's channel.
+ suiteChannel = new MultiChannel(
+ suiteVirtualChannel.stream, suiteVirtualChannel.sink);
+
+ var completer = new Completer();
+ suiteChannel.stream.listen((response) {
+ if (response["type"] == "print") {
+ print(response["line"]);
+ } else {
+ completer.complete(response);
+ }
+ }, onDone: () {
+ if (!completer.isCompleted) completer.complete();
+ });
+
+ return completer.future.timeout(new Duration(minutes: 1), onTimeout: () {
+ throw new LoadException(
+ path,
+ "Timed out waiting for the test suite to connect on "
+ "${_platform.name}.");
+ });
});
+
+ if (response == null) {
+ closeIframe();
+ throw new LoadException(
+ path, "Connection closed before test suite loaded.");
+ }
+
+ if (response["type"] == "loadException") {
+ closeIframe();
+ throw new LoadException(path, response["message"]);
+ }
+
+ if (response["type"] == "error") {
+ closeIframe();
+ var asyncError = RemoteException.deserialize(response["error"]);
+ await new Future.error(
+ new LoadException(path, asyncError.error),
+ asyncError.stackTrace);
+ }
+
+ return new RunnerSuite(
+ await _environment,
+ _deserializeGroup(suiteChannel, mapper, response["root"]),
+ platform: _platform,
+ path: path,
+ onClose: () => closeIframe());
+ }
+
+ /// Deserializes [group] into a concrete [Group] class.
+ Group _deserializeGroup(MultiChannel suiteChannel,
+ StackTraceMapper mapper, Map group) {
+ var metadata = new Metadata.deserialize(group['metadata']);
+ return new Group(group['name'], group['entries'].map((entry) {
+ if (entry['type'] == 'group') {
+ return _deserializeGroup(suiteChannel, mapper, entry);
+ }
+
+ return _deserializeTest(suiteChannel, mapper, entry);
+ }),
+ metadata: metadata,
+ setUpAll: _deserializeTest(suiteChannel, mapper, group['setUpAll']),
+ tearDownAll:
+ _deserializeTest(suiteChannel, mapper, group['tearDownAll']));
+ }
+
+ /// Deserializes [test] into a concrete [Test] class.
+ ///
+ /// Returns `null` if [test] is `null`.
+ Test _deserializeTest(MultiChannel suiteChannel, StackTraceMapper mapper,
+ Map test) {
+ if (test == null) return null;
+
+ var metadata = new Metadata.deserialize(test['metadata']);
+ var testChannel = suiteChannel.virtualChannel(test['channel']);
+ return new IframeTest(test['name'], metadata, testChannel,
+ mapper: mapper);
}
/// An implementation of [Environment.displayPause].
diff --git a/lib/src/runner/browser/iframe_listener.dart b/lib/src/runner/browser/iframe_listener.dart
index 2a658cf..18a938d 100644
--- a/lib/src/runner/browser/iframe_listener.dart
+++ b/lib/src/runner/browser/iframe_listener.dart
@@ -40,11 +40,6 @@
static Future start(Function getMain()) async {
var channel = _postMessageChannel();
- // Send periodic pings to the test runner so it can know when the suite is
- // paused for debugging.
- new Timer.periodic(new Duration(seconds: 1),
- (_) => channel.sink.add({"type": "ping"}));
-
var main;
try {
main = getMain();
diff --git a/lib/src/runner/browser/suite.dart b/lib/src/runner/browser/suite.dart
deleted file mode 100644
index 075a413..0000000
--- a/lib/src/runner/browser/suite.dart
+++ /dev/null
@@ -1,166 +0,0 @@
-// 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.suite;
-
-import 'dart:async';
-
-import 'package:async/async.dart';
-
-import '../../backend/group.dart';
-import '../../backend/metadata.dart';
-import '../../backend/test.dart';
-import '../../backend/test_platform.dart';
-import '../../util/multi_channel.dart';
-import '../../util/remote_exception.dart';
-import '../../util/stack_trace_mapper.dart';
-import '../../util/stream_channel.dart';
-import '../../utils.dart';
-import '../environment.dart';
-import '../load_exception.dart';
-import '../runner_suite.dart';
-import 'iframe_test.dart';
-
-/// Loads a [RunnerSuite] for a browser.
-///
-/// [channel] should connect to the iframe containing the suite, which should
-/// eventually emit a message containing the suite's test information.
-/// [environment], [path], [platform], and [onClose] are passed to the
-/// [RunnerSuite]. If passed, [mapper] is used to reformat the test's stack
-/// traces.
-Future<RunnerSuite> loadBrowserSuite(StreamChannel channel,
- Environment environment, String path, {StackTraceMapper mapper,
- TestPlatform platform, AsyncFunction onClose}) async {
- // The controller for the returned suite. This is set once we've loaded the
- // information about the tests in the suite.
- var controller;
-
- // A timer that's reset whenever we receive a message from the browser.
- // Because the browser stops running code when the user is actively debugging,
- // this lets us detect whether they're debugging reasonably accurately.
- //
- // The duration should be short enough that the debugging console is open as
- // soon as the user is done setting breakpoints, but long enough that a test
- // doing a lot of synchronous work doesn't trigger a false positive.
- //
- // Start this canceled because we don't want it to start ticking until we get
- // some response from the iframe.
- var timer = new RestartableTimer(new Duration(seconds: 3), () {
- controller.setDebugging(true);
- })..cancel();
-
- // Even though [channel] is probably a [MultiChannel] already, create a
- // nested MultiChannel because the iframe will be using a channel wrapped
- // within the host's channel.
- var suiteChannel = new MultiChannel(channel.stream.map((message) {
- // Whenever we get a message, no matter which child channel it's for, we the
- // browser is still running code which means the using isn't debugging.
- if (controller != null) {
- timer.reset();
- controller.setDebugging(false);
- }
-
- return message;
- }), channel.sink);
-
- var response = await _getResponse(suiteChannel.stream)
- .timeout(new Duration(minutes: 1), onTimeout: () {
- suiteChannel.sink.close();
- throw new LoadException(
- path,
- "Timed out waiting for the test suite to connect.");
- });
-
- try {
- _validateResponse(path, response);
- } catch (_) {
- suiteChannel.sink.close();
- rethrow;
- }
-
- controller = new RunnerSuiteController(environment,
- _deserializeGroup(suiteChannel, response["root"], mapper),
- platform: platform, path: path,
- onClose: () {
- suiteChannel.sink.close();
- timer.cancel();
- return onClose == null ? null : onClose();
- });
-
- // Start the debugging timer counting down.
- timer.reset();
- return controller.suite;
-}
-
-/// Listens for responses from the iframe on [stream].
-///
-/// Returns the serialized representation of the the root group for the suite,
-/// or a response indicating that an error occurred.
-Future<Map> _getResponse(Stream stream) {
- var completer = new Completer();
- stream.listen((response) {
- if (response["type"] == "print") {
- print(response["line"]);
- } else if (response["type"] != "ping") {
- completer.complete(response);
- }
- }, onDone: () {
- if (!completer.isCompleted) completer.complete();
- });
-
- return completer.future;
-}
-
-/// Throws an error encoded in [response], if there is one.
-///
-/// [path] is used for the error's metadata.
-Future _validateResponse(String path, Map response) {
- if (response == null) {
- throw new LoadException(
- path, "Connection closed before test suite loaded.");
- }
-
- if (response["type"] == "loadException") {
- throw new LoadException(path, response["message"]);
- }
-
- if (response["type"] == "error") {
- var asyncError = RemoteException.deserialize(response["error"]);
- return new Future.error(
- new LoadException(path, asyncError.error),
- asyncError.stackTrace);
- }
-
- return new Future.value();
-}
-
-/// Deserializes [group] into a concrete [Group] class.
-Group _deserializeGroup(MultiChannel suiteChannel, Map group,
- [StackTraceMapper mapper]) {
- var metadata = new Metadata.deserialize(group['metadata']);
- return new Group(group['name'], group['entries'].map((entry) {
- if (entry['type'] == 'group') {
- return _deserializeGroup(suiteChannel, entry, mapper);
- }
-
- return _deserializeTest(suiteChannel, entry, mapper);
- }),
- metadata: metadata,
- setUpAll: _deserializeTest(suiteChannel, group['setUpAll'], mapper),
- tearDownAll:
- _deserializeTest(suiteChannel, group['tearDownAll'], mapper));
-}
-
-/// Deserializes [test] into a concrete [Test] class.
-///
-/// Returns `null` if [test] is `null`.
-Test _deserializeTest(MultiChannel suiteChannel, Map test,
- [StackTraceMapper mapper]) {
- if (test == null) return null;
-
- var metadata = new Metadata.deserialize(test['metadata']);
- var testChannel = suiteChannel.virtualChannel(test['channel']);
- return new IframeTest(test['name'], metadata, testChannel,
- mapper: mapper);
-}
diff --git a/lib/src/runner/load_suite.dart b/lib/src/runner/load_suite.dart
index 460e5e8..a831777 100644
--- a/lib/src/runner/load_suite.dart
+++ b/lib/src/runner/load_suite.dart
@@ -12,7 +12,6 @@
import '../backend/group.dart';
import '../backend/invoker.dart';
import '../backend/metadata.dart';
-import '../backend/suite.dart';
import '../backend/test.dart';
import '../backend/test_platform.dart';
import '../utils.dart';
@@ -34,11 +33,7 @@
/// a normal test body, this logic isn't run until [LiveTest.run] is called. The
/// suite itself is returned by [suite] once it's avaialble, but any errors or
/// prints will be emitted through the running [LiveTest].
-class LoadSuite extends Suite implements RunnerSuite {
- final environment = const VMEnvironment();
- final isDebugging = false;
- final onDebugging = new StreamController<bool>().stream;
-
+class LoadSuite extends RunnerSuite {
/// A future that completes to the loaded suite once the suite's test has been
/// run and completed successfully.
///
@@ -113,7 +108,7 @@
}
LoadSuite._(String name, void body(), this.suite, {TestPlatform platform})
- : super(new Group.root([
+ : super(const VMEnvironment(), new Group.root([
new LocalTest(name,
new Metadata(timeout: new Timeout(new Duration(minutes: 5))),
body)
@@ -121,7 +116,7 @@
/// A constructor used by [changeSuite].
LoadSuite._changeSuite(LoadSuite old, Future<RunnerSuite> this.suite)
- : super(old.group, platform: old.platform);
+ : super(const VMEnvironment(), old.group, platform: old.platform);
/// Creates a new [LoadSuite] that's identical to this one, but that
/// transforms [suite] once it's loaded.
@@ -149,6 +144,4 @@
await new Future.error(error.error, error.stackTrace);
throw 'unreachable';
}
-
- Future close() async {}
}
diff --git a/lib/src/runner/runner_suite.dart b/lib/src/runner/runner_suite.dart
index 018cccd..fe6090c 100644
--- a/lib/src/runner/runner_suite.dart
+++ b/lib/src/runner/runner_suite.dart
@@ -22,88 +22,31 @@
/// This is separated from [Suite] because the backend library (which will
/// eventually become its own package) is primarily for test code itself to use,
/// for which the [RunnerSuite] APIs don't make sense.
-///
-/// A [RunnerSuite] can be produced and controlled using a
-/// [RunnerSuiteController].
class RunnerSuite extends Suite {
- final RunnerSuiteController _controller;
+ final Environment environment;
- /// The environment in which this suite runs.
- Environment get environment => _controller._environment;
-
- /// Whether the suite is paused for debugging.
- ///
- /// When using a dev inspector, this may also mean that the entire browser is
- /// paused.
- bool get isDebugging => _controller._isDebugging;
-
- /// A broadcast stream that emits an event whenever the suite is paused for
- /// debugging or resumed afterwards.
- ///
- /// The event is `true` when debugging starts and `false` when it ends.
- Stream<bool> get onDebugging => _controller._onDebuggingController.stream;
-
- /// A shortcut constructor for creating a [RunnerSuite] that never goes into
- /// debugging mode.
- factory RunnerSuite(Environment environment, Group group, {String path,
- TestPlatform platform, OperatingSystem os, AsyncFunction onClose}) {
- var controller = new RunnerSuiteController(environment, group,
- path: path, platform: platform, os: os, onClose: onClose);
- return controller.suite;
- }
-
- RunnerSuite._(this._controller, Group group, String path,
- TestPlatform platform, OperatingSystem os)
- : super(group, path: path, platform: platform, os: os);
-
- RunnerSuite filter(bool callback(Test test)) {
- var filtered = group.filter(callback);
- filtered ??= new Group.root([], metadata: metadata);
- return new RunnerSuite._(_controller, filtered, path, platform, os);
- }
-
- /// Closes the suite and releases any resources associated with it.
- Future close() => _controller._close();
-}
-
-/// A class that exposes and controls a [RunnerSuite].
-class RunnerSuiteController {
- /// The suite controlled by this controller.
- RunnerSuite get suite => _suite;
- RunnerSuite _suite;
-
- /// The backing value for [suite.environment].
- final Environment _environment;
+ /// The memoizer for running [close] exactly once.
+ final _closeMemo = new AsyncMemoizer();
/// The function to call when the suite is closed.
final AsyncFunction _onClose;
- /// The backing value for [suite.isDebugging].
- bool _isDebugging = false;
-
- /// The controller for [suite.onDebugging].
- final _onDebuggingController = new StreamController<bool>.broadcast();
-
- RunnerSuiteController(this._environment, Group group, {String path,
+ RunnerSuite(this.environment, Group group, {String path,
TestPlatform platform, OperatingSystem os, AsyncFunction onClose})
- : _onClose = onClose {
- _suite = new RunnerSuite._(this, group, path, platform, os);
+ : _onClose = onClose,
+ super(group, path: path, platform: platform, os: os);
+
+ RunnerSuite filter(bool callback(Test test)) {
+ var filtered = group.filter(callback);
+ filtered ??= new Group.root([], metadata: metadata);
+ return new RunnerSuite(environment, filtered,
+ platform: platform, os: os, path: path);
}
- /// Sets whether the suite is paused for debugging.
- ///
- /// If this is different than [suite.isDebugging], this will automatically
- /// send out an event along [suite.onDebugging].
- void setDebugging(bool debugging) {
- if (debugging == _isDebugging) return;
- _isDebugging = debugging;
- _onDebuggingController.add(debugging);
+ /// Closes the suite and releases any resources associated with it.
+ Future close() {
+ return _closeMemo.runOnce(() async {
+ if (_onClose != null) await _onClose();
+ });
}
-
- /// The backing function for [suite.close].
- Future _close() => _closeMemo.runOnce(() async {
- _onDebuggingController.close();
- if (_onClose != null) await _onClose();
- });
- final _closeMemo = new AsyncMemoizer();
}