Split up tests and add some heartbeats to try to make them not timeout.

BUG=
R=nweiz@google.com

Review URL: https://codereview.chromium.org//18877005

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/watcher@24978 260f80e4-7a28-3924-810f-c04153c831b5
diff --git a/pkgs/watcher/lib/src/directory_watcher.dart b/pkgs/watcher/lib/src/directory_watcher.dart
index 0f297ba..61bf6c5 100644
--- a/pkgs/watcher/lib/src/directory_watcher.dart
+++ b/pkgs/watcher/lib/src/directory_watcher.dart
@@ -42,13 +42,24 @@
   Future get ready => _ready.future;
   Completer _ready = new Completer();
 
+  /// The amount of time the watcher pauses between successive polls of the
+  /// directory contents.
+  final Duration pollingDelay;
+
   /// The previous status of the files in the directory.
   ///
   /// Used to tell which files have been modified.
   final _statuses = new Map<String, _FileStatus>();
 
   /// Creates a new [DirectoryWatcher] monitoring [directory].
-  DirectoryWatcher(this.directory) {
+  ///
+  /// If [pollingDelay] is passed, it specifies the amount of time the watcher
+  /// will pause between successive polls of the directory contents. Making
+  /// this shorter will give more immediate feedback at the expense of doing
+  /// more IO and higher CPU usage. Defaults to one second.
+  DirectoryWatcher(this.directory, {Duration pollingDelay})
+      : pollingDelay = pollingDelay != null ? pollingDelay :
+                                              new Duration(seconds: 1) {
     _events = new StreamController<WatchEvent>.broadcast(onListen: () {
       _state = _state.listen(this);
     }, onCancel: () {
@@ -93,7 +104,7 @@
       // restarting just so that we don't whale on the file system.
       // TODO(rnystrom): Tune this and/or make it tunable?
       if (_state.shouldNotify) {
-        return new Future.delayed(new Duration(seconds: 1));
+        return new Future.delayed(pollingDelay);
       }
     }).then((_) {
       // Make sure we haven't transitioned to a non-watching state during the
diff --git a/pkgs/watcher/test/directory_watcher_test.dart b/pkgs/watcher/test/directory_watcher_test.dart
index 635f7ee..800d54d 100644
--- a/pkgs/watcher/test/directory_watcher_test.dart
+++ b/pkgs/watcher/test/directory_watcher_test.dart
@@ -90,150 +90,4 @@
     writeFile("a/b/c/d/file.txt");
     expectAddEvent("a/b/c/d/file.txt");
   });
-
-  test('does not notify for changes when there were no subscribers', () {
-    // Note that this test doesn't rely as heavily on the test functions in
-    // utils.dart because it needs to be very explicit about when the event
-    // stream is and is not subscribed.
-    var watcher = createWatcher();
-
-    // Subscribe to the events.
-    var completer = new Completer();
-    var subscription = watcher.events.listen((event) {
-      expect(event.type, equals(ChangeType.ADD));
-      expect(event.path, endsWith("file.txt"));
-      completer.complete();
-    });
-
-    writeFile("file.txt");
-
-    // Then wait until we get an event for it.
-    schedule(() => completer.future);
-
-    // Unsubscribe.
-    schedule(() {
-      subscription.cancel();
-    });
-
-    // Now write a file while we aren't listening.
-    writeFile("unwatched.txt");
-
-    // Then start listening again.
-    schedule(() {
-      completer = new Completer();
-      subscription = watcher.events.listen((event) {
-        // We should get an event for the third file, not the one added while
-        // we weren't subscribed.
-        expect(event.type, equals(ChangeType.ADD));
-        expect(event.path, endsWith("added.txt"));
-        completer.complete();
-      });
-    });
-
-    // The watcher will have been cancelled and then resumed in the middle of
-    // its pause between polling loops. That means the second scan to skip
-    // what changed while we were unsubscribed won't happen until after that
-    // delay is done. Wait long enough for that to happen.
-    schedule(() => new Future.delayed(new Duration(seconds: 1)));
-
-    // And add a third file.
-    writeFile("added.txt");
-
-    // Wait until we get an event for the third file.
-    schedule(() => completer.future);
-
-    schedule(() {
-      subscription.cancel();
-    });
-  });
-
-
-  test('ready does not complete until after subscription', () {
-    var watcher = createWatcher(waitForReady: false);
-
-    var ready = false;
-    watcher.ready.then((_) {
-      ready = true;
-    });
-
-    // Should not be ready yet.
-    schedule(() {
-      expect(ready, isFalse);
-    });
-
-    // Subscribe to the events.
-    schedule(() {
-      var subscription = watcher.events.listen((event) {});
-
-      currentSchedule.onComplete.schedule(() {
-        subscription.cancel();
-      });
-    });
-
-    // Should eventually be ready.
-    schedule(() => watcher.ready);
-
-    schedule(() {
-      expect(ready, isTrue);
-    });
-  });
-
-  test('ready completes immediately when already ready', () {
-    var watcher = createWatcher(waitForReady: false);
-
-    // Subscribe to the events.
-    schedule(() {
-      var subscription = watcher.events.listen((event) {});
-
-      currentSchedule.onComplete.schedule(() {
-        subscription.cancel();
-      });
-    });
-
-    // Should eventually be ready.
-    schedule(() => watcher.ready);
-
-    // Now ready should be a future that immediately completes.
-    var ready = false;
-    schedule(() {
-      watcher.ready.then((_) {
-        ready = true;
-      });
-    });
-
-    schedule(() {
-      expect(ready, isTrue);
-    });
-  });
-
-  test('ready returns a future that does not complete after unsubscribing', () {
-    var watcher = createWatcher(waitForReady: false);
-
-    // Subscribe to the events.
-    var subscription;
-    schedule(() {
-      subscription = watcher.events.listen((event) {});
-    });
-
-    var ready = false;
-
-    // Wait until ready.
-    schedule(() => watcher.ready);
-
-    // Now unsubscribe.
-    schedule(() {
-      subscription.cancel();
-
-      // Track when it's ready again.
-      ready = false;
-      watcher.ready.then((_) {
-        ready = true;
-      });
-    });
-
-    // Should be back to not ready.
-    schedule(() {
-      expect(ready, isFalse);
-    });
-  });
 }
diff --git a/pkgs/watcher/test/no_subscription_test.dart b/pkgs/watcher/test/no_subscription_test.dart
new file mode 100644
index 0000000..e9cb4e3
--- /dev/null
+++ b/pkgs/watcher/test/no_subscription_test.dart
@@ -0,0 +1,73 @@
+// Copyright (c) 2012, 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.
+
+import 'dart:async';
+import 'dart:io';
+
+import 'package:scheduled_test/scheduled_test.dart';
+import 'package:watcher/watcher.dart';
+
+import 'utils.dart';
+
+main() {
+  initConfig();
+
+  setUp(createSandbox);
+
+  test('does not notify for changes when there were no subscribers', () {
+    // Note that this test doesn't rely as heavily on the test functions in
+    // utils.dart because it needs to be very explicit about when the event
+    // stream is and is not subscribed.
+    var watcher = createWatcher();
+
+    // Subscribe to the events.
+    var completer = new Completer();
+    var subscription = watcher.events.listen((event) {
+      expect(event.type, equals(ChangeType.ADD));
+      expect(event.path, endsWith("file.txt"));
+      completer.complete();
+    });
+
+    writeFile("file.txt");
+
+    // Then wait until we get an event for it.
+    schedule(() => completer.future);
+
+    // Unsubscribe.
+    schedule(() {
+      subscription.cancel();
+    });
+
+    // Now write a file while we aren't listening.
+    writeFile("unwatched.txt");
+
+    // Then start listening again.
+    schedule(() {
+      completer = new Completer();
+      subscription = watcher.events.listen((event) {
+        // We should get an event for the third file, not the one added while
+        // we weren't subscribed.
+        expect(event.type, equals(ChangeType.ADD));
+        expect(event.path, endsWith("added.txt"));
+        completer.complete();
+      });
+    });
+
+    // The watcher will have been cancelled and then resumed in the middle of
+    // its pause between polling loops. That means the second scan to skip
+    // what changed while we were unsubscribed won't happen until after that
+    // delay is done. Wait long enough for that to happen.
+    schedule(() => new Future.delayed(new Duration(seconds: 1)));
+
+    // And add a third file.
+    writeFile("added.txt");
+
+    // Wait until we get an event for the third file.
+    schedule(() => completer.future);
+
+    schedule(() {
+      subscription.cancel();
+    });
+  });
+}
diff --git a/pkgs/watcher/test/ready_test.dart b/pkgs/watcher/test/ready_test.dart
new file mode 100644
index 0000000..dd799ce
--- /dev/null
+++ b/pkgs/watcher/test/ready_test.dart
@@ -0,0 +1,106 @@
+// Copyright (c) 2012, 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.
+
+import 'dart:async';
+import 'dart:io';
+
+import 'package:scheduled_test/scheduled_test.dart';
+import 'package:watcher/watcher.dart';
+
+import 'utils.dart';
+
+main() {
+  initConfig();
+
+  setUp(createSandbox);
+
+  test('ready does not complete until after subscription', () {
+    var watcher = createWatcher(waitForReady: false);
+
+    var ready = false;
+    watcher.ready.then((_) {
+      ready = true;
+    });
+
+    // Should not be ready yet.
+    schedule(() {
+      expect(ready, isFalse);
+    });
+
+    // Subscribe to the events.
+    schedule(() {
+      var subscription = watcher.events.listen((event) {});
+
+      currentSchedule.onComplete.schedule(() {
+        subscription.cancel();
+      });
+    });
+
+    // Should eventually be ready.
+    schedule(() => watcher.ready);
+
+    schedule(() {
+      expect(ready, isTrue);
+    });
+  });
+
+  test('ready completes immediately when already ready', () {
+    var watcher = createWatcher(waitForReady: false);
+
+    // Subscribe to the events.
+    schedule(() {
+      var subscription = watcher.events.listen((event) {});
+
+      currentSchedule.onComplete.schedule(() {
+        subscription.cancel();
+      });
+    });
+
+    // Should eventually be ready.
+    schedule(() => watcher.ready);
+
+    // Now ready should be a future that immediately completes.
+    var ready = false;
+    schedule(() {
+      watcher.ready.then((_) {
+        ready = true;
+      });
+    });
+
+    schedule(() {
+      expect(ready, isTrue);
+    });
+  });
+
+  test('ready returns a future that does not complete after unsubscribing', () {
+    var watcher = createWatcher(waitForReady: false);
+
+    // Subscribe to the events.
+    var subscription;
+    schedule(() {
+      subscription = watcher.events.listen((event) {});
+    });
+
+    var ready = false;
+
+    // Wait until ready.
+    schedule(() => watcher.ready);
+
+    // Now unsubscribe.
+    schedule(() {
+      subscription.cancel();
+
+      // Track when it's ready again.
+      ready = false;
+      watcher.ready.then((_) {
+        ready = true;
+      });
+    });
+
+    // Should be back to not ready.
+    schedule(() {
+      expect(ready, isFalse);
+    });
+  });
+}
diff --git a/pkgs/watcher/test/utils.dart b/pkgs/watcher/test/utils.dart
index 7b3da02..5a22e60 100644
--- a/pkgs/watcher/test/utils.dart
+++ b/pkgs/watcher/test/utils.dart
@@ -77,7 +77,9 @@
 /// and is polling for changes. If you pass `false` for [waitForReady], it will
 /// not schedule this delay.
 DirectoryWatcher createWatcher({bool waitForReady}) {
-  _watcher = new DirectoryWatcher(_sandboxDir);
+  // Use a short delay to make the tests run quickly.
+  _watcher = new DirectoryWatcher(_sandboxDir,
+      pollingDelay: new Duration(milliseconds: 100));
 
   // Wait until the scan is finished so that we don't miss changes to files
   // that could occur before the scan completes.