Revert "Mac no longer fire bogus events. Fix Mac watcher."

BUG=

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

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/watcher@41221 260f80e4-7a28-3924-810f-c04153c831b5
diff --git a/pkgs/watcher/CHANGELOG.md b/pkgs/watcher/CHANGELOG.md
index 175a860..eb544f6 100644
--- a/pkgs/watcher/CHANGELOG.md
+++ b/pkgs/watcher/CHANGELOG.md
@@ -1,7 +1,3 @@
-# 0.9.4
-
-* Remove delay-fixes in MacOSDirectoryWatcher, by depending on sdk 1.6.
-
 # 0.9.3
 
 * Improved support for Windows via `WindowsDirectoryWatcher`.
diff --git a/pkgs/watcher/lib/src/directory_watcher/mac_os.dart b/pkgs/watcher/lib/src/directory_watcher/mac_os.dart
index 65f07fa..e3efa2d 100644
--- a/pkgs/watcher/lib/src/directory_watcher/mac_os.dart
+++ b/pkgs/watcher/lib/src/directory_watcher/mac_os.dart
@@ -73,15 +73,30 @@
   /// a subdirectory that was moved into the watched directory.
   StreamSubscription<FileSystemEntity> _listSubscription;
 
+  /// The timer for tracking how long we wait for an initial batch of bogus
+  /// events (see issue 14373).
+  Timer _bogusEventTimer;
+
   _MacOSDirectoryWatcher(String directory, int parentId)
       : directory = directory,
         _files = new PathSet(directory),
         _id = "$parentId/${_count++}" {
-    _listDir().then((_) {
-      if (MacOSDirectoryWatcher.logDebugInfo) {
-        print("[$_id] finished initial directory list");
-      }
-      _startWatch();
+    _startWatch();
+
+    // Before we're ready to emit events, wait for [_listDir] to complete and
+    // for enough time to elapse that if bogus events (issue 14373) would be
+    // emitted, they will be.
+    //
+    // If we do receive a batch of events, [_onBatch] will ensure that these
+    // futures don't fire and that the directory is re-listed.
+    Future.wait([
+      _listDir().then((_) {
+        if (MacOSDirectoryWatcher.logDebugInfo) {
+          print("[$_id] finished initial directory list");
+        }
+      }),
+      _waitForBogusEvents()
+    ]).then((_) {
       if (MacOSDirectoryWatcher.logDebugInfo) {
         print("[$_id] watcher is ready, known files:");
         for (var file in _files.toSet()) {
@@ -119,6 +134,29 @@
       }
     }
 
+    // If we get a batch of events before we're ready to begin emitting events,
+    // it's probable that it's a batch of pre-watcher events (see issue 14373).
+    // Ignore those events and re-list the directory.
+    if (!isReady) {
+      if (MacOSDirectoryWatcher.logDebugInfo) {
+        print("[$_id] not ready to emit events, re-listing directory");
+      }
+
+      // Cancel the timer because bogus events only occur in the first batch, so
+      // we can fire [ready] as soon as we're done listing the directory.
+      _bogusEventTimer.cancel();
+      _listDir().then((_) {
+        if (MacOSDirectoryWatcher.logDebugInfo) {
+          print("[$_id] watcher is ready, known files:");
+          for (var file in _files.toSet()) {
+            print("[$_id]   ${p.relative(file, from: directory)}");
+          }
+        }
+        _readyCompleter.complete();
+      });
+      return;
+    }
+
     _sortEvents(batch).forEach((path, events) {
       var relativePath = p.relative(path, from: directory);
       if (MacOSDirectoryWatcher.logDebugInfo) {
@@ -354,6 +392,17 @@
 
     _watchSubscription = null;
 
+    // If the directory still exists and we're still expecting bogus events,
+    // this is probably issue 14849 rather than a real close event. We should
+    // just restart the watcher.
+    if (!isReady && new Directory(directory).existsSync()) {
+      if (MacOSDirectoryWatcher.logDebugInfo) {
+        print("[$_id] fake closure (issue 14849), re-opening stream");
+      }
+      _startWatch();
+      return;
+    }
+
     // FSEvents can fail to report the contents of the directory being removed
     // when the directory itself is removed, so we need to manually mark the
     // files as removed.
@@ -393,6 +442,19 @@
     return completer.future;
   }
 
+  /// Wait 200ms for a batch of bogus events (issue 14373) to come in.
+  ///
+  /// 200ms is short in terms of human interaction, but longer than any Mac OS
+  /// watcher tests take on the bots, so it should be safe to assume that any
+  /// bogus events will be signaled in that time frame.
+  Future _waitForBogusEvents() {
+    var completer = new Completer();
+    _bogusEventTimer = new Timer(
+        new Duration(milliseconds: 200),
+        completer.complete);
+    return completer.future;
+  }
+
   /// Emit an event with the given [type] and [path].
   void _emitEvent(ChangeType type, String path) {
     if (!isReady) return;
diff --git a/pkgs/watcher/pubspec.yaml b/pkgs/watcher/pubspec.yaml
index 473e1c9..6f96a56 100644
--- a/pkgs/watcher/pubspec.yaml
+++ b/pkgs/watcher/pubspec.yaml
@@ -1,5 +1,5 @@
 name: watcher
-version: 0.9.4
+version: 0.9.3
 author: Dart Team <misc@dartlang.org>
 homepage: http://www.dartlang.org
 description: >
@@ -13,5 +13,3 @@
 dev_dependencies:
   scheduled_test: '>=0.9.3 <0.12.0'
   unittest: '>=0.9.2 <0.12.0'
-environment:
-  sdk: ">=1.6.0 <2.0.0"