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();
 }