Add more tests

Review URL: https://codereview.chromium.org//1152173005
diff --git a/pkgs/package_config/lib/discovery.dart b/pkgs/package_config/lib/discovery.dart
index 78254ee..52f208d 100644
--- a/pkgs/package_config/lib/discovery.dart
+++ b/pkgs/package_config/lib/discovery.dart
@@ -44,9 +44,9 @@
   if (baseUri.scheme == "file") {
     return new Future<Packages>.sync(() => findPackagesFromFile(baseUri));
   } else if (baseUri.scheme == "http" || baseUri.scheme == "https") {
-    return findPackagesFromNonFile(baseUri, _httpGet);
+    return findPackagesFromNonFile(baseUri, loader: _httpGet);
   } else if (loader != null) {
-    return findPackagesFromNonFile(baseUri, loader);
+    return findPackagesFromNonFile(baseUri, loader: loader);
   } else {
     return new Future<Packages>.value(Packages.noPackages);
   }
@@ -138,7 +138,7 @@
 /// of the requestsed `.packages` file as bytes, which will be assumed to be
 /// UTF-8 encoded.
 Future<Packages> findPackagesFromNonFile(Uri nonFileUri,
-                                         [Future<List<int>> loader(Uri name)]) {
+                                         {Future<List<int>> loader(Uri name)}) {
   if (loader == null) loader = _httpGet;
   Uri packagesFileUri = nonFileUri.resolve(".packages");
   return loader(packagesFileUri).then((List<int> fileBytes) {
diff --git a/pkgs/package_config/lib/src/packages_impl.dart b/pkgs/package_config/lib/src/packages_impl.dart
index 612dc34..9563762 100644
--- a/pkgs/package_config/lib/src/packages_impl.dart
+++ b/pkgs/package_config/lib/src/packages_impl.dart
@@ -70,7 +70,7 @@
   FilePackagesDirectoryPackages(this._packageDir);
 
   Uri _getBase(String packageName) =>
-      new Uri.directory(path.join(packageName,''));
+      new Uri.directory(path.join(_packageDir.path, packageName, ''));
 
   Iterable<String> _listPackageNames() {
     return _packageDir.listSync()
diff --git a/pkgs/package_config/test/all.dart b/pkgs/package_config/test/all.dart
index 310e1ee..79f5b35 100644
--- a/pkgs/package_config/test/all.dart
+++ b/pkgs/package_config/test/all.dart
@@ -2,8 +2,10 @@
 // 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 'parse_test.dart' as parse;
+import "discovery_test.dart" as discovery;
+import "parse_test.dart" as parse;
 
 main() {
   parse.main();
+  discovery.main();
 }
diff --git a/pkgs/package_config/test/discovery_test.dart b/pkgs/package_config/test/discovery_test.dart
new file mode 100644
index 0000000..4a88928
--- /dev/null
+++ b/pkgs/package_config/test/discovery_test.dart
@@ -0,0 +1,281 @@
+// 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:io";
+import "package:test/test.dart";
+import "package:package_config/packages.dart";
+import "package:package_config/discovery.dart";
+import "package:path/path.dart" as path;
+
+const packagesFile = """
+# A comment
+foo=file:///dart/packages/foo/
+bar=http://example.com/dart/packages/bar/
+baz=packages/baz/
+""";
+
+void validatePackagesFile(Packages resolver, Uri location) {
+  expect(resolver, isNotNull);
+  expect(resolver.resolve(pkg("foo", "bar/baz")),
+         equals(Uri.parse("file:///dart/packages/foo/bar/baz")));
+  expect(resolver.resolve(pkg("bar", "baz/qux")),
+         equals(Uri.parse("http://example.com/dart/packages/bar/baz/qux")));
+  expect(resolver.resolve(pkg("baz", "qux/foo")),
+         equals(location.resolve("packages/baz/qux/foo")));
+  expect(resolver.packages, unorderedEquals(["foo", "bar", "baz"]));
+}
+
+void validatePackagesDir(Packages resolver, Uri location) {
+  // Expect three packages: foo, bar and baz
+  expect(resolver, isNotNull);
+  expect(resolver.resolve(pkg("foo", "bar/baz")),
+         equals(location.resolve("packages/foo/bar/baz")));
+  expect(resolver.resolve(pkg("bar", "baz/qux")),
+         equals(location.resolve("packages/bar/baz/qux")));
+  expect(resolver.resolve(pkg("baz", "qux/foo")),
+         equals(location.resolve("packages/baz/qux/foo")));
+  if (location.scheme == "file") {
+    expect(resolver.packages, unorderedEquals(["foo", "bar", "baz"]));
+  } else {
+    expect(() => resolver.packages, throws);
+  }
+}
+
+
+Uri pkg(String packageName, String packagePath) {
+  var path;
+  if (packagePath.startsWith('/')) {
+    path = "$packageName$packagePath";
+  } else {
+    path = "$packageName/$packagePath";
+  }
+  return new Uri(scheme: "package", path: path);
+}
+
+main() {
+  generalTest(".packages",
+              {".packages": packagesFile,
+               "script.dart": "main(){}",
+               "packages": {"shouldNotBeFound": {}}},
+              (Uri location) async {
+    Packages resolver;
+    resolver = await findPackages(location);
+    validatePackagesFile(resolver, location);
+    resolver = await findPackages(location.resolve("script.dart"));
+    validatePackagesFile(resolver, location);
+    var specificDiscovery = (location.scheme == "file")
+        ? findPackagesFromFile
+        : findPackagesFromNonFile;
+    resolver = await specificDiscovery(location);
+    validatePackagesFile(resolver, location);
+    resolver = await specificDiscovery(location.resolve("script.dart"));
+    validatePackagesFile(resolver, location);
+  });
+
+  generalTest("packages/",
+              {"packages": { "foo": {}, "bar": {}, "baz": {}},
+               "script.dart": "main(){}"},
+              (Uri location) async {
+    Packages resolver;
+    bool isFile = (location.scheme == "file");
+    resolver = await findPackages(location);
+    validatePackagesDir(resolver, location);
+    resolver = await findPackages(location.resolve("script.dart"));
+    validatePackagesDir(resolver, location);
+    var specificDiscovery = isFile
+        ? findPackagesFromFile
+        : findPackagesFromNonFile;
+    resolver = await specificDiscovery(location);
+    validatePackagesDir(resolver, location);
+    resolver = await specificDiscovery(location.resolve("script.dart"));
+    validatePackagesDir(resolver, location);
+  });
+
+  fileTest(".packages recursive",
+           {".packages": packagesFile, "subdir": {"script.dart": "main(){}"}},
+           (Uri location) async {
+    Packages resolver;
+    resolver = await findPackages(location.resolve("subdir/"));
+    validatePackagesFile(resolver, location);
+    resolver = await findPackages(location.resolve("subdir/script.dart"));
+    validatePackagesFile(resolver, location);
+    resolver = await findPackagesFromFile(location.resolve("subdir/"));
+    validatePackagesFile(resolver, location);
+    resolver =
+        await findPackagesFromFile(location.resolve("subdir/script.dart"));
+    validatePackagesFile(resolver, location);
+  });
+
+  httpTest(".packages not recursive",
+           {".packages": packagesFile, "subdir": {"script.dart": "main(){}"}},
+           (Uri location) async {
+    Packages resolver;
+    var subdir = location.resolve("subdir/");
+    resolver = await findPackages(subdir);
+    validatePackagesDir(resolver, subdir);
+    resolver = await findPackages(subdir.resolve("script.dart"));
+    validatePackagesDir(resolver, subdir);
+    resolver = await findPackagesFromNonFile(subdir);
+    validatePackagesDir(resolver, subdir);
+    resolver = await findPackagesFromNonFile(subdir.resolve("script.dart"));
+    validatePackagesDir(resolver, subdir);
+  });
+
+  fileTest("no packages",
+          {"script.dart": "main(){}"},
+          (Uri location) async {
+    // A file: location with no .packages or packages returns
+    // Packages.noPackages.
+    Packages resolver;
+    resolver = await findPackages(location);
+    expect(resolver, same(Packages.noPackages));
+    resolver = await findPackages(location.resolve("script.dart"));
+    expect(resolver, same(Packages.noPackages));
+    resolver = findPackagesFromFile(location);
+    expect(resolver, same(Packages.noPackages));
+    resolver = findPackagesFromFile(location.resolve("script.dart"));
+    expect(resolver, same(Packages.noPackages));
+  });
+
+  httpTest("no packages",
+           {"script.dart": "main(){}"},
+           (Uri location) async {
+    // A non-file: location with no .packages or packages/:
+    // Assumes a packages dir exists, and resolves relative to that.
+    Packages resolver;
+    resolver = await findPackages(location);
+    validatePackagesDir(resolver, location);
+    resolver = await findPackages(location.resolve("script.dart"));
+    validatePackagesDir(resolver, location);
+    resolver = await findPackagesFromNonFile(location);
+    validatePackagesDir(resolver, location);
+    resolver = await findPackagesFromNonFile(location.resolve("script.dart"));
+    validatePackagesDir(resolver, location);
+  });
+
+  test(".packages w/ loader", () async {
+    Uri location = Uri.parse("krutch://example.com/path/");
+    Future loader(Uri file) async {
+      if (file.path.endsWith(".packages")) {
+        return packagesFile.codeUnits;
+      }
+      throw "not found";
+    }
+    // A non-file: location with no .packages or packages/:
+    // Assumes a packages dir exists, and resolves relative to that.
+    Packages resolver;
+    resolver = await findPackages(location, loader: loader);
+    validatePackagesFile(resolver, location);
+    resolver = await findPackages(location.resolve("script.dart"),
+                                  loader: loader);
+    validatePackagesFile(resolver, location);
+    resolver = await findPackagesFromNonFile(location, loader: loader);
+    validatePackagesFile(resolver, location);
+    resolver = await findPackagesFromNonFile(location.resolve("script.dart"),
+                                             loader: loader);
+    validatePackagesFile(resolver, location);
+  });
+
+  test("no packages w/ loader", () async {
+    Uri location = Uri.parse("krutch://example.com/path/");
+    Future loader(Uri file) async {
+      throw "not found";
+    }
+    // A non-file: location with no .packages or packages/:
+    // Assumes a packages dir exists, and resolves relative to that.
+    Packages resolver;
+    resolver = await findPackages(location, loader: loader);
+    validatePackagesDir(resolver, location);
+    resolver = await findPackages(location.resolve("script.dart"),
+                                  loader: loader);
+    validatePackagesDir(resolver, location);
+    resolver = await findPackagesFromNonFile(location, loader: loader);
+    validatePackagesDir(resolver, location);
+    resolver = await findPackagesFromNonFile(location.resolve("script.dart"),
+                                             loader:loader);
+    validatePackagesDir(resolver, location);
+  });
+}
+
+/// Create a directory structure from [description] and run [fileTest].
+///
+/// Description is a map, each key is a file entry. If the value is a map,
+/// it's a sub-dir, otherwise it's a file and the value is the content
+/// as a string.
+void fileTest(String name,
+              Map description,
+              Future fileTest(Uri directory)) {
+  group("file-test", () {
+    Directory tempDir = Directory.systemTemp.createTempSync("file-test");
+    setUp(() {
+      _createFiles(tempDir, description);
+    });
+    tearDown(() {
+      tempDir.deleteSync(recursive: true);
+    });
+    test(name, () => fileTest(new Uri.directory(tempDir.path)));
+  });
+}
+
+/// HTTP-server the directory structure from [description] and run [htpTest].
+///
+/// Description is a map, each key is a file entry. If the value is a map,
+/// it's a sub-dir, otherwise it's a file and the value is the content
+/// as a string.
+void httpTest(String name, Map description, Future httpTest(Uri directory)) {
+  group("http-test", () {
+    var serverSub;
+    var uri;
+    setUp(() {
+      return HttpServer
+        .bind(InternetAddress.LOOPBACK_IP_V4, 0)
+        .then((server) {
+          uri = new Uri(scheme: "http",
+                        host: "127.0.0.1",
+                        port: server.port,
+                        path: "/");
+          serverSub = server.listen((HttpRequest request) {
+            // No error handling.
+            var path = request.uri.path;
+            if (path.startsWith('/')) path = path.substring(1);
+            if (path.endsWith('/')) path = path.substring(0, path.length - 1);
+            var parts = path.split('/');
+            var fileOrDir = description;
+            for (int i = 0; i < parts.length; i++) {
+              fileOrDir = fileOrDir[parts[i]];
+              if (fileOrDir == null) {
+                request.response.statusCode = 404;
+                request.response.close();
+              }
+            }
+            request.response.write(fileOrDir);
+            request.response.close();
+          });
+        });
+    });
+    tearDown(() => serverSub.cancel());
+    test(name, () => httpTest(uri));
+  });
+}
+
+void generalTest(String name, Map description, Future action(Uri location)) {
+  fileTest(name, description, action);
+  httpTest(name, description, action);
+}
+
+void _createFiles(Directory target, Map description) {
+  description.forEach((name, content) {
+    if (content is Map) {
+      Directory subDir = new Directory(path.join(target.path, name));
+      subDir.createSync();
+      _createFiles(subDir, content);
+    } else {
+      File file = new File(path.join(target.path, name));
+      file.writeAsStringSync(content, flush: true);
+    }
+  });
+}
+
+