Fix Package constructor not accepting relative packageUriRoot. (#82)

* Fix Package constructor not accepting relative packageUriRoot.
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3015523..6dcae80 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 1.9.3
+
+- Fix `Package` constructor not accepting relative `packageUriRoot`.
+
 ## 1.9.2
 
 - Updated to support new rules for picking `package_config.json` over
diff --git a/lib/src/package_config.dart b/lib/src/package_config.dart
index 364df75..30c758a 100644
--- a/lib/src/package_config.dart
+++ b/lib/src/package_config.dart
@@ -219,7 +219,7 @@
   /// The [packageUriRoot], if provided, must be either an absolute
   /// directory URI or a relative URI reference which is then resolved
   /// relative to [root]. It must then also be a subdirectory of [root],
-  /// or the same directory.
+  /// or the same directory, and must end with `/`.
   /// If [languageVersion] is supplied, it must be a valid Dart language
   /// version, which means two decimal integer literals separated by a `.`,
   /// where the integer literals have no leading zeros unless they are
diff --git a/lib/src/package_config_impl.dart b/lib/src/package_config_impl.dart
index f68a9ea..9e23af0 100644
--- a/lib/src/package_config_impl.dart
+++ b/lib/src/package_config_impl.dart
@@ -214,7 +214,10 @@
         root = root.replace(path: root.path + "/");
       }
     }
-    if (!fatalError) {
+    if (packageUriRoot == null) {
+      packageUriRoot = root;
+    } else if (!fatalError) {
+      packageUriRoot = root.resolveUri(packageUriRoot);
       if (!isAbsoluteDirectoryUri(packageUriRoot)) {
         onError(PackageConfigArgumentError(
             packageUriRoot,
diff --git a/pubspec.yaml b/pubspec.yaml
index 3f5ec27..b7d5969 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,5 +1,5 @@
 name: package_config
-version: 1.9.2
+version: 1.9.3
 description: Support for working with Package Configuration files.
 homepage: https://github.com/dart-lang/package_config
 
diff --git a/test/package_config_impl_test.dart b/test/package_config_impl_test.dart
new file mode 100644
index 0000000..6921118
--- /dev/null
+++ b/test/package_config_impl_test.dart
@@ -0,0 +1,145 @@
+// Copyright (c) 2020, 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 "package:package_config/package_config_types.dart";
+import "package:test/test.dart";
+import "src/util.dart";
+
+void main() {
+  var unique = Object();
+  var root = Uri.file("/tmp/root/");
+
+  group("LanguageVersion", () {
+    test("minimal", () {
+      var version = LanguageVersion(3, 5);
+      expect(version.major, 3);
+      expect(version.minor, 5);
+    });
+
+    test("negative major", () {
+      expect(() => LanguageVersion(-1, 1), throwsArgumentError);
+    });
+
+    test("negative minor", () {
+      expect(() => LanguageVersion(1, -1), throwsArgumentError);
+    });
+
+    test("minimal parse", () {
+      var version = LanguageVersion.parse("3.5");
+      expect(version.major, 3);
+      expect(version.minor, 5);
+    });
+
+    void failParse(String name, String input) {
+      test("$name - error", () {
+        expect(() => LanguageVersion.parse(input),
+            throwsA(TypeMatcher<PackageConfigError>()));
+        expect(() => LanguageVersion.parse(input), throwsFormatException);
+        var failed = false;
+        var actual = LanguageVersion.parse(input, onError: (_) {
+          failed = true;
+        });
+        expect(failed, true);
+        expect(actual, isA<LanguageVersion>());
+      });
+    }
+
+    failParse("Leading zero major", "01.1");
+    failParse("Leading zero minor", "1.01");
+    failParse("Sign+ major", "+1.1");
+    failParse("Sign- major", "-1.1");
+    failParse("Sign+ minor", "1.+1");
+    failParse("Sign- minor", "1.-1");
+    failParse("WhiteSpace 1", " 1.1");
+    failParse("WhiteSpace 2", "1 .1");
+    failParse("WhiteSpace 3", "1. 1");
+    failParse("WhiteSpace 4", "1.1 ");
+  });
+
+  group("Package", () {
+    test("minimal", () {
+      var package = Package("name", root, extraData: unique);
+      expect(package.name, "name");
+      expect(package.root, root);
+      expect(package.packageUriRoot, root);
+      expect(package.languageVersion, null);
+      expect(package.extraData, same(unique));
+    });
+
+    test("absolute package root", () {
+      var version = LanguageVersion(1, 1);
+      var absolute = root.resolve("foo/bar/");
+      var package = Package("name", root,
+          packageUriRoot: absolute,
+          languageVersion: version,
+          extraData: unique);
+      expect(package.name, "name");
+      expect(package.root, root);
+      expect(package.packageUriRoot, absolute);
+      expect(package.languageVersion, version);
+      expect(package.extraData, same(unique));
+    });
+
+    test("relative package root", () {
+      var relative = Uri.parse("foo/bar/");
+      var absolute = root.resolveUri(relative);
+      var package =
+          Package("name", root, packageUriRoot: relative, extraData: unique);
+      expect(package.name, "name");
+      expect(package.root, root);
+      expect(package.packageUriRoot, absolute);
+      expect(package.languageVersion, null);
+      expect(package.extraData, same(unique));
+    });
+
+    for (var badName in ["a/z", "a:z", "", "..."]) {
+      test("Invalid name '$badName'", () {
+        expect(() => Package(badName, root), throwsPackageConfigError);
+      });
+    }
+
+    test("Invalid root, not absolute", () {
+      expect(
+          () => Package("name", Uri.parse("/foo/")), throwsPackageConfigError);
+    });
+
+    test("Invalid root, not ending in slash", () {
+      expect(() => Package("name", Uri.parse("file:///foo")),
+          throwsPackageConfigError);
+    });
+
+    test("invalid package root, not ending in slash", () {
+      expect(() => Package("name", root, packageUriRoot: Uri.parse("foo")),
+          throwsPackageConfigError);
+    });
+
+    test("invalid package root, not inside root", () {
+      expect(() => Package("name", root, packageUriRoot: Uri.parse("../baz/")),
+          throwsPackageConfigError);
+    });
+  });
+
+  group("package config", () {
+    test("emtpy", () {
+      var empty = PackageConfig([], extraData: unique);
+      expect(empty.version, 2);
+      expect(empty.packages, isEmpty);
+      expect(empty.extraData, same(unique));
+      expect(empty.resolve(pkg("a", "b")), isNull);
+    });
+
+    test("single", () {
+      var package = Package("name", root);
+      var single = PackageConfig([package], extraData: unique);
+      expect(single.version, 2);
+      expect(single.packages, hasLength(1));
+      expect(single.extraData, same(unique));
+      expect(single.resolve(pkg("a", "b")), isNull);
+      var resolved = single.resolve(pkg("name", "a/b"));
+      expect(resolved, root.resolve("a/b"));
+    });
+  });
+}
+
+final Matcher throwsPackageConfigError = throwsA(isA<PackageConfigError>());