| Index: test/test_pub.dart
 | 
| diff --git a/test/test_pub.dart b/test/test_pub.dart
 | 
| index 1ef30206f5835a58ce6e7d3c68adabb861a0edea..22da90d9b6229b68c749a15900e21b4272f96bfe 100644
 | 
| --- a/test/test_pub.dart
 | 
| +++ b/test/test_pub.dart
 | 
| @@ -35,22 +35,12 @@ import 'package:scheduled_test/scheduled_process.dart';
 | 
|  import 'package:scheduled_test/scheduled_server.dart';
 | 
|  import 'package:scheduled_test/scheduled_stream.dart';
 | 
|  import 'package:scheduled_test/scheduled_test.dart' hide fail;
 | 
| -import 'package:shelf/shelf.dart' as shelf;
 | 
| -import 'package:shelf/shelf_io.dart' as shelf_io;
 | 
|  
 | 
|  import 'descriptor.dart' as d;
 | 
| +import 'descriptor_server.dart';
 | 
|  
 | 
| -export 'serve_packages.dart';
 | 
| -
 | 
| -/// The current [HttpServer] created using [serve].
 | 
| -var _server;
 | 
| -
 | 
| -/// The list of paths that have been requested from the server since the last
 | 
| -/// call to [getRequestedPaths].
 | 
| -final _requestedPaths = <String>[];
 | 
| -
 | 
| -/// The cached value for [_portCompleter].
 | 
| -Completer<int> _portCompleterCache;
 | 
| +export 'descriptor_server.dart';
 | 
| +export 'package_server.dart';
 | 
|  
 | 
|  /// A [Matcher] that matches JavaScript generated by dart2js with minification
 | 
|  /// enabled.
 | 
| @@ -66,91 +56,6 @@ Matcher isUnminifiedDart2JSOutput =
 | 
|  final _entrypoint = new Entrypoint(
 | 
|      pubRoot, new SystemCache.withSources(isOffline: true));
 | 
|  
 | 
| -/// The completer for [port].
 | 
| -Completer<int> get _portCompleter {
 | 
| -  if (_portCompleterCache != null) return _portCompleterCache;
 | 
| -  _portCompleterCache = new Completer<int>();
 | 
| -  currentSchedule.onComplete.schedule(() {
 | 
| -    _portCompleterCache = null;
 | 
| -  }, 'clearing the port completer');
 | 
| -  return _portCompleterCache;
 | 
| -}
 | 
| -
 | 
| -/// A future that will complete to the port used for the current server.
 | 
| -Future<int> get port => _portCompleter.future;
 | 
| -
 | 
| -/// Gets the list of paths that have been requested from the server since the
 | 
| -/// last time this was called (or since the server was first spun up).
 | 
| -Future<List<String>> getRequestedPaths() {
 | 
| -  return schedule(() {
 | 
| -    var paths = _requestedPaths.toList();
 | 
| -    _requestedPaths.clear();
 | 
| -    return paths;
 | 
| -  }, "get previous network requests");
 | 
| -}
 | 
| -
 | 
| -/// Creates an HTTP server to serve [contents] as static files.
 | 
| -///
 | 
| -/// This server will exist only for the duration of the pub run. Subsequent
 | 
| -/// calls to [serve] replace the previous server.
 | 
| -void serve([List<d.Descriptor> contents]) {
 | 
| -  var baseDir = d.dir("serve-dir", contents);
 | 
| -
 | 
| -  _hasServer = true;
 | 
| -
 | 
| -  schedule(() {
 | 
| -    return _closeServer().then((_) {
 | 
| -      return shelf_io.serve((request) {
 | 
| -        var path = p.posix.fromUri(request.url.path);
 | 
| -        _requestedPaths.add(path);
 | 
| -
 | 
| -        return validateStream(baseDir.load(path))
 | 
| -            .then((stream) => new shelf.Response.ok(stream))
 | 
| -            .catchError((error) {
 | 
| -          return new shelf.Response.notFound('File "$path" not found.');
 | 
| -        });
 | 
| -      }, 'localhost', 0).then((server) {
 | 
| -        _server = server;
 | 
| -        _portCompleter.complete(_server.port);
 | 
| -        currentSchedule.onComplete.schedule(_closeServer);
 | 
| -      });
 | 
| -    });
 | 
| -  }, 'starting a server serving:\n${baseDir.describe()}');
 | 
| -}
 | 
| -
 | 
| -/// Like [serve], but reports an error if a request ever comes in to the server.
 | 
| -void serveErrors() {
 | 
| -  _hasServer = true;
 | 
| -
 | 
| -  schedule(() async {
 | 
| -    await _closeServer();
 | 
| -
 | 
| -    _server = await shelf_io.serve((request) {
 | 
| -      fail("The HTTP server received an unexpected request:\n"
 | 
| -          "${request.method} ${request.requestedUri}");
 | 
| -      return new shelf.Response.forbidden(null);
 | 
| -    }, 'localhost', 0);
 | 
| -
 | 
| -    _portCompleter.complete(_server.port);
 | 
| -    currentSchedule.onComplete.schedule(_closeServer);
 | 
| -  });
 | 
| -}
 | 
| -
 | 
| -/// Closes [_server].
 | 
| -///
 | 
| -/// Returns a [Future] that completes after the [_server] is closed.
 | 
| -Future _closeServer() {
 | 
| -  if (_server == null) return new Future.value();
 | 
| -  var future = _server.close();
 | 
| -  _server = null;
 | 
| -  _hasServer = false;
 | 
| -  _portCompleterCache = null;
 | 
| -  return future;
 | 
| -}
 | 
| -
 | 
| -/// `true` if the current test spins up an HTTP server.
 | 
| -bool _hasServer = false;
 | 
| -
 | 
|  /// Converts [value] into a YAML string.
 | 
|  String yaml(value) => JSON.encode(value);
 | 
|  
 | 
| @@ -405,8 +310,9 @@ Future<Map> getPubTestEnvironment([String tokenEndpoint]) async {
 | 
|      environment['_PUB_TEST_TOKEN_ENDPOINT'] = tokenEndpoint.toString();
 | 
|    }
 | 
|  
 | 
| -  if (_hasServer) {
 | 
| -    environment['PUB_HOSTED_URL'] = "http://localhost:${await port}";
 | 
| +  if (globalServer != null) {
 | 
| +    environment['PUB_HOSTED_URL'] =
 | 
| +        "http://localhost:${await globalServer.port}";
 | 
|    }
 | 
|  
 | 
|    return environment;
 | 
| 
 |