Migrate more tests (#12)

diff --git a/lib/src/http_impl.dart b/lib/src/http_impl.dart
index e9760d4..ba24cc7 100644
--- a/lib/src/http_impl.dart
+++ b/lib/src/http_impl.dart
@@ -1634,7 +1634,7 @@
       // If the proxy configuration contains user information use that
       // for proxy basic authorization.
       String auth = _CryptoUtils
-          .bytesToBase64(utf8.encode("${proxy.username}:${proxy.password}"));
+          .bytesToBase64(UTF8.encode("${proxy.username}:${proxy.password}"));
       request.headers.set(HttpHeaders.PROXY_AUTHORIZATION, "Basic $auth");
     } else if (!proxy.isDirect && _httpClient._proxyCredentials.isNotEmpty) {
       proxyCreds = _httpClient._findProxyCredentials(proxy);
@@ -1645,7 +1645,7 @@
     if (uri.userInfo != null && uri.userInfo.isNotEmpty) {
       // If the URL contains user information use that for basic
       // authorization.
-      String auth = _CryptoUtils.bytesToBase64(utf8.encode(uri.userInfo));
+      String auth = _CryptoUtils.bytesToBase64(UTF8.encode(uri.userInfo));
       request.headers.set(HttpHeaders.AUTHORIZATION, "Basic $auth");
     } else {
       // Look for credentials.
@@ -1756,7 +1756,7 @@
       // If the proxy configuration contains user information use that
       // for proxy basic authorization.
       String auth = _CryptoUtils
-          .bytesToBase64(utf8.encode("${proxy.username}:${proxy.password}"));
+          .bytesToBase64(UTF8.encode("${proxy.username}:${proxy.password}"));
       request.headers.set(HttpHeaders.PROXY_AUTHORIZATION, "Basic $auth");
     }
     return request.close().then((response) {
@@ -2831,11 +2831,11 @@
       // now always use UTF-8 encoding.
       _HttpClientDigestCredentials creds = credentials;
       var hasher = new _MD5()
-        ..add(utf8.encode(creds.username))
+        ..add(UTF8.encode(creds.username))
         ..add([_CharCode.COLON])
         ..add(realm.codeUnits)
         ..add([_CharCode.COLON])
-        ..add(utf8.encode(creds.password));
+        ..add(UTF8.encode(creds.password));
       ha1 = _CryptoUtils.bytesToHex(hasher.close());
     }
   }
@@ -2918,7 +2918,7 @@
     // http://tools.ietf.org/html/draft-reschke-basicauth-enc-06. For
     // now always use UTF-8 encoding.
     String auth =
-        _CryptoUtils.bytesToBase64(utf8.encode("$username:$password"));
+        _CryptoUtils.bytesToBase64(UTF8.encode("$username:$password"));
     return "Basic $auth";
   }
 
diff --git a/pubspec.yaml b/pubspec.yaml
index 4a71c68..8e40eb7 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -4,6 +4,8 @@
 homepage: https://github.com/dart-lang/http_io
 description: HTTP Client and Server APIs.
 dev_dependencies:
+  convert: "^2.0.1"
+  crypto: "^2.0.2+1"
   test: "^0.12.18"
 environment:
   sdk: ">=1.24.0 <3.0.0"
