Change most build-delay tests  to unit tests so they aren't flaky (#1655)

diff --git a/test/serve/build_delay_test.dart b/test/serve/build_delay_test.dart
index 81dccba..1d13a06 100644
--- a/test/serve/build_delay_test.dart
+++ b/test/serve/build_delay_test.dart
@@ -4,9 +4,15 @@
 
 import 'dart:async';
 
+import 'package:barback/barback.dart';
+import 'package:pub/src/compiler.dart';
+import 'package:pub/src/entrypoint.dart';
+import 'package:pub/src/barback/asset_environment.dart';
 import 'package:pub/src/io.dart';
+import 'package:pub/src/system_cache.dart';
 import 'package:path/path.dart' as p;
 import 'package:test/test.dart';
+import 'package:watcher/watcher.dart';
 
 import '../descriptor.dart' as d;
 import '../test_pub.dart';
@@ -27,30 +33,6 @@
     libFilePath = p.join(d.sandbox, appPath, "lib", "lib.dart");
   });
 
-  test("setting build-delay to zero causes a build per edit", () async {
-    var pubServeProcess =
-        await pubServe(forcePoll: false, args: ['--build-delay', '0']);
-    expect(pubServeProcess.stdout,
-        emitsThrough(contains('Build completed successfully')));
-    await requestShouldSucceed("packages/myapp/lib.dart", "foo() => 'foo';");
-
-    // Flush here as that helps to make sure we get multiple modify events,
-    // otherwise we tend to only get one.
-    writeTextFile(libFilePath, "foo() => 'bar';");
-    await new Future(() {});
-    writeTextFile(libFilePath, "foo() => 'baz';");
-
-    // Should see multiple builds.
-    expect(pubServeProcess.stdout,
-        emitsThrough(contains('Build completed successfully')));
-    expect(pubServeProcess.stdout,
-        emitsThrough(contains('Build completed successfully')));
-
-    await requestShouldSucceed("packages/myapp/lib.dart", "foo() => 'baz';");
-
-    await endPubServe();
-  });
-
   test("setting a long build-delay works", () async {
     var pubServeProcess =
         await pubServe(forcePoll: false, args: ['--build-delay', '1000']);
@@ -72,52 +54,107 @@
     await endPubServe();
   });
 
-  test("continual fast edits won't cause multiple builds", () async {
-    var pubServeProcess =
-        await pubServe(forcePoll: false, args: ['--build-delay', '100']);
-    expect(pubServeProcess.stdout,
-        emitsThrough(contains('Build completed successfully')));
-    await requestShouldSucceed("packages/myapp/lib.dart", "foo() => 'foo';");
+  group('unit tests', () {
+    Barback barback;
+    List<BuildResult> barbackResults;
+    AssetEnvironment environment;
+    final libAssetId = new AssetId('myapp', 'lib/lib.dart');
+    // Watcher for the lib dir.
+    _MockDirectoryWatcher libWatcher;
+    _MockWatcherType watcherType;
 
-    for (var i = 0; i < 10; i++) {
-      writeTextFile(libFilePath, "foo() => '$i';");
-      await new Future.delayed(new Duration(milliseconds: 50));
+    setUp(() async {
+      var entrypoint =
+          new Entrypoint(p.join(d.sandbox, appPath), new SystemCache());
+      watcherType = new _MockWatcherType();
+      environment = await AssetEnvironment.create(entrypoint, BarbackMode.DEBUG,
+          watcherType: watcherType,
+          buildDelay: new Duration(milliseconds: 50),
+          compiler: Compiler.none);
+      barback = environment.barback;
+      libWatcher = watcherType.watchers[p.join(d.sandbox, appPath, 'lib')];
+      // Collect build results.
+      barbackResults = <BuildResult>[];
+      barback.results.listen(barbackResults.add);
+    });
+
+    tearDown(() async {
+      await environment.cleanUp();
+    });
+
+    // Attempts to wait for all pending barback builds.
+    Future waitForBarback() async {
+      // First, wait for the next build to complete.
+      await barback.results.first;
+      // Then wait for all assets, which should capture additional builds if
+      // they occur.
+      await barback.getAllAssets();
+      // Give the stream a chance to deliver a new build if one did occur.
+      await new Future(() {});
     }
 
-    // Should only see one build.
-    expect(pubServeProcess.stdout,
-        emitsThrough(contains('Build completed successfully')));
-    expect(pubServeProcess.stdout, neverEmits('Build completed successfully'));
+    test("continual fast edits don't cause multiple builds", () async {
+      expect(await (await barback.getAssetById(libAssetId)).readAsString(),
+          "foo() => 'foo';");
 
-    await requestShouldSucceed("packages/myapp/lib.dart", "foo() => '9';");
+      for (var i = 0; i < 10; i++) {
+        writeTextFile(libFilePath, "foo() => '$i';");
+        libWatcher.addEvent(new WatchEvent(ChangeType.MODIFY, libFilePath));
+        await new Future.delayed(new Duration(milliseconds: 10));
+      }
 
-    await endPubServe();
+      // Should get exactly one build result.
+      await waitForBarback();
+      expect(barbackResults.length, 1);
+      expect(await (await barback.getAssetById(libAssetId)).readAsString(),
+          "foo() => '9';");
+    });
+
+    // Regression test for https://github.com/dart-lang/sdk/issues/29890
+    test("editors safe write features shouldn't cause failed builds", () async {
+      // Simulate the safe-write feature from many editors:
+      //   - Create a backup file
+      //   - Edit original file
+      //   - Delete backup file
+      var backupFilePath = p.join(d.sandbox, appPath, "lib", "lib.dart.bak");
+      writeTextFile(backupFilePath, "foo() => 'foo';");
+      libWatcher.addEvent(new WatchEvent(ChangeType.ADD, backupFilePath));
+      await new Future(() {});
+      deleteEntry(backupFilePath);
+      libWatcher.addEvent(new WatchEvent(ChangeType.REMOVE, backupFilePath));
+
+      // Should get a single successful build result.
+      await waitForBarback();
+      expect(barbackResults.length, 1);
+      expect(barbackResults.first.succeeded, isTrue);
+    });
   });
+}
 
-  // Regression test for https://github.com/dart-lang/sdk/issues/29890
-  test("editors safe write features shouldn't cause failed builds", () async {
-    var pubServeProcess = await pubServe(forcePoll: false);
-    expect(pubServeProcess.stdout,
-        emitsThrough(contains('Build completed successfully')));
-    await requestShouldSucceed("packages/myapp/lib.dart", "foo() => 'foo';");
+/// Mock [WatcherType] that creates [_MockDirectoryWatcher]s and gives you
+/// access to them.
+class _MockWatcherType implements WatcherType {
+  final watchers = <String, _MockDirectoryWatcher>{};
 
-    // Simulate the safe-write feature from many editors:
-    //   - Create a backup file
-    //   - Edit original file
-    //   - Delete backup file
-    var backupFilePath = p.join(d.sandbox, appPath, "lib", "lib.dart.bak");
-    writeTextFile(backupFilePath, "foo() => 'foo';");
-    // Allow pub to schedule a new build.
-    await new Future(() {});
-    writeTextFile(libFilePath, "foo() => 'bar';");
-    deleteEntry(backupFilePath);
+  _MockDirectoryWatcher create(String dir) {
+    var watcher = new _MockDirectoryWatcher(dir);
+    watchers[dir] = watcher;
+    return watcher;
+  }
+}
 
-    expect(pubServeProcess.stderr, neverEmits(contains("Build error")));
-    expect(pubServeProcess.stdout,
-        emitsThrough(contains('Build completed successfully')));
-    await requestShould404("packages/myapp/lib.dart.bak");
-    await requestShouldSucceed("packages/myapp/lib.dart", "foo() => 'bar';");
+/// Mock [DirectoryWatcher] that you add events to manually using [addEvent].
+class _MockDirectoryWatcher implements DirectoryWatcher {
+  final _eventsController = new StreamController<WatchEvent>();
+  Stream<WatchEvent> get events => _eventsController.stream;
 
-    await endPubServe();
-  });
+  final String path;
+  String get directory => path;
+
+  final ready = new Future<Null>(() {});
+  bool get isReady => true;
+
+  _MockDirectoryWatcher(this.path);
+
+  void addEvent(WatchEvent event) => _eventsController.add(event);
 }