blob: 59a7e7126a04f83f934374cfbcf5f9539119af36 [file] [log] [blame]
// Copyright (c) 2019, 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:convert";
import 'dart:typed_data';
import "package:test/test.dart";
import "package:package_config/package_config_types.dart";
import "package:package_config/src/packages_file.dart" as packages;
import "package:package_config/src/package_config_json.dart";
import "src/util.dart";
void throwError(Object error) => throw error;
void main() {
group(".packages", () {
test("valid", () {
var packagesFile = "# Generated by pub yadda yadda\n"
"foo:file:///foo/lib/\n"
"bar:/bar/lib/\n"
"baz:lib/\n";
var result = packages.parse(utf8.encode(packagesFile),
Uri.parse("file:///tmp/file.dart"), throwError);
expect(result.version, 1);
expect({for (var p in result.packages) p.name}, {"foo", "bar", "baz"});
expect(result.resolve(pkg("foo", "foo.dart")),
Uri.parse("file:///foo/lib/foo.dart"));
expect(result.resolve(pkg("bar", "bar.dart")),
Uri.parse("file:///bar/lib/bar.dart"));
expect(result.resolve(pkg("baz", "baz.dart")),
Uri.parse("file:///tmp/lib/baz.dart"));
var foo = result["foo"];
expect(foo, isNotNull);
expect(foo.root, Uri.parse("file:///foo/"));
expect(foo.packageUriRoot, Uri.parse("file:///foo/lib/"));
expect(foo.languageVersion, LanguageVersion(2, 7));
});
test("valid empty", () {
var packagesFile = "# Generated by pub yadda yadda\n";
var result = packages.parse(
utf8.encode(packagesFile), Uri.file("/tmp/file.dart"), throwError);
expect(result.version, 1);
expect({for (var p in result.packages) p.name}, <String>{});
});
group("invalid", () {
var baseFile = Uri.file("/tmp/file.dart");
void testThrows(String name, String content) {
test(name, () {
expect(
() => packages.parse(utf8.encode(content), baseFile, throwError),
throwsA(TypeMatcher<FormatException>()));
});
test(name + ", handle error", () {
var hadError = false;
packages.parse(utf8.encode(content), baseFile, (error) {
hadError = true;
expect(error, isA<FormatException>());
});
expect(hadError, true);
});
}
testThrows("repeated package name", "foo:lib/\nfoo:lib\n");
testThrows("no colon", "foo\n");
testThrows("empty package name", ":lib/\n");
testThrows("dot only package name", ".:lib/\n");
testThrows("dot only package name", "..:lib/\n");
testThrows("invalid package name character", "f\\o:lib/\n");
testThrows("package URI", "foo:package:bar/lib/");
testThrows("location with query", "f\\o:lib/?\n");
testThrows("location with fragment", "f\\o:lib/#\n");
});
});
group("package_config.json", () {
test("valid", () {
var packageConfigFile = """
{
"configVersion": 2,
"packages": [
{
"name": "foo",
"rootUri": "file:///foo/",
"packageUri": "lib/",
"languageVersion": "2.5",
"nonstandard": true
},
{
"name": "bar",
"rootUri": "/bar/",
"packageUri": "lib/",
"languageVersion": "9999.9999"
},
{
"name": "baz",
"rootUri": "../",
"packageUri": "lib/"
},
{
"name": "noslash",
"rootUri": "../noslash",
"packageUri": "lib"
}
],
"generator": "pub",
"other": [42]
}
""";
var config = parsePackageConfigBytes(utf8.encode(packageConfigFile),
Uri.parse("file:///tmp/.dart_tool/file.dart"), throwError);
expect(config.version, 2);
expect({for (var p in config.packages) p.name},
{"foo", "bar", "baz", "noslash"});
expect(config.resolve(pkg("foo", "foo.dart")),
Uri.parse("file:///foo/lib/foo.dart"));
expect(config.resolve(pkg("bar", "bar.dart")),
Uri.parse("file:///bar/lib/bar.dart"));
expect(config.resolve(pkg("baz", "baz.dart")),
Uri.parse("file:///tmp/lib/baz.dart"));
var foo = config["foo"];
expect(foo, isNotNull);
expect(foo.root, Uri.parse("file:///foo/"));
expect(foo.packageUriRoot, Uri.parse("file:///foo/lib/"));
expect(foo.languageVersion, LanguageVersion(2, 5));
expect(foo.extraData, {"nonstandard": true});
var bar = config["bar"];
expect(bar, isNotNull);
expect(bar.root, Uri.parse("file:///bar/"));
expect(bar.packageUriRoot, Uri.parse("file:///bar/lib/"));
expect(bar.languageVersion, LanguageVersion(9999, 9999));
expect(bar.extraData, null);
var baz = config["baz"];
expect(baz, isNotNull);
expect(baz.root, Uri.parse("file:///tmp/"));
expect(baz.packageUriRoot, Uri.parse("file:///tmp/lib/"));
expect(baz.languageVersion, null);
// No slash after root or package root. One is inserted.
var noslash = config["noslash"];
expect(noslash, isNotNull);
expect(noslash.root, Uri.parse("file:///tmp/noslash/"));
expect(noslash.packageUriRoot, Uri.parse("file:///tmp/noslash/lib/"));
expect(noslash.languageVersion, null);
expect(config.extraData, {
"generator": "pub",
"other": [42]
});
});
test("valid other order", () {
// The ordering in the file is not important.
var packageConfigFile = """
{
"generator": "pub",
"other": [42],
"packages": [
{
"languageVersion": "2.5",
"packageUri": "lib/",
"rootUri": "file:///foo/",
"name": "foo"
},
{
"packageUri": "lib/",
"languageVersion": "9999.9999",
"rootUri": "/bar/",
"name": "bar"
},
{
"packageUri": "lib/",
"name": "baz",
"rootUri": "../"
}
],
"configVersion": 2
}
""";
var config = parsePackageConfigBytes(utf8.encode(packageConfigFile),
Uri.parse("file:///tmp/.dart_tool/file.dart"), throwError);
expect(config.version, 2);
expect({for (var p in config.packages) p.name}, {"foo", "bar", "baz"});
expect(config.resolve(pkg("foo", "foo.dart")),
Uri.parse("file:///foo/lib/foo.dart"));
expect(config.resolve(pkg("bar", "bar.dart")),
Uri.parse("file:///bar/lib/bar.dart"));
expect(config.resolve(pkg("baz", "baz.dart")),
Uri.parse("file:///tmp/lib/baz.dart"));
expect(config.extraData, {
"generator": "pub",
"other": [42]
});
});
// Check that a few minimal configurations are valid.
// These form the basis of invalid tests below.
var cfg = '"configVersion":2';
var pkgs = '"packages":[]';
var name = '"name":"foo"';
var root = '"rootUri":"/foo/"';
test("minimal", () {
var config = parsePackageConfigBytes(utf8.encode("{$cfg,$pkgs}"),
Uri.parse("file:///tmp/.dart_tool/file.dart"), throwError);
expect(config.version, 2);
expect(config.packages, isEmpty);
});
test("minimal package", () {
// A package must have a name and a rootUri, the remaining properties
// are optional.
var config = parsePackageConfigBytes(
utf8.encode('{$cfg,"packages":[{$name,$root}]}'),
Uri.parse("file:///tmp/.dart_tool/file.dart"),
throwError);
expect(config.version, 2);
expect(config.packages.first.name, "foo");
});
test("nested packages", () {
var configBytes = utf8.encode(json.encode({
"configVersion": 2,
"packages": [
{"name": "foo", "rootUri": "/foo/", "packageUri": "lib/"},
{"name": "bar", "rootUri": "/foo/bar/", "packageUri": "lib/"},
{"name": "baz", "rootUri": "/foo/bar/baz/", "packageUri": "lib/"},
{"name": "qux", "rootUri": "/foo/qux/", "packageUri": "lib/"},
]
}));
var config = parsePackageConfigBytes(configBytes,
Uri.parse("file:///tmp/.dart_tool/file.dart"), throwError);
expect(config.version, 2);
expect(config.packageOf(Uri.parse("file:///foo/lala/lala.dart")).name,
"foo");
expect(
config.packageOf(Uri.parse("file:///foo/bar/lala.dart")).name, "bar");
expect(config.packageOf(Uri.parse("file:///foo/bar/baz/lala.dart")).name,
"baz");
expect(
config.packageOf(Uri.parse("file:///foo/qux/lala.dart")).name, "qux");
expect(config.toPackageUri(Uri.parse("file:///foo/lib/diz")),
Uri.parse("package:foo/diz"));
expect(config.toPackageUri(Uri.parse("file:///foo/bar/lib/diz")),
Uri.parse("package:bar/diz"));
expect(config.toPackageUri(Uri.parse("file:///foo/bar/baz/lib/diz")),
Uri.parse("package:baz/diz"));
expect(config.toPackageUri(Uri.parse("file:///foo/qux/lib/diz")),
Uri.parse("package:qux/diz"));
});
group("invalid", () {
void testThrows(String name, String source) {
test(name, () {
expect(
() => parsePackageConfigBytes(utf8.encode(source),
Uri.parse("file:///tmp/.dart_tool/file.dart"), throwError),
throwsA(TypeMatcher<FormatException>()));
});
}
testThrows("comment", '# comment\n {$cfg,$pkgs}');
testThrows(".packages file", 'foo:/foo\n');
testThrows("no configVersion", '{$pkgs}');
testThrows("no packages", '{$cfg}');
group("config version:", () {
testThrows("null", '{"configVersion":null,$pkgs}');
testThrows("string", '{"configVersion":"2",$pkgs}');
testThrows("array", '{"configVersion":[2],$pkgs}');
});
group("packages:", () {
testThrows("null", '{$cfg,"packages":null}');
testThrows("string", '{$cfg,"packages":"foo"}');
testThrows("object", '{$cfg,"packages":{}}');
});
group("packages entry:", () {
testThrows("null", '{$cfg,"packages":[null]}');
testThrows("string", '{$cfg,"packages":["foo"]}');
testThrows("array", '{$cfg,"packages":[[]]}');
});
group("package", () {
testThrows("no name", '{$cfg,"packages":[{$root}]}');
group("name:", () {
testThrows("null", '{$cfg,"packages":[{"name":null,$root}]}');
testThrows("num", '{$cfg,"packages":[{"name":1,$root}]}');
testThrows("object", '{$cfg,"packages":[{"name":{},$root}]}');
testThrows("empty", '{$cfg,"packages":[{"name":"",$root}]}');
testThrows("one-dot", '{$cfg,"packages":[{"name":".",$root}]}');
testThrows("two-dot", '{$cfg,"packages":[{"name":"..",$root}]}');
testThrows(
"invalid char '\\'", '{$cfg,"packages":[{"name":"\\",$root}]}');
testThrows(
"invalid char ':'", '{$cfg,"packages":[{"name":":",$root}]}');
testThrows(
"invalid char ' '", '{$cfg,"packages":[{"name":" ",$root}]}');
});
testThrows("no root", '{$cfg,"packages":[{$name}]}');
group("root:", () {
testThrows("null", '{$cfg,"packages":[{$name,"rootUri":null}]}');
testThrows("num", '{$cfg,"packages":[{$name,"rootUri":1}]}');
testThrows("object", '{$cfg,"packages":[{$name,"rootUri":{}}]}');
testThrows("fragment", '{$cfg,"packages":[{$name,"rootUri":"x/#"}]}');
testThrows("query", '{$cfg,"packages":[{$name,"rootUri":"x/?"}]}');
testThrows("package-URI",
'{$cfg,"packages":[{$name,"rootUri":"package:x/x/"}]}');
});
group("package-URI root:", () {
testThrows(
"null", '{$cfg,"packages":[{$name,$root,"packageUri":null}]}');
testThrows("num", '{$cfg,"packages":[{$name,$root,"packageUri":1}]}');
testThrows(
"object", '{$cfg,"packages":[{$name,$root,"packageUri":{}}]}');
testThrows("fragment",
'{$cfg,"packages":[{$name,$root,"packageUri":"x/#"}]}');
testThrows(
"query", '{$cfg,"packages":[{$name,$root,"packageUri":"x/?"}]}');
testThrows("package: URI",
'{$cfg,"packages":[{$name,$root,"packageUri":"package:x/x/"}]}');
testThrows("not inside root",
'{$cfg,"packages":[{$name,$root,"packageUri":"../other/"}]}');
});
group("language version", () {
testThrows("null",
'{$cfg,"packages":[{$name,$root,"languageVersion":null}]}');
testThrows(
"num", '{$cfg,"packages":[{$name,$root,"languageVersion":1}]}');
testThrows("object",
'{$cfg,"packages":[{$name,$root,"languageVersion":{}}]}');
testThrows("empty",
'{$cfg,"packages":[{$name,$root,"languageVersion":""}]}');
testThrows("non number.number",
'{$cfg,"packages":[{$name,$root,"languageVersion":"x.1"}]}');
testThrows("number.non number",
'{$cfg,"packages":[{$name,$root,"languageVersion":"1.x"}]}');
testThrows("non number",
'{$cfg,"packages":[{$name,$root,"languageVersion":"x"}]}');
testThrows("one number",
'{$cfg,"packages":[{$name,$root,"languageVersion":"1"}]}');
testThrows("three numbers",
'{$cfg,"packages":[{$name,$root,"languageVersion":"1.2.3"}]}');
testThrows("leading zero first",
'{$cfg,"packages":[{$name,$root,"languageVersion":"01.1"}]}');
testThrows("leading zero second",
'{$cfg,"packages":[{$name,$root,"languageVersion":"1.01"}]}');
testThrows("trailing-",
'{$cfg,"packages":[{$name,$root,"languageVersion":"1.1-1"}]}');
testThrows("trailing+",
'{$cfg,"packages":[{$name,$root,"languageVersion":"1.1+1"}]}');
});
});
testThrows("duplicate package name",
'{$cfg,"packages":[{$name,$root},{$name,"rootUri":"/other/"}]}');
testThrows("same roots",
'{$cfg,"packages":[{$name,$root},{"name":"bar",$root}]}');
testThrows(
// The roots of foo and bar are the same.
"same roots",
'{$cfg,"packages":[{$name,$root},{"name":"bar",$root}]}');
testThrows(
// The root of bar is inside the root of foo,
// but the package root of foo is inside the root of bar.
"between root and lib",
'{$cfg,"packages":['
'{"name":"foo","rootUri":"/foo/","packageUri":"bar/lib/"},'
'{"name":"bar","rootUri":"/foo/bar/"},"packageUri":"baz/lib"]}');
});
});
group("factories", () {
void testConfig(String name, PackageConfig config, PackageConfig expected) {
group(name, () {
test("structure", () {
expect(config.version, expected.version);
var expectedPackages = {for (var p in expected.packages) p.name};
var actualPackages = {for (var p in config.packages) p.name};
expect(actualPackages, expectedPackages);
});
for (var package in config.packages) {
var name = package.name;
test("package $name", () {
var expectedPackage = expected[name];
expect(expectedPackage, isNotNull);
expect(package.root, expectedPackage.root, reason: "root");
expect(package.packageUriRoot, expectedPackage.packageUriRoot,
reason: "package root");
expect(package.languageVersion, expectedPackage.languageVersion,
reason: "languageVersion");
});
}
});
}
var configText = """
{"configVersion": 2, "packages": [
{
"name": "foo",
"rootUri": "foo/",
"packageUri": "bar/",
"languageVersion": "1.2"
}
]}
""";
var baseUri = Uri.parse("file:///start/");
var config = PackageConfig([
Package("foo", Uri.parse("file:///start/foo/"),
packageUriRoot: Uri.parse("file:///start/foo/bar/"),
languageVersion: LanguageVersion(1, 2))
]);
testConfig(
"string", PackageConfig.parseString(configText, baseUri), config);
testConfig(
"bytes",
PackageConfig.parseBytes(
Uint8List.fromList(configText.codeUnits), baseUri),
config);
testConfig("json", PackageConfig.parseJson(jsonDecode(configText), baseUri),
config);
baseUri = Uri.parse("file:///start2/");
config = PackageConfig([
Package("foo", Uri.parse("file:///start2/foo/"),
packageUriRoot: Uri.parse("file:///start2/foo/bar/"),
languageVersion: LanguageVersion(1, 2))
]);
testConfig(
"string2", PackageConfig.parseString(configText, baseUri), config);
testConfig(
"bytes2",
PackageConfig.parseBytes(
Uint8List.fromList(configText.codeUnits), baseUri),
config);
testConfig("json2",
PackageConfig.parseJson(jsonDecode(configText), baseUri), config);
});
}