diff --git a/test/http_100_continue_test.dart b/test/http_100_continue_test.dart
new file mode 100644
index 0000000..9b284c6
--- /dev/null
+++ b/test/http_100_continue_test.dart
@@ -0,0 +1,80 @@
+// 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.
+
+import 'dart:async';
+import 'dart:convert';
+import "dart:io" hide HttpServer, HttpClient;
+
+import "package:http_io/http_io.dart";
+import "package:test/test.dart";
+
+Future<Null> doTest(responseBytes, bodyLength) async {
+  fullRequest(bytes) {
+    var len = bytes.length;
+    return len > 4 &&
+        bytes[len - 4] == 13 &&
+        bytes[len - 3] == 10 &&
+        bytes[len - 2] == 13 &&
+        bytes[len - 1] == 10;
+  }
+
+  handleSocket(socket) async {
+    var bytes = [];
+    await for (var data in socket) {
+      bytes.addAll(data);
+      if (fullRequest(bytes)) {
+        socket.add(responseBytes);
+        socket.close();
+      }
+    }
+  }
+
+  var server = await ServerSocket.bind('127.0.0.1', 0);
+  server.listen(handleSocket);
+
+  var client = new HttpClient();
+  var request =
+      await client.getUrl(Uri.parse('http://127.0.0.1:${server.port}/'));
+  var response = await request.close();
+  expect(response.statusCode, equals(200));
+  expect(bodyLength,
+      equals((await response.fold([], (p, e) => p..addAll(e))).length));
+  await server.close();
+}
+
+main() {
+  var r1 = '''
+HTTP/1.1 100 Continue\r
+\r
+HTTP/1.1 200 OK\r
+\r
+''';
+
+  var r2 = '''
+HTTP/1.1 100 Continue\r
+My-Header-1: hello\r
+My-Header-2: world\r
+\r
+HTTP/1.1 200 OK\r
+\r
+''';
+
+  var r3 = '''
+HTTP/1.1 100 Continue\r
+\r
+HTTP/1.1 200 OK\r
+Content-Length: 2\r
+\r
+AB''';
+
+  test("Continue OK", () async {
+    await doTest(ASCII.encode(r1), 0);
+  });
+  test("Continue hello world OK", () async {
+    await doTest(ASCII.encode(r2), 0);
+  });
+  test("Continue OK length AB", () async {
+    await doTest(ASCII.encode(r3), 2);
+  });
+}
diff --git a/test/http_10_test.dart b/test/http_10_test.dart
new file mode 100644
index 0000000..c2a19ba
--- /dev/null
+++ b/test/http_10_test.dart
@@ -0,0 +1,228 @@
+// (c) 2013, 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" hide HttpServer, HttpClient, HttpRequest, HttpException;
+
+import "package:http_io/http_io.dart";
+import "package:test/test.dart";
+
+// Client makes a HTTP 1.0 request without connection keep alive. The
+// server sets a content length but still needs to close the
+// connection as there is no keep alive.
+Future<Null> testHttp10NoKeepAlive() {
+  Completer<Null> completer = new Completer();
+  HttpServer.bind("127.0.0.1", 0).then((server) {
+    server.listen((HttpRequest request) {
+      expect(request.headers.value('content-length'), isNull);
+      expect(-1, equals(request.contentLength));
+      var response = request.response;
+      response.contentLength = 1;
+      expect("1.0", equals(request.protocolVersion));
+      response.done
+          .then((_) => fail("Unexpected response completion"))
+          .catchError((error) => expect((error is HttpException), isTrue));
+      response.write("Z");
+      response.write("Z");
+      response.close();
+    }, onError: (e, trace) {
+      String msg = "Unexpected error $e";
+      if (trace != null) msg += "\nStackTrace: $trace";
+      fail(msg);
+    });
+
+    int count = 0;
+    makeRequest() {
+      Socket.connect("127.0.0.1", server.port).then((socket) {
+        socket.write("GET / HTTP/1.0\r\n\r\n");
+
+        List<int> response = [];
+        socket.listen(response.addAll, onDone: () {
+          count++;
+          socket.destroy();
+          String s = new String.fromCharCodes(response).toLowerCase();
+          expect(-1, equals(s.indexOf("keep-alive")));
+          if (count < 10) {
+            makeRequest();
+          } else {
+            server.close();
+            completer.complete(null);
+          }
+        });
+      });
+    }
+
+    makeRequest();
+  });
+  return completer.future;
+}
+
+// Client makes a HTTP 1.0 request and the server does not set a
+// content length so it has to close the connection to mark the end of
+// the response.
+Future<Null> testHttp10ServerClose() {
+  Completer<Null> completer = new Completer();
+  HttpServer.bind("127.0.0.1", 0).then((server) {
+    server.listen((HttpRequest request) {
+      expect(request.headers.value('content-length'), isNull);
+      expect(-1, equals(request.contentLength));
+      request.listen((_) {}, onDone: () {
+        var response = request.response;
+        expect("1.0", equals(request.protocolVersion));
+        response.write("Z");
+        response.close();
+      });
+    }, onError: (e, trace) {
+      String msg = "Unexpected error $e";
+      if (trace != null) msg += "\nStackTrace: $trace";
+      fail(msg);
+    });
+
+    int count = 0;
+    makeRequest() {
+      Socket.connect("127.0.0.1", server.port).then((socket) {
+        socket.write("GET / HTTP/1.0\r\n");
+        socket.write("Connection: Keep-Alive\r\n\r\n");
+
+        List<int> response = [];
+        socket.listen(response.addAll,
+            onDone: () {
+              socket.destroy();
+              count++;
+              String s = new String.fromCharCodes(response).toLowerCase();
+              expect("z", equals(s[s.length - 1]));
+              expect(-1, equals(s.indexOf("content-length:")));
+              expect(-1, equals(s.indexOf("keep-alive")));
+              if (count < 10) {
+                makeRequest();
+              } else {
+                server.close();
+                completer.complete(null);
+              }
+            },
+            onError: (e) => print(e));
+      });
+    }
+
+    makeRequest();
+  });
+  return completer.future;
+}
+
+// Client makes a HTTP 1.0 request with connection keep alive. The
+// server sets a content length so the persistent connection can be
+// used.
+Future<Null> testHttp10KeepAlive() {
+  Completer<Null> completer = new Completer();
+  HttpServer.bind("127.0.0.1", 0).then((server) {
+    server.listen((HttpRequest request) {
+      expect(request.headers.value('content-length'), isNull);
+      expect(-1, equals(request.contentLength));
+      var response = request.response;
+      response.contentLength = 1;
+      response.persistentConnection = true;
+      expect("1.0", equals(request.protocolVersion));
+      response.write("Z");
+      response.close();
+    }, onError: (e, trace) {
+      String msg = "Unexpected error $e";
+      if (trace != null) msg += "\nStackTrace: $trace";
+      fail(msg);
+    });
+
+    Socket.connect("127.0.0.1", server.port).then((socket) {
+      void sendRequest() {
+        socket.write("GET / HTTP/1.0\r\n");
+        socket.write("Connection: Keep-Alive\r\n\r\n");
+      }
+
+      List<int> response = [];
+      int count = 0;
+      socket.listen((d) {
+        response.addAll(d);
+        if (response[response.length - 1] == "Z".codeUnitAt(0)) {
+          String s = new String.fromCharCodes(response).toLowerCase();
+          expect(s.indexOf("\r\nconnection: keep-alive\r\n") > 0, isTrue);
+          expect(s.indexOf("\r\ncontent-length: 1\r\n") > 0, isTrue);
+          count++;
+          if (count < 10) {
+            response = [];
+            sendRequest();
+          } else {
+            socket.close();
+          }
+        }
+      }, onDone: () {
+        socket.destroy();
+        server.close();
+        completer.complete(null);
+      });
+      sendRequest();
+    });
+  });
+  return completer.future;
+}
+
+// Client makes a HTTP 1.0 request with connection keep alive. The
+// server does not set a content length so it cannot honor connection
+// keep alive.
+Future<Null> testHttp10KeepAliveServerCloses() {
+  Completer<Null> completer = new Completer();
+  HttpServer.bind("127.0.0.1", 0).then((server) {
+    server.listen((HttpRequest request) {
+      expect(request.headers.value('content-length'), isNull);
+      expect(-1, equals(request.contentLength));
+      var response = request.response;
+      expect("1.0", equals(request.protocolVersion));
+      response.write("Z");
+      response.close();
+    }, onError: (e, trace) {
+      String msg = "Unexpected error $e";
+      if (trace != null) msg += "\nStackTrace: $trace";
+      fail(msg);
+    });
+
+    int count = 0;
+    makeRequest() {
+      Socket.connect("127.0.0.1", server.port).then((socket) {
+        socket.write("GET / HTTP/1.0\r\n");
+        socket.write("Connection: Keep-Alive\r\n\r\n");
+
+        List<int> response = [];
+        socket.listen(response.addAll, onDone: () {
+          socket.destroy();
+          count++;
+          String s = new String.fromCharCodes(response).toLowerCase();
+          expect("z", equals(s[s.length - 1]));
+          expect(-1, equals(s.indexOf("content-length")));
+          expect(-1, equals(s.indexOf("connection")));
+          if (count < 10) {
+            makeRequest();
+          } else {
+            server.close();
+            completer.complete(null);
+          }
+        });
+      });
+    }
+
+    makeRequest();
+  });
+  return completer.future;
+}
+
+void main() {
+  test("Http10NoKeepAlive", () async {
+    await testHttp10NoKeepAlive();
+  });
+  test("Http10ServerClose", () async {
+    await testHttp10ServerClose();
+  });
+  test("Http10KeepAlive", () async {
+    await testHttp10KeepAlive();
+  });
+  test("Http10KeepAliveServerCloses", () async {
+    await testHttp10KeepAliveServerCloses();
+  });
+}
diff --git a/test/http_auth_digest_test.dart b/test/http_auth_digest_test.dart
new file mode 100644
index 0000000..5101744
--- /dev/null
+++ b/test/http_auth_digest_test.dart
@@ -0,0 +1,365 @@
+// Copyright (c) 2013, 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 "package:convert/convert.dart";
+import "package:crypto/crypto.dart";
+import "package:http_io/http_io.dart";
+import "package:test/test.dart";
+
+class Server {
+  HttpServer server;
+  int unauthCount = 0; // Counter of the 401 responses.
+  int successCount = 0; // Counter of the successful responses.
+  int nonceCount = 0; // Counter of use of current nonce.
+  String ha1;
+
+  static Future<Server> start(String algorithm, String qop,
+      {int nonceStaleAfter, bool useNextNonce: false}) {
+    return new Server()._start(algorithm, qop, nonceStaleAfter, useNextNonce);
+  }
+
+  Future<Server> _start(String serverAlgorithm, String serverQop,
+      int nonceStaleAfter, bool useNextNonce) {
+    Set ncs = new Set();
+    // Calculate ha1.
+    String realm = "test";
+    String username = "dart";
+    String password = "password";
+    var hasher = md5.convert("$username:$realm:$password".codeUnits);
+    ha1 = hex.encode(hasher.bytes);
+
+    var nonce = "12345678"; // No need for random nonce in test.
+
+    var completer = new Completer<Server>();
+    HttpServer.bind("127.0.0.1", 0).then((s) {
+      server = s;
+      server.listen((HttpRequest request) {
+        sendUnauthorizedResponse(HttpResponse response, {stale: false}) {
+          response.statusCode = HttpStatus.UNAUTHORIZED;
+          StringBuffer authHeader = new StringBuffer();
+          authHeader.write('Digest');
+          authHeader.write(', realm="$realm"');
+          authHeader.write(', nonce="$nonce"');
+          if (stale) authHeader.write(', stale="true"');
+          if (serverAlgorithm != null) {
+            authHeader.write(', algorithm=$serverAlgorithm');
+          }
+          authHeader.write(', domain="/digest/"');
+          if (serverQop != null) authHeader.write(', qop="$serverQop"');
+          response.headers.set(HttpHeaders.WWW_AUTHENTICATE, authHeader);
+          unauthCount++;
+        }
+
+        var response = request.response;
+        if (request.headers[HttpHeaders.AUTHORIZATION] != null) {
+          expect(1, equals(request.headers[HttpHeaders.AUTHORIZATION].length));
+          String authorization = request.headers[HttpHeaders.AUTHORIZATION][0];
+          HeaderValue header =
+              HeaderValue.parse(authorization, parameterSeparator: ",");
+          if (header.value.toLowerCase() == "basic") {
+            sendUnauthorizedResponse(response);
+          } else if (!useNextNonce && nonceCount == nonceStaleAfter) {
+            nonce = "87654321";
+            nonceCount = 0;
+            sendUnauthorizedResponse(response, stale: true);
+          } else {
+            var uri = header.parameters["uri"];
+            var qop = header.parameters["qop"];
+            var cnonce = header.parameters["cnonce"];
+            var nc = header.parameters["nc"];
+            expect("digest", equals(header.value.toLowerCase()));
+            expect("dart", equals(header.parameters["username"]));
+            expect(realm, equals(header.parameters["realm"]));
+            expect("MD5", equals(header.parameters["algorithm"]));
+            expect(nonce, equals(header.parameters["nonce"]));
+            expect(request.uri.toString(), equals(uri));
+            if (qop != null) {
+              // A server qop of auth-int is downgraded to none by the client.
+              expect("auth", equals(serverQop));
+              expect("auth", equals(header.parameters["qop"]));
+              expect(cnonce, isNotNull);
+              expect(nc, isNotNull);
+              expect(ncs.contains(nc), isFalse);
+              ncs.add(nc);
+            } else {
+              expect(cnonce, isNull);
+              expect(nc, isNull);
+            }
+            expect(header.parameters["response"], isNotNull);
+
+            var hasher = md5.convert("${request.method}:$uri".codeUnits);
+            var ha2 = hex.encode(hasher.bytes);
+
+            Digest digest;
+            if (qop == null || qop == "" || qop == "none") {
+              digest = md5.convert("$ha1:$nonce:$ha2".codeUnits);
+            } else {
+              digest =
+                  md5.convert("$ha1:$nonce:$nc:$cnonce:$qop:$ha2".codeUnits);
+            }
+            expect(hex.encode(digest.bytes),
+                equals(header.parameters["response"]));
+
+            successCount++;
+            nonceCount++;
+
+            // Add a bogus Authentication-Info for testing.
+            var info = 'rspauth="77180d1ab3d6c9de084766977790f482", '
+                'cnonce="8f971178", '
+                'nc=000002c74, '
+                'qop=auth';
+            if (useNextNonce && nonceCount == nonceStaleAfter) {
+              nonce = "abcdef01";
+              info += ', nextnonce="$nonce"';
+            }
+            response.headers.set("Authentication-Info", info);
+          }
+        } else {
+          sendUnauthorizedResponse(response);
+        }
+        response.close();
+      });
+      completer.complete(this);
+    });
+    return completer.future;
+  }
+
+  void shutdown() {
+    server.close();
+  }
+
+  int get port => server.port;
+}
+
+Future<Null> testNoCredentials(String algorithm, String qop) {
+  Completer<Null> completer = new Completer();
+  Server.start(algorithm, qop).then((server) {
+    HttpClient client = new HttpClient();
+
+    // Add digest credentials which does not match the path requested.
+    client.addCredentials(Uri.parse("http://127.0.0.1:${server.port}/xxx"),
+        "test", new HttpClientDigestCredentials("dart", "password"));
+
+    // Add basic credentials for the path requested.
+    client.addCredentials(Uri.parse("http://127.0.0.1:${server.port}/digest"),
+        "test", new HttpClientBasicCredentials("dart", "password"));
+
+    Future makeRequest(Uri url) {
+      return client
+          .getUrl(url)
+          .then((HttpClientRequest request) => request.close())
+          .then((HttpClientResponse response) {
+        expect(HttpStatus.UNAUTHORIZED, equals(response.statusCode));
+        return response.fold(null, (x, y) {});
+      });
+    }
+
+    var futures = <Future>[];
+    for (int i = 0; i < 5; i++) {
+      futures.add(
+          makeRequest(Uri.parse("http://127.0.0.1:${server.port}/digest")));
+    }
+    Future.wait(futures).then((_) {
+      server.shutdown();
+      client.close();
+      completer.complete(null);
+    });
+  });
+  return completer.future;
+}
+
+Future<Null> testCredentials(String algorithm, String qop) {
+  Completer<Null> completer = new Completer();
+  Server.start(algorithm, qop).then((server) {
+    HttpClient client = new HttpClient();
+
+    Future makeRequest(Uri url) {
+      return client
+          .getUrl(url)
+          .then((HttpClientRequest request) => request.close())
+          .then((HttpClientResponse response) {
+        expect(HttpStatus.OK, equals(response.statusCode));
+        expect(1, equals(response.headers["Authentication-Info"].length));
+        return response.fold(null, (x, y) {});
+      });
+    }
+
+    client.addCredentials(Uri.parse("http://127.0.0.1:${server.port}/digest"),
+        "test", new HttpClientDigestCredentials("dart", "password"));
+
+    var futures = <Future>[];
+    for (int i = 0; i < 2; i++) {
+      String uriBase = "http://127.0.0.1:${server.port}/digest";
+      futures.add(makeRequest(Uri.parse(uriBase)));
+      futures.add(makeRequest(Uri.parse("$uriBase?querystring")));
+      futures.add(makeRequest(Uri.parse("$uriBase?querystring#fragment")));
+    }
+    Future.wait(futures).then((_) {
+      server.shutdown();
+      client.close();
+      completer.complete(null);
+    });
+  });
+  return completer.future;
+}
+
+Future<Null> testAuthenticateCallback(String algorithm, String qop) {
+  Completer<Null> completer = new Completer();
+  Server.start(algorithm, qop).then((server) {
+    HttpClient client = new HttpClient();
+
+    client.authenticate = (Uri url, String scheme, String realm) {
+      expect("Digest", equals(scheme));
+      expect("test", equals(realm));
+      Completer completer = new Completer();
+      new Timer(const Duration(milliseconds: 10), () {
+        client.addCredentials(
+            Uri.parse("http://127.0.0.1:${server.port}/digest"),
+            "test",
+            new HttpClientDigestCredentials("dart", "password"));
+        completer.complete(true);
+      });
+      return completer.future;
+    };
+
+    Future makeRequest(Uri url) {
+      return client
+          .getUrl(url)
+          .then((HttpClientRequest request) => request.close())
+          .then((HttpClientResponse response) {
+        expect(HttpStatus.OK, equals(response.statusCode));
+        expect(1, equals(response.headers["Authentication-Info"].length));
+        return response.fold(null, (x, y) {});
+      });
+    }
+
+    var futures = <Future>[];
+    for (int i = 0; i < 5; i++) {
+      futures.add(
+          makeRequest(Uri.parse("http://127.0.0.1:${server.port}/digest")));
+    }
+    Future.wait(futures).then((_) {
+      server.shutdown();
+      client.close();
+      completer.complete(null);
+    });
+  });
+  return completer.future;
+}
+
+Future<Null> testStaleNonce() {
+  Completer<Null> completer = new Completer();
+  Server.start("MD5", "auth", nonceStaleAfter: 2).then((server) {
+    HttpClient client = new HttpClient();
+
+    Future makeRequest(Uri url) {
+      return client
+          .getUrl(url)
+          .then((HttpClientRequest request) => request.close())
+          .then((HttpClientResponse response) {
+        expect(HttpStatus.OK, equals(response.statusCode));
+        expect(1, equals(response.headers["Authentication-Info"].length));
+        return response.fold(null, (x, y) {});
+      });
+    }
+
+    Uri uri = Uri.parse("http://127.0.0.1:${server.port}/digest");
+    var credentials = new HttpClientDigestCredentials("dart", "password");
+    client.addCredentials(uri, "test", credentials);
+
+    makeRequest(uri)
+        .then((_) => makeRequest(uri))
+        .then((_) => makeRequest(uri))
+        .then((_) => makeRequest(uri))
+        .then((_) {
+      expect(2, equals(server.unauthCount));
+      expect(4, equals(server.successCount));
+      server.shutdown();
+      client.close();
+      completer.complete(null);
+    });
+  });
+  return completer.future;
+}
+
+Future<Null> testNextNonce() {
+  Completer<Null> completer = new Completer();
+  Server
+      .start("MD5", "auth", nonceStaleAfter: 2, useNextNonce: true)
+      .then((server) {
+    HttpClient client = new HttpClient();
+
+    Future makeRequest(Uri url) {
+      return client
+          .getUrl(url)
+          .then((HttpClientRequest request) => request.close())
+          .then((HttpClientResponse response) {
+        expect(HttpStatus.OK, equals(response.statusCode));
+        expect(1, equals(response.headers["Authentication-Info"].length));
+        return response.fold(null, (x, y) {});
+      });
+    }
+
+    Uri uri = Uri.parse("http://127.0.0.1:${server.port}/digest");
+    var credentials = new HttpClientDigestCredentials("dart", "password");
+    client.addCredentials(uri, "test", credentials);
+
+    makeRequest(uri)
+        .then((_) => makeRequest(uri))
+        .then((_) => makeRequest(uri))
+        .then((_) => makeRequest(uri))
+        .then((_) {
+      expect(1, equals(server.unauthCount));
+      expect(4, equals(server.successCount));
+      server.shutdown();
+      client.close();
+      completer.complete(null);
+    });
+  });
+  return completer.future;
+}
+
+main() {
+  test("NoCredentials", () async {
+    await testNoCredentials(null, null);
+  });
+  test("NoCredentials MD5", () async {
+    await testNoCredentials("MD5", null);
+  });
+  test("NoCredentials MD5 auth", () async {
+    await testNoCredentials("MD5", "auth");
+  });
+  test("Credentials", () async {
+    await testCredentials(null, null);
+  });
+  test("Credentials MD5", () async {
+    await testCredentials("MD5", null);
+  });
+  test("Credentials MD5 auth", () async {
+    await testCredentials("MD5", "auth");
+  });
+  test("Credentials MD5 auth-int", () async {
+    await testCredentials("MD5", "auth-int");
+  });
+  test("AuthenticateCallback", () async {
+    await testAuthenticateCallback(null, null);
+  });
+  test("AuthenticateCallback MD5", () async {
+    await testAuthenticateCallback("MD5", null);
+  });
+  test("AuthenticateCallback MD5 auth", () async {
+    await testAuthenticateCallback("MD5", "auth");
+  });
+  test("AuthenticateCallback MD5 auth-int", () async {
+    await testAuthenticateCallback("MD5", "auth-int");
+  });
+  test("StaleNonce", () async {
+    await testStaleNonce();
+  });
+  test("NextNonce", () async {
+    await testNextNonce();
+  });
+}