| // 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); |
| }); |
| } |