DRY up logic for handling serviceUri
diff --git a/bin/test_with_coverage.dart b/bin/test_with_coverage.dart
index c606931..df1a523 100644
--- a/bin/test_with_coverage.dart
+++ b/bin/test_with_coverage.dart
@@ -3,11 +3,11 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'dart:async';
-import 'dart:convert' show utf8, LineSplitter;
 import 'dart:io';
 
 import 'package:args/args.dart';
-import 'package:coverage/src/util.dart' show extractVMServiceUri;
+import 'package:coverage/src/util.dart'
+    show StandardOutExtension, extractVMServiceUri;
 import 'package:package_config/package_config.dart';
 import 'package:path/path.dart' as path;
 
@@ -27,10 +27,7 @@
   final broadStdout = process.stdout.asBroadcastStream();
   broadStdout.listen(stdout.add);
   if (onStdout != null) {
-    broadStdout
-        .transform(utf8.decoder)
-        .transform(const LineSplitter())
-        .listen(onStdout);
+    broadStdout.lines().listen(onStdout);
   }
   process.stderr.listen(stderr.add);
   final result = await process.exitCode;
diff --git a/lib/src/run_and_collect.dart b/lib/src/run_and_collect.dart
index a448e8d..b6e6288 100644
--- a/lib/src/run_and_collect.dart
+++ b/lib/src/run_and_collect.dart
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'dart:async';
-import 'dart:convert' show utf8, LineSplitter;
 import 'dart:io';
 
 import 'collect.dart';
@@ -30,18 +29,8 @@
   }
 
   final process = await Process.start(Platform.executable, dartArgs);
-  final serviceUriCompleter = Completer<Uri>();
-  process.stdout
-      .transform(utf8.decoder)
-      .transform(const LineSplitter())
-      .listen((line) {
-    final uri = extractVMServiceUri(line);
-    if (uri != null) {
-      serviceUriCompleter.complete(uri);
-    }
-  });
 
-  final serviceUri = await serviceUriCompleter.future;
+  final serviceUri = await serviceUriFromProcess(process);
   Map<String, dynamic> coverage;
   try {
     coverage = await collect(serviceUri, true, true, includeDart, <String>{},
diff --git a/lib/src/util.dart b/lib/src/util.dart
index 21949bd..42b1466 100644
--- a/lib/src/util.dart
+++ b/lib/src/util.dart
@@ -2,6 +2,8 @@
 // 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 'dart:io';
 
 // TODO(cbracken) make generic
@@ -137,3 +139,22 @@
 
   return isError ? [] : ignoredLines;
 }
+
+extension StandardOutExtension on Stream<List<int>> {
+  Stream<String> lines() =>
+      transform(SystemEncoding().decoder).transform(const LineSplitter());
+}
+
+Future<Uri> serviceUriFromProcess(Process sampleProcess) {
+  // Capture the VM service URI.
+  final serviceUriCompleter = Completer<Uri>();
+  sampleProcess.stdout.lines().listen((line) {
+    if (!serviceUriCompleter.isCompleted) {
+      final serviceUri = extractVMServiceUri(line);
+      if (serviceUri != null) {
+        serviceUriCompleter.complete(serviceUri);
+      }
+    }
+  });
+  return serviceUriCompleter.future;
+}
diff --git a/test/collect_coverage_api_test.dart b/test/collect_coverage_api_test.dart
index 91cd5b9..d387a8c 100644
--- a/test/collect_coverage_api_test.dart
+++ b/test/collect_coverage_api_test.dart
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'dart:async';
-import 'dart:convert';
 
 import 'package:coverage/coverage.dart';
 import 'package:coverage/src/util.dart';
@@ -112,21 +111,7 @@
   // run the sample app, with the right flags
   final sampleProcess = await runTestApp(openPort);
 
-  // Capture the VM service URI.
-  final serviceUriCompleter = Completer<Uri>();
-  sampleProcess.stdout
-      .transform(utf8.decoder)
-      .transform(LineSplitter())
-      .listen((line) {
-    if (!serviceUriCompleter.isCompleted) {
-      final serviceUri = extractVMServiceUri(line);
-      if (serviceUri != null) {
-        serviceUriCompleter.complete(serviceUri);
-      }
-    }
-  });
-
-  final serviceUri = await serviceUriCompleter.future;
+  final serviceUri = await serviceUriFromProcess(sampleProcess);
   final isolateIdSet = isolateIds ? <String>{} : null;
 
   return collect(serviceUri, true, true, false, scopedOutput,
diff --git a/test/collect_coverage_test.dart b/test/collect_coverage_test.dart
index 108b52c..7cf33e2 100644
--- a/test/collect_coverage_test.dart
+++ b/test/collect_coverage_test.dart
@@ -4,7 +4,7 @@
 
 @Retry(3)
 import 'dart:async';
-import 'dart:convert' show json, LineSplitter, utf8;
+import 'dart:convert' show json;
 import 'dart:io';
 
 import 'package:coverage/coverage.dart';
@@ -350,19 +350,7 @@
   final sampleProcess = await runTestApp(openPort);
 
   // Capture the VM service URI.
-  final serviceUriCompleter = Completer<Uri>();
-  sampleProcess.stdout
-      .transform(utf8.decoder)
-      .transform(LineSplitter())
-      .listen((line) {
-    if (!serviceUriCompleter.isCompleted) {
-      final serviceUri = extractVMServiceUri(line);
-      if (serviceUri != null) {
-        serviceUriCompleter.complete(serviceUri);
-      }
-    }
-  });
-  final serviceUri = await serviceUriCompleter.future;
+  final serviceUri = await serviceUriFromProcess(sampleProcess);
 
   // Run the collection tool.
   // TODO: need to get all of this functionality in the lib
diff --git a/test/function_coverage_test.dart b/test/function_coverage_test.dart
index 2d2356b..81ccea7 100644
--- a/test/function_coverage_test.dart
+++ b/test/function_coverage_test.dart
@@ -3,7 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'dart:async';
-import 'dart:convert' show json, LineSplitter, utf8;
+import 'dart:convert' show json;
 import 'dart:io';
 
 import 'package:coverage/coverage.dart';
@@ -85,20 +85,7 @@
     _funcCovApp
   ]);
 
-  // Capture the VM service URI.
-  final serviceUriCompleter = Completer<Uri>();
-  sampleProcess.stdout
-      .transform(utf8.decoder)
-      .transform(LineSplitter())
-      .listen((line) {
-    if (!serviceUriCompleter.isCompleted) {
-      final serviceUri = extractVMServiceUri(line);
-      if (serviceUri != null) {
-        serviceUriCompleter.complete(serviceUri);
-      }
-    }
-  });
-  final serviceUri = await serviceUriCompleter.future;
+  final serviceUri = await serviceUriFromProcess(sampleProcess);
 
   // Run the collection tool.
   final toolResult = await TestProcess.start(Platform.resolvedExecutable, [
diff --git a/test/lcov_test.dart b/test/lcov_test.dart
index a32f3fd..5d40bf6 100644
--- a/test/lcov_test.dart
+++ b/test/lcov_test.dart
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'dart:async';
-import 'dart:convert';
 import 'dart:io';
 
 import 'package:coverage/coverage.dart';
@@ -265,20 +264,7 @@
   final sampleProcess =
       await Process.start(Platform.resolvedExecutable, sampleAppArgs);
 
-  // Capture the VM service URI.
-  final serviceUriCompleter = Completer<Uri>();
-  sampleProcess.stdout
-      .transform(utf8.decoder)
-      .transform(LineSplitter())
-      .listen((line) {
-    if (!serviceUriCompleter.isCompleted) {
-      final serviceUri = extractVMServiceUri(line);
-      if (serviceUri != null) {
-        serviceUriCompleter.complete(serviceUri);
-      }
-    }
-  });
-  final serviceUri = await serviceUriCompleter.future;
+  final serviceUri = await serviceUriFromProcess(sampleProcess);
 
   // collect hit map.
   final coverageJson = (await collect(serviceUri, true, true, false, <String>{},