| // 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. |
| |
| // @dart=2.10 |
| |
| import 'dart:async' show Future; |
| import 'dart:convert' show JsonEncoder, json, utf8; |
| import 'dart:io' show File; |
| |
| import 'package:path/path.dart' as p; |
| import 'package:pub/src/package_config.dart'; |
| import 'package:pub/src/packages_file.dart' as packages_file; |
| import 'package:pub_semver/pub_semver.dart'; |
| import 'package:test/test.dart'; |
| import 'package:test_descriptor/test_descriptor.dart'; |
| |
| import '../test_pub.dart'; |
| |
| // Resolve against a dummy URL so that we can test whether the URLs in |
| // the package file are themselves relative. We can't resolve against just |
| // "." due to sdk#23809. |
| const _base = '/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p'; |
| |
| /// Describes a `.packages` file and its contents. |
| class PackagesFileDescriptor extends Descriptor { |
| /// A map from package names to either version strings or path to the package. |
| final Map<String, String> _dependencies; |
| |
| /// Describes a `.packages` file with the given dependencies. |
| /// |
| /// [dependencies] maps package names to strings describing where the packages |
| /// are located on disk. |
| PackagesFileDescriptor([this._dependencies]) : super('.packages'); |
| |
| @override |
| Future create([String parent]) { |
| var contents = const <int>[]; |
| if (_dependencies != null) { |
| var mapping = <String, Uri>{}; |
| _dependencies.forEach((package, version) { |
| String packagePath; |
| if (_isSemver(version)) { |
| // It's a cache reference. |
| packagePath = p.join(cachePath, '$package-$version'); |
| } else { |
| // Otherwise it's a path relative to the pubspec file, |
| // which is also relative to the .packages file. |
| packagePath = version; |
| } |
| mapping[package] = p.toUri(p.join(packagePath, 'lib', '')); |
| }); |
| var buffer = StringBuffer(); |
| packages_file.write(buffer, mapping); |
| contents = utf8.encode(buffer.toString()); |
| } |
| return File(p.join(parent ?? sandbox, name)).writeAsBytes(contents); |
| } |
| |
| @override |
| Future validate([String parent]) async { |
| var fullPath = p.join(parent ?? sandbox, name); |
| if (!await File(fullPath).exists()) { |
| fail("File not found: '$fullPath'."); |
| } |
| |
| var bytes = await File(fullPath).readAsBytes(); |
| |
| var map = packages_file.parse(bytes, Uri.parse(_base)); |
| |
| for (var package in _dependencies.keys) { |
| if (!map.containsKey(package)) { |
| fail('.packages does not contain $package entry'); |
| } |
| |
| var description = _dependencies[package]; |
| if (_isSemver(description)) { |
| if (!map[package].path.contains(description)) { |
| fail('.packages of $package has incorrect version. ' |
| 'Expected $description, found location: ${map[package]}.'); |
| } |
| } else { |
| var expected = p.normalize(p.join(description, 'lib')); |
| var actual = p.normalize(p.fromUri( |
| p.url.relative(map[package].toString(), from: p.dirname(_base)))); |
| |
| if (expected != actual) { |
| fail('Relative path: Expected $expected, found $actual'); |
| } |
| } |
| } |
| |
| if (map.length != _dependencies.length) { |
| for (var key in map.keys) { |
| if (!_dependencies.containsKey(key)) { |
| fail('.packages file contains unexpected entry: $key'); |
| } |
| } |
| } |
| } |
| |
| @override |
| String describe() => name; |
| } |
| |
| /// Describes a `.dart_tools/package_config.json` file and its contents. |
| class PackageConfigFileDescriptor extends Descriptor { |
| final String _generatorVersion; |
| |
| /// A map describing the packages in this `package_config.json` file. |
| final List<PackageConfigEntry> _packages; |
| |
| PackageConfig get _config { |
| return PackageConfig( |
| configVersion: 2, |
| packages: _packages, |
| generatorVersion: Version.parse(_generatorVersion), |
| generator: 'pub', |
| generated: DateTime.now().toUtc(), |
| ); |
| } |
| |
| /// Describes a `.packages` file with the given dependencies. |
| /// |
| /// [dependencies] maps package names to strings describing where the packages |
| /// are located on disk. |
| PackageConfigFileDescriptor(this._packages, this._generatorVersion) |
| : super('.dart_tool/package_config.json'); |
| |
| @override |
| Future<void> create([String parent]) async { |
| final packageConfigFile = File(p.join(parent ?? sandbox, name)); |
| await packageConfigFile.parent.create(); |
| await packageConfigFile.writeAsString( |
| const JsonEncoder.withIndent(' ').convert(_config.toJson()) + '\n', |
| ); |
| } |
| |
| @override |
| Future<void> validate([String parent]) async { |
| final packageConfigFile = p.join(parent ?? sandbox, name); |
| if (!await File(packageConfigFile).exists()) { |
| fail("File not found: '$packageConfigFile'."); |
| } |
| |
| Map<String, Object> rawJson = json.decode( |
| await File(packageConfigFile).readAsString(), |
| ); |
| PackageConfig config; |
| try { |
| config = PackageConfig.fromJson(rawJson); |
| } on FormatException catch (e) { |
| fail('File "$packageConfigFile" is not valid: $e'); |
| } |
| |
| // Compare packages as sets to ignore ordering. |
| expect( |
| config.packages.map((e) => e.toJson()).toSet(), |
| equals(_packages.map((e) => e.toJson()).toSet()), |
| reason: |
| '"packages" property in "$packageConfigFile" does not expected values', |
| ); |
| |
| final expected = PackageConfig.fromJson(_config.toJson()); |
| // omit generated date-time and packages |
| expected.generated = null; // comparing timestamps is unnecessary. |
| config.generated = null; |
| expected.packages = []; // Already compared packages (ignoring ordering) |
| config.packages = []; |
| expect(config.toJson(), equals(expected.toJson()), |
| reason: '"$packageConfigFile" does not match expected values'); |
| } |
| |
| @override |
| String describe() => name; |
| } |
| |
| /// Returns `true` if [text] is a valid semantic version number string. |
| bool _isSemver(String text) { |
| try { |
| // See if it's a semver. |
| Version.parse(text); |
| return true; |
| } on FormatException catch (_) { |
| // Do nothing. |
| } |
| return false; |
| } |