| Index: pkg/watcher/test/directory_watcher_test.dart | 
| diff --git a/pkg/watcher/test/directory_watcher_test.dart b/pkg/watcher/test/directory_watcher_test.dart | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..635f7ee6704e9ab680b01392d7c5e2f353ff8884 | 
| --- /dev/null | 
| +++ b/pkg/watcher/test/directory_watcher_test.dart | 
| @@ -0,0 +1,239 @@ | 
| +// 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 files that already exist when started', () { | 
| +    // Make some pre-existing files. | 
| +    writeFile("a.txt"); | 
| +    writeFile("b.txt"); | 
| + | 
| +    createWatcher(); | 
| + | 
| +    // Change one after the watcher is running. | 
| +    writeFile("b.txt", contents: "modified"); | 
| + | 
| +    // We should get a modify event for the changed file, but no add events | 
| +    // for them before this. | 
| +    expectModifyEvent("b.txt"); | 
| +  }); | 
| + | 
| +  test('notifies when a file is added', () { | 
| +    createWatcher(); | 
| +    writeFile("file.txt"); | 
| +    expectAddEvent("file.txt"); | 
| +  }); | 
| + | 
| +  test('notifies when a file is modified', () { | 
| +    writeFile("file.txt"); | 
| +    createWatcher(); | 
| +    writeFile("file.txt", contents: "modified"); | 
| +    expectModifyEvent("file.txt"); | 
| +  }); | 
| + | 
| +  test('notifies when a file is removed', () { | 
| +    writeFile("file.txt"); | 
| +    createWatcher(); | 
| +    deleteFile("file.txt"); | 
| +    expectRemoveEvent("file.txt"); | 
| +  }); | 
| + | 
| +  test('notifies when a file is moved', () { | 
| +    writeFile("old.txt"); | 
| +    createWatcher(); | 
| +    renameFile("old.txt", "new.txt"); | 
| +    expectAddEvent("new.txt"); | 
| +    expectRemoveEvent("old.txt"); | 
| +  }); | 
| + | 
| +  test('notifies when a file is modified multiple times', () { | 
| +    writeFile("file.txt"); | 
| +    createWatcher(); | 
| +    writeFile("file.txt", contents: "modified"); | 
| +    expectModifyEvent("file.txt"); | 
| +    writeFile("file.txt", contents: "modified again"); | 
| +    expectModifyEvent("file.txt"); | 
| +  }); | 
| + | 
| +  test('does not notify if the file contents are unchanged', () { | 
| +    writeFile("a.txt", contents: "same"); | 
| +    writeFile("b.txt", contents: "before"); | 
| +    createWatcher(); | 
| +    writeFile("a.txt", contents: "same"); | 
| +    writeFile("b.txt", contents: "after"); | 
| +    expectModifyEvent("b.txt"); | 
| +  }); | 
| + | 
| +  test('does not notify if the modification time did not change', () { | 
| +    writeFile("a.txt", contents: "before"); | 
| +    writeFile("b.txt", contents: "before"); | 
| +    createWatcher(); | 
| +    writeFile("a.txt", contents: "after", updateModified: false); | 
| +    writeFile("b.txt", contents: "after"); | 
| +    expectModifyEvent("b.txt"); | 
| +  }); | 
| + | 
| +  test('watches files in subdirectories', () { | 
| +    createWatcher(); | 
| +    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); | 
| +    }); | 
| +  }); | 
| +} | 
|  |