Merge pull request #720 from dart-lang/load-suite-timeout

Fix load suite timeout weirdness
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4a87965..f0e14a4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.12.27+1
+
+* Increase the timeout for loading tests to 12 minutes.
+
 ## 0.12.27
 
 * When `addTearDown()` is called within a call to `setUpAll()`, it runs its
diff --git a/lib/src/runner/load_suite.dart b/lib/src/runner/load_suite.dart
index 308357a..1241ad2 100644
--- a/lib/src/runner/load_suite.dart
+++ b/lib/src/runner/load_suite.dart
@@ -19,6 +19,14 @@
 import 'plugin/environment.dart';
 import 'runner_suite.dart';
 
+/// The timeout for loading a test suite.
+///
+/// We want this to be long enough that even a very large application being
+/// compiled with dart2js doesn't trigger it, but short enough that it fires
+/// before the host kills it. For example, Google's Forge service has a
+/// 15-minute timeout.
+final _timeout = new Duration(minutes: 12);
+
 /// A [Suite] emitted by a [Loader] that provides a test-like interface for
 /// loading a test file.
 ///
@@ -75,31 +83,29 @@
       invoker.addOutstandingCallback();
 
       invoke(() async {
-        try {
-          var suite = await body();
-          if (completer.isCompleted) {
-            // If the load test has already been closed, close the suite it
-            // generated.
-            suite?.close();
-            return;
-          }
-
-          completer
-              .complete(suite == null ? null : new Pair(suite, Zone.current));
-          invoker.removeOutstandingCallback();
-        } catch (error, stackTrace) {
-          registerException(error, stackTrace);
-          if (!completer.isCompleted) completer.complete();
+        var suite = await body();
+        if (completer.isCompleted) {
+          // If the load test has already been closed, close the suite it
+          // generated.
+          suite?.close();
+          return;
         }
-      });
 
-      // If the test is forcibly closed, exit immediately. It doesn't have any
-      // cleanup to do that won't be handled by Loader.close.
-      invoker.onClose.then((_) {
-        if (completer.isCompleted) return;
-        completer.complete();
+        completer
+            .complete(suite == null ? null : new Pair(suite, Zone.current));
         invoker.removeOutstandingCallback();
       });
+
+      // If the test completes before the body callback, either an out-of-band
+      // error occurred or the test was canceled. Either way, we return a `null`
+      // suite.
+      invoker.liveTest.onComplete.then((_) {
+        if (!completer.isCompleted) completer.complete();
+      });
+
+      // If the test is forcibly closed, let it complete, since load tests don't
+      // have timeouts.
+      invoker.onClose.then((_) => invoker.removeOutstandingCallback());
     }, completer.future, path: path, platform: platform);
   }
 
@@ -130,9 +136,7 @@
       : super(
             new Group.root([
               new LocalTest(
-                  name,
-                  new Metadata(timeout: new Timeout(new Duration(minutes: 5))),
-                  body)
+                  name, new Metadata(timeout: new Timeout(_timeout)), body)
             ]),
             path: path,
             platform: platform);
diff --git a/lib/src/runner/plugin/platform_helpers.dart b/lib/src/runner/plugin/platform_helpers.dart
index b41e5ba..fa0c2a1 100644
--- a/lib/src/runner/plugin/platform_helpers.dart
+++ b/lib/src/runner/plugin/platform_helpers.dart
@@ -23,8 +23,6 @@
 import '../runner_suite.dart';
 import '../runner_test.dart';
 
-final _deserializeTimeout = new Duration(minutes: 8);
-
 /// A helper method for creating a [RunnerSuiteController] containing tests
 /// that communicate over [channel].
 ///
@@ -113,13 +111,7 @@
       });
 
   return new RunnerSuiteController(
-      environment,
-      suiteConfig,
-      await completer.future.timeout(_deserializeTimeout, onTimeout: () {
-        throw new ApplicationException(
-            "Timed out while loading the test suite.\n"
-            "It's likely that there's a missing import or syntax error.");
-      }),
+      environment, suiteConfig, await completer.future,
       path: path,
       platform: platform,
       os: currentOS,