| Index: test/descriptor_server.dart
 | 
| diff --git a/test/descriptor_server.dart b/test/descriptor_server.dart
 | 
| new file mode 100644
 | 
| index 0000000000000000000000000000000000000000..4270dc6a840dd7fe7dbe6ca8174865ed6134b963
 | 
| --- /dev/null
 | 
| +++ b/test/descriptor_server.dart
 | 
| @@ -0,0 +1,124 @@
 | 
| +// Copyright (c) 2016, 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.
 | 
| +
 | 
| +import 'dart:async';
 | 
| +import 'dart:io';
 | 
| +
 | 
| +import 'package:path/path.dart' as p;
 | 
| +import 'package:pub/src/utils.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;
 | 
| +
 | 
| +/// The global [DescriptorServer] that's used by default.
 | 
| +///
 | 
| +/// `null` if there's no global server in use. This can be set to replace the
 | 
| +/// existing global server.
 | 
| +DescriptorServer get globalServer => _globalServer;
 | 
| +set globalServer(DescriptorServer value) {
 | 
| +  if (_globalServer == null) {
 | 
| +    currentSchedule.onComplete.schedule(() {
 | 
| +      _globalServer = null;
 | 
| +    }, 'clearing the global server');
 | 
| +  } else {
 | 
| +    _globalServer.close();
 | 
| +  }
 | 
| +
 | 
| +  _globalServer = value;
 | 
| +}
 | 
| +DescriptorServer _globalServer;
 | 
| +
 | 
| +/// Creates a global [DescriptorServer] to serve [contents] as static files.
 | 
| +///
 | 
| +/// This server will exist only for the duration of the pub run. It's accessible
 | 
| +/// via [server]. Subsequent calls to [serve] replace the previous server.
 | 
| +void serve([List<d.Descriptor> contents]) {
 | 
| +  globalServer = new DescriptorServer(contents);
 | 
| +}
 | 
| +
 | 
| +/// Like [serve], but reports an error if a request ever comes in to the server.
 | 
| +void serveErrors() {
 | 
| +  globalServer = new DescriptorServer.errors();
 | 
| +}
 | 
| +
 | 
| +class DescriptorServer {
 | 
| +  /// The server, or `null` before it's available.
 | 
| +  HttpServer _server;
 | 
| +
 | 
| +  /// A future that will complete to the port used for the server.
 | 
| +  Future<int> get port => _portCompleter.future;
 | 
| +  final _portCompleter = new Completer<int>();
 | 
| +
 | 
| +  /// Gets the list of paths that have been requested from the server.
 | 
| +  Future<List<String>> get requestedPaths =>
 | 
| +      schedule(() => _requestedPaths.toList(),
 | 
| +          "get previous network requests");
 | 
| +
 | 
| +  /// The list of paths that have been requested from this server.
 | 
| +  final _requestedPaths = <String>[];
 | 
| +
 | 
| +  /// Creates an HTTP server to serve [contents] as static files.
 | 
| +  ///
 | 
| +  /// This server exists only for the duration of the pub run. Subsequent calls
 | 
| +  /// to [serve] replace the previous server.
 | 
| +  DescriptorServer([List<d.Descriptor> contents]) {
 | 
| +    var baseDir = d.dir("serve-dir", contents);
 | 
| +
 | 
| +    schedule(() async {
 | 
| +      _server = await shelf_io.serve((request) async {
 | 
| +        var path = p.posix.fromUri(request.url.path);
 | 
| +        _requestedPaths.add(path);
 | 
| +
 | 
| +        try {
 | 
| +          var stream = await validateStream(baseDir.load(path));
 | 
| +          return new shelf.Response.ok(stream);
 | 
| +        } catch (_) {
 | 
| +          return new shelf.Response.notFound('File "$path" not found.');
 | 
| +        }
 | 
| +      }, 'localhost', 0);
 | 
| +
 | 
| +      _portCompleter.complete(_server.port);
 | 
| +      _closeOnComplete();
 | 
| +    }, 'starting a server serving:\n${baseDir.describe()}');
 | 
| +  }
 | 
| +
 | 
| +  /// Creates a server that reports an error if a request is ever received.
 | 
| +  DescriptorServer.errors() {
 | 
| +    schedule(() async {
 | 
| +      _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);
 | 
| +      _closeOnComplete();
 | 
| +    });
 | 
| +  }
 | 
| +
 | 
| +  /// Schedules [requestedPaths] to be emptied.
 | 
| +  void clearRequestedPaths() {
 | 
| +    schedule(() {
 | 
| +      _requestedPaths.clear();
 | 
| +    }, "clearing requested paths");
 | 
| +  }
 | 
| +
 | 
| +  /// Schedules the closing of this server.
 | 
| +  void close() {
 | 
| +    schedule(() async {
 | 
| +      if (_server == null) return;
 | 
| +      await _server.close();
 | 
| +    }, "closing DescriptorServer");
 | 
| +  }
 | 
| +
 | 
| +  /// Schedules this server to close once the schedule is done.
 | 
| +  void _closeOnComplete() {
 | 
| +    currentSchedule.onComplete.schedule(() async {
 | 
| +      if (_server == null) return;
 | 
| +      await _server.close();
 | 
| +    }, "closing DescriptorServer");
 | 
| +  }
 | 
| +}
 | 
| 
 |