diff --git a/test/test_pub.dart b/test/test_pub.dart
index 78d198ae0da8e43644995f3f9511f2cfe4bf23e2..44290b02e5c01cb0021a2386468ca0adb714c018 100644
--- a/test/test_pub.dart
+++ b/test/test_pub.dart
@@ -509,6 +509,10 @@ List<_ScheduledEvent> _scheduledOnException;
  */
 bool _abortScheduled = false;
 
+/// The time (in milliseconds) to wait for the entire scheduled test to
+/// complete.
+final _TIMEOUT = 30000;
+
 /**
  * Runs all the scheduled events for a test case. This should only be called
  * once per test case.
@@ -549,9 +553,9 @@ void run() {
     return true;
   });
 
-  future.chain((_) => cleanup()).then((_) {
-    asyncDone();
-  });
+  timeout(future, _TIMEOUT, 'waiting for a test to complete')
+      .chain((_) => cleanup())
+      .then((_) => asyncDone());
 }
 
 /// Get the path to the root "util/test/pub" directory containing the pub tests.