use vm_service_client to talk to Observatory
diff --git a/pkgs/coverage/lib/src/collect.dart b/pkgs/coverage/lib/src/collect.dart
index c4a850a..6d84e16 100644
--- a/pkgs/coverage/lib/src/collect.dart
+++ b/pkgs/coverage/lib/src/collect.dart
@@ -6,15 +6,17 @@
 
 import 'dart:async';
 
-import 'devtools.dart';
+import 'vm_service_client.dart';
 import 'util.dart';
 
 const _retryInterval = const Duration(milliseconds: 200);
 
 Future<Map> collect(String host, int port, bool resume, bool waitPaused,
     {Duration timeout}) async {
+  var uri = 'ws://$host:$port/ws';
+
   var vmService = await retry(
-      () => VMService.connect(host, port), _retryInterval,
+      () => VMServiceClient.connect(uri), _retryInterval,
       timeout: timeout);
   try {
     if (waitPaused) {
@@ -30,30 +32,34 @@
   }
 }
 
-Future<Map> _getAllCoverage(VMService service) async {
+Future<Map> _getAllCoverage(VMServiceClient service) async {
   var vm = await service.getVM();
   var allCoverage = [];
 
   for (var isolateRef in vm.isolates) {
-    var coverage = await service.getCoverage(isolateRef.id);
+    var isolate = await isolateRef.load();
+    var coverage = await service.getCoverage(isolate);
     allCoverage.addAll(coverage.coverage);
   }
   return {'type': 'CodeCoverage', 'coverage': allCoverage};
 }
 
-Future _resumeIsolates(VMService service) async {
+Future _resumeIsolates(VMServiceClient service) async {
   var vm = await service.getVM();
   for (var isolateRef in vm.isolates) {
-    await service.resume(isolateRef.id);
+    var isolate = await isolateRef.load();
+    if (isolate.isPaused) {
+      await isolateRef.resume();
+    }
   }
 }
 
-Future _waitIsolatesPaused(VMService service, {Duration timeout}) async {
+Future _waitIsolatesPaused(VMServiceClient service, {Duration timeout}) async {
   allPaused() async {
     var vm = await service.getVM();
     for (var isolateRef in vm.isolates) {
-      var isolate = await service.getIsolate(isolateRef.id);
-      if (!isolate.paused) throw "Unpaused isolates remaining.";
+      var isolate = await isolateRef.load();
+      if (!isolate.isPaused) throw "Unpaused isolates remaining.";
     }
   }
   return retry(allPaused, _retryInterval, timeout: timeout);
diff --git a/pkgs/coverage/lib/src/devtools.dart b/pkgs/coverage/lib/src/devtools.dart
deleted file mode 100644
index 1c2f534..0000000
--- a/pkgs/coverage/lib/src/devtools.dart
+++ /dev/null
@@ -1,319 +0,0 @@
-// Copyright (c) 2014, 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.
-
-library coverage.src.devtools;
-
-import 'dart:async';
-import 'dart:convert' show JSON;
-import 'dart:io';
-import 'package:logging/logging.dart';
-
-final Logger _log = new Logger('coverage.src.devtools');
-
-class VMService {
-  final _Connection _connection;
-
-  VMService._(this._connection);
-
-  Future<VM> getVM() async {
-    var response = await _connection.request('getVM');
-    return new VM.fromJson(response);
-  }
-
-  Future<Isolate> getIsolate(String isolateId) async {
-    var response =
-        await _connection.request('getIsolate', {'isolateId': isolateId});
-    return new Isolate.fromJson(response);
-  }
-
-  //TODO(kevmoo): Test this - https://github.com/dart-lang/coverage/issues/90
-  Future<AllocationProfile> getAllocationProfile(String isolateId,
-      {bool reset, bool gc}) async {
-    var params = {'isolateId': isolateId};
-    if (reset != null) {
-      params['reset'] = reset;
-    }
-    if (gc != null) {
-      params['gc'] = 'full';
-    }
-
-    // TODO(kevmoo) Remove fallback logic once 1.11 is stable
-    // https://github.com/dart-lang/coverage/issues/91
-    var response;
-    try {
-      // For Dart >=1.11.0-dev.3.0 - _getAllocationProfile is considered private
-      response = await _connection.request('_getAllocationProfile', params);
-    } on ServiceProtocolErrorBase catch (error) {
-      if (error.isMethodNotFound) {
-        // For Dart <1.11.0-dev.3.0 - getAllocationProfile is considered public
-        response = await _connection.request('getAllocationProfile', params);
-      } else {
-        rethrow;
-      }
-    }
-
-    return new AllocationProfile.fromJson(response);
-  }
-
-  Future<CodeCoverage> getCoverage(String isolateId, {String targetId}) async {
-    var params = {'isolateId': isolateId};
-    if (targetId != null) {
-      params['targetId'] = targetId;
-    }
-
-    var response;
-
-    // TODO(kevmoo) Remove fallback logic once 1.11 is stable
-    // https://github.com/dart-lang/coverage/issues/91
-    try {
-      // For Dart >=1.11.0-dev.3.0 - _getCoverage is considered private
-      response = await _connection.request('_getCoverage', params);
-    } on ServiceProtocolErrorBase catch (error) {
-      if (error.isMethodNotFound) {
-        // For Dart <1.11.0-dev.3.0 - getCoverage is considered public
-        response = await _connection.request('getCoverage', params);
-      } else {
-        rethrow;
-      }
-    }
-    return new CodeCoverage.fromJson(response);
-  }
-
-  Future resume(String isolateId) =>
-      _connection.request('resume', {'isolateId': isolateId});
-
-  static Future<VMService> connect(String host, int port) async {
-    _log.fine('Connecting to host $host on port $port');
-
-    return connectToVMWebsocket(host, port);
-  }
-
-  static Future<VMService> connectToVMWebsocket(String host, int port) async {
-    var connection = await _Connection.connect(host, port);
-    return new VMService._(connection);
-  }
-
-  Future close() => _connection.close();
-}
-
-class VM {
-  final String id;
-  final String targetCPU;
-  final String hostCPU;
-  final String version;
-  final String pid;
-  final List<IsolateRef> isolates;
-
-  VM(this.id, this.targetCPU, this.hostCPU, this.version, this.pid,
-      this.isolates);
-
-  factory VM.fromJson(json) => new VM(
-      json['id'],
-      json['targetCPU'],
-      json['hostCPU'],
-      json['version'],
-      json['pid'],
-      json['isolates'].map((i) => new IsolateRef.fromJson(i)).toList());
-}
-
-class IsolateRef {
-  final String id;
-  final String name;
-
-  IsolateRef(this.id, this.name);
-
-  factory IsolateRef.fromJson(json) => new IsolateRef(json['id'], json['name']);
-}
-
-class Isolate {
-  final String id;
-  final String name;
-  final bool pauseOnExit;
-  final ServiceEvent pauseEvent;
-  bool get paused =>
-      pauseOnExit && pauseEvent != null && pauseEvent.kind == 'PauseExit';
-
-  Isolate(this.id, this.name, this.pauseOnExit, this.pauseEvent);
-
-  factory Isolate.fromJson(json) => new Isolate(json['id'], json['name'],
-      json['pauseOnExit'], new ServiceEvent.fromJson(json['pauseEvent']));
-}
-
-class ServiceEvent {
-  final String kind;
-  final IsolateRef isolate;
-
-  @Deprecated('Will be removed in 0.8')
-  String get eventType => kind;
-
-  ServiceEvent(this.kind, this.isolate);
-
-  factory ServiceEvent.fromJson(Map json) {
-    // TODO(kevmoo) Keep around until 1.11 is stable
-    // https://github.com/dart-lang/coverage/issues/91
-    // 'kind' is the key for >= 1.11-dev.5.0.
-    // 'eventType' is for older versions
-    var kind = json.containsKey('kind') ? json['kind'] : json['eventType'];
-
-    return new ServiceEvent(kind, new IsolateRef.fromJson(json['isolate']));
-  }
-}
-
-class CodeCoverage {
-  final String id;
-  final List coverage;
-
-  CodeCoverage(this.id, this.coverage);
-
-  factory CodeCoverage.fromJson(json) =>
-      new CodeCoverage(json['id'], json['coverage']);
-}
-
-class AllocationProfile {
-  final String id;
-
-  AllocationProfile(this.id);
-
-  factory AllocationProfile.fromJson(json) => new AllocationProfile(json['id']);
-}
-
-/// Observatory connection via websocket.
-class _Connection {
-  final WebSocket _socket;
-  final Map<int, Completer> _pendingRequests = {};
-  int _requestId = 1;
-
-  _Connection(this._socket) {
-    _socket.listen(_handleResponse);
-  }
-
-  static Future<_Connection> connect(String host, int port) async {
-    _log.fine('Connecting to VM via HTTP websocket protocol');
-    var uri = 'ws://$host:$port/ws';
-    var socket = await WebSocket.connect(uri);
-    return new _Connection(socket);
-  }
-
-  Future<Map> request(String method, [Map params = const {}]) {
-    _pendingRequests[_requestId] = new Completer();
-    var message =
-        JSON.encode({'id': _requestId, 'method': method, 'params': params,});
-    _log.fine('Send> $message');
-    _socket.add(message);
-    return _pendingRequests[_requestId++].future;
-  }
-
-  Future close() => _socket.close();
-
-  void _handleResponse(String response) {
-    _log.fine('Recv< $response');
-    var json = JSON.decode(response);
-    var id = json['id'];
-    if (id is String) {
-      // Support for vm version >= 1.11.0
-      id = int.parse(id);
-    }
-    if (id == null || !_pendingRequests.keys.contains(id)) {
-      // Suppress unloved messages.
-      return;
-    }
-
-    var completer = _pendingRequests.remove(id);
-    if (completer == null) {
-      _log.severe('Failed to pair response with request');
-    }
-
-    // Behavior >= Dart 1.11-dev.3
-    var error = json['error'];
-    if (error != null) {
-      var errorObj = new JsonRpcError.fromJson(error);
-      completer.completeError(errorObj);
-      return;
-    }
-
-    var innerResponse = json['result'];
-    if (innerResponse == null) {
-      // Support for 1.9.0 <= vm version < 1.10.0.
-      innerResponse = json['response'];
-    }
-    if (innerResponse == null) {
-      completer.completeError('Failed to get JSON response for message $id');
-      return;
-    }
-    var message;
-    if (innerResponse != null) {
-      if (innerResponse is Map) {
-        // Support for vm version >= 1.11.0
-        message = innerResponse;
-      } else {
-        message = JSON.decode(innerResponse);
-      }
-    }
-
-    // need to check this for errors in the Dart 1.10 version
-    var type = message['type'];
-    if (type == 'Error') {
-      var errorObj = new Dart_1_10_RpcError.fromJson(message);
-      completer.completeError(errorObj);
-      return;
-    }
-
-    completer.complete(message);
-  }
-}
-
-abstract class ServiceProtocolErrorBase extends Error {
-  String get message;
-  bool get isMethodNotFound;
-}
-
-// TODO(kevmoo) Remove this logic once 1.11 is stable
-// https://github.com/dart-lang/coverage/issues/91
-class Dart_1_10_RpcError extends ServiceProtocolErrorBase {
-  final String message;
-  final bool isMethodNotFound;
-
-  Dart_1_10_RpcError(this.message, this.isMethodNotFound);
-
-  factory Dart_1_10_RpcError.fromJson(Map<String, dynamic> json) {
-    assert(json['type'] == 'Error');
-    var message = json['message'];
-
-    var isMethodNotFound = message.startsWith('unrecognized method:');
-
-    return new Dart_1_10_RpcError(message, isMethodNotFound);
-  }
-}
-
-class JsonRpcError extends ServiceProtocolErrorBase {
-  final int code;
-  final String message;
-  final data;
-
-  // http://www.jsonrpc.org/specification
-  // -32601	Method not found	The method does not exist / is not available.
-  bool get isMethodNotFound => code == -32601;
-
-  JsonRpcError(this.code, this.message, this.data);
-
-  factory JsonRpcError.fromJson(Map<String, dynamic> json) =>
-      new JsonRpcError(json['code'], json['message'], json['data']);
-
-  String toString() {
-    var msg = 'JsonRpcError: $message';
-    if (isMethodNotFound) {
-      if (data is Map) {
-        var request = data['request'];
-        if (request is Map) {
-          var method = request['method'];
-          if (method != null) {
-            msg = '$msg - "$method"';
-          }
-        }
-      }
-    }
-
-    return '$msg ($code)';
-  }
-}
diff --git a/pkgs/coverage/lib/src/vm_service_client.dart b/pkgs/coverage/lib/src/vm_service_client.dart
new file mode 100644
index 0000000..8bd676d
--- /dev/null
+++ b/pkgs/coverage/lib/src/vm_service_client.dart
@@ -0,0 +1,218 @@
+// Copyright (c) 2015, 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.
+
+// Contents of this file are mostly copied from
+// https://github.com/dart-lang/vm_service_client/blob/master/lib/vm_service_client.dart
+// At or about commit
+// https://github.com/dart-lang/vm_service_client/commit/a5d18cf62777d850b6e4505205f7b254679446ef
+
+import 'dart:async';
+import 'dart:convert';
+import 'dart:io';
+
+import 'package:async/async.dart';
+import 'package:json_rpc_2/json_rpc_2.dart' as rpc;
+
+import 'package:vm_service_client/src/exceptions.dart';
+import 'package:vm_service_client/src/flag.dart';
+import 'package:vm_service_client/src/isolate.dart';
+import 'package:vm_service_client/src/service_version.dart';
+import 'package:vm_service_client/src/stream_manager.dart';
+import 'package:vm_service_client/src/utils.dart';
+import 'package:vm_service_client/src/v1_compatibility.dart';
+import 'package:vm_service_client/src/vm.dart';
+
+/// A [StreamSinkTransformer] that converts encodes JSON messages.
+///
+/// We can't use fromStreamTransformer with JSON.encoder because it isn't
+/// guaranteed to emit the entire object as a single message, and the WebSocket
+/// protocol cares about that.
+final _jsonSinkEncoder = new StreamSinkTransformer.fromHandlers(
+    handleData: (data, sink) => sink.add(JSON.encode(data)));
+
+/// A client for the [Dart VM service protocol][service api].
+///
+/// [service api]: https://github.com/dart-lang/sdk/blob/master/runtime/vm/service/service.md
+///
+/// Connect to a VM service endpoint using [connect], and use [getVM] to load
+/// information about the VM itself.
+///
+/// The client supports VM service versions 1.x (which first shipped with Dart
+/// 1.11), 2.x (which first shipped with Dart 1.12), and 3.x (which first
+/// shipped with Dart 1.13). Some functionality may be unavailable in older VM
+/// service versions; those places will be clearly documented. You can check the
+/// version of the VM service you're connected to using [getVersion].
+///
+/// Because it takes an extra RPC call to verify compatibility with the protocol
+/// version, the client doesn't do so by default. Users who want to be sure
+/// they're talking to a supported protocol version can call [validateVersion].
+class VMServiceClient {
+  /// The underlying JSON-RPC peer used to communicate with the VM service.
+  final rpc.Peer _peer;
+
+  /// The streams shared among the entire service protocol client.
+  final StreamManager _streams;
+
+  /// A broadcast stream that emits every isolate as it starts.
+  Stream<VMIsolateRef> get onIsolateStart => _onIsolateStart;
+  Stream<VMIsolateRef> _onIsolateStart;
+
+  /// A broadcast stream that emits every isolate as it becomes runnable.
+  ///
+  /// These isolates are guaranteed to return a [VMRunnableIsolate] from
+  /// [VMIsolateRef.load].
+  ///
+  /// This is only supported on the VM service protocol version 3.0 and greater.
+  Stream<VMIsolateRef> get onIsolateRunnable => _onIsolateRunnable;
+  Stream<VMIsolateRef> _onIsolateRunnable;
+
+  /// A future that fires when the underlying connection has been closed.
+  ///
+  /// Any connection-level errors will also be emitted through this future.
+  final Future done;
+
+  /// Connects to the VM service protocol at [url].
+  ///
+  /// [url] may be a `ws://` or a `http://` URL. If it's `ws://`, it's
+  /// interpreted as the URL to connect to directly. If it's `http://`, it's
+  /// interpreted as the URL for the Dart observatory, and the corresponding
+  /// WebSocket URL is determined based on that. It may be either a [String] or
+  /// a [Uri].
+  static Future<VMServiceClient> connect(url) async {
+    if (url is! Uri && url is! String) {
+      throw new ArgumentError.value(url, "url", "must be a String or a Uri");
+    }
+
+    var uri = url is String ? Uri.parse(url) : url;
+    if (uri.scheme == 'http') uri = uri.replace(scheme: 'ws', path: '/ws');
+
+    return new VMServiceClient(await WebSocket.connect(uri.toString()));
+  }
+
+  /// Creates a client that reads incoming messages from [incoming] and writes
+  /// outgoing messages to [outgoing].
+  ///
+  /// If [incoming] is a [StreamSink] as well as a [Stream] (for example, a
+  /// [WebSocket]), [outgoing] may be omitted.
+  ///
+  /// This is useful when using the client over a pre-existing connection. To
+  /// establish a connection from scratch, use [connect].
+  factory VMServiceClient(Stream<String> incoming,
+      [StreamSink<String> outgoing]) {
+    if (outgoing == null) outgoing = incoming as StreamSink;
+
+    var incomingEncoded =
+        incoming.map(JSON.decode).transform(v1CompatibilityTransformer);
+    var outgoingEncoded = _jsonSinkEncoder.bind(outgoing);
+    return new VMServiceClient._(
+        new rpc.Peer.withoutJson(incomingEncoded, outgoingEncoded));
+  }
+
+  /// Creates a client that reads incoming decoded messages from [incoming] and
+  /// writes outgoing decoded messages to [outgoing].
+  ///
+  /// Unlike [new VMServiceClient], this doesn't read or write JSON strings.
+  /// Instead, it reads and writes decoded maps.
+  ///
+  /// If [incoming] is a [StreamSink] as well as a [Stream], [outgoing] may be
+  /// omitted.
+  ///
+  /// This is useful when using the client over a pre-existing connection. To
+  /// establish a connection from scratch, use [connect].
+  factory VMServiceClient.withoutJson(Stream incoming, [StreamSink outgoing]) {
+    if (outgoing == null) outgoing = incoming as StreamSink;
+
+    incoming = incoming.transform(v1CompatibilityTransformer);
+    return new VMServiceClient._(new rpc.Peer.withoutJson(incoming, outgoing));
+  }
+
+  VMServiceClient._(rpc.Peer peer)
+      : _peer = peer,
+        _streams = new StreamManager(peer),
+        done = peer.listen() {
+    _onIsolateStart = transform(_streams.isolate, (json, sink) {
+      if (json["kind"] != "IsolateStart") return;
+      sink.add(newVMIsolateRef(_peer, _streams, json["isolate"]));
+    });
+
+    _onIsolateRunnable = transform(_streams.isolate, (json, sink) {
+      if (json["kind"] != "IsolateRunnable") return;
+      sink.add(newVMIsolateRef(_peer, _streams, json["isolate"]));
+    });
+  }
+
+  /// Checks the VM service protocol version and throws a
+  /// [VMUnsupportedVersionException] if it's not a supported version.
+  ///
+  /// Because it's possible the VM service protocol doesn't speak JSON-RPC 2.0
+  /// at all, by default this will also throw a [VMUnsupportedVersionException]
+  /// if a reply isn't received within two seconds. This timeout can be
+  /// controlled with [timeout], or `null` can be passed to use no timeout.
+  Future validateVersion({Duration timeout: const Duration(seconds: 2)}) {
+    var future = _peer.sendRequest("getVersion", {}).then((json) {
+      var version;
+      try {
+        version = newVMServiceVersion(json);
+      } catch (_) {
+        throw new VMUnsupportedVersionException();
+      }
+
+      if (version.major < 2 || version.major > 3) {
+        throw new VMUnsupportedVersionException(version);
+      }
+    });
+
+    if (timeout == null) return future;
+
+    return future.timeout(timeout, onTimeout: () {
+      throw new VMUnsupportedVersionException();
+    });
+  }
+
+  /// Closes the underlying connection to the VM service.
+  ///
+  /// Returns a [Future] that fires once the connection has been closed.
+  Future close() => _peer.close();
+
+  /// Returns a list of flags that were passed to the VM.
+  ///
+  /// As of VM service version 3.0, this only includes VM-internal flags.
+  Future<List<VMFlag>> getFlags() async =>
+      newVMFlagList(await _peer.sendRequest("getFlagList", {}));
+
+  /// Returns the version of the VM service protocol that this client is
+  /// communicating with.
+  ///
+  /// Note that this is distinct from the version of Dart, which is accessible
+  /// via [VM.version].
+  Future<VMServiceVersion> getVersion() async =>
+      newVMServiceVersion(await _peer.sendRequest("getVersion", {}));
+
+  /// Returns information about the Dart VM.
+  Future<VM> getVM() async =>
+      newVM(_peer, _streams, await _peer.sendRequest("getVM", {}));
+
+  // Function added to original VmServiceClient class to enable coverage access
+  Future<CodeCoverage> getCoverage(VMIsolate isolate) async {
+    var json = await _peer.sendRequest(
+            '_getCoverage', {'isolateId': 'isolates/${isolate.numberAsString}'})
+        as Map;
+
+    if (json['type'] == 'Error') {
+      throw json;
+    }
+
+    return new CodeCoverage.fromJson(json);
+  }
+}
+
+class CodeCoverage {
+  final String id;
+  final List coverage;
+
+  CodeCoverage(this.id, this.coverage);
+
+  factory CodeCoverage.fromJson(json) =>
+      new CodeCoverage(json['id'], json['coverage']);
+}
diff --git a/pkgs/coverage/pubspec.yaml b/pkgs/coverage/pubspec.yaml
index 58b906b..48ede69 100644
--- a/pkgs/coverage/pubspec.yaml
+++ b/pkgs/coverage/pubspec.yaml
@@ -7,9 +7,12 @@
   sdk: '>=1.12.0 <2.0.0'
 dependencies:
   args: '>=0.12.1 <1.0.0'
+  async: ^1.8.0
+  json_rpc_2: ^1.2.0
   logging: '>=0.9.0 <0.12.0'
   path: '>=0.9.0 <2.0.0'
   stack_trace: ^1.3.0
+  vm_service_client: ^0.1.1+1
 dev_dependencies:
   test: '>=0.12.0 <0.13.0'
 executables:
diff --git a/pkgs/coverage/test/collect_coverage_api_test.dart b/pkgs/coverage/test/collect_coverage_api_test.dart
index 43f2eff..14c8976 100644
--- a/pkgs/coverage/test/collect_coverage_api_test.dart
+++ b/pkgs/coverage/test/collect_coverage_api_test.dart
@@ -5,7 +5,6 @@
 library coverage.test.collect_coverage_api_test;
 
 import 'dart:async';
-import 'dart:io';
 
 import 'package:coverage/coverage.dart';
 import 'package:coverage/src/util.dart';
@@ -19,8 +18,6 @@
 final _sampleAppFileUri = p.toUri(p.absolute(testAppPath)).toString();
 final _isolateLibFileUri = p.toUri(p.absolute(_isolateLibPath)).toString();
 
-const _timeout = const Duration(seconds: 5);
-
 void main() {
   test('collect_coverage_api', () async {
     var json = await _getCoverageResult();
@@ -40,8 +37,8 @@
       return map;
     });
 
-    for (var sampleCoverageData in sources[_sampleAppFileUri]) {
-      expect(sampleCoverageData['hits'], isNotEmpty);
+    for (Map sampleCoverageData in sources[_sampleAppFileUri]) {
+      expect(sampleCoverageData['hits'], isNotNull);
     }
 
     for (var sampleCoverageData in sources[_isolateLibFileUri]) {
@@ -63,15 +60,9 @@
   var openPort = await getOpenPort();
 
   // run the sample app, with the right flags
-  var sampleProcFuture = Process.run('dart', [
-    '--enable-vm-service=$openPort',
-    '--pause_isolates_on_exit',
-    testAppPath
-  ]).timeout(_timeout, onTimeout: () {
-    throw 'We timed out waiting for the sample app to finish.';
-  });
+  var sampleProcFuture = runTestApp(openPort);
 
-  var result = collect('127.0.0.1', openPort, true, false, timeout: _timeout);
+  var result = collect('127.0.0.1', openPort, true, false, timeout: timeout);
   await sampleProcFuture;
 
   return result;
diff --git a/pkgs/coverage/test/collect_coverage_test.dart b/pkgs/coverage/test/collect_coverage_test.dart
index 3f1ad31..9a8ba28 100644
--- a/pkgs/coverage/test/collect_coverage_test.dart
+++ b/pkgs/coverage/test/collect_coverage_test.dart
@@ -21,8 +21,6 @@
 final _sampleAppFileUri = p.toUri(p.absolute(testAppPath)).toString();
 final _isolateLibFileUri = p.toUri(p.absolute(_isolateLibPath)).toString();
 
-const _timeout = const Duration(seconds: 5);
-
 void main() {
   test('collect_coverage', () async {
     var resultString = await _getCoverageResult();
@@ -46,7 +44,7 @@
     });
 
     for (var sampleCoverageData in sources[_sampleAppFileUri]) {
-      expect(sampleCoverageData['hits'], isNotEmpty);
+      expect(sampleCoverageData['hits'], isNotNull);
     }
 
     for (var sampleCoverageData in sources[_isolateLibFileUri]) {
@@ -67,7 +65,7 @@
 
     var isolateFile = hitMap[_isolateLibFileUri];
 
-    expect(isolateFile, {11: 1, 12: 1, 14: 1, 16: 3, 18: 1});
+    expect(isolateFile, {12: 1, 14: 1, 16: 3, 18: 1});
   });
 
   test('parseCoverage', () async {
@@ -104,13 +102,7 @@
   var openPort = await getOpenPort();
 
   // run the sample app, with the right flags
-  var sampleProcFuture = Process.run('dart', [
-    '--enable-vm-service=$openPort',
-    '--pause_isolates_on_exit',
-    testAppPath
-  ]).timeout(_timeout, onTimeout: () {
-    throw 'We timed out waiting for the sample app to finish.';
-  });
+  var sampleProcFuture = runTestApp(openPort);
 
   // run the tool with the right flags
   // TODO: need to get all of this functionality in the lib
@@ -120,7 +112,7 @@
     openPort.toString(),
     '--resume-isolates',
     '--wait-paused'
-  ]).timeout(_timeout, onTimeout: () {
+  ]).timeout(timeout, onTimeout: () {
     throw 'We timed out waiting for the tool to finish.';
   });
 
diff --git a/pkgs/coverage/test/lcov_test.dart b/pkgs/coverage/test/lcov_test.dart
index 06ee63e..033f0d6 100644
--- a/pkgs/coverage/test/lcov_test.dart
+++ b/pkgs/coverage/test/lcov_test.dart
@@ -95,7 +95,7 @@
       // be very careful if you change the test file
       expect(res, contains("      0|  return a - b;"));
 
-      expect(res, contains('      1|  return _withTimeout(() async {'),
+      expect(res, contains('|  return _withTimeout(() async {'),
           reason: 'be careful if you change lib/src/util.dart');
 
       var hitLineRegexp = new RegExp(r'\s+(\d+)\|  return a \+ b;');
diff --git a/pkgs/coverage/test/run_and_collect_test.dart b/pkgs/coverage/test/run_and_collect_test.dart
index c73001c..c1ca14a 100644
--- a/pkgs/coverage/test/run_and_collect_test.dart
+++ b/pkgs/coverage/test/run_and_collect_test.dart
@@ -37,7 +37,7 @@
     });
 
     for (var sampleCoverageData in sources[_sampleAppFileUri]) {
-      expect(sampleCoverageData['hits'], isNotEmpty);
+      expect(sampleCoverageData['hits'], isNotNull);
     }
 
     for (var sampleCoverageData in sources[_isolateLibFileUri]) {
@@ -50,6 +50,6 @@
 
     var isolateFile = hitMap[_isolateLibFileUri];
 
-    expect(isolateFile, {11: 1, 12: 1, 14: 1, 16: 3, 18: 1});
+    expect(isolateFile, {12: 1, 14: 1, 16: 3, 18: 1});
   });
 }
diff --git a/pkgs/coverage/test/test_util.dart b/pkgs/coverage/test/test_util.dart
index 8e94121..8909238 100644
--- a/pkgs/coverage/test/test_util.dart
+++ b/pkgs/coverage/test/test_util.dart
@@ -4,6 +4,38 @@
 
 library coverage.test.util;
 
+import 'dart:async';
+import 'dart:convert';
+import 'dart:io';
+
 import 'package:path/path.dart' as p;
 
 final testAppPath = p.join('test', 'test_files', 'test_app.dart');
+
+const timeout = const Duration(seconds: 10);
+
+Future runTestApp(int openPort) async {
+  var proc = await Process.start('dart', [
+    '--enable-vm-service=$openPort',
+    '--pause_isolates_on_exit',
+    testAppPath
+  ]);
+
+  return Future.wait([
+    _transformStd(proc.stdout),
+    _transformStd(proc.stderr),
+    proc.exitCode
+  ]).timeout(timeout, onTimeout: () {
+    throw 'We timed out waiting for the sample app to finish.';
+  });
+}
+
+Future<Null> _transformStd(Stream<List<int>> source) {
+  return source
+      .transform(SYSTEM_ENCODING.decoder)
+      .transform(const LineSplitter())
+      .forEach((line) {
+    // Uncomment to debug output from `testApp`
+    // print(line);
+  });
+}