Fix bug (and re-enable test) in ReplayIOSink (#34)

diff --git a/lib/src/backends/record_replay/replay_io_sink.dart b/lib/src/backends/record_replay/replay_io_sink.dart
index 2bacb1f..8f0f91d 100644
--- a/lib/src/backends/record_replay/replay_io_sink.dart
+++ b/lib/src/backends/record_replay/replay_io_sink.dart
@@ -2,6 +2,7 @@
 // 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:convert';
 
 import 'package:file/file.dart';
@@ -41,4 +42,14 @@
 
   @override
   List<Map<String, dynamic>> get manifest => _fileSystem.manifest;
+
+  @override
+  dynamic onResult(Invocation invocation, dynamic result) {
+    if (invocation.memberName == #addStream) {
+      Stream<List<int>> stream = invocation.positionalArguments.first;
+      Future<dynamic> future = result;
+      return future.then((_) => stream.drain());
+    }
+    return result;
+  }
 }
diff --git a/lib/src/backends/record_replay/replay_proxy_mixin.dart b/lib/src/backends/record_replay/replay_proxy_mixin.dart
index 401781b..56d8258 100644
--- a/lib/src/backends/record_replay/replay_proxy_mixin.dart
+++ b/lib/src/backends/record_replay/replay_proxy_mixin.dart
@@ -90,8 +90,20 @@
   /// receive a [NoMatchingInvocationError].
   ///
   /// This manifest exists as `MANIFEST.txt` in a recording directory.
+  @protected
   List<Map<String, dynamic>> get manifest;
 
+  /// Protected method for subclasses to be notified when an invocation has
+  /// been successfully replayed, and the result is about to be returned to
+  /// the caller.
+  ///
+  /// Returns the value that is to be returned to the caller. The default
+  /// implementation returns [result] (replayed from the recording); subclasses
+  /// may override this method to alter the result that's returned to the
+  /// caller.
+  @protected
+  dynamic onResult(Invocation invocation, dynamic result) => result;
+
   @override
   dynamic noSuchMethod(Invocation invocation) {
     Symbol name = invocation.memberName;
@@ -114,7 +126,9 @@
     }
     entry[kManifestOrdinalKey] = _nextOrdinal++;
 
-    return reviver.convert(entry[kManifestResultKey]);
+    dynamic result = reviver.convert(entry[kManifestResultKey]);
+    result = onResult(invocation, result);
+    return result;
   }
 
   /// Finds the next available invocation event in the [manifest] that matches
diff --git a/test/replay_test.dart b/test/replay_test.dart
index f68ebf3..f9f7fa9 100644
--- a/test/replay_test.dart
+++ b/test/replay_test.dart
@@ -40,10 +40,7 @@
       replay: replay,
       skip: <String>[
         // ReplayFileSystem does not yet replay exceptions
-        '.*(disallows|throws).*',
-
-        // TODO(tvolkert): Fix breakage, and re-enable
-        'File > openWrite > ioSink > addStream',
+        '.*(disallows|throws|blocks).*',
 
         'File > open', // Not yet implemented in MemoryFileSystem
       ],