diff --git a/.github/ISSUE_TEMPLATE/1-PUB_SITE.md b/.github/ISSUE_TEMPLATE/1-PUB_SITE.md index df33eee..3b90c7a 100644 --- a/.github/ISSUE_TEMPLATE/1-PUB_SITE.md +++ b/.github/ISSUE_TEMPLATE/1-PUB_SITE.md
@@ -1,6 +1,6 @@ --- name: There is a problem with pub.dev -about: A `pub get` fails because of an outage at `pub.dev`, the site +about: A `dart pub get` or `flutter pub get` fails because of an outage at `pub.dev`, the site cannot be loaded, or there is an issue with a particular package such as a request to have a published package removed.
diff --git a/README.md b/README.md index 0d40e6c..83f7ec1 100644 --- a/README.md +++ b/README.md
@@ -64,7 +64,7 @@ the test description. This is used when tests can take a long time to run to avoid having the tests time out when running on the build bots. For example, `tests/get/hosted/get_transitive_test.dart` tests the resolution of transitive -hosted dependencies when using `pub get`. +hosted dependencies when using `dart pub get`/`flutter pub get`. ## Landing your patch
diff --git a/doc/repository-spec-v2.md b/doc/repository-spec-v2.md index 224aa2d..b65b4d2 100644 --- a/doc/repository-spec-v2.md +++ b/doc/repository-spec-v2.md
@@ -4,12 +4,12 @@ implement. A package repository is a server from which packages can be downloaded, -the default package repository is `'https://pub.dartlang.org'`, with public -interface hosted at [pub.dev](https://pub.dev). +the default package repository is `'https://pub.dev'`. +It used to be [pub.dartlang.org](https://pub.dartlang.org). ## Hosted URL A custom package repository is identified by a _hosted-url_, like -`https://pub.dartlang.org` or `https://some-server.com/prefix/pub/`. +`https://pub.dev` or `https://some-server.com/prefix/pub/`. The _hosted-url_ always includes protocol `http://` or `https://`. For the purpose of this specification the _hosted-url_ should always be normalized such that it doesn't end with a slash (`/`). As all URL end-points @@ -17,7 +17,7 @@ For the remainder of this specification the placeholder `<hosted-url>` will be used in place of a _hosted-url_ such as: - * `https://pub.dartlang.org` + * `https://pub.dev` * `https://some-server.com/prefix/pub` * `https://pub.other-server.com/prefix` * `http://localhost:8080`
diff --git a/lib/src/ascii_tree.dart b/lib/src/ascii_tree.dart index 302607f..a63d623 100644 --- a/lib/src/ascii_tree.dart +++ b/lib/src/ascii_tree.dart
@@ -2,13 +2,21 @@ // 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. -/// A simple library for rendering tree-like structures in ASCII. +/// A simple library for rendering tree-like structures in Unicode symbols with +/// a fallback to ASCII. +import 'dart:io'; + import 'package:path/path.dart' as path; import 'log.dart' as log; import 'utils.dart'; -/// Draws a tree for the given list of files. Given files like: +/// Draws a tree for the given list of files +/// +/// Shows each file with the file size if [showFileSize] is `true`. +/// This will stats each file in the list for finding the size. +/// +/// Given files like: /// /// TODO /// example/console_example.dart @@ -16,11 +24,6 @@ /// example/web copy/web_example.dart /// test/absolute_test.dart /// test/basename_test.dart -/// test/dirname_test.dart -/// test/extension_test.dart -/// test/is_absolute_test.dart -/// test/is_relative_test.dart -/// test/join_test.dart /// test/normalize_test.dart /// test/relative_test.dart /// test/split_test.dart @@ -34,48 +37,52 @@ /// /// this renders: /// -/// |-- .gitignore -/// |-- README.md -/// |-- TODO +/// |-- .gitignore (1 KB) +/// |-- README.md (23 KB) +/// |-- TODO (1 MB) /// |-- example -/// | |-- console_example.dart -/// | |-- main.dart +/// | |-- console_example.dart (20 B) +/// | |-- main.dart (200 B) /// | '-- web copy -/// | '-- web_example.dart +/// | '-- web_example.dart (3 KB) /// |-- lib -/// | '-- path.dart -/// |-- pubspec.yaml +/// | '-- path.dart (4 KB) +/// |-- pubspec.yaml (10 KB) /// '-- test -/// |-- absolute_test.dart -/// |-- all_test.dart -/// |-- basename_test.dart -/// | (7 more...) -/// |-- path_windows_test.dart -/// |-- relative_test.dart -/// '-- split_test.dart +/// |-- absolute_test.dart (102 KB) +/// |-- all_test.dart (100 KB) +/// |-- basename_test.dart (4 KB) +/// |-- path_windows_test.dart (2 KB) +/// |-- relative_test.dart (10 KB) +/// '-- split_test.dart (50 KB) /// /// If [baseDir] is passed, it will be used as the root of the tree. -/// -/// If [showAllChildren] is `false`, then directories with more than ten items -/// will have their contents truncated. Defaults to `false`. + String fromFiles( List<String> files, { String? baseDir, - bool showAllChildren = false, + required bool showFileSizes, }) { // Parse out the files into a tree of nested maps. var root = <String, Map>{}; for (var file in files) { - if (baseDir != null) file = path.relative(file, from: baseDir); + final relativeFile = + baseDir == null ? file : path.relative(file, from: baseDir); + final parts = path.split(relativeFile); + if (showFileSizes) { + final size = File(path.normalize(file)).statSync().size; + final sizeString = _readableFileSize(size); + parts.last = '${parts.last} $sizeString'; + } var directory = root; - for (var part in path.split(file)) { + for (var part in parts) { directory = directory.putIfAbsent(part, () => <String, Map>{}) as Map<String, Map>; } } // Walk the map recursively and render to a string. - return fromMap(root, showAllChildren: showAllChildren); + return fromMap(root); } /// Draws a tree from a nested map. Given a map like: @@ -99,12 +106,9 @@ /// barback /// /// Items with no children should have an empty map as the value. -/// -/// If [showAllChildren] is `false`, then directories with more than ten items -/// will have their contents truncated. Defaults to `false`. -String fromMap(Map<String, Map> map, {bool showAllChildren = false}) { +String fromMap(Map<String, Map> map) { var buffer = StringBuffer(); - _draw(buffer, '', null, map, showAllChildren: showAllChildren); + _draw(buffer, '', null, map); return buffer.toString(); } @@ -118,9 +122,9 @@ buffer.write(prefix); if (name != null) { if (isLastChild) { - buffer.write(log.gray("'-- ")); + buffer.write(log.gray(emoji('└── ', "'-- "))); } else { - buffer.write(log.gray('|-- ')); + buffer.write(log.gray(emoji('├── ', '|-- '))); } } @@ -131,7 +135,7 @@ String _getPrefix(bool isRoot, bool isLast) { if (isRoot) return ''; if (isLast) return ' '; - return log.gray('| '); + return log.gray(emoji('│ ', '| ')); } void _draw( @@ -155,25 +159,19 @@ showAllChildren: showAllChildren, isLast: isLastChild); } - if (name == null || showAllChildren || childNames.length <= 10) { - // Not too many, so show all the children. - for (var i = 0; i < childNames.length; i++) { - drawChild(i == childNames.length - 1, childNames[i]); - } + for (var i = 0; i < childNames.length; i++) { + drawChild(i == childNames.length - 1, childNames[i]); + } +} + +String _readableFileSize(int size) { + if (size >= 1 << 30) { + return log.red('(${size ~/ (1 << 30)} GB)'); + } else if (size >= 1 << 20) { + return log.yellow('(${size ~/ (1 << 20)} MB)'); + } else if (size >= 1 << 10) { + return log.gray('(${size ~/ (1 << 10)} KB)'); } else { - // Show the first few. - drawChild(false, childNames[0]); - drawChild(false, childNames[1]); - drawChild(false, childNames[2]); - - // Elide the middle ones. - buffer.write(prefix); - buffer.write(_getPrefix(false, isLast)); - buffer.writeln(log.gray('| (${childNames.length - 6} more...)')); - - // Show the last few. - drawChild(false, childNames[childNames.length - 3]); - drawChild(false, childNames[childNames.length - 2]); - drawChild(true, childNames[childNames.length - 1]); + return log.gray('(<1 KB)'); } }
diff --git a/lib/src/command/add.dart b/lib/src/command/add.dart index 78b1e5d..15b64a4 100644 --- a/lib/src/command/add.dart +++ b/lib/src/command/add.dart
@@ -213,11 +213,10 @@ final range = package.ref.withConstraint(package.constraint ?? VersionConstraint.any); if (isDev) { - /// TODO(walnut): Change the error message once pub upgrade --bump is - /// released if (devDependencyNames.contains(name)) { - dataError('"$name" is already in "dev_dependencies". ' - 'Use "pub upgrade $name" to upgrade to a later version!'); + log.message('"$name" is already in "dev_dependencies". ' + 'Will try to update the constraint.'); + devDependencies.removeWhere((element) => element.name == name); } /// If package is originally in dependencies and we wish to add it to @@ -232,11 +231,10 @@ devDependencies.add(range); } else { - /// TODO(walnut): Change the error message once pub upgrade --bump is - /// released if (dependencyNames.contains(name)) { - dataError('"$name" is already in "dependencies". ' - 'Use "pub upgrade $name" to upgrade to a later version!'); + log.message( + '"$name" is already in "dependencies". Will try to update the constraint.'); + dependencies.removeWhere((element) => element.name == name); } /// If package is originally in dev_dependencies and we wish to add it to @@ -245,7 +243,7 @@ if (devDependencyNames.contains(name)) { log.message('"$name" was found in dev_dependencies. ' 'Removing "$name" and adding it to dependencies instead.'); - devDependencies = devDependencies.where((d) => d.name != name).toList(); + devDependencies.removeWhere((element) => element.name == name); } dependencies.add(range);
diff --git a/lib/src/command/deps.dart b/lib/src/command/deps.dart index 678aafa..a4203ab 100644 --- a/lib/src/command/deps.dart +++ b/lib/src/command/deps.dart
@@ -102,8 +102,7 @@ ? 'dev' : 'transitive')); final source = - entrypoint.packageGraph.lockFile.packages[current]?.source.name ?? - 'root'; + entrypoint.lockFile.packages[current]?.source.name ?? 'root'; packagesJson.add({ 'name': current, 'version': currentPackage.version.toString(), @@ -288,7 +287,7 @@ } } - _buffer.write(tree.fromMap(packageTree, showAllChildren: true)); + _buffer.write(tree.fromMap(packageTree)); } String _labelPackage(Package package) =>
diff --git a/lib/src/command/lish.dart b/lib/src/command/lish.dart index 69c1c7e..e45a735 100644 --- a/lib/src/command/lish.dart +++ b/lib/src/command/lish.dart
@@ -16,6 +16,7 @@ import '../io.dart'; import '../log.dart' as log; import '../oauth2.dart' as oauth2; +import '../solver/type.dart'; import '../source/hosted.dart' show validateAndNormalizeHostedUrl; import '../utils.dart'; import '../validator.dart'; @@ -25,7 +26,7 @@ @override String get name => 'publish'; @override - String get description => 'Publish the current package to pub.dartlang.org.'; + String get description => 'Publish the current package to pub.dev.'; @override String get argumentsDescription => '[options]'; @override @@ -154,9 +155,9 @@ Future<void> _publish(List<int> packageBytes) async { try { final officialPubServers = { - 'https://pub.dartlang.org', - // [validateAndNormalizeHostedUrl] normalizes https://pub.dev to - // https://pub.dartlang.org, so we don't need to do allow that here. + 'https://pub.dev', + // [validateAndNormalizeHostedUrl] normalizes https://pub.dartlang.org + // to https://pub.dev, so we don't need to do allow that here. // Pub uses oauth2 credentials only for authenticating official pub // servers for security purposes (to not expose pub.dev access token to @@ -219,6 +220,8 @@ 'pubspec.'); } + await entrypoint.acquireDependencies(SolveType.get, analytics: analytics); + var files = entrypoint.root.listFiles(); log.fine('Archiving and publishing ${entrypoint.root.name}.'); @@ -226,7 +229,7 @@ var package = entrypoint.root; log.message( 'Publishing ${package.name} ${package.version} to $host:\n' - '${tree.fromFiles(files, baseDir: entrypoint.root.dir, showAllChildren: true)}', + '${tree.fromFiles(files, baseDir: entrypoint.root.dir, showFileSizes: true)}', ); var packageBytesFuture =
diff --git a/lib/src/command/login.dart b/lib/src/command/login.dart index 96c04d8..8f428ab 100644 --- a/lib/src/command/login.dart +++ b/lib/src/command/login.dart
@@ -40,7 +40,7 @@ 'Run `$topLevelProgram pub logout` to delete your credentials and try again.'); } log.warning('You are already logged in as $userInfo\n' - 'Run `pub logout` to log out and try again.'); + 'Run `$topLevelProgram pub logout` to log out and try again.'); } }
diff --git a/lib/src/command/outdated.dart b/lib/src/command/outdated.dart index 59131c4..6f22fd5 100644 --- a/lib/src/command/outdated.dart +++ b/lib/src/command/outdated.dart
@@ -556,7 +556,7 @@ } } else { log.message('\nNo pubspec.lock found. There are no Current versions.\n' - 'Run `pub get` to create a pubspec.lock with versions matching your ' + 'Run `$topLevelProgram pub get` to create a pubspec.lock with versions matching your ' 'pubspec.yaml.'); } if (notAtResolvable != 0) {
diff --git a/lib/src/command/upgrade.dart b/lib/src/command/upgrade.dart index 25feb70..a83d129 100644 --- a/lib/src/command/upgrade.dart +++ b/lib/src/command/upgrade.dart
@@ -222,6 +222,14 @@ } final newPubspecText = _updatePubspec(changes); + // When doing '--majorVersions' for specific packages we try to update other + // packages as little as possible to make a focused change (SolveType.get). + // + // But without a specific package we want to get as many non-major updates + // as possible (SolveType.upgrade). + final solveType = + argResults.rest.isEmpty ? SolveType.upgrade : SolveType.get; + if (_dryRun) { // Even if it is a dry run, run `acquireDependencies` so that the user // gets a report on changes. @@ -233,7 +241,7 @@ lockFile: entrypoint.lockFile, solveResult: solveResult, ).acquireDependencies( - SolveType.get, + solveType, dryRun: true, precompile: _precompile, analytics: null, // No analytics for dry-run @@ -246,7 +254,7 @@ // we can show the changes when not in --dry-run mode. For now we only show // the changes made to pubspec.yaml in dry-run mode. await Entrypoint(directory, cache).acquireDependencies( - SolveType.get, + solveType, precompile: _precompile, analytics: analytics, );
diff --git a/lib/src/command/uploader.dart b/lib/src/command/uploader.dart index cae4963..07ffacc 100644 --- a/lib/src/command/uploader.dart +++ b/lib/src/command/uploader.dart
@@ -13,8 +13,7 @@ @override String get name => 'uploader'; @override - String get description => - 'Manage uploaders for a package on pub.dartlang.org.'; + String get description => 'Manage uploaders for a package on pub.dev.'; @override String get argumentsDescription => '[options] {add/remove} <email>'; @override
diff --git a/lib/src/crc32c.dart b/lib/src/crc32c.dart new file mode 100644 index 0000000..e346a59 --- /dev/null +++ b/lib/src/crc32c.dart
@@ -0,0 +1,103 @@ +// Copyright (c) 2022, 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. + +/// Computes a crc32c checksum. +class Crc32c { + int _current = mask; + static const mask = 0xFFFFFFFF; + + // Algorithm based on https://en.wikipedia.org/wiki/Cyclic_redundancy_check + void update(List<int> data) { + for (var i = 0; i < data.length; i++) { + final lookupIndex = (_current ^ data[i]) & 0xff; + _current = (_current >> 8) ^ _crcTable[lookupIndex]; + } + } + + int finalize() { + // Finalize the CRC-32 value by inverting all the bits + return _current ^ mask & mask; + } + + /// Consumes the entirety of "stream" and returns the CRC32C checksum of its + /// data once the stream is finished. + static Future<int> computeByConsumingStream(Stream<List<int>> stream) async { + final checksumComputer = Crc32c(); + + await for (final chunk in stream) { + checksumComputer.update(chunk); + } + + return checksumComputer.finalize(); + } +} + +// Generated by ./pycrc.py --algorithm=table-driven --model=crc-32c --generate=c +// See: https://pycrc.org/ +const _crcTable = [ + 0x00000000, 0xf26b8303, 0xe13b70f7, 0x1350f3f4, // + 0xc79a971f, 0x35f1141c, 0x26a1e7e8, 0xd4ca64eb, + 0x8ad958cf, 0x78b2dbcc, 0x6be22838, 0x9989ab3b, + 0x4d43cfd0, 0xbf284cd3, 0xac78bf27, 0x5e133c24, + 0x105ec76f, 0xe235446c, 0xf165b798, 0x030e349b, + 0xd7c45070, 0x25afd373, 0x36ff2087, 0xc494a384, + 0x9a879fa0, 0x68ec1ca3, 0x7bbcef57, 0x89d76c54, + 0x5d1d08bf, 0xaf768bbc, 0xbc267848, 0x4e4dfb4b, + 0x20bd8ede, 0xd2d60ddd, 0xc186fe29, 0x33ed7d2a, + 0xe72719c1, 0x154c9ac2, 0x061c6936, 0xf477ea35, + 0xaa64d611, 0x580f5512, 0x4b5fa6e6, 0xb93425e5, + 0x6dfe410e, 0x9f95c20d, 0x8cc531f9, 0x7eaeb2fa, + 0x30e349b1, 0xc288cab2, 0xd1d83946, 0x23b3ba45, + 0xf779deae, 0x05125dad, 0x1642ae59, 0xe4292d5a, + 0xba3a117e, 0x4851927d, 0x5b016189, 0xa96ae28a, + 0x7da08661, 0x8fcb0562, 0x9c9bf696, 0x6ef07595, + 0x417b1dbc, 0xb3109ebf, 0xa0406d4b, 0x522bee48, + 0x86e18aa3, 0x748a09a0, 0x67dafa54, 0x95b17957, + 0xcba24573, 0x39c9c670, 0x2a993584, 0xd8f2b687, + 0x0c38d26c, 0xfe53516f, 0xed03a29b, 0x1f682198, + 0x5125dad3, 0xa34e59d0, 0xb01eaa24, 0x42752927, + 0x96bf4dcc, 0x64d4cecf, 0x77843d3b, 0x85efbe38, + 0xdbfc821c, 0x2997011f, 0x3ac7f2eb, 0xc8ac71e8, + 0x1c661503, 0xee0d9600, 0xfd5d65f4, 0x0f36e6f7, + 0x61c69362, 0x93ad1061, 0x80fde395, 0x72966096, + 0xa65c047d, 0x5437877e, 0x4767748a, 0xb50cf789, + 0xeb1fcbad, 0x197448ae, 0x0a24bb5a, 0xf84f3859, + 0x2c855cb2, 0xdeeedfb1, 0xcdbe2c45, 0x3fd5af46, + 0x7198540d, 0x83f3d70e, 0x90a324fa, 0x62c8a7f9, + 0xb602c312, 0x44694011, 0x5739b3e5, 0xa55230e6, + 0xfb410cc2, 0x092a8fc1, 0x1a7a7c35, 0xe811ff36, + 0x3cdb9bdd, 0xceb018de, 0xdde0eb2a, 0x2f8b6829, + 0x82f63b78, 0x709db87b, 0x63cd4b8f, 0x91a6c88c, + 0x456cac67, 0xb7072f64, 0xa457dc90, 0x563c5f93, + 0x082f63b7, 0xfa44e0b4, 0xe9141340, 0x1b7f9043, + 0xcfb5f4a8, 0x3dde77ab, 0x2e8e845f, 0xdce5075c, + 0x92a8fc17, 0x60c37f14, 0x73938ce0, 0x81f80fe3, + 0x55326b08, 0xa759e80b, 0xb4091bff, 0x466298fc, + 0x1871a4d8, 0xea1a27db, 0xf94ad42f, 0x0b21572c, + 0xdfeb33c7, 0x2d80b0c4, 0x3ed04330, 0xccbbc033, + 0xa24bb5a6, 0x502036a5, 0x4370c551, 0xb11b4652, + 0x65d122b9, 0x97baa1ba, 0x84ea524e, 0x7681d14d, + 0x2892ed69, 0xdaf96e6a, 0xc9a99d9e, 0x3bc21e9d, + 0xef087a76, 0x1d63f975, 0x0e330a81, 0xfc588982, + 0xb21572c9, 0x407ef1ca, 0x532e023e, 0xa145813d, + 0x758fe5d6, 0x87e466d5, 0x94b49521, 0x66df1622, + 0x38cc2a06, 0xcaa7a905, 0xd9f75af1, 0x2b9cd9f2, + 0xff56bd19, 0x0d3d3e1a, 0x1e6dcdee, 0xec064eed, + 0xc38d26c4, 0x31e6a5c7, 0x22b65633, 0xd0ddd530, + 0x0417b1db, 0xf67c32d8, 0xe52cc12c, 0x1747422f, + 0x49547e0b, 0xbb3ffd08, 0xa86f0efc, 0x5a048dff, + 0x8ecee914, 0x7ca56a17, 0x6ff599e3, 0x9d9e1ae0, + 0xd3d3e1ab, 0x21b862a8, 0x32e8915c, 0xc083125f, + 0x144976b4, 0xe622f5b7, 0xf5720643, 0x07198540, + 0x590ab964, 0xab613a67, 0xb831c993, 0x4a5a4a90, + 0x9e902e7b, 0x6cfbad78, 0x7fab5e8c, 0x8dc0dd8f, + 0xe330a81a, 0x115b2b19, 0x020bd8ed, 0xf0605bee, + 0x24aa3f05, 0xd6c1bc06, 0xc5914ff2, 0x37faccf1, + 0x69e9f0d5, 0x9b8273d6, 0x88d28022, 0x7ab90321, + 0xae7367ca, 0x5c18e4c9, 0x4f48173d, 0xbd23943e, + 0xf36e6f75, 0x0105ec76, 0x12551f82, 0xe03e9c81, + 0x34f4f86a, 0xc69f7b69, 0xd5cf889d, 0x27a40b9e, + 0x79b737ba, 0x8bdcb4b9, 0x988c474d, 0x6ae7c44e, + 0xbe2da0a5, 0x4c4623a6, 0x5f16d052, 0xad7d5351 +];
diff --git a/lib/src/dart.dart b/lib/src/dart.dart index 0169174..5c3828e 100644 --- a/lib/src/dart.dart +++ b/lib/src/dart.dart
@@ -186,7 +186,7 @@ final result = await client.compile(); final highlightedName = log.bold(name); - if (result?.errorCount == 0) { + if (result.errorCount == 0) { log.message('Built $highlightedName.'); // By using rename we ensure atomicity. An external observer will either // see the old or the new snapshot. @@ -200,7 +200,7 @@ throw ApplicationException( log.yellow('Failed to build $highlightedName:\n') + - (result?.compilerOutputLines.join('\n') ?? '')); + result.compilerOutputLines.join('\n')); } } finally { client?.kill();
diff --git a/lib/src/entrypoint.dart b/lib/src/entrypoint.dart index eb643fe..b536cca 100644 --- a/lib/src/entrypoint.dart +++ b/lib/src/entrypoint.dart
@@ -107,6 +107,34 @@ LockFile? _lockFile; + /// The `.dart_tool/package_config.json` package-config of this entrypoint. + /// + /// Lazily initialized. Will throw [DataError] when initializing if the + /// `.dart_tool/packageConfig.json` file doesn't exist or has a bad format . + late PackageConfig packageConfig = () { + late String packageConfigRaw; + try { + packageConfigRaw = readTextFile(packageConfigPath); + } on FileException { + dataError( + 'The "$packageConfigPath" file does not exist, please run "$topLevelProgram pub get".'); + } + late PackageConfig result; + try { + result = PackageConfig.fromJson(json.decode(packageConfigRaw)); + } on FormatException { + badPackageConfig(); + } + // Version 2 is the initial version number for `package_config.json`, + // because `.packages` was version 1 (even if it was a different file). + // If the version is different from 2, then it must be a newer incompatible + // version, hence, the user should run `pub get` with the downgraded SDK. + if (result.configVersion != 2) { + badPackageConfig(); + } + return result; + }(); + /// The package graph for the application and all of its transitive /// dependencies. /// @@ -117,11 +145,16 @@ PackageGraph _createPackageGraph() { assertUpToDate(); var packages = { - for (var id in lockFile.packages.values) id.name: cache.load(id) + for (var packageEntry in packageConfig.nonInjectedPackages) + packageEntry.name: Package.load( + packageEntry.name, + packageEntry.resolvedRootDir(packageConfigPath), + cache.sources, + ), }; packages[root.name] = root; - return PackageGraph(this, lockFile, packages); + return PackageGraph(this, packages); } PackageGraph? _packageGraph; @@ -139,9 +172,10 @@ /// not require it or make use of it within pub. String get packagesFile => p.normalize(p.join(_configRoot!, '.packages')); - /// The path to the entrypoint's ".dart_tool/package_config.json" file. - String get packageConfigFile => - p.normalize(p.join(_configRoot!, '.dart_tool', 'package_config.json')); + /// The path to the entrypoint's ".dart_tool/package_config.json" file + /// relative to the current working directory . + late String packageConfigPath = p.relative( + p.normalize(p.join(_configRoot!, '.dart_tool', 'package_config.json'))); /// The path to the entrypoint package's pubspec. String get pubspecPath => p.normalize(root.path('pubspec.yaml')); @@ -190,7 +224,11 @@ bool withPubspecOverrides = true, }) : root = Package.load(null, rootDir, cache.sources, withPubspecOverrides: withPubspecOverrides), - globalDir = null; + globalDir = null { + if (p.isWithin(cache.rootDir, rootDir)) { + fail('Cannot operate on packages inside the cache.'); + } + } Entrypoint.inMemory(this.root, this.cache, {required LockFile? lockFile, SolveResult? solveResult}) @@ -226,9 +264,9 @@ /// Writes .packages and .dart_tool/package_config.json Future<void> writePackageConfigFile() async { final entrypointName = isGlobal ? null : root.name; - ensureDir(p.dirname(packageConfigFile)); + ensureDir(p.dirname(packageConfigPath)); writeTextFile( - packageConfigFile, + packageConfigPath, await lockFile.packageConfigFile( cache, entrypoint: entrypointName, @@ -421,7 +459,7 @@ executablePath: resolveExecutable(executable), outputPath: pathOfExecutable(executable), incrementalDillPath: incrementalDillPathOfExecutable(executable), - packageConfigPath: packageConfigFile, + packageConfigPath: packageConfigPath, name: '$package:${p.basenameWithoutExtension(executable.relativePath)}'); } @@ -512,9 +550,9 @@ dataError( 'No $lockFilePath file found, please run "$topLevelProgram pub get" first.'); } - if (!entryExists(packageConfigFile)) { + if (!entryExists(packageConfigPath)) { dataError( - 'No $packageConfigFile file found, please run "$topLevelProgram pub get".\n' + 'No $packageConfigPath file found, please run "$topLevelProgram pub get".\n' '\n' 'Starting with Dart 2.7, the package_config.json file configures ' 'resolution of package import URIs; run "$topLevelProgram pub get" to generate it.', @@ -559,7 +597,7 @@ } } - var packageConfigModified = File(packageConfigFile).lastModifiedSync(); + var packageConfigModified = File(packageConfigPath).lastModifiedSync(); if (packageConfigModified.isBefore(lockFileModified) || hasPathDependencies) { // If `package_config.json` is older than `pubspec.lock` or we have @@ -568,9 +606,9 @@ // * Mitigate issues when copying a folder from one machine to another. // * Force `pub get` if a path dependency has changed language version. _checkPackageConfigUpToDate(); - touch(packageConfigFile); + touch(packageConfigPath); } else if (touchedLockFile) { - touch(packageConfigFile); + touch(packageConfigPath); } for (var match in _sdkConstraint.allMatches(lockFileText)) { @@ -724,49 +762,13 @@ void _checkPackageConfigUpToDate() { void outOfDate() { dataError('The $lockFilePath file has changed since the ' - '$packageConfigFile file ' + '$packageConfigPath file ' 'was generated, please run "$topLevelProgram pub get" again.'); } - void badPackageConfig() { - dataError('The "$packageConfigFile" file is not recognized by ' - '"pub" version, please run "$topLevelProgram pub get".'); - } - - late String packageConfigRaw; - try { - packageConfigRaw = readTextFile(packageConfigFile); - } on FileException { - dataError( - 'The "$packageConfigFile" file does not exist, please run "$topLevelProgram pub get".'); - } - - late PackageConfig cfg; - try { - cfg = PackageConfig.fromJson(json.decode(packageConfigRaw)); - } on FormatException { - badPackageConfig(); - } - - // Version 2 is the initial version number for `package_config.json`, - // because `.packages` was version 1 (even if it was a different file). - // If the version is different from 2, then it must be a newer incompatible - // version, hence, the user should run `pub get` with the downgraded SDK. - if (cfg.configVersion != 2) { - badPackageConfig(); - } - final packagePathsMapping = <String, String>{}; - // We allow the package called 'flutter_gen' to be injected into - // package_config. - // - // This is somewhat a hack. But it allows flutter to generate code in a - // package as it likes. - // - // See https://github.com/flutter/flutter/issues/73870 . - final packagesToCheck = - cfg.packages.where((package) => package.name != 'flutter_gen'); + final packagesToCheck = packageConfig.nonInjectedPackages; for (final pkg in packagesToCheck) { // Pub always makes a packageUri of lib/ if (pkg.packageUri == null || pkg.packageUri.toString() != 'lib/') { @@ -781,7 +783,7 @@ // Check if language version specified in the `package_config.json` is // correct. This is important for path dependencies as these can mutate. - for (final pkg in cfg.packages) { + for (final pkg in packageConfig.packages) { if (pkg.name == root.name || pkg.name == 'flutter_gen') continue; final id = lockFile.packages[pkg.name]; if (id == null) { @@ -909,6 +911,11 @@ } } } + + Never badPackageConfig() { + dataError('The "$packageConfigPath" file is not recognized by ' + '"pub" version, please run "$topLevelProgram pub get".'); + } } /// Returns `true` if the [text] looks like it uses windows line endings.
diff --git a/lib/src/exceptions.dart b/lib/src/exceptions.dart index fcde747..ec9f876 100644 --- a/lib/src/exceptions.dart +++ b/lib/src/exceptions.dart
@@ -104,6 +104,15 @@ String toString() => 'Package not available ($message).'; } +/// A class for exceptions where a package's checksum could not be validated. +class PackageIntegrityException extends WrappedException { + PackageIntegrityException( + String message, { + Object? innerError, + StackTrace? innerTrace, + }) : super(message, innerError, innerTrace); +} + /// Returns whether [error] is a user-facing error object. /// /// This includes both [ApplicationException] and any dart:io errors.
diff --git a/lib/src/executable.dart b/lib/src/executable.dart index 5d8ea8b..d926023 100644 --- a/lib/src/executable.dart +++ b/lib/src/executable.dart
@@ -106,7 +106,7 @@ // helpful for the subprocess to be able to spawn Dart with // Platform.executableArguments and have that work regardless of the working // directory. - final packageConfigAbsolute = p.absolute(entrypoint.packageConfigFile); + final packageConfigAbsolute = p.absolute(entrypoint.packageConfigPath); try { return await _runDartProgram( @@ -335,14 +335,13 @@ command = package; } - if (!entrypoint.packageGraph.packages.containsKey(package)) { + if (!entrypoint.packageConfig.packages.any((p) => p.name == package)) { throw CommandResolutionFailedException._( 'Could not find package `$package` or file `$descriptor`', CommandResolutionIssue.packageNotFound, ); } final executable = Executable(package, p.join('bin', '$command.dart')); - final packageConfig = p.join('.dart_tool', 'package_config.json'); final path = entrypoint.resolveExecutable(executable); if (!fileExists(path)) { @@ -351,10 +350,12 @@ CommandResolutionIssue.noBinaryFound, ); } + final packageConfigPath = + p.relative(entrypoint.packageConfigPath, from: root); if (!allowSnapshot) { return DartExecutableWithPackageConfig( executable: p.relative(path, from: root), - packageConfig: packageConfig, + packageConfig: packageConfigPath, ); } else { final snapshotPath = entrypoint.pathOfExecutable(executable); @@ -373,7 +374,7 @@ } return DartExecutableWithPackageConfig( executable: p.relative(snapshotPath, from: root), - packageConfig: packageConfig, + packageConfig: packageConfigPath, ); } }
diff --git a/lib/src/http.dart b/lib/src/http.dart index 724962b..6473a8e 100644 --- a/lib/src/http.dart +++ b/lib/src/http.dart
@@ -19,14 +19,15 @@ import 'oauth2.dart' as oauth2; import 'package.dart'; import 'sdk.dart'; +import 'source/hosted.dart'; import 'utils.dart'; /// Headers and field names that should be censored in the log output. const _censoredFields = ['refresh_token', 'authorization']; -/// Headers required for pub.dartlang.org API requests. +/// Headers required for pub.dev API requests. /// -/// The Accept header tells pub.dartlang.org which version of the API we're +/// The Accept header tells pub.dev which version of the API we're /// expecting, so it can either serve that version or give us a 406 error if /// it's not supported. const pubApiHeaders = {'Accept': 'application/vnd.pub.v2+json'}; @@ -79,7 +80,7 @@ return false; } } else { - if (request.url.origin != 'https://pub.dartlang.org') return false; + if (!HostedSource.isPubDevUrl(request.url.toString())) return false; } if (Platform.environment.containsKey('CI') && @@ -222,7 +223,7 @@ } if (status == 500 && - (request.url.host == 'pub.dartlang.org' || + (request.url.host == 'pub.dev' || request.url.host == 'storage.googleapis.com')) { fail('HTTP error 500: Internal Server Error at ${request.url}.\n' 'This is likely a transient error. Please try again later.'); @@ -281,7 +282,7 @@ http.Client get innerHttpClient => _pubClient._inner; set innerHttpClient(http.Client client) => _pubClient._inner = client; -/// Runs [callback] in a zone where all HTTP requests sent to `pub.dartlang.org` +/// Runs [callback] in a zone where all HTTP requests sent to `pub.dev` /// will indicate the [type] of the relationship between the root package and /// the package being requested. /// @@ -293,7 +294,7 @@ return runZoned(callback, zoneValues: {#_dependencyType: type}); } -/// Handles a successful JSON-formatted response from pub.dartlang.org. +/// Handles a successful JSON-formatted response from pub.dev. /// /// These responses are expected to be of the form `{"success": {"message": /// "some message"}}`. If the format is correct, the message will be printed; @@ -308,7 +309,7 @@ log.message(log.green(parsed['success']['message'])); } -/// Handles an unsuccessful JSON-formatted response from pub.dartlang.org. +/// Handles an unsuccessful JSON-formatted response from pub.dev. /// /// These responses are expected to be of the form `{"error": {"message": "some /// message"}}`. If the format is correct, the message will be raised as an
diff --git a/lib/src/io.dart b/lib/src/io.dart index 50f7eb9..2b5d0cd 100644 --- a/lib/src/io.dart +++ b/lib/src/io.dart
@@ -172,7 +172,7 @@ } /// Reads the contents of the binary file [file] as a [Stream]. -Stream<List<int>> readBinaryFileAsSream(String file) { +Stream<List<int>> readBinaryFileAsStream(String file) { log.io('Reading binary file $file.'); var contents = File(file).openRead(); return contents;
diff --git a/lib/src/oauth2.dart b/lib/src/oauth2.dart index 7229bb1..802a8c2 100644 --- a/lib/src/oauth2.dart +++ b/lib/src/oauth2.dart
@@ -78,7 +78,7 @@ void logout(SystemCache cache) { var credentialsFile = _credentialsFile(cache); if (credentialsFile != null && entryExists(credentialsFile)) { - log.message('Logging out of pub.dartlang.org.'); + log.message('Logging out of pub.dev.'); log.message('Deleting $credentialsFile'); _clearCredentials(cache); // Test if we also have a legacy credentials file.
diff --git a/lib/src/package_config.dart b/lib/src/package_config.dart index e5e5e7d..dec8b5c 100644 --- a/lib/src/package_config.dart +++ b/lib/src/package_config.dart
@@ -4,6 +4,7 @@ import 'dart:convert'; +import 'package:path/path.dart' as p; import 'package:pub_semver/pub_semver.dart'; import 'language_version.dart'; @@ -139,6 +140,16 @@ 'generator': generator, 'generatorVersion': generatorVersion?.toString(), }..addAll(additionalProperties); + + // We allow the package called 'flutter_gen' to be injected into + // package_config. + // + // This is somewhat a hack. But it allows flutter to generate code in a + // package as it likes. + // + // See https://github.com/flutter/flutter/issues/73870 . + Iterable<PackageConfigEntry> get nonInjectedPackages => + packages.where((package) => package.name != 'flutter_gen'); } class PackageConfigEntry { @@ -260,4 +271,8 @@ // TODO: implement toString return JsonEncoder.withIndent(' ').convert(toJson()); } + + String resolvedRootDir(String packageConfigPath) { + return p.join(p.dirname(packageConfigPath), p.fromUri(rootUri)); + } }
diff --git a/lib/src/package_graph.dart b/lib/src/package_graph.dart index cbd91ec..6c736e6 100644 --- a/lib/src/package_graph.dart +++ b/lib/src/package_graph.dart
@@ -3,12 +3,11 @@ // BSD-style license that can be found in the LICENSE file. import 'package:collection/collection.dart' hide mapMap; +import 'package:path/path.dart' as p; import 'entrypoint.dart'; -import 'lock_file.dart'; import 'package.dart'; import 'solver.dart'; -import 'source/cached.dart'; import 'utils.dart'; /// A holistic view of the entire transitive dependency graph for an entrypoint. @@ -16,12 +15,6 @@ /// The entrypoint. final Entrypoint entrypoint; - /// The entrypoint's lockfile. - /// - /// This describes the sources and resolved descriptions of everything in - /// [packages]. - final LockFile lockFile; - /// The transitive dependencies of the entrypoint (including itself). /// /// This may not include all transitive dependencies of the entrypoint if the @@ -32,7 +25,7 @@ /// A map of transitive dependencies for each package. Map<String, Set<Package>>? _transitiveDependencies; - PackageGraph(this.entrypoint, this.lockFile, this.packages); + PackageGraph(this.entrypoint, this.packages); /// Creates a package graph using the data from [result]. /// @@ -50,7 +43,7 @@ ) }; - return PackageGraph(entrypoint, result.lockFile, packages); + return PackageGraph(entrypoint, packages); } /// Returns all transitive dependencies of [package]. @@ -81,12 +74,7 @@ // the entrypoint. // TODO(sigurdm): there should be a way to get the id of any package // including the root. - if (package == entrypoint.root.name) { - return entrypoint.isCached; - } else { - var id = lockFile.packages[package]!; - return id.source is CachedSource; - } + return p.isWithin(entrypoint.cache.rootDir, packages[package]!.dir); } /// Returns whether [package] is mutable.
diff --git a/lib/src/pubspec.dart b/lib/src/pubspec.dart index d06b025..0b6cd5b 100644 --- a/lib/src/pubspec.dart +++ b/lib/src/pubspec.dart
@@ -301,7 +301,6 @@ {String? expectedName, bool allowOverridesFile = false}) { var pubspecPath = path.join(packageDir, pubspecYamlFilename); var overridesPath = path.join(packageDir, pubspecOverridesFilename); - if (!fileExists(pubspecPath)) { throw FileException( // Make the package dir absolute because for the entrypoint it'll just
diff --git a/lib/src/pubspec_parse.dart b/lib/src/pubspec_parse.dart index 62eaa13..c86a706 100644 --- a/lib/src/pubspec_parse.dart +++ b/lib/src/pubspec_parse.dart
@@ -14,7 +14,7 @@ /// /// This allows dot-separated valid Dart identifiers. The dots are there for /// compatibility with Google's internal Dart packages, but they may not be used -/// when publishing a package to pub.dartlang.org. +/// when publishing a package to pub.dev. final packageNameRegExp = RegExp('^${identifierRegExp.pattern}(\\.${identifierRegExp.pattern})*\$');
diff --git a/lib/src/solver/package_lister.dart b/lib/src/solver/package_lister.dart index 1198864..ce2b4c1 100644 --- a/lib/src/solver/package_lister.dart +++ b/lib/src/solver/package_lister.dart
@@ -140,22 +140,22 @@ /// /// Throws a [PackageNotFoundException] if this lister's package doesn't /// exist. - Future<PackageId?> bestVersion(VersionConstraint? constraint) async { + Future<PackageId?> bestVersion(VersionConstraint constraint) async { final locked = _locked; - if (locked != null && constraint!.allows(locked.version)) return locked; + if (locked != null && constraint.allows(locked.version)) return locked; var versions = await _versions; // If [constraint] has a minimum (or a maximum in downgrade mode), we can // bail early once we're past it. - var isPastLimit = (Version? _) => false; + var isPastLimit = (Version _) => false; if (constraint is VersionRange) { if (_isDowngrade) { var max = constraint.max; - if (max != null) isPastLimit = (version) => version! > max; + if (max != null) isPastLimit = (version) => version > max; } else { var min = constraint.min; - if (min != null) isPastLimit = (version) => version! < min; + if (min != null) isPastLimit = (version) => version < min; } } @@ -166,7 +166,7 @@ for (var id in _isDowngrade ? versions : versions.reversed) { if (isPastLimit(id.version)) break; - if (!constraint!.allows(id.version)) continue; + if (!constraint.allows(id.version)) continue; if (!id.version.isPreRelease) { return id; }
diff --git a/lib/src/source.dart b/lib/src/source.dart index e331da1..a918c68 100644 --- a/lib/src/source.dart +++ b/lib/src/source.dart
@@ -66,7 +66,7 @@ /// [containingDir] is the path to the directory of the pubspec where this /// description appears. It may be `null` if the description is coming from /// some in-memory source (such as pulling down a pubspec from - /// pub.dartlang.org). + /// pub.dev). /// /// [languageVersion] is the minimum Dart version parsed from the pubspec's /// `environment` field. Source implementations may use this parameter to only
diff --git a/lib/src/source/hosted.dart b/lib/src/source/hosted.dart index e7dc870..451dce1 100644 --- a/lib/src/source/hosted.dart +++ b/lib/src/source/hosted.dart
@@ -5,15 +5,19 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io' as io; +import 'dart:math' as math; +import 'dart:typed_data'; import 'package:collection/collection.dart' show maxBy, IterableNullableExtension; import 'package:http/http.dart' as http; +import 'package:meta/meta.dart'; import 'package:path/path.dart' as p; import 'package:pub_semver/pub_semver.dart'; import 'package:stack_trace/stack_trace.dart'; import '../authentication/client.dart'; +import '../crc32c.dart'; import '../exceptions.dart'; import '../http.dart'; import '../io.dart'; @@ -31,15 +35,18 @@ /// Validates and normalizes a [hostedUrl] which is pointing to a pub server. /// /// A [hostedUrl] is a URL pointing to a _hosted pub server_ as defined by the -/// [repository-spec-v2][1]. The default value is `pub.dartlang.org`, and can be +/// [repository-spec-v2][1]. The default value is `pub.dev`, and can be /// overwritten using `PUB_HOSTED_URL`. It can also specified for individual /// hosted-dependencies in `pubspec.yaml`, and for the root package using the /// `publish_to` key. /// /// The [hostedUrl] is always normalized to a [Uri] with path that ends in slash -/// unless the path is merely `/`, in which case we normalize to the bare domain -/// this keeps the [hostedUrl] and maintains avoids unnecessary churn in -/// `pubspec.lock` files which contain `https://pub.dartlang.org`. +/// unless the path is merely `/`, in which case we normalize to the bare +/// domain. +/// +/// We change `https://pub.dev` to `https://pub.dartlang.org`, this maintains +/// avoids churn for `pubspec.lock`-files which contain +/// `https://pub.dartlang.org`. /// /// Throws [FormatException] if there is anything wrong [hostedUrl]. /// @@ -83,6 +90,18 @@ // // We rewrite here to avoid caching both, and to avoid having different // credentials for these two. + // + // Changing this to pub.dev raises the following concerns: + // + // 1. It would blow through users caches. + // 2. It would cause conflicts for users checking pubspec.lock into git, if using + // different versions of the dart-sdk / pub client. + // 3. It might cause other problems (investigation needed) for pubspec.lock across + // different versions of the dart-sdk / pub client. + // 4. It would expand the API surface we're committed to supporting long-term. + // + // Clearly, a bit of investigation is necessary before we update this to + // pub.dev, it might be attractive to do next time we change the server API. if (u == Uri.parse('https://pub.dev')) { log.fine('Using https://pub.dartlang.org instead of https://pub.dev.'); u = Uri.parse('https://pub.dartlang.org'); @@ -91,7 +110,7 @@ } /// A package source that gets packages from a package hosting site that uses -/// the same API as pub.dartlang.org. +/// the same API as pub.dev. class HostedSource extends CachedSource { static HostedSource instance = HostedSource._(); @@ -102,11 +121,17 @@ @override final hasMultipleVersions = true; - static String pubDevUrl = 'https://pub.dartlang.org'; + static String pubDevUrl = 'https://pub.dev'; + static String pubDartlangUrl = 'https://pub.dartlang.org'; + + static bool isPubDevUrl(String url) { + final origin = Uri.parse(url).origin; + return origin == pubDevUrl || origin == pubDartlangUrl; + } static bool isFromPubDev(PackageId id) { final description = id.description.description; - return description is HostedDescription && description.url == pubDevUrl; + return description is HostedDescription && isPubDevUrl(description.url); } /// Gets the default URL for the package server for hosted dependencies. @@ -851,27 +876,53 @@ 'Package $packageName has no version $version'); } - var url = versionInfo.archiveUrl; - log.io('Get package from $url.'); + final archiveUrl = versionInfo.archiveUrl; + log.io('Get package from $archiveUrl.'); log.message('Downloading ${log.bold(id.name)} ${id.version}...'); // Download and extract the archive to a temp directory. await withTempDir((tempDirForArchive) async { - var archivePath = - p.join(tempDirForArchive, '$packageName-$version.tar.gz'); - var response = await withAuthenticatedClient( - cache, - Uri.parse(description.url), - (client) => client.send(http.Request('GET', url))); + var fileName = '$packageName-$version.tar.gz'; + var archivePath = p.join(tempDirForArchive, fileName); - // We download the archive to disk instead of streaming it directly into - // the tar unpacking. This simplifies stream handling. - // Package:tar cancels the stream when it reaches end-of-archive, and - // cancelling a http stream makes it not reusable. - // There are ways around this, and we might revisit this later. - await createFileFromStream(response.stream, archivePath); + // The client from `withAuthenticatedClient` will retry HTTP requests. + // This wrapper is one layer up and will retry checksum validation errors. + await retry( + // Attempt to download archive and validate its checksum. + () async { + final request = http.Request('GET', archiveUrl); + final response = await withAuthenticatedClient(cache, + Uri.parse(description.url), (client) => client.send(request)); + final expectedChecksum = _parseCrc32c(response.headers, fileName); + + Stream<List<int>> stream = response.stream; + if (expectedChecksum != null) { + stream = _validateStream( + response.stream, expectedChecksum, id, archiveUrl); + } + + // We download the archive to disk instead of streaming it directly + // into the tar unpacking. This simplifies stream handling. + // Package:tar cancels the stream when it reaches end-of-archive, and + // cancelling a http stream makes it not reusable. + // There are ways around this, and we might revisit this later. + await createFileFromStream(stream, archivePath); + }, + // Retry if the checksum response header was malformed or the actual + // checksum did not match the expected checksum. + retryIf: (e) => e is PackageIntegrityException, + onRetry: (e, retryCount) => log + .io('Retry #${retryCount + 1} because of checksum error with GET ' + '$archiveUrl...'), + maxAttempts: math.max( + 1, // Having less than 1 attempt doesn't make sense. + int.tryParse(io.Platform.environment['PUB_MAX_HTTP_RETRIES'] ?? '') ?? + 7, + ), + ); + var tempDir = cache.createTempDir(); - await extractTarGz(readBinaryFileAsSream(archivePath), tempDir); + await extractTarGz(readBinaryFileAsStream(archivePath), tempDir); // Now that the get has succeeded, move it to the real location in the // cache. @@ -1100,3 +1151,84 @@ @override bool operator ==(Object other) => other is _RefAndCache && other.ref == ref; } + +@visibleForTesting +const checksumHeaderName = 'x-goog-hash'; + +/// Adds a checksum validation "tap" to the response stream and returns a +/// wrapped `Stream` object, which should be used to consume the incoming data. +/// +/// As chunks are received, a CRC32C checksum is updated. +/// Once the download is completed, the final checksum is compared with +/// the one present in the checksum response header. +/// +/// Throws [PackageIntegrityException] if there is a checksum mismatch. +Stream<List<int>> _validateStream(Stream<List<int>> stream, + int expectedChecksum, PackageId id, Uri archiveUrl) async* { + final crc32c = Crc32c(); + + await for (final chunk in stream) { + crc32c.update(chunk); + yield chunk; + } + + final actualChecksum = crc32c.finalize(); + + log.fine( + 'Computed checksum $actualChecksum for ${id.name} ${id.version} with ' + 'expected CRC32C of $expectedChecksum.'); + + if (actualChecksum != expectedChecksum) { + throw PackageIntegrityException( + 'Package archive for ${id.name} ${id.version} downloaded from ' + '"$archiveUrl" has "x-goog-hash: crc32c=$expectedChecksum", which ' + 'doesn\'t match the checksum of the archive downloaded.'); + } +} + +/// Parses response [headers] and returns the archive's CRC32C checksum. +/// +/// In most cases, GCS provides both MD5 and CRC32C checksums in its response +/// headers. It uses the header name "x-goog-hash" for these values. It has +/// been documented and observed that GCS will send multiple response headers +/// with the same "x-goog-hash" token as the key. +/// https://cloud.google.com/storage/docs/xml-api/reference-headers#xgooghash +/// +/// Additionally, when the Dart http client encounters multiple response +/// headers with the same key, it concatenates their values with a comma +/// before inserting a single item with that key and concatenated value into +/// its response "headers" Map. +/// See https://github.com/dart-lang/http/issues/24 +/// https://github.com/dart-lang/http/blob/06649afbb5847dbb0293816ba8348766b116e419/pkgs/http/lib/src/base_response.dart#L29 +/// +/// Throws [PackageIntegrityException] if the CRC32C checksum cannot be parsed. +int? _parseCrc32c(Map<String, String> headers, String fileName) { + final checksumHeader = headers[checksumHeaderName]; + if (checksumHeader == null) return null; + + final parts = checksumHeader.split(','); + for (final part in parts) { + if (part.startsWith('crc32c=')) { + final undecoded = part.substring('crc32c='.length); + + try { + final bytes = base64Decode(undecoded); + + // CRC32C must be 32 bits, or 4 bytes. + if (bytes.length != 4) { + throw FormatException('CRC32C checksum has invalid length', bytes); + } + + return ByteData.view(bytes.buffer).getUint32(0); + } on FormatException catch (e, s) { + throw PackageIntegrityException( + 'Package archive "$fileName" has a malformed CRC32C checksum in ' + 'its response headers', + innerError: e, + innerTrace: s); + } + } + } + + return null; +}
diff --git a/lib/src/third_party/tar/README.md b/lib/src/third_party/tar/README.md index a2fbf7b..5e12e5a 100644 --- a/lib/src/third_party/tar/README.md +++ b/lib/src/third_party/tar/README.md
@@ -4,4 +4,4 @@ tar-archives. * Repository: `https://github.com/simolus3/tar/` - * Revision: `901ae404e0a225d9b08e5253415ca092f5c08706` + * Revision: `23ee71d667f003fba8c80ee126d5e1330d17c141`
diff --git a/lib/src/third_party/tar/src/reader.dart b/lib/src/third_party/tar/src/reader.dart index b9bc3d3..8502a26 100644 --- a/lib/src/third_party/tar/src/reader.dart +++ b/lib/src/third_party/tar/src/reader.dart
@@ -3,7 +3,6 @@ import 'dart:convert'; import 'dart:typed_data'; -import 'package:async/async.dart'; import 'package:meta/meta.dart'; import 'package:typed_data/typed_data.dart'; @@ -27,21 +26,7 @@ final int _maxSpecialFileSize; TarEntry? _current; - - /// The underlying content stream for the [_current] entry. Draining this - /// stream will move the tar reader to the beginning of the next file. - /// - /// This is not the same as `_current.stream` for sparse files, which are - /// reported as expanded through [TarEntry.contents]. - /// For that reason, we prefer to drain this stream when skipping a tar entry. - /// When we know we're skipping data, there's no point expanding sparse holes. - /// - /// This stream is always set to null after being drained, and there can only - /// be one [_underlyingContentStream] at a time. - Stream<List<int>>? _underlyingContentStream; - - /// Whether [_current] has ever been listened to. - bool _listenedToContentsOnce = false; + _CurrentEntryStream? _currentStream; /// Whether we're in the process of reading tar headers. bool _isReadingHeaders = false; @@ -220,7 +205,9 @@ nextHeader.format = format; _current = TarEntry(nextHeader, content); - _listenedToContentsOnce = false; + final currentStreams = _currentStream; + assert(currentStreams == null || + currentStreams.state == _EntryStreamState.preListen); _isReadingHeaders = false; return true; } @@ -233,8 +220,7 @@ _isDone = true; _current = null; - _underlyingContentStream = null; - _listenedToContentsOnce = false; + _currentStream = null; _isReadingHeaders = false; // Note: Calling cancel is safe when the stream has already been completed. @@ -276,18 +262,51 @@ } _isReadingHeaders = true; - final underlyingStream = _underlyingContentStream; + final underlyingStream = _currentStream; if (underlyingStream != null) { - if (_listenedToContentsOnce) { - throw StateError( + switch (underlyingStream.state) { + case _EntryStreamState.preListen: + await underlyingStream.drain<void>(); + // The stream should reset when drained (we do this in _publishStream) + assert(_currentStream == null); + + break; + case _EntryStreamState.subscriptionActive: + throw StateError( 'Illegal call to TarReader.moveNext() while a previous stream was ' 'active.\n' 'When listening to tar contents, make sure the stream is ' - 'complete or cancelled before calling TarReader.moveNext() again.'); - } else { - await underlyingStream.drain<void>(); - // The stream should reset when drained (we do this in _publishStream) - assert(_underlyingContentStream == null); + 'complete or cancelled before calling TarReader.moveNext() again.', + ); + case _EntryStreamState.cancelled: + // ignore: cancel_subscriptions + final subscription = underlyingStream._sourceSubscription!; + + // Re-purpose the existing subscription to drain the stream + assert(subscription.isPaused); + + subscription + ..onData(null) + ..resume(); + + try { + await subscription.asFuture<void>(); + } on Object { + await cancel(); + rethrow; + } finally { + // This also draines the stream + _currentStream = null; + } + + break; + case _EntryStreamState.done: + assert( + false, + 'Unreachable: There should not be a currentStream in a done state, ' + 'as the stream is no longer current at that point.', + ); + break; } } } @@ -418,59 +437,14 @@ /// Publishes an library-internal stream for users. /// /// This adds a check to ensure that the stream we're exposing has the - /// expected length. It also sets the [_underlyingContentStream] field when - /// the stream starts and resets it when it's done. - Stream<List<int>> _publishStream(Stream<List<int>> stream, int length) { + /// expected length. It also sets the [_currentStream] field and resets it + /// when it's done. + Stream<List<int>> _publishStream(Stream<Uint8List> stream, int length) { // There can only be one content stream at a time. This precondition is // checked by _prepareToReadHeaders. - assert(_underlyingContentStream == null); - Stream<List<int>>? thisStream; + assert(_currentStream == null); - return thisStream = - _underlyingContentStream = Stream.eventTransformed(stream, (sink) { - // This callback is called when we have a listener. Make sure that, at - // this point, this stream is still the active content stream. - // If users store the contents of a tar header, then read more tar - // entries, and finally try to read the stream of the old contents, they'd - // get an exception about the straem already being listened to. - // This can be a bit confusing, so this check enables a better error UX. - if (thisStream != _underlyingContentStream) { - throw StateError( - 'Tried listening to an outdated tar entry. \n' - 'As all tar entries found by a reader are backed by a single source ' - 'stream, only the latest tar entry can be read. It looks like you ' - 'stored the results of `tarEntry.contents` somewhere, called ' - '`reader.moveNext()` and then read the contents of the previous ' - 'entry.\n' - 'For more details, including a discussion of workarounds, see ' - 'https://github.com/simolus3/tar/issues/18', - ); - } else if (_listenedToContentsOnce) { - throw StateError( - 'A tar entry has been listened to multiple times. \n' - 'As all tar entries are read from what\'s likely a single-' - 'subscription stream, this is unsupported. If you didn\'t read a tar ' - 'entry multiple times yourself, perhaps you\'ve called `moveNext()` ' - 'before reading contents?', - ); - } - - _listenedToContentsOnce = true; - - late _OutgoingStreamGuard guard; - return guard = _OutgoingStreamGuard( - length, - sink, - // Reset state when the stream is done. This will only be called when - // the stream is done, not when a listener cancels. - () { - _underlyingContentStream = null; - if (guard.hadError) { - cancel(); - } - }, - ); - }); + return _currentStream = _CurrentEntryStream(this, stream, length); } /// Checks the PAX headers for GNU sparse headers. @@ -881,23 +855,96 @@ } } -/// Event-sink tracking the length of emitted tar entry streams. +enum _EntryStreamState { + preListen, + subscriptionActive, + cancelled, + done, +} + +/// The underlying content stream for the [TarReader._current] entry. Draining +/// this stream will move the tar reader to the beginning of the next file. /// -/// [ChunkedStreamReader.readStream] might return a stream shorter than -/// expected. That indicates an invalid tar file though, since the correct size -/// is stored in the header. -class _OutgoingStreamGuard extends EventSink<Uint8List> { - int remainingContentSize; - int remainingPaddingSize; +/// This is not the same as `_current.stream` for sparse files, which are +/// reported as expanded through [TarEntry.contents]. +/// For that reason, we prefer to drain this stream when skipping a tar entry. +/// When we know we're skipping data, there's no point expanding sparse holes. +/// +/// Draining this stream will set the [TarReader._currentStream] field back to +/// null. There can only be one content stream at the time. +class _CurrentEntryStream extends Stream<List<int>> { + _EntryStreamState state = _EntryStreamState.preListen; - final EventSink<List<int>> out; - void Function() onDone; + final TarReader _reader; + final Stream<Uint8List> _source; - bool hadError = false; - bool isInContent = true; + final StreamController<List<int>> _listener = StreamController(sync: true); + // ignore: cancel_subscriptions + StreamSubscription<List<int>>? _sourceSubscription; - _OutgoingStreamGuard(this.remainingContentSize, this.out, this.onDone) - : remainingPaddingSize = _paddingFor(remainingContentSize); + int _remainingContentSize; + int _remainingPaddingSize; + bool _hadError = false; + bool _isInContent = true; + + _CurrentEntryStream(this._reader, this._source, this._remainingContentSize) + : _remainingPaddingSize = _paddingFor(_remainingContentSize); + + @override + StreamSubscription<List<int>> listen(void Function(List<int> event)? onData, + {Function? onError, void Function()? onDone, bool? cancelOnError}) { + // Make sure that this entry is still the current one: If users store the + // contents of a tar entry, then read more tar entries, and finally try to + // read the stream of the old contents, they'd get an exception about the + // stream already being listened to. + // This can be a bit confusing, so this check enables a better error UX. + if (_reader._currentStream != this) { + throw StateError( + 'Tried listening to an outdated tar entry. \n' + 'As all tar entries found by a reader are backed by a single source ' + 'stream, only the latest tar entry can be read. It looks like you ' + 'stored the results of `tarEntry.contents` somewhere, called ' + '`reader.moveNext()` and then read the contents of the previous ' + 'entry.\n' + 'For more details, including a discussion of workarounds, see ' + 'https://github.com/simolus3/tar/issues/18', + ); + } else if (state != _EntryStreamState.preListen) { + throw StateError( + 'A tar entry has been listened to multiple times. \n' + 'As all tar entries are read from what\'s likely a single-' + 'subscription stream, this is unsupported. If you didn\'t read a tar ' + 'entry multiple times yourself, perhaps you\'ve called `moveNext()` ' + 'before reading contents?', + ); + } + + // Now we have a listener, so + state = _EntryStreamState.subscriptionActive; + // ignore: cancel_subscriptions + final sub = _sourceSubscription = _source.listen( + _forwardData, + onError: _forwardError, + onDone: _forwardDone, + ); + + _listener + ..onPause = sub.pause + ..onResume = sub.resume + ..onCancel = () { + // Pause the source subscription. When reading the next entry, the tar + // reader will drain the remaining source stream. + sub.pause(); + state = _EntryStreamState.cancelled; + }; + + return _listener.stream.listen( + onData, + onError: onError, + onDone: onDone, + cancelOnError: cancelOnError, + ); + } static int _paddingFor(int contentSize) { final offsetInLastBlock = contentSize.toUnsigned(blockSizeLog2); @@ -907,47 +954,59 @@ return 0; } - @override - void add(Uint8List event) { - if (isInContent) { - if (event.length <= remainingContentSize) { + void _assertInStateForForwarding() { + assert(state == _EntryStreamState.subscriptionActive && + _listener.hasListener && + !_listener.isPaused); + } + + void _forwardData(Uint8List event) { + _assertInStateForForwarding(); + + if (_isInContent) { + if (event.length <= _remainingContentSize) { // We can fully add this chunk as it consists entirely of data - out.add(event); - remainingContentSize -= event.length; + _listener.add(event); + _remainingContentSize -= event.length; } else { // We can add the first bytes as content, the others are padding that we // shouldn't emit - out.add(event.sublistView(0, remainingContentSize)); - isInContent = false; - remainingPaddingSize -= event.length - remainingContentSize; - remainingContentSize = 0; + _listener.add(event.sublistView(0, _remainingContentSize)); + _isInContent = false; + _remainingPaddingSize -= event.length - _remainingContentSize; + _remainingContentSize = 0; } } else { // Ok, the entire event is padding - remainingPaddingSize -= event.length; + _remainingPaddingSize -= event.length; } // The underlying stream comes from pkg:tar, so if we get too many bytes // that's a bug in this package. - assert(remainingPaddingSize >= 0, 'Stream emitted to many bytes'); + assert(_remainingPaddingSize >= 0, 'Stream emitted to many bytes'); } - @override - void addError(Object error, [StackTrace? stackTrace]) { - hadError = true; - out.addError(error, stackTrace); + void _forwardError(Object error, StackTrace trace) { + _assertInStateForForwarding(); + + _hadError = true; + _listener.addError(error, trace); } - @override - void close() { + void _forwardDone() { + _assertInStateForForwarding(); + + // Now that the source stream is done, reset the stream state on the reader. + state = _EntryStreamState.done; + _sourceSubscription = null; + _reader._currentStream = null; + // If the stream stopped after an error, the user is already aware that // something is wrong. - if (remainingContentSize > 0 && !hadError) { - out.addError( + if (_remainingContentSize > 0 && !_hadError) { + _listener.addError( TarException('Unexpected end of tar file'), StackTrace.current); } - - onDone(); - out.close(); + _listener.close(); } }
diff --git a/lib/src/utils.dart b/lib/src/utils.dart index e4451db..dbc7622 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart
@@ -638,3 +638,63 @@ key(entry.key, entry.value): value(entry.key, entry.value), }; } + +/// Call [fn] retrying so long as [retryIf] return `true` for the exception +/// thrown, up-to [maxAttempts] times. +/// +/// Defaults to 8 attempts, sleeping as following after 1st, 2nd, 3rd, ..., +/// 7th attempt: +/// 1. 400 ms +/- 25% +/// 2. 800 ms +/- 25% +/// 3. 1600 ms +/- 25% +/// 4. 3200 ms +/- 25% +/// 5. 6400 ms +/- 25% +/// 6. 12800 ms +/- 25% +/// 7. 25600 ms +/- 25% +/// +/// ```dart +/// final response = await retry( +/// // Make a GET request +/// () => http.get('https://google.com').timeout(Duration(seconds: 5)), +/// // Retry on SocketException or TimeoutException +/// retryIf: (e) => e is SocketException || e is TimeoutException, +/// ); +/// print(response.body); +/// ``` +/// +/// If no [retryIf] function is given this will retry any for any [Exception] +/// thrown. To retry on an [Error], the error must be caught and _rethrown_ +/// as an [Exception]. +/// +/// See https://github.com/google/dart-neats/blob/master/retry/lib/retry.dart +Future<T> retry<T>( + FutureOr<T> Function() fn, { + Duration delayFactor = const Duration(milliseconds: 200), + double randomizationFactor = 0.25, + Duration maxDelay = const Duration(seconds: 30), + int maxAttempts = 8, + FutureOr<bool> Function(Exception)? retryIf, + FutureOr<void> Function(Exception, int retryCount)? onRetry, +}) async { + var attempt = 0; + // ignore: literal_only_boolean_expressions + while (true) { + attempt++; // first invocation is the first attempt + try { + return await fn(); + } on Exception catch (e) { + if (attempt >= maxAttempts || (retryIf != null && !(await retryIf(e)))) { + rethrow; + } + if (onRetry != null) { + await onRetry(e, attempt); + } + } + + // Sleep for a delay + final rf = randomizationFactor * (random.nextDouble() * 2 - 1) + 1; + final exp = math.min(attempt, 31); // prevent overflows. + final delay = delayFactor * math.pow(2.0, exp) * rf; + await Future.delayed(delay < maxDelay ? delay : maxDelay); + } +}
diff --git a/lib/src/validator/dependency_override.dart b/lib/src/validator/dependency_override.dart index b6a49a0..a16367a 100644 --- a/lib/src/validator/dependency_override.dart +++ b/lib/src/validator/dependency_override.dart
@@ -16,10 +16,15 @@ var overridden = MapKeySet(entrypoint.root.dependencyOverrides); var dev = MapKeySet(entrypoint.root.devDependencies); if (overridden.difference(dev).isNotEmpty) { - errors.add('Your pubspec.yaml must not override non-dev dependencies.\n' - 'This ensures you test your package against the same versions of ' - 'its dependencies\n' - 'that users will have when they use it.'); + warnings.add(''' +Your pubspec.yaml is overriding non-dev dependencies. + +This indicates you are not testing your package against the same versions of its +dependencies that users will have when they use it. + +This might be necessary for packages with cyclic dependencies. + +Please be extra careful when publising.'''); } return Future.value(); }
diff --git a/lib/src/validator/null_safety_mixed_mode.dart b/lib/src/validator/null_safety_mixed_mode.dart index 31b755c..b17e0e5 100644 --- a/lib/src/validator/null_safety_mixed_mode.dart +++ b/lib/src/validator/null_safety_mixed_mode.dart
@@ -6,6 +6,7 @@ import 'package:path/path.dart' as p; +import '../command_runner.dart'; import '../null_safety_analysis.dart'; import '../package_name.dart'; import '../source/path.dart'; @@ -44,8 +45,8 @@ We highly recommend that you wait until all of your dependencies have been migrated before publishing. -Run `pub outdated --mode=null-safety` for more information about the state of -dependencies. +Run `$topLevelProgram pub outdated --mode=null-safety` for more information about the state +of dependencies. See ${NullSafetyAnalysis.guideUrl} for more information about migrating.
diff --git a/pubspec.yaml b/pubspec.yaml index e44e0e7..eb2983f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml
@@ -6,13 +6,13 @@ dependencies: # Note: Pub's test infrastructure assumes that any dependencies used in tests # will be hosted dependencies. - analyzer: ^4.0.0 + analyzer: ^5.1.0 args: ^2.1.0 async: ^2.6.1 cli_util: ^0.3.5 collection: ^1.15.0 crypto: ^3.0.1 - frontend_server_client: ^2.0.0 + frontend_server_client: ^3.0.0 http: ^0.13.3 http_multi_server: ^3.0.1 http_parser: ^4.0.1 @@ -31,6 +31,6 @@ dev_dependencies: lints: ^2.0.0 shelf_test_handler: ^2.0.0 - test: ^1.17.3 + test: ^1.21.5 test_descriptor: ^2.0.0 test_process: ^2.0.0
diff --git a/test/add/common/add_test.dart b/test/add/common/add_test.dart index f1e76a7..25f9d8d 100644 --- a/test/add/common/add_test.dart +++ b/test/add/common/add_test.dart
@@ -185,7 +185,7 @@ await d.appDir({'foo': '1.2.3'}).validate(); }); - group('warns user to use pub upgrade if package exists', () { + group('notifies user about existing constraint', () { test('if package is added without a version constraint', () async { await servePackages() ..serve('foo', '1.2.3') @@ -194,13 +194,13 @@ await d.appDir({'foo': '1.2.2'}).create(); await pubAdd( - args: ['foo'], - exitCode: exit_codes.DATA, - error: - contains('"foo" is already in "dependencies". Use "pub upgrade ' - 'foo" to upgrade to a later version!')); + args: ['foo'], + output: contains( + '"foo" is already in "dependencies". Will try to update the constraint.', + ), + ); - await d.appDir({'foo': '1.2.2'}).validate(); + await d.appDir({'foo': '^1.2.3'}).validate(); }); test('if package is added with a specific version constraint', () async { @@ -211,13 +211,13 @@ await d.appDir({'foo': '1.2.2'}).create(); await pubAdd( - args: ['foo:1.2.3'], - exitCode: exit_codes.DATA, - error: - contains('"foo" is already in "dependencies". Use "pub upgrade ' - 'foo" to upgrade to a later version!')); + args: ['foo:1.2.3'], + output: contains( + '"foo" is already in "dependencies". Will try to update the constraint.', + ), + ); - await d.appDir({'foo': '1.2.2'}).validate(); + await d.appDir({'foo': '1.2.3'}).validate(); }); test('if package is added with a version constraint range', () async { @@ -229,12 +229,10 @@ await pubAdd( args: ['foo:>=1.2.2'], - exitCode: exit_codes.DATA, - error: - contains('"foo" is already in "dependencies". Use "pub upgrade ' - 'foo" to upgrade to a later version!')); + output: contains( + '"foo" is already in "dependencies". Will try to update the constraint.')); - await d.appDir({'foo': '1.2.2'}).validate(); + await d.appDir({'foo': '>=1.2.2'}).validate(); }); }); @@ -517,7 +515,7 @@ ]).validate(); }); - group('warns user to use pub upgrade if package exists', () { + group('notifies user if package exists', () { test('if package is added without a version constraint', () async { await servePackages() ..serve('foo', '1.2.3') @@ -532,15 +530,13 @@ await pubAdd( args: ['foo', '--dev'], - exitCode: exit_codes.DATA, - error: contains( - '"foo" is already in "dev_dependencies". Use "pub upgrade ' - 'foo" to upgrade to a later version!')); + output: contains( + '"foo" is already in "dev_dependencies". Will try to update the constraint.')); await d.dir(appPath, [ d.pubspec({ 'name': 'myapp', - 'dev_dependencies': {'foo': '1.2.2'} + 'dev_dependencies': {'foo': '^1.2.3'} }) ]).validate(); }); @@ -559,15 +555,13 @@ await pubAdd( args: ['foo:1.2.3', '--dev'], - exitCode: exit_codes.DATA, - error: contains( - '"foo" is already in "dev_dependencies". Use "pub upgrade ' - 'foo" to upgrade to a later version!')); + output: contains( + '"foo" is already in "dev_dependencies". Will try to update the constraint.')); await d.dir(appPath, [ d.pubspec({ 'name': 'myapp', - 'dev_dependencies': {'foo': '1.2.2'} + 'dev_dependencies': {'foo': '1.2.3'} }) ]).validate(); }); @@ -586,15 +580,13 @@ await pubAdd( args: ['foo:>=1.2.2', '--dev'], - exitCode: exit_codes.DATA, - error: contains( - '"foo" is already in "dev_dependencies". Use "pub upgrade ' - 'foo" to upgrade to a later version!')); + output: contains( + '"foo" is already in "dev_dependencies". Will try to update the constraint.')); await d.dir(appPath, [ d.pubspec({ 'name': 'myapp', - 'dev_dependencies': {'foo': '1.2.2'} + 'dev_dependencies': {'foo': '>=1.2.2'} }) ]).validate(); });
diff --git a/test/ascii_tree_test.dart b/test/ascii_tree_test.dart index 5d82d49..a4b0163 100644 --- a/test/ascii_tree_test.dart +++ b/test/ascii_tree_test.dart
@@ -3,240 +3,102 @@ // BSD-style license that can be found in the LICENSE file. import 'package:pub/src/ascii_tree.dart' as tree; +import 'package:pub/src/package.dart'; +import 'package:pub/src/utils.dart'; import 'package:test/test.dart'; +import 'descriptor.dart'; +import 'golden_file.dart'; +import 'test_pub.dart'; + /// Removes ansi color codes from [s]. String stripColors(String s) { return s.replaceAll(RegExp('\u001b\\[.*?m'), ''); } void main() { - group('tree.fromFiles', () { - test('no files', () { - expect(stripColors(tree.fromFiles([])), equals('')); - }); - - test('up to ten files in one directory are shown', () { - var files = [ - 'dir/a.dart', - 'dir/b.dart', - 'dir/c.dart', - 'dir/d.dart', - 'dir/e.dart', - 'dir/f.dart', - 'dir/g.dart', - 'dir/h.dart', - 'dir/i.dart', - 'dir/j.dart' - ]; - expect(stripColors(tree.fromFiles(files)), equals(""" -'-- dir - |-- a.dart - |-- b.dart - |-- c.dart - |-- d.dart - |-- e.dart - |-- f.dart - |-- g.dart - |-- h.dart - |-- i.dart - '-- j.dart -""")); - }); - - test('files are elided if there are more than ten', () { - var files = [ - 'dir/a.dart', - 'dir/b.dart', - 'dir/c.dart', - 'dir/d.dart', - 'dir/e.dart', - 'dir/f.dart', - 'dir/g.dart', - 'dir/h.dart', - 'dir/i.dart', - 'dir/j.dart', - 'dir/k.dart' - ]; - expect(stripColors(tree.fromFiles(files)), equals(""" -'-- dir - |-- a.dart - |-- b.dart - |-- c.dart - | (5 more...) - |-- i.dart - |-- j.dart - '-- k.dart -""")); - }); - - test('files are not elided at the top level', () { - var files = [ - 'a.dart', - 'b.dart', - 'c.dart', - 'd.dart', - 'e.dart', - 'f.dart', - 'g.dart', - 'h.dart', - 'i.dart', - 'j.dart', - 'k.dart' - ]; - expect(stripColors(tree.fromFiles(files)), equals(""" -|-- a.dart -|-- b.dart -|-- c.dart -|-- d.dart -|-- e.dart -|-- f.dart -|-- g.dart -|-- h.dart -|-- i.dart -|-- j.dart -'-- k.dart -""")); - }); - - test('a complex example', () { - var files = [ - 'TODO', - 'example/console_example.dart', - 'example/main.dart', - 'example/web copy/web_example.dart', - 'test/absolute_test.dart', - 'test/basename_test.dart', - 'test/dirname_test.dart', - 'test/extension_test.dart', - 'test/is_absolute_test.dart', - 'test/is_relative_test.dart', - 'test/join_test.dart', - 'test/normalize_test.dart', - 'test/relative_test.dart', - 'test/split_test.dart', - '.gitignore', - 'README.md', - 'lib/path.dart', - 'pubspec.yaml', - 'test/all_test.dart', - 'test/path_posix_test.dart', - 'test/path_windows_test.dart' - ]; - - expect(stripColors(tree.fromFiles(files)), equals(""" -|-- .gitignore -|-- README.md -|-- TODO -|-- example -| |-- console_example.dart -| |-- main.dart -| '-- web copy -| '-- web_example.dart -|-- lib -| '-- path.dart -|-- pubspec.yaml -'-- test - |-- absolute_test.dart - |-- all_test.dart - |-- basename_test.dart - | (7 more...) - |-- path_windows_test.dart - |-- relative_test.dart - '-- split_test.dart -""")); - }); + setUp(() { + forceColors = ForceColorOption.always; }); - group('treeFromMap', () { - test('empty map', () { - expect(stripColors(tree.fromMap({})), equals('')); - }); - - test('a complex example', () { - var map = { - '.gitignore': <String, Map>{}, - 'README.md': <String, Map>{}, - 'TODO': <String, Map>{}, - 'example': { - 'console_example.dart': <String, Map>{}, - 'main.dart': <String, Map>{}, - 'web copy': {'web_example.dart': <String, Map>{}}, - }, - 'lib': {'path.dart': <String, Map>{}}, - 'pubspec.yaml': <String, Map>{}, - 'test': { - 'absolute_test.dart': <String, Map>{}, - 'basename_test.dart': <String, Map>{}, - 'dirname_test.dart': <String, Map>{}, - 'extension_test.dart': <String, Map>{}, - 'is_absolute_test.dart': <String, Map>{}, - 'is_relative_test.dart': <String, Map>{}, - 'join_test.dart': <String, Map>{}, - 'normalize_test.dart': <String, Map>{}, - 'relative_test.dart': <String, Map>{}, - 'split_test.dart': <String, Map>{} - } - }; - - expect(stripColors(tree.fromMap(map)), equals(""" -|-- .gitignore -|-- README.md -|-- TODO -|-- example -| |-- console_example.dart -| |-- main.dart -| '-- web copy -| '-- web_example.dart -|-- lib -| '-- path.dart -|-- pubspec.yaml -'-- test - |-- absolute_test.dart - |-- basename_test.dart - |-- dirname_test.dart - |-- extension_test.dart - |-- is_absolute_test.dart - |-- is_relative_test.dart - |-- join_test.dart - |-- normalize_test.dart - |-- relative_test.dart - '-- split_test.dart -""")); - }); + tearDown(() { + forceColors = ForceColorOption.auto; + }); + test('tree.fromFiles no files', () { + expect(tree.fromFiles([], showFileSizes: true), equals('')); }); - test('does not elide children if showAllChildren is true', () { + List<int> bytes(int size) => List.filled(size, 0); + testWithGolden('tree.fromFiles a complex example', colors: true, (ctx) async { + await dir(appPath, [ + libPubspec('app', '1.0.0'), + file('TODO', bytes(10)), + dir('example', [ + file('console_example.dart', bytes(1000)), + file('main.dart', bytes(1024)), + dir('web copy', [ + file('web_example.dart', bytes(1025)), + ]), + ]), + dir('test', [ + file('absolute_test.dart', bytes(0)), + file('basename_test.dart', bytes(1 << 20)), + file('dirname_test.dart', bytes((1 << 20) + 1)), + file('extension_test.dart', bytes(2300)), + file('is_absolute_test.dart', bytes(2400)), + file('is_relative_test.dart', bytes((1 << 20) * 25)), + file('join_test.dart', bytes(1023)), + file('normalize_test.dart', bytes((1 << 20) - 1)), + file('relative_test.dart', bytes(100)), + file('split_test.dart', bytes(1)), + file('all_test.dart', bytes(100)), + file('path_posix_test.dart', bytes(100)), + file('path_windows_test.dart', bytes(100)), + ]), + file('.gitignore', bytes(100)), + file('README.md', bytes(100)), + dir('lib', [ + file('path.dart', bytes(100)), + ]), + ]).create(); + var files = Package.load( + null, + path(appPath), + (name) => throw UnimplementedError(), + ).listFiles(); + ctx.expectNextSection( + tree.fromFiles(files, baseDir: path(appPath), showFileSizes: true)); + }); + test('tree.fromMap empty map', () { + expect(tree.fromMap({}), equals('')); + }); + + testWithGolden('tree.fromMap a complex example', colors: true, (ctx) { var map = { - 'dir': { - 'a.dart': <String, Map>{}, - 'b.dart': <String, Map>{}, - 'c.dart': <String, Map>{}, - 'd.dart': <String, Map>{}, - 'e.dart': <String, Map>{}, - 'f.dart': <String, Map>{}, - 'g.dart': <String, Map>{}, - 'h.dart': <String, Map>{}, - 'i.dart': <String, Map>{}, - 'j.dart': <String, Map>{}, - 'k.dart': <String, Map>{}, - 'l.dart': <String, Map>{}, + '.gitignore': <String, Map>{}, + 'README.md': <String, Map>{}, + 'TODO': <String, Map>{}, + 'example': { + 'console_example.dart': <String, Map>{}, + 'main.dart': <String, Map>{}, + 'web copy': {'web_example.dart': <String, Map>{}}, + }, + 'lib': {'path.dart': <String, Map>{}}, + 'pubspec.yaml': <String, Map>{}, + 'test': { + 'absolute_test.dart': <String, Map>{}, + 'basename_test.dart': <String, Map>{}, + 'dirname_test.dart': <String, Map>{}, + 'extension_test.dart': <String, Map>{}, + 'is_absolute_test.dart': <String, Map>{}, + 'is_relative_test.dart': <String, Map>{}, + 'join_test.dart': <String, Map>{}, + 'normalize_test.dart': <String, Map>{}, + 'relative_test.dart': <String, Map>{}, + 'split_test.dart': <String, Map>{} } }; - expect(stripColors(tree.fromMap(map, showAllChildren: true)), equals(""" -'-- dir - |-- a.dart - |-- b.dart - |-- c.dart - |-- d.dart - |-- e.dart - |-- f.dart - |-- g.dart - |-- h.dart - |-- i.dart - |-- j.dart - |-- k.dart - '-- l.dart -""")); + + ctx.expectNextSection(tree.fromMap(map)); }); }
diff --git a/test/deps_test.dart b/test/deps_test.dart index 7336f13..b4ded45 100644 --- a/test/deps_test.dart +++ b/test/deps_test.dart
@@ -112,20 +112,20 @@ await runPub(args: ['deps'], output: ''' Dart SDK 0.1.2+3 myapp 0.0.0 - |-- from_path 1.2.3 - |-- normal 1.2.3 - | |-- circular_a 1.2.3 - | | '-- circular_b 1.2.3 - | | '-- circular_a... - | '-- transitive 1.2.3 - | '-- shared... - |-- overridden 2.0.0 - |-- override_only 1.2.3 - '-- unittest 1.2.3 - |-- dev_only 1.2.3 - '-- shared 1.2.3 - '-- other 1.0.0 - '-- myapp... + ├── from_path 1.2.3 + ├── normal 1.2.3 + │ ├── circular_a 1.2.3 + │ │ └── circular_b 1.2.3 + │ │ └── circular_a... + │ └── transitive 1.2.3 + │ └── shared... + ├── overridden 2.0.0 + ├── override_only 1.2.3 + └── unittest 1.2.3 + ├── dev_only 1.2.3 + └── shared 1.2.3 + └── other 1.0.0 + └── myapp... '''); }); test('in json form', () async { @@ -333,17 +333,17 @@ await runPub(args: ['deps', '--no-dev'], output: ''' Dart SDK 0.1.2+3 myapp 0.0.0 - |-- from_path 1.2.3 - |-- normal 1.2.3 - | |-- circular_a 1.2.3 - | | '-- circular_b 1.2.3 - | | '-- circular_a... - | '-- transitive 1.2.3 - | '-- shared 1.2.3 - | '-- other 1.0.0 - | '-- myapp... - |-- overridden 2.0.0 - '-- override_only 1.2.3 + ├── from_path 1.2.3 + ├── normal 1.2.3 + │ ├── circular_a 1.2.3 + │ │ └── circular_b 1.2.3 + │ │ └── circular_a... + │ └── transitive 1.2.3 + │ └── shared 1.2.3 + │ └── other 1.0.0 + │ └── myapp... + ├── overridden 2.0.0 + └── override_only 1.2.3 '''); }); });
diff --git a/test/descriptor.dart b/test/descriptor.dart index 9248ad4..669d8ef 100644 --- a/test/descriptor.dart +++ b/test/descriptor.dart
@@ -33,7 +33,7 @@ /// Describes a package that passes all validation. DirectoryDescriptor get validPackage => dir(appPath, [ - libPubspec('test_pkg', '1.0.0', sdk: '>=1.8.0 <=2.0.0'), + libPubspec('test_pkg', '1.0.0', sdk: '>=0.1.2 <=0.2.0'), file('LICENSE', 'Eh, do what you want.'), file('README.md', "This package isn't real."), file('CHANGELOG.md', '# 1.0.0\nFirst version\n'), @@ -188,11 +188,12 @@ /// If [port] is passed, it's used as the port number of the local hosted server /// that this cache represents. It defaults to [globalServer.port]. Descriptor hostedCache(Iterable<Descriptor> contents, {int? port}) { - return dir(cachePath, [ - dir('hosted', [dir('localhost%58${port ?? globalServer.port}', contents)]) - ]); + return dir(hostedCachePath(port: port), contents); } +String hostedCachePath({int? port}) => + p.join(cachePath, 'hosted', 'localhost%58${port ?? globalServer.port}'); + /// Describes the file that contains the client's OAuth2 /// credentials. The URL "/token" on [server] will be used as the token /// endpoint for refreshing the access token.
diff --git a/test/directory_option_test.dart b/test/directory_option_test.dart index 65193bf..5a483d9 100644 --- a/test/directory_option_test.dart +++ b/test/directory_option_test.dart
@@ -34,13 +34,13 @@ await dir(appPath, [ dir('bin', [ file('app.dart', ''' -main() => print('Hi'); +main() => print('Hi'); ''') ]), dir('example', [ pubspec({ 'name': 'example', - 'environment': {'sdk': '>=1.2.0 <2.0.0'}, + 'environment': {'sdk': '>=0.1.2 <0.2.0'}, 'dependencies': { 'test_pkg': {'path': '../'} } @@ -49,7 +49,7 @@ dir('example2', [ pubspec({ 'name': 'example', - 'environment': {'sdk': '>=1.2.0 <2.0.0'}, + 'environment': {'sdk': '>=0.1.2 <0.2.0'}, 'dependencies': { 'myapp': {'path': '../'} // Wrong name of dependency } @@ -82,7 +82,6 @@ await ctx.run( cases[i], workingDirectory: sandbox, - environment: {'_PUB_TEST_SDK_VERSION': '1.12.0'}, ); } });
diff --git a/test/embedding/embedding_test.dart b/test/embedding/embedding_test.dart index 73370d7..9f74405 100644 --- a/test/embedding/embedding_test.dart +++ b/test/embedding/embedding_test.dart
@@ -357,6 +357,18 @@ RegExp(r'Writing \d+ characters', multiLine: true), r'Writing $N characters', ) + .replaceAll( + RegExp(r'x-goog-hash(.*)$', multiLine: true), + r'x-goog-hash: $CHECKSUM_HEADER', + ) + .replaceAll( + RegExp( + r'Computed checksum \d+ for foo 1.0.0 with expected CRC32C of ' + r'\d+\.', + multiLine: true), + r'Computed checksum $CRC32C for foo 1.0.0 with expected CRC32C of ' + r'$CRC32C.', + ) /// TODO(sigurdm): This hack suppresses differences in stack-traces /// between dart 2.17 and 2.18. Remove when 2.18 is stable.
diff --git a/test/get/get_inside_cache_fails_test.dart b/test/get/get_inside_cache_fails_test.dart new file mode 100644 index 0000000..a7a6b45 --- /dev/null +++ b/test/get/get_inside_cache_fails_test.dart
@@ -0,0 +1,27 @@ +// Copyright (c) 2014, 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:path/path.dart' as p; +import 'package:test/test.dart'; + +import '../descriptor.dart' as d; +import '../test_pub.dart'; + +void main() { + test('`pub get` inside the cache fails gracefully', () async { + final server = await servePackages(); + server.serve('foo', '1.0.0', pubspec: { + 'name': 'foo', + 'version': '1.0.0', + 'environment': {'sdk': '>=0.1.2+3 <0.2.0'} + }); + await d.appDir({'foo': 'any'}).create(); + + await pubGet(); + + await pubGet( + workingDirectory: p.join(d.sandbox, d.hostedCachePath(), 'foo-1.0.0'), + error: 'Cannot operate on packages inside the cache.'); + }); +}
diff --git a/test/get/hosted/get_test.dart b/test/get/hosted/get_test.dart index f0270a3..81d4845 100644 --- a/test/get/hosted/get_test.dart +++ b/test/get/hosted/get_test.dart
@@ -12,10 +12,13 @@ import '../../test_pub.dart'; void main() { - test('gets a package from a pub server', () async { + test('gets a package from a pub server and validates its CRC32C checksum', + () async { final server = await servePackages(); server.serve('foo', '1.2.3'); + expect(await server.peekArchiveChecksumHeader('foo', '1.2.3'), isNotNull); + await d.appDir({'foo': '1.2.3'}).create(); await pubGet(); @@ -26,6 +29,62 @@ ]).validate(); }); + group('gets a package from a pub server without validating its checksum', () { + late PackageServer server; + + setUp(() async { + server = await servePackages() + ..serveChecksums = false + ..serve('foo', '1.2.3') + ..serve('bar', '1.2.3', headers: { + 'x-goog-hash': [''] + }) + ..serve('baz', '1.2.3', headers: { + 'x-goog-hash': ['md5=loremipsum'] + }); + }); + + test('because of omitted checksum header', () async { + expect(await server.peekArchiveChecksumHeader('foo', '1.2.3'), isNull); + + await d.appDir({'foo': '1.2.3'}).create(); + + await pubGet(); + + await d.cacheDir({'foo': '1.2.3'}).validate(); + await d.appPackageConfigFile([ + d.packageConfigEntry(name: 'foo', version: '1.2.3'), + ]).validate(); + }); + + test('because of empty checksum header', () async { + expect(await server.peekArchiveChecksumHeader('bar', '1.2.3'), ''); + + await d.appDir({'bar': '1.2.3'}).create(); + + await pubGet(); + + await d.cacheDir({'bar': '1.2.3'}).validate(); + await d.appPackageConfigFile([ + d.packageConfigEntry(name: 'bar', version: '1.2.3'), + ]).validate(); + }); + + test('because of missing CRC32C in checksum header', () async { + expect(await server.peekArchiveChecksumHeader('baz', '1.2.3'), + 'md5=loremipsum'); + + await d.appDir({'baz': '1.2.3'}).create(); + + await pubGet(); + + await d.cacheDir({'baz': '1.2.3'}).validate(); + await d.appPackageConfigFile([ + d.packageConfigEntry(name: 'baz', version: '1.2.3'), + ]).validate(); + }); + }); + test('URL encodes the package name', () async { await servePackages(); @@ -64,6 +123,148 @@ ]).validate(); }); + test('recognizes and retries a package with a CRC32C checksum mismatch', + () async { + var server = await startPackageServer(); + + server.serve('foo', '1.2.3', headers: { + 'x-goog-hash': PackageServer.composeChecksumHeader(crc32c: 3381945770) + }); + + await d.appDir({ + 'foo': { + 'version': '1.2.3', + 'hosted': {'name': 'foo', 'url': 'http://localhost:${server.port}'} + } + }).create(); + + await pubGet( + error: RegExp( + r'''Package archive for foo 1.2.3 downloaded from "(.+)" has ''' + r'''"x-goog-hash: crc32c=(\d+)", which doesn't match the checksum ''' + r'''of the archive downloaded\.'''), + silent: contains('Retry #2 because of checksum error'), + environment: { + 'PUB_MAX_HTTP_RETRIES': '2', + }, + ); + }); + + group('recognizes bad checksum header and retries', () { + late PackageServer server; + + setUp(() async { + server = await servePackages() + ..serve('foo', '1.2.3', headers: { + 'x-goog-hash': ['crc32c=,md5='] + }) + ..serve('bar', '1.2.3', headers: { + 'x-goog-hash': ['crc32c=loremipsum,md5=loremipsum'] + }) + ..serve('baz', '1.2.3', headers: { + 'x-goog-hash': ['crc32c=MTIzNDU=,md5=NTQzMjE='] + }); + }); + + test('when the CRC32C checksum is empty', () async { + await d.appDir({ + 'foo': { + 'version': '1.2.3', + 'hosted': {'name': 'foo', 'url': 'http://localhost:${server.port}'} + } + }).create(); + + await pubGet( + exitCode: exit_codes.DATA, + error: contains( + 'Package archive "foo-1.2.3.tar.gz" has a malformed CRC32C ' + 'checksum in its response headers'), + silent: contains('Retry #2 because of checksum error'), + environment: { + 'PUB_MAX_HTTP_RETRIES': '2', + }, + ); + }); + + test('when the CRC32C checksum has bad encoding', () async { + await d.appDir({ + 'bar': { + 'version': '1.2.3', + 'hosted': {'name': 'bar', 'url': 'http://localhost:${server.port}'} + } + }).create(); + + await pubGet( + exitCode: exit_codes.DATA, + error: contains( + 'Package archive "bar-1.2.3.tar.gz" has a malformed CRC32C ' + 'checksum in its response headers'), + silent: contains('Retry #2 because of checksum error'), + environment: { + 'PUB_MAX_HTTP_RETRIES': '2', + }, + ); + }); + + test('when the CRC32C checksum is malformed', () async { + await d.appDir({ + 'baz': { + 'version': '1.2.3', + 'hosted': {'name': 'baz', 'url': 'http://localhost:${server.port}'} + } + }).create(); + + await pubGet( + exitCode: exit_codes.DATA, + error: contains( + 'Package archive "baz-1.2.3.tar.gz" has a malformed CRC32C ' + 'checksum in its response headers'), + silent: contains('Retry #2 because of checksum error'), + environment: { + 'PUB_MAX_HTTP_RETRIES': '2', + }, + ); + }); + }); + + test('gets a package from a pub server that uses gzip response compression', + () async { + final server = await servePackages(); + server.autoCompress = true; + server.serveChecksums = false; + server.serve('foo', '1.2.3'); + + expect(await server.peekArchiveChecksumHeader('foo', '1.2.3'), isNull); + + await d.appDir({'foo': '1.2.3'}).create(); + + await pubGet(); + + await d.cacheDir({'foo': '1.2.3'}).validate(); + await d.appPackageConfigFile([ + d.packageConfigEntry(name: 'foo', version: '1.2.3'), + ]).validate(); + }); + + test( + 'gets a package from a pub server that uses gzip response compression ' + 'and validates its CRC32C checksum', () async { + final server = await servePackages(); + server.autoCompress = true; + server.serve('foo', '1.2.3'); + + expect(await server.peekArchiveChecksumHeader('foo', '1.2.3'), isNotNull); + + await d.appDir({'foo': '1.2.3'}).create(); + + await pubGet(); + + await d.cacheDir({'foo': '1.2.3'}).validate(); + await d.appPackageConfigFile([ + d.packageConfigEntry(name: 'foo', version: '1.2.3'), + ]).validate(); + }); + group('categorizes dependency types in the lockfile', () { setUp(() async { await servePackages()
diff --git a/test/global/activate/custom_hosted_url_test.dart b/test/global/activate/custom_hosted_url_test.dart index 8b2f0fc..1c0bc04 100644 --- a/test/global/activate/custom_hosted_url_test.dart +++ b/test/global/activate/custom_hosted_url_test.dart
@@ -8,7 +8,7 @@ void main() { test('activating a package from a custom pub server', () async { - // The default pub server (i.e. pub.dartlang.org). + // The default pub server (i.e. pub.dev). final server = await servePackages(); server.serve('baz', '1.0.0');
diff --git a/test/global/run/runs_script_without_packages_file_test.dart b/test/global/run/runs_script_without_packages_file_test.dart deleted file mode 100644 index dc225ba..0000000 --- a/test/global/run/runs_script_without_packages_file_test.dart +++ /dev/null
@@ -1,49 +0,0 @@ -// 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 'package:path/path.dart' as p; -import 'package:pub/src/io.dart'; -import 'package:test/test.dart'; - -import '../../descriptor.dart' as d; -import '../../test_pub.dart'; - -void main() { - test('runs a snapshotted script without a .dart_tool/package_config file', - () async { - final server = await servePackages(); - server.serve('foo', '1.0.0', contents: [ - d.dir('bin', [d.file('script.dart', "main(args) => print('ok');")]) - ]); - - await runPub(args: ['global', 'activate', 'foo']); - - // Mimic the global packages installed by pub <1.12, which didn't create a - // .packages file for global installs. - deleteEntry(p.join(d.sandbox, cachePath, - 'global_packages/foo/.dart_tool/package_config.json')); - - var pub = await pubRun(global: true, args: ['foo:script']); - expect(pub.stdout, emitsThrough('ok')); - await pub.shouldExit(); - }); - - test( - 'runs an unsnapshotted script without a .dart_tool/package_config.json file', - () async { - await d.dir('foo', [ - d.libPubspec('foo', '1.0.0'), - d.dir('bin', [d.file('foo.dart', "main() => print('ok');")]) - ]).create(); - - await runPub(args: ['global', 'activate', '--source', 'path', '../foo']); - - deleteEntry(p.join(d.sandbox, cachePath, - 'global_packages/foo/.dart_tool/package_config.json')); - - var pub = await pubRun(global: true, args: ['foo']); - expect(pub.stdout, emitsThrough('ok')); - await pub.shouldExit(); - }); -}
diff --git a/test/golden_file.dart b/test/golden_file.dart index 9db8ad0..3dcbfb6 100644 --- a/test/golden_file.dart +++ b/test/golden_file.dart
@@ -46,10 +46,15 @@ late String _header; final _results = <String>[]; late bool _shouldRegenerateGolden; + final bool colors; bool _generatedNewData = false; // track if new data is generated int _nextSectionIndex = 0; - GoldenTestContext._(this._currentTestFile, this._testName) { + GoldenTestContext._( + this._currentTestFile, + this._testName, { + required this.colors, + }) { final rel = p.relative( _currentTestFile.replaceAll(RegExp(r'\.dart$'), ''), from: p.join(p.current, 'test'), @@ -59,8 +64,8 @@ 'testdata', 'goldens', rel, - // Sanitize the name, and add .txt - '${_testName.replaceAll(RegExp(r'[<>:"/\|?*%#]'), '~')}.txt', + // Sanitize the name, and add .ans or .txt. + '${_testName.replaceAll(RegExp(r'[<>:"/\|?*%#]'), '~')}.${colors ? 'ans' : 'txt'}', ); _goldenFile = File(_goldenFilePath); _header = '# GENERATED BY: ${p.relative(_currentTestFile)}\n\n'; @@ -184,10 +189,12 @@ s.writeln('\$ cd $directory'); } s.writeln('\$ tree'); - s.writeln(stripColors(ascii_tree.fromFiles( + final tree = ascii_tree.fromFiles( listDir(target, recursive: true), baseDir: target, - ))); + showFileSizes: false, + ); + s.writeln(colors ? tree : stripColors(tree)); _expectSection(sectionIndex, s.toString()); } @@ -203,11 +210,20 @@ /// `test/testdata/goldens/path/to/myfile_test/<name>.txt` /// , when `path/to/myfile_test.dart` is the `_test.dart` file from which this /// function is called. +/// +/// If [colors] is `true` the file will be created with an `.ans` extension that +/// indicates ANSI colors will be used. +/// +/// Such a file can eg. be viewed in vscode with this plugin: +/// https://marketplace.visualstudio.com/items?itemName=iliazeus.vscode-ansi void testWithGolden( - String name, - FutureOr<void> Function(GoldenTestContext ctx) fn, -) { - final ctx = GoldenTestContext._(_findCurrentTestFilename(), name); + String name, FutureOr<void> Function(GoldenTestContext ctx) fn, + {bool colors = false}) { + final ctx = GoldenTestContext._( + _findCurrentTestFilename(), + name, + colors: colors, + ); test(name, () async { ctx._readGoldenFile(); await fn(ctx);
diff --git a/test/lish/archives_and_uploads_a_package_test.dart b/test/lish/archives_and_uploads_a_package_test.dart index 41db57c..da7fdf7 100644 --- a/test/lish/archives_and_uploads_a_package_test.dart +++ b/test/lish/archives_and_uploads_a_package_test.dart
@@ -15,10 +15,9 @@ import 'utils.dart'; void main() { - setUp(d.validPackage.create); - test('archives and uploads a package', () async { await servePackages(); + await d.validPackage.create(); await d.credentialsFile(globalServer, 'access token').create(); var pub = await startPublish(globalServer); @@ -39,6 +38,7 @@ test('archives and uploads a package using token', () async { await servePackages(); + await d.validPackage.create(); await d.tokensFile({ 'version': 1, 'hosted': [ @@ -64,6 +64,7 @@ test('publishes to hosted-url with path', () async { await servePackages(); + await d.validPackage.create(); await d.tokensFile({ 'version': 1, 'hosted': [ @@ -98,7 +99,7 @@ test('with an empty Git submodule', () async { await d.git('empty').create(); - var repo = d.git(appPath); + var repo = d.git(appPath, d.validPackage.contents); await repo.create(); await repo.runGit(['submodule', 'add', '../empty', 'empty']);
diff --git a/test/lish/cloud_storage_upload_doesnt_redirect_test.dart b/test/lish/cloud_storage_upload_doesnt_redirect_test.dart index 0f792d5..19b8761 100644 --- a/test/lish/cloud_storage_upload_doesnt_redirect_test.dart +++ b/test/lish/cloud_storage_upload_doesnt_redirect_test.dart
@@ -10,10 +10,9 @@ import 'utils.dart'; void main() { - setUp(d.validPackage.create); - test("cloud storage upload doesn't redirect", () async { await servePackages(); + await d.validPackage.create(); await d.credentialsFile(globalServer, 'access token').create(); var pub = await startPublish(globalServer);
diff --git a/test/lish/cloud_storage_upload_provides_an_error_test.dart b/test/lish/cloud_storage_upload_provides_an_error_test.dart index 5dec03d..85e55af 100644 --- a/test/lish/cloud_storage_upload_provides_an_error_test.dart +++ b/test/lish/cloud_storage_upload_provides_an_error_test.dart
@@ -10,10 +10,9 @@ import 'utils.dart'; void main() { - setUp(d.validPackage.create); - test('cloud storage upload provides an error', () async { await servePackages(); + await d.validPackage.create(); await d.credentialsFile(globalServer, 'access token').create(); var pub = await startPublish(globalServer);
diff --git a/test/lish/dot_folder_name_test.dart b/test/lish/dot_folder_name_test.dart index 9f64839..d709f77 100644 --- a/test/lish/dot_folder_name_test.dart +++ b/test/lish/dot_folder_name_test.dart
@@ -16,12 +16,11 @@ d.dir('.vscode', [d.file('a')]), d.file('.pubignore', '!.vscode/') ]).create(); - await runPub( args: ['lish', '--dry-run'], output: contains(''' -|-- .vscode -| '-- a'''), +├── .vscode +│ └── a'''), exitCode: exit_codes.SUCCESS, ); });
diff --git a/test/lish/preview_errors_if_private_test.dart b/test/lish/dry_run_errors_if_private_test.dart similarity index 91% rename from test/lish/preview_errors_if_private_test.dart rename to test/lish/dry_run_errors_if_private_test.dart index fb7160a..10dd739 100644 --- a/test/lish/preview_errors_if_private_test.dart +++ b/test/lish/dry_run_errors_if_private_test.dart
@@ -10,7 +10,7 @@ import '../test_pub.dart'; void main() { - test('preview shows an error if the package is private', () async { + test('dry-run shows an error if the package is private', () async { var pkg = packageMap('test_pkg', '1.0.0'); pkg['publish_to'] = 'none'; await d.dir(appPath, [d.pubspec(pkg)]).create();
diff --git a/test/lish/dry_run_package_validation_has_a_warning_test.dart b/test/lish/dry_run_package_validation_has_a_warning_test.dart new file mode 100644 index 0000000..1edd48d --- /dev/null +++ b/test/lish/dry_run_package_validation_has_a_warning_test.dart
@@ -0,0 +1,30 @@ +// 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. + +import 'package:pub/src/exit_codes.dart' as exit_codes; + +import 'package:test/test.dart'; + +import '../descriptor.dart' as d; +import '../test_pub.dart'; + +void main() { + test('dry-run package validation gives a warning', () async { + (await servePackages()).serve('foo', '1.0.0'); + await d.validPackage.create(); + + var pkg = + packageMap('test_pkg', '1.0.0', null, null, {'sdk': '>=0.1.2 <0.2.0'}); + pkg['dependencies'] = {'foo': 'any'}; + await d.dir(appPath, [d.pubspec(pkg)]).create(); + + var pub = await startPublish(globalServer, args: ['--dry-run']); + + await pub.shouldExit(exit_codes.DATA); + expect( + pub.stderr, + emitsThrough('Package has 1 warning.'), + ); + }); +}
diff --git a/test/lish/preview_package_validation_has_no_warnings_test.dart b/test/lish/dry_run_package_validation_has_no_warnings_test.dart similarity index 71% rename from test/lish/preview_package_validation_has_no_warnings_test.dart rename to test/lish/dry_run_package_validation_has_no_warnings_test.dart index 179c648..13ed386 100644 --- a/test/lish/preview_package_validation_has_no_warnings_test.dart +++ b/test/lish/dry_run_package_validation_has_no_warnings_test.dart
@@ -10,12 +10,9 @@ import '../test_pub.dart'; void main() { - setUp(d.validPackage.create); - - test('preview package validation has no warnings', () async { - var pkg = - packageMap('test_pkg', '1.0.0', null, null, {'sdk': '>=1.8.0 <2.0.0'}); - await d.dir(appPath, [d.pubspec(pkg)]).create(); + test('--dry-run package validation on valid package has no warnings', + () async { + await d.validPackage.create(); await servePackages(); var pub = await startPublish(globalServer, args: ['--dry-run']);
diff --git a/test/lish/force_cannot_be_combined_with_dry_run_test.dart b/test/lish/force_cannot_be_combined_with_dry_run_test.dart index 9d2233c..b82e7ce 100644 --- a/test/lish/force_cannot_be_combined_with_dry_run_test.dart +++ b/test/lish/force_cannot_be_combined_with_dry_run_test.dart
@@ -9,9 +9,9 @@ import '../test_pub.dart'; void main() { - setUp(d.validPackage.create); - test('--force cannot be combined with --dry-run', () async { + await d.validPackage.create(); + await runPub( args: ['lish', '--force', '--dry-run'], error: contains('Cannot use both --force and --dry-run.'),
diff --git a/test/lish/force_does_not_publish_if_there_are_errors_test.dart b/test/lish/force_does_not_publish_if_there_are_errors_test.dart index 481f627..69c770a 100644 --- a/test/lish/force_does_not_publish_if_there_are_errors_test.dart +++ b/test/lish/force_does_not_publish_if_there_are_errors_test.dart
@@ -2,6 +2,9 @@ // 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:io'; + +import 'package:path/path.dart' as p; import 'package:pub/src/exit_codes.dart' as exit_codes; import 'package:test/test.dart'; @@ -9,24 +12,20 @@ import '../test_pub.dart'; void main() { - setUp(d.validPackage.create); - test('--force does not publish if there are errors', () async { - await d.dir(appPath, [ - d.rawPubspec({ - 'name': 'test_pkg', - 'homepage': 'http://pub.dartlang.org', - 'version': '1.0.0', - }), - ]).create(); + await servePackages(); + await d.validPackage.create(); + // It is an error to publish without a LICENSE file. + File(d.path(p.join(appPath, 'LICENSE'))).deleteSync(); await servePackages(); var pub = await startPublish(globalServer, args: ['--force']); await pub.shouldExit(exit_codes.DATA); expect( - pub.stderr, - emitsThrough('Sorry, your package is missing some ' - "requirements and can't be published yet.")); + pub.stderr, + emitsThrough( + "Sorry, your package is missing a requirement and can't be published yet."), + ); }); }
diff --git a/test/lish/force_publishes_if_tests_are_no_warnings_or_errors_test.dart b/test/lish/force_publishes_if_tests_are_no_warnings_or_errors_test.dart index c5d8957..5b25f48 100644 --- a/test/lish/force_publishes_if_tests_are_no_warnings_or_errors_test.dart +++ b/test/lish/force_publishes_if_tests_are_no_warnings_or_errors_test.dart
@@ -13,10 +13,9 @@ import 'utils.dart'; void main() { - setUp(d.validPackage.create); - test('--force publishes if there are no warnings or errors', () async { await servePackages(); + await d.validPackage.create(); await d.credentialsFile(globalServer, 'access token').create(); var pub = await startPublish(globalServer, args: ['--force']);
diff --git a/test/lish/force_publishes_if_there_are_warnings_test.dart b/test/lish/force_publishes_if_there_are_warnings_test.dart index 9fb3d53..41a115c 100644 --- a/test/lish/force_publishes_if_there_are_warnings_test.dart +++ b/test/lish/force_publishes_if_there_are_warnings_test.dart
@@ -13,15 +13,15 @@ import 'utils.dart'; void main() { - setUp(d.validPackage.create); - test('--force publishes if there are warnings', () async { + await d.validPackage.create(); var pkg = - packageMap('test_pkg', '1.0.0', null, null, {'sdk': '>=1.8.0 <2.0.0'}); + packageMap('test_pkg', '1.0.0', null, null, {'sdk': '>=0.1.2 <0.2.0'}); pkg['dependencies'] = {'foo': 'any'}; await d.dir(appPath, [d.pubspec(pkg)]).create(); - await servePackages(); + (await servePackages()).serve('foo', '1.0.0'); + await d.credentialsFile(globalServer, 'access token').create(); var pub = await startPublish(globalServer, args: ['--force']); @@ -35,15 +35,14 @@ }); await pub.shouldExit(exit_codes.SUCCESS); + final stderrLines = await pub.stderr.rest.toList(); expect( - pub.stderr, - emitsThrough('Package validation found the following potential issue:'), - ); - expect( - pub.stderr, - emitsLines( - '* Your dependency on "foo" should have a version constraint.\n' - ' Without a constraint, you\'re promising to support all future versions of "foo".')); + stderrLines, + allOf([ + contains('Package validation found the following potential issue:'), + contains( + '* Your dependency on "foo" should have a version constraint. For example:'), + ])); expect(pub.stdout, emitsThrough('Package test_pkg 1.0.0 uploaded!')); }); }
diff --git a/test/lish/package_creation_provides_a_malformed_error_test.dart b/test/lish/package_creation_provides_a_malformed_error_test.dart index 7ab0bcd..cb18e56 100644 --- a/test/lish/package_creation_provides_a_malformed_error_test.dart +++ b/test/lish/package_creation_provides_a_malformed_error_test.dart
@@ -12,10 +12,9 @@ import 'utils.dart'; void main() { - setUp(d.validPackage.create); - test('package creation provides a malformed error', () async { await servePackages(); + await d.validPackage.create(); await d.credentialsFile(globalServer, 'access token').create(); var pub = await startPublish(globalServer);
diff --git a/test/lish/package_creation_provides_a_malformed_success_test.dart b/test/lish/package_creation_provides_a_malformed_success_test.dart index 089d09e..b65317c 100644 --- a/test/lish/package_creation_provides_a_malformed_success_test.dart +++ b/test/lish/package_creation_provides_a_malformed_success_test.dart
@@ -12,10 +12,9 @@ import 'utils.dart'; void main() { - setUp(d.validPackage.create); - test('package creation provides a malformed success', () async { await servePackages(); + await d.validPackage.create(); await d.credentialsFile(globalServer, 'access token').create(); var pub = await startPublish(globalServer);
diff --git a/test/lish/package_creation_provides_an_error_test.dart b/test/lish/package_creation_provides_an_error_test.dart index f5ff128..a4fde1b 100644 --- a/test/lish/package_creation_provides_an_error_test.dart +++ b/test/lish/package_creation_provides_an_error_test.dart
@@ -12,10 +12,9 @@ import 'utils.dart'; void main() { - setUp(d.validPackage.create); - test('package creation provides an error', () async { await servePackages(); + await d.validPackage.create(); await d.credentialsFile(globalServer, 'access token').create(); var pub = await startPublish(globalServer);
diff --git a/test/lish/package_creation_provides_invalid_json_test.dart b/test/lish/package_creation_provides_invalid_json_test.dart index 2cd6212..7181d8e 100644 --- a/test/lish/package_creation_provides_invalid_json_test.dart +++ b/test/lish/package_creation_provides_invalid_json_test.dart
@@ -10,10 +10,9 @@ import 'utils.dart'; void main() { - setUp(d.validPackage.create); - test('package creation provides invalid JSON', () async { await servePackages(); + await d.validPackage.create(); await d.credentialsFile(globalServer, 'access token').create(); var pub = await startPublish(globalServer);
diff --git a/test/lish/package_validation_has_a_warning_and_continues_test.dart b/test/lish/package_validation_has_a_warning_and_continues_test.dart index c39dbc4..92c74de 100644 --- a/test/lish/package_validation_has_a_warning_and_continues_test.dart +++ b/test/lish/package_validation_has_a_warning_and_continues_test.dart
@@ -3,7 +3,9 @@ // BSD-style license that can be found in the LICENSE file. import 'dart:convert'; +import 'dart:io'; +import 'package:path/path.dart' as p; import 'package:pub/src/exit_codes.dart' as exit_codes; import 'package:shelf/shelf.dart' as shelf; import 'package:test/test.dart'; @@ -13,17 +15,16 @@ import 'utils.dart'; void main() { - setUp(d.validPackage.create); - test('package validation has a warning and continues', () async { - var pkg = - packageMap('test_pkg', '1.0.0', null, null, {'sdk': '>=1.8.0 <2.0.0'}); - pkg['author'] = 'Natalie Weizenbaum'; - await d.dir(appPath, [d.pubspec(pkg)]).create(); + await servePackages(); + await d.validPackage.create(); + // Publishing without a README.md gives a warning. + File(d.path(p.join(appPath, 'README.md'))).deleteSync(); await servePackages(); await d.credentialsFile(globalServer, 'access token').create(); var pub = await startPublish(globalServer); + expect(pub.stdout, emitsThrough(startsWith('Package has 1 warning.'))); pub.stdin.writeln('y'); handleUploadForm(globalServer); handleUpload(globalServer);
diff --git a/test/lish/package_validation_has_a_warning_and_is_canceled_test.dart b/test/lish/package_validation_has_a_warning_and_is_canceled_test.dart index 0d5a563..b552a25 100644 --- a/test/lish/package_validation_has_a_warning_and_is_canceled_test.dart +++ b/test/lish/package_validation_has_a_warning_and_is_canceled_test.dart
@@ -9,13 +9,14 @@ import '../test_pub.dart'; void main() { - setUp(d.validPackage.create); - test('package validation has a warning and is canceled', () async { + await d.validPackage.create(); var pkg = - packageMap('test_pkg', '1.0.0', null, null, {'sdk': '>=1.8.0 <2.0.0'}); + packageMap('test_pkg', '1.0.0', null, null, {'sdk': '>=0.1.2 <0.2.0'}); pkg['author'] = 'Natalie Weizenbaum'; - await d.dir(appPath, [d.pubspec(pkg)]).create(); + await d.dir(appPath, [ + d.pubspec(pkg), + ]).create(); await servePackages(); var pub = await startPublish(globalServer);
diff --git a/test/lish/package_validation_has_an_error_test.dart b/test/lish/package_validation_has_an_error_test.dart index 14c83c0..aa0e6f6 100644 --- a/test/lish/package_validation_has_an_error_test.dart +++ b/test/lish/package_validation_has_an_error_test.dart
@@ -9,14 +9,13 @@ import '../test_pub.dart'; void main() { - setUp(d.validPackage.create); - test('package validation has an error', () async { await d.dir(appPath, [ d.rawPubspec({ 'name': 'test_pkg', - 'homepage': 'http://pub.dartlang.org', + 'homepage': 'https://pub.dev', 'version': '1.0.0', + 'environment': {'sdk': '>=0.1.2 <0.2.0'} }), ]).create();
diff --git a/test/lish/preview_package_validation_has_a_warning_test.dart b/test/lish/preview_package_validation_has_a_warning_test.dart deleted file mode 100644 index 69f59af..0000000 --- a/test/lish/preview_package_validation_has_a_warning_test.dart +++ /dev/null
@@ -1,37 +0,0 @@ -// 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. - -import 'package:pub/src/exit_codes.dart' as exit_codes; - -import 'package:test/test.dart'; - -import '../descriptor.dart' as d; -import '../test_pub.dart'; - -void main() { - setUp(d.validPackage.create); - - test('preview package validation has a warning', () async { - var pkg = - packageMap('test_pkg', '1.0.0', null, null, {'sdk': '>=1.8.0 <2.0.0'}); - pkg['dependencies'] = {'foo': 'any'}; - await d.dir(appPath, [d.pubspec(pkg)]).create(); - - await servePackages(); - var pub = await startPublish(globalServer, args: ['--dry-run']); - - await pub.shouldExit(exit_codes.DATA); - expect( - pub.stderr, - emitsThrough('Package validation found the following potential issue:'), - ); - expect( - pub.stderr, - emitsLines( - '* Your dependency on "foo" should have a version constraint.\n' - ' Without a constraint, you\'re promising to support all future versions of "foo".\n' - '\n' - 'Package has 1 warning.')); - }); -}
diff --git a/test/lish/requires_resolution_before_publishing_test.dart b/test/lish/requires_resolution_before_publishing_test.dart new file mode 100644 index 0000000..e3294bc --- /dev/null +++ b/test/lish/requires_resolution_before_publishing_test.dart
@@ -0,0 +1,22 @@ +// Copyright (c) 2022, 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:pub/src/exit_codes.dart' as exit_codes; +import 'package:test/test.dart'; + +import '../descriptor.dart' as d; +import '../test_pub.dart'; + +void main() { + test('does not publish if no resolution can be found', () async { + await servePackages(); // No packages. + await d.validPackage.create(); + await d.appDir({'foo': '1.0.0'}).create(); + await runPub( + args: ['lish'], + error: contains("Because myapp depends on foo any which doesn't exist"), + exitCode: exit_codes.UNAVAILABLE, + ); + }); +}
diff --git a/test/lish/server_arg_overrides_publish_to_url_test.dart b/test/lish/server_arg_overrides_publish_to_url_test.dart index 1597146..fcfdf14 100644 --- a/test/lish/server_arg_overrides_publish_to_url_test.dart +++ b/test/lish/server_arg_overrides_publish_to_url_test.dart
@@ -18,7 +18,6 @@ var pkg = packageMap('test_pkg', '1.0.0'); pkg['publish_to'] = 'http://pubspec.com'; await d.dir(appPath, [d.pubspec(pkg)]).create(); - await runPub( args: ['lish', '--dry-run', '--server', packageServer.url], output: contains(packageServer.url),
diff --git a/test/lish/upload_form_fields_has_a_non_string_value_test.dart b/test/lish/upload_form_fields_has_a_non_string_value_test.dart index 78a54fa..8758bca 100644 --- a/test/lish/upload_form_fields_has_a_non_string_value_test.dart +++ b/test/lish/upload_form_fields_has_a_non_string_value_test.dart
@@ -11,10 +11,9 @@ import 'utils.dart'; void main() { - setUp(d.validPackage.create); - test('upload form fields has a non-string value', () async { await servePackages(); + await d.validPackage.create(); await d.credentialsFile(globalServer, 'access token').create(); var pub = await startPublish(globalServer);
diff --git a/test/lish/upload_form_fields_is_not_a_map_test.dart b/test/lish/upload_form_fields_is_not_a_map_test.dart index d18c1ee..dc41511 100644 --- a/test/lish/upload_form_fields_is_not_a_map_test.dart +++ b/test/lish/upload_form_fields_is_not_a_map_test.dart
@@ -11,10 +11,9 @@ import 'utils.dart'; void main() { - setUp(d.validPackage.create); - test('upload form fields is not a map', () async { await servePackages(); + await d.validPackage.create(); await d.credentialsFile(globalServer, 'access token').create(); var pub = await startPublish(globalServer);
diff --git a/test/lish/upload_form_is_missing_fields_test.dart b/test/lish/upload_form_is_missing_fields_test.dart index b0032f8..e909c0a 100644 --- a/test/lish/upload_form_is_missing_fields_test.dart +++ b/test/lish/upload_form_is_missing_fields_test.dart
@@ -11,10 +11,9 @@ import 'utils.dart'; void main() { - setUp(d.validPackage.create); - test('upload form is missing fields', () async { await servePackages(); + await d.validPackage.create(); await d.credentialsFile(globalServer, 'access token').create(); var pub = await startPublish(globalServer);
diff --git a/test/lish/upload_form_is_missing_url_test.dart b/test/lish/upload_form_is_missing_url_test.dart index eae43ea..418813f 100644 --- a/test/lish/upload_form_is_missing_url_test.dart +++ b/test/lish/upload_form_is_missing_url_test.dart
@@ -11,10 +11,9 @@ import 'utils.dart'; void main() { - setUp(d.validPackage.create); - test('upload form is missing url', () async { await servePackages(); + await d.validPackage.create(); await d.credentialsFile(globalServer, 'access token').create(); var pub = await startPublish(globalServer);
diff --git a/test/lish/upload_form_provides_an_error_test.dart b/test/lish/upload_form_provides_an_error_test.dart index 35932b8..68a6c66 100644 --- a/test/lish/upload_form_provides_an_error_test.dart +++ b/test/lish/upload_form_provides_an_error_test.dart
@@ -11,10 +11,9 @@ import '../test_pub.dart'; void main() { - setUp(d.validPackage.create); - test('upload form provides an error', () async { await servePackages(); + await d.validPackage.create(); await d.credentialsFile(globalServer, 'access token').create(); var pub = await startPublish(globalServer);
diff --git a/test/lish/upload_form_provides_invalid_json_test.dart b/test/lish/upload_form_provides_invalid_json_test.dart index a046755..73ffb9a 100644 --- a/test/lish/upload_form_provides_invalid_json_test.dart +++ b/test/lish/upload_form_provides_invalid_json_test.dart
@@ -9,10 +9,10 @@ import '../test_pub.dart'; void main() { - setUp(d.validPackage.create); - test('upload form provides invalid JSON', () async { await servePackages(); + await d.validPackage.create(); + await servePackages(); await d.credentialsFile(globalServer, 'access token').create(); var pub = await startPublish(globalServer);
diff --git a/test/lish/upload_form_url_is_not_a_string_test.dart b/test/lish/upload_form_url_is_not_a_string_test.dart index f69fd1f..b37d24f 100644 --- a/test/lish/upload_form_url_is_not_a_string_test.dart +++ b/test/lish/upload_form_url_is_not_a_string_test.dart
@@ -11,10 +11,9 @@ import 'utils.dart'; void main() { - setUp(d.validPackage.create); - test('upload form url is not a string', () async { await servePackages(); + await d.validPackage.create(); await d.credentialsFile(globalServer, 'access token').create(); var pub = await startPublish(globalServer);
diff --git a/test/lish/uses_publish_to_url_test.dart b/test/lish/uses_publish_to_url_test.dart index 47496f7..d9408c4 100644 --- a/test/lish/uses_publish_to_url_test.dart +++ b/test/lish/uses_publish_to_url_test.dart
@@ -14,7 +14,6 @@ var pkg = packageMap('test_pkg', '1.0.0'); pkg['publish_to'] = 'http://example.com'; await d.dir(appPath, [d.pubspec(pkg)]).create(); - await runPub( args: ['lish', '--dry-run'], output: contains('Publishing test_pkg 1.0.0 to http://example.com'),
diff --git a/test/oauth2/logout_test.dart b/test/oauth2/logout_test.dart index a12a652..fb95c73 100644 --- a/test/oauth2/logout_test.dart +++ b/test/oauth2/logout_test.dart
@@ -16,8 +16,7 @@ expiration: DateTime.now().add(Duration(hours: 1))) .create(); - await runPub( - args: ['logout'], output: contains('Logging out of pub.dartlang.org.')); + await runPub(args: ['logout'], output: contains('Logging out of pub.dev.')); await d.dir(configPath, [d.nothing('pub-credentials.json')]).validate(); }); @@ -48,7 +47,7 @@ args: ['logout'], output: allOf( [ - contains('Logging out of pub.dartlang.org.'), + contains('Logging out of pub.dev.'), contains('Also deleting legacy credentials at ') ], ),
diff --git a/test/oauth2/with_an_expired_credentials_without_a_refresh_token_authenticates_again_test.dart b/test/oauth2/with_an_expired_credentials_without_a_refresh_token_authenticates_again_test.dart index 803c529..fb63cde 100644 --- a/test/oauth2/with_an_expired_credentials_without_a_refresh_token_authenticates_again_test.dart +++ b/test/oauth2/with_an_expired_credentials_without_a_refresh_token_authenticates_again_test.dart
@@ -13,9 +13,9 @@ test( 'with an expired credentials.json without a refresh token, ' 'authenticates again and saves credentials.json', () async { + await servePackages(); await d.validPackage.create(); - await servePackages(); await d .credentialsFile(globalServer, 'access token', expiration: DateTime.now().subtract(Duration(hours: 1)))
diff --git a/test/oauth2/with_no_credentials_authenticates_and_saves_credentials_test.dart b/test/oauth2/with_no_credentials_authenticates_and_saves_credentials_test.dart index 961a465..2fdb7a4 100644 --- a/test/oauth2/with_no_credentials_authenticates_and_saves_credentials_test.dart +++ b/test/oauth2/with_no_credentials_authenticates_and_saves_credentials_test.dart
@@ -14,7 +14,6 @@ 'with no credentials.json, authenticates and saves ' 'credentials.json', () async { await d.validPackage.create(); - await servePackages(); var pub = await startPublish(globalServer); await confirmPublish(pub);
diff --git a/test/package_server.dart b/test/package_server.dart index 6aaea68..b7587e7 100644 --- a/test/package_server.dart +++ b/test/package_server.dart
@@ -5,8 +5,11 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; +import 'dart:typed_data'; import 'package:path/path.dart' as p; +import 'package:pub/src/crc32c.dart'; +import 'package:pub/src/source/hosted.dart'; import 'package:pub/src/third_party/tar/tar.dart'; import 'package:pub_semver/pub_semver.dart'; import 'package:shelf/shelf.dart' as shelf; @@ -18,8 +21,8 @@ import 'test_pub.dart'; class PackageServer { - /// The inner [DescriptorServer] that this uses to serve its descriptors. - final shelf.Server _inner; + /// The inner [IOServer] that this uses to serve its descriptors. + final shelf_io.IOServer _inner; /// Handlers of requests. Last matching handler will be used. final List<_PatternAndHandler> _handlers = []; @@ -27,6 +30,16 @@ // A list of all the requests recieved up till now. final List<String> requestedPaths = <String>[]; + /// Whether the [IOServer] should compress the content, if possible. + /// The default value is `false` (compression disabled). + /// See [HttpServer.autoCompress] for details. + bool get autoCompress => _inner.server.autoCompress; + set autoCompress(bool shouldAutoCompress) => + _inner.server.autoCompress = shouldAutoCompress; + + // Setting this to false will disable automatic calculation of checksums. + bool serveChecksums = true; + PackageServer._(this._inner) { _inner.mount((request) { final path = request.url.path; @@ -63,26 +76,30 @@ if (package == null) { return shelf.Response.notFound('No package named $name'); } - return shelf.Response.ok(jsonEncode({ - 'name': name, - 'uploaders': ['nweiz@google.com'], - 'versions': package.versions.values - .map((version) => packageVersionApiMap( - server._inner.url.toString(), - version.pubspec, - retracted: version.isRetracted, - )) - .toList(), - if (package.isDiscontinued) 'isDiscontinued': true, - if (package.discontinuedReplacementText != null) - 'replacedBy': package.discontinuedReplacementText, - })); + return shelf.Response.ok( + jsonEncode({ + 'name': name, + 'uploaders': ['nweiz@google.com'], + 'versions': package.versions.values + .map((version) => packageVersionApiMap( + server._inner.url.toString(), + version.pubspec, + retracted: version.isRetracted, + )) + .toList(), + if (package.isDiscontinued) 'isDiscontinued': true, + if (package.discontinuedReplacementText != null) + 'replacedBy': package.discontinuedReplacementText, + }), + headers: { + HttpHeaders.contentTypeHeader: 'application/vnd.pub.v2+json' + }); }, ); server.handle( _downloadPattern, - (shelf.Request request) { + (shelf.Request request) async { final parts = request.url.pathSegments; assert(parts[0] == 'packages'); final name = parts[1]; @@ -98,7 +115,21 @@ for (final packageVersion in package.versions.values) { if (packageVersion.version == version) { - return shelf.Response.ok(packageVersion.contents()); + final headers = packageVersion.headers ?? {}; + headers[HttpHeaders.contentTypeHeader] ??= [ + 'application/octet-stream' + ]; + + // This gate enables tests to validate the CRC32C parser by + // passing in arbitrary values for the checksum header. + if (server.serveChecksums && + !headers.containsKey(checksumHeaderName)) { + headers[checksumHeaderName] = composeChecksumHeader( + crc32c: await packageVersion.computeArchiveCrc32c()); + } + + return shelf.Response.ok(packageVersion.contents(), + headers: headers); } } return shelf.Response.notFound('No version $version of $name'); @@ -178,7 +209,8 @@ void serve(String name, String version, {Map<String, dynamic>? deps, Map<String, dynamic>? pubspec, - List<d.Descriptor>? contents}) { + List<d.Descriptor>? contents, + Map<String, List<String>>? headers}) { var pubspecFields = <String, dynamic>{'name': name, 'version': version}; if (pubspec != null) pubspecFields.addAll(pubspec); if (deps != null) pubspecFields['dependencies'] = deps; @@ -189,6 +221,7 @@ var package = _packages.putIfAbsent(name, _ServedPackage.new); package.versions[version] = _ServedPackageVersion( pubspecFields, + headers: headers, contents: () { final entries = <TarEntry>[]; @@ -243,6 +276,37 @@ void retractPackageVersion(String name, String version) { _packages[name]!.versions[version]!.isRetracted = true; } + + Future<String?> peekArchiveChecksumHeader(String name, String version) async { + final v = _packages[name]!.versions[version]!; + + // If the test configured an overriding header value. + var checksumHeader = v.headers?[checksumHeaderName]; + + // Otherwise, compute from package contents. + if (serveChecksums) { + checksumHeader ??= + composeChecksumHeader(crc32c: await v.computeArchiveCrc32c()); + } + + return checksumHeader?.join(','); + } + + static List<String> composeChecksumHeader( + {int? crc32c, String? md5 = '5f4dcc3b5aa765d61d8327deb882cf99'}) { + List<String> header = []; + + if (crc32c != null) { + final bytes = Uint8List(4)..buffer.asByteData().setUint32(0, crc32c); + header.add('crc32c=${base64.encode(bytes)}'); + } + + if (md5 != null) { + header.add('md5=${base64.encode(utf8.encode(md5))}'); + } + + return header; + } } class _ServedPackage { @@ -255,11 +319,16 @@ class _ServedPackageVersion { final Map pubspec; final Stream<List<int>> Function() contents; + final Map<String, List<String>>? headers; bool isRetracted = false; Version get version => Version.parse(pubspec['version']); - _ServedPackageVersion(this.pubspec, {required this.contents}); + _ServedPackageVersion(this.pubspec, {required this.contents, this.headers}); + + Future<int> computeArchiveCrc32c() async { + return await Crc32c.computeByConsumingStream(contents()); + } } class _PatternAndHandler {
diff --git a/test/pubspec_overrides_test.dart b/test/pubspec_overrides_test.dart index 514d52f..9f48c23 100644 --- a/test/pubspec_overrides_test.dart +++ b/test/pubspec_overrides_test.dart
@@ -2,7 +2,6 @@ // 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:pub/src/exit_codes.dart' as exit_codes; import 'package:test/test.dart'; import 'descriptor.dart' as d; @@ -47,18 +46,4 @@ ]).validate(); }); }); - - test('is ignored by publish command', () async { - await d.validPackage.create(); - await d.dir(appPath, [ - d.pubspecOverrides({ - 'dependency_overrides': {'lib': '1.0.0'} - }), - ]).create(); - - await runPub( - args: ['lish', '--dry-run'], - exitCode: exit_codes.SUCCESS, - ); - }); }
diff --git a/test/pubspec_test.dart b/test/pubspec_test.dart index 1e9c8bd..2ccb30f 100644 --- a/test/pubspec_test.dart +++ b/test/pubspec_test.dart
@@ -622,7 +622,7 @@ test('throws on non-absolute URLs', () { expectPubspecException( - 'publish_to: pub.dartlang.org', (pubspec) => pubspec.publishTo); + 'publish_to: pub.dev', (pubspec) => pubspec.publishTo); }); });
diff --git a/test/reformat_ranges_test.dart b/test/reformat_ranges_test.dart index 9a88df6..d2c91ca 100644 --- a/test/reformat_ranges_test.dart +++ b/test/reformat_ranges_test.dart
@@ -11,7 +11,7 @@ void main() { final description = ResolvedHostedDescription( - HostedDescription('foo', 'https://pub.dartlang.org'), + HostedDescription('foo', 'https://pub.dev'), ); test('reformatMax when max has a build identifier', () { expect(
diff --git a/test/test_pub.dart b/test/test_pub.dart index 99f5655..be89bda 100644 --- a/test/test_pub.dart +++ b/test/test_pub.dart
@@ -74,7 +74,7 @@ orElse: () => null) as Map<String, dynamic>; /// The suffix appended to a built snapshot. -final versionSuffix = testVersion; +const versionSuffix = testVersion; /// Enum identifying a pub command that can be run with a well-defined success /// output. @@ -182,6 +182,7 @@ Iterable<String>? args, Object? output, Object? error, + Object? silent, Object? warning, int? exitCode, Map<String, String?>? environment, @@ -193,6 +194,7 @@ args: args, output: output, error: error, + silent: silent, warning: warning, exitCode: exitCode, environment: environment, @@ -391,7 +393,7 @@ // TODO(rnystrom): This is overly specific and inflexible regarding different // test packages. Should validate this a little more loosely. await expectLater( - pub.stdout, emits(startsWith('Publishing test_pkg 1.0.0 to '))); + pub.stdout, emitsThrough(startsWith('Publishing test_pkg 1.0.0 to '))); await expectLater( pub.stdout, emitsThrough(matches( @@ -407,7 +409,7 @@ /// sandbox. String _pathInSandbox(String relPath) => p.join(d.sandbox, relPath); -String testVersion = '0.1.2+3'; +const String testVersion = '0.1.2+3'; /// Gets the environment variables used to run pub in a test context. Map<String, String> getPubTestEnvironment([String? tokenEndpoint]) => { @@ -681,7 +683,7 @@ var package = <String, Object>{ 'name': name, 'version': version, - 'homepage': 'http://pub.dartlang.org', + 'homepage': 'http://pub.dev', 'description': 'A package, I guess.' }; @@ -691,7 +693,7 @@ return package; } -/// Returns a Map in the format used by the pub.dartlang.org API to represent a +/// Returns a Map in the format used by the pub.dev API to represent a /// package version. /// /// [pubspec] is the parsed pubspec of the package version. If [full] is true, @@ -946,8 +948,8 @@ PackageServer get globalServer => _globalServer!; PackageServer? _globalServer; -/// Creates an HTTP server that replicates the structure of pub.dartlang.org and -/// makes it the current [globalServer]. +/// Creates an HTTP server that replicates the structure of pub.dev and makes it +/// the current [globalServer]. Future<PackageServer> servePackages() async { final server = await startPackageServer(); _globalServer = server;
diff --git a/test/testdata/goldens/ascii_tree_test/tree.fromFiles a complex example.ans b/test/testdata/goldens/ascii_tree_test/tree.fromFiles a complex example.ans new file mode 100644 index 0000000..7b913f0 --- /dev/null +++ b/test/testdata/goldens/ascii_tree_test/tree.fromFiles a complex example.ans
@@ -0,0 +1,26 @@ +# GENERATED BY: test/ascii_tree_test.dart + +[38;5;245m├── [0mREADME.md [38;5;245m(<1 KB)[0m +[38;5;245m├── [0mTODO [38;5;245m(<1 KB)[0m +[38;5;245m├── [0mexample +[38;5;245m│ [0m[38;5;245m├── [0mconsole_example.dart [38;5;245m(<1 KB)[0m +[38;5;245m│ [0m[38;5;245m├── [0mmain.dart [38;5;245m(1 KB)[0m +[38;5;245m│ [0m[38;5;245m└── [0mweb copy +[38;5;245m│ [0m [38;5;245m└── [0mweb_example.dart [38;5;245m(1 KB)[0m +[38;5;245m├── [0mlib +[38;5;245m│ [0m[38;5;245m└── [0mpath.dart [38;5;245m(<1 KB)[0m +[38;5;245m├── [0mpubspec.yaml [38;5;245m(<1 KB)[0m +[38;5;245m└── [0mtest + [38;5;245m├── [0mabsolute_test.dart [38;5;245m(<1 KB)[0m + [38;5;245m├── [0mall_test.dart [38;5;245m(<1 KB)[0m + [38;5;245m├── [0mbasename_test.dart [33m(1 MB)[39m + [38;5;245m├── [0mdirname_test.dart [33m(1 MB)[39m + [38;5;245m├── [0mextension_test.dart [38;5;245m(2 KB)[0m + [38;5;245m├── [0mis_absolute_test.dart [38;5;245m(2 KB)[0m + [38;5;245m├── [0mis_relative_test.dart [33m(25 MB)[39m + [38;5;245m├── [0mjoin_test.dart [38;5;245m(<1 KB)[0m + [38;5;245m├── [0mnormalize_test.dart [38;5;245m(1023 KB)[0m + [38;5;245m├── [0mpath_posix_test.dart [38;5;245m(<1 KB)[0m + [38;5;245m├── [0mpath_windows_test.dart [38;5;245m(<1 KB)[0m + [38;5;245m├── [0mrelative_test.dart [38;5;245m(<1 KB)[0m + [38;5;245m└── [0msplit_test.dart [38;5;245m(<1 KB)[0m
diff --git a/test/testdata/goldens/ascii_tree_test/tree.fromMap a complex example.ans b/test/testdata/goldens/ascii_tree_test/tree.fromMap a complex example.ans new file mode 100644 index 0000000..0089c11 --- /dev/null +++ b/test/testdata/goldens/ascii_tree_test/tree.fromMap a complex example.ans
@@ -0,0 +1,24 @@ +# GENERATED BY: test/ascii_tree_test.dart + +[38;5;245m├── [0m.gitignore +[38;5;245m├── [0mREADME.md +[38;5;245m├── [0mTODO +[38;5;245m├── [0mexample +[38;5;245m│ [0m[38;5;245m├── [0mconsole_example.dart +[38;5;245m│ [0m[38;5;245m├── [0mmain.dart +[38;5;245m│ [0m[38;5;245m└── [0mweb copy +[38;5;245m│ [0m [38;5;245m└── [0mweb_example.dart +[38;5;245m├── [0mlib +[38;5;245m│ [0m[38;5;245m└── [0mpath.dart +[38;5;245m├── [0mpubspec.yaml +[38;5;245m└── [0mtest + [38;5;245m├── [0mabsolute_test.dart + [38;5;245m├── [0mbasename_test.dart + [38;5;245m├── [0mdirname_test.dart + [38;5;245m├── [0mextension_test.dart + [38;5;245m├── [0mis_absolute_test.dart + [38;5;245m├── [0mis_relative_test.dart + [38;5;245m├── [0mjoin_test.dart + [38;5;245m├── [0mnormalize_test.dart + [38;5;245m├── [0mrelative_test.dart + [38;5;245m└── [0msplit_test.dart
diff --git a/test/testdata/goldens/dependency_services/dependency_services_test/Can update a git package.txt b/test/testdata/goldens/dependency_services/dependency_services_test/Can update a git package.txt index 1f90e4c..35de0ad 100644 --- a/test/testdata/goldens/dependency_services/dependency_services_test/Can update a git package.txt +++ b/test/testdata/goldens/dependency_services/dependency_services_test/Can update a git package.txt
@@ -11,7 +11,7 @@ description: path: "." ref: HEAD - resolved-ref: "92cdd4ac724a6da0db2a69ac149820aa220e8518" + resolved-ref: "5373af3230028f3e31e9ee39e326228db83710cb" url: "../bar.git" source: git version: "1.0.0" @@ -20,7 +20,7 @@ description: path: "." ref: HEAD - resolved-ref: "1ae220cc484311a7a1e2e31d1ccc6ea995acb6ba" + resolved-ref: "428f5dd6e627cb2384343eca6aed099f4f7d183d" url: "../foo.git" source: git version: "1.0.0" @@ -34,7 +34,7 @@ "dependencies": [ { "name": "bar", - "version": "92cdd4ac724a6da0db2a69ac149820aa220e8518", + "version": "5373af3230028f3e31e9ee39e326228db83710cb", "kind": "direct", "constraint": "^1.0.0", "source": { @@ -42,14 +42,14 @@ "description": { "url": "../bar.git", "ref": "HEAD", - "resolved-ref": "92cdd4ac724a6da0db2a69ac149820aa220e8518", + "resolved-ref": "5373af3230028f3e31e9ee39e326228db83710cb", "path": "." } } }, { "name": "foo", - "version": "1ae220cc484311a7a1e2e31d1ccc6ea995acb6ba", + "version": "428f5dd6e627cb2384343eca6aed099f4f7d183d", "kind": "direct", "constraint": "any", "source": { @@ -57,7 +57,7 @@ "description": { "url": "../foo.git", "ref": "HEAD", - "resolved-ref": "1ae220cc484311a7a1e2e31d1ccc6ea995acb6ba", + "resolved-ref": "428f5dd6e627cb2384343eca6aed099f4f7d183d", "path": "." } } @@ -73,45 +73,45 @@ "dependencies": [ { "name": "bar", - "version": "92cdd4ac724a6da0db2a69ac149820aa220e8518", + "version": "5373af3230028f3e31e9ee39e326228db83710cb", "kind": "direct", "source": { "type": "git", "description": { "url": "../bar.git", "ref": "HEAD", - "resolved-ref": "92cdd4ac724a6da0db2a69ac149820aa220e8518", + "resolved-ref": "5373af3230028f3e31e9ee39e326228db83710cb", "path": "." } }, - "latest": "cf473c33e39eeec4615bc2cde8bf68cbb10ad02c", + "latest": "40d0ffbfc376f2a796b0bbb928636c242285a01c", "constraint": "^1.0.0", "compatible": [], "singleBreaking": [ { "name": "bar", - "version": "cf473c33e39eeec4615bc2cde8bf68cbb10ad02c", + "version": "40d0ffbfc376f2a796b0bbb928636c242285a01c", "kind": "direct", "source": { "type": "git", "description": { "url": "../bar.git", "ref": "HEAD", - "resolved-ref": "cf473c33e39eeec4615bc2cde8bf68cbb10ad02c", + "resolved-ref": "40d0ffbfc376f2a796b0bbb928636c242285a01c", "path": "." } }, "constraintBumped": "^2.0.0", "constraintWidened": ">=1.0.0 <3.0.0", "constraintBumpedIfNeeded": "^2.0.0", - "previousVersion": "92cdd4ac724a6da0db2a69ac149820aa220e8518", + "previousVersion": "5373af3230028f3e31e9ee39e326228db83710cb", "previousConstraint": "^1.0.0", "previousSource": { "type": "git", "description": { "url": "../bar.git", "ref": "HEAD", - "resolved-ref": "92cdd4ac724a6da0db2a69ac149820aa220e8518", + "resolved-ref": "5373af3230028f3e31e9ee39e326228db83710cb", "path": "." } } @@ -120,28 +120,28 @@ "multiBreaking": [ { "name": "bar", - "version": "cf473c33e39eeec4615bc2cde8bf68cbb10ad02c", + "version": "40d0ffbfc376f2a796b0bbb928636c242285a01c", "kind": "direct", "source": { "type": "git", "description": { "url": "../bar.git", "ref": "HEAD", - "resolved-ref": "cf473c33e39eeec4615bc2cde8bf68cbb10ad02c", + "resolved-ref": "40d0ffbfc376f2a796b0bbb928636c242285a01c", "path": "." } }, "constraintBumped": "^2.0.0", "constraintWidened": ">=1.0.0 <3.0.0", "constraintBumpedIfNeeded": "^2.0.0", - "previousVersion": "92cdd4ac724a6da0db2a69ac149820aa220e8518", + "previousVersion": "5373af3230028f3e31e9ee39e326228db83710cb", "previousConstraint": "^1.0.0", "previousSource": { "type": "git", "description": { "url": "../bar.git", "ref": "HEAD", - "resolved-ref": "92cdd4ac724a6da0db2a69ac149820aa220e8518", + "resolved-ref": "5373af3230028f3e31e9ee39e326228db83710cb", "path": "." } } @@ -150,46 +150,46 @@ }, { "name": "foo", - "version": "1ae220cc484311a7a1e2e31d1ccc6ea995acb6ba", + "version": "428f5dd6e627cb2384343eca6aed099f4f7d183d", "kind": "direct", "source": { "type": "git", "description": { "url": "../foo.git", "ref": "HEAD", - "resolved-ref": "1ae220cc484311a7a1e2e31d1ccc6ea995acb6ba", + "resolved-ref": "428f5dd6e627cb2384343eca6aed099f4f7d183d", "path": "." } }, - "latest": "ad2d659389d4475f6c3a34282e2204160b753fd9", + "latest": "40c4eb4fb235961264ee9cdbdfa54ef7f6aa5199", "constraint": "any", "compatible": [], "singleBreaking": [], "multiBreaking": [ { "name": "foo", - "version": "ad2d659389d4475f6c3a34282e2204160b753fd9", + "version": "40c4eb4fb235961264ee9cdbdfa54ef7f6aa5199", "kind": "direct", "source": { "type": "git", "description": { "url": "../foo.git", "ref": "HEAD", - "resolved-ref": "ad2d659389d4475f6c3a34282e2204160b753fd9", + "resolved-ref": "40c4eb4fb235961264ee9cdbdfa54ef7f6aa5199", "path": "." } }, "constraintBumped": "^2.0.0", "constraintWidened": "any", "constraintBumpedIfNeeded": "any", - "previousVersion": "1ae220cc484311a7a1e2e31d1ccc6ea995acb6ba", + "previousVersion": "428f5dd6e627cb2384343eca6aed099f4f7d183d", "previousConstraint": "any", "previousSource": { "type": "git", "description": { "url": "../foo.git", "ref": "HEAD", - "resolved-ref": "1ae220cc484311a7a1e2e31d1ccc6ea995acb6ba", + "resolved-ref": "428f5dd6e627cb2384343eca6aed099f4f7d183d", "path": "." } } @@ -202,7 +202,7 @@ -------------------------------- END OF OUTPUT --------------------------------- ## Section apply -$ echo '{"dependencyChanges":[{"name":"foo","version":"ad2d659389d4475f6c3a34282e2204160b753fd9"}]}' | dependency_services apply +$ echo '{"dependencyChanges":[{"name":"foo","version":"40c4eb4fb235961264ee9cdbdfa54ef7f6aa5199"}]}' | dependency_services apply {"dependencies":[]} -------------------------------- END OF OUTPUT --------------------------------- @@ -218,7 +218,7 @@ description: path: "." ref: HEAD - resolved-ref: "92cdd4ac724a6da0db2a69ac149820aa220e8518" + resolved-ref: "5373af3230028f3e31e9ee39e326228db83710cb" url: "../bar.git" source: git version: "1.0.0" @@ -227,7 +227,7 @@ description: path: "." ref: HEAD - resolved-ref: ad2d659389d4475f6c3a34282e2204160b753fd9 + resolved-ref: "40c4eb4fb235961264ee9cdbdfa54ef7f6aa5199" url: "../foo.git" source: git version: "2.0.0"
diff --git a/test/testdata/goldens/dependency_services/dependency_services_test/No pubspec.lock.txt b/test/testdata/goldens/dependency_services/dependency_services_test/No pubspec.lock.txt index ca80a34..e5ab484 100644 --- a/test/testdata/goldens/dependency_services/dependency_services_test/No pubspec.lock.txt +++ b/test/testdata/goldens/dependency_services/dependency_services_test/No pubspec.lock.txt
@@ -12,7 +12,7 @@ "dependencies": [ { "name": "bar", - "version": "92cdd4ac724a6da0db2a69ac149820aa220e8518", + "version": "5373af3230028f3e31e9ee39e326228db83710cb", "kind": "direct", "constraint": "any", "source": { @@ -20,7 +20,7 @@ "description": { "url": "../bar.git", "ref": "HEAD", - "resolved-ref": "92cdd4ac724a6da0db2a69ac149820aa220e8518", + "resolved-ref": "5373af3230028f3e31e9ee39e326228db83710cb", "path": "." } } @@ -62,18 +62,18 @@ "dependencies": [ { "name": "bar", - "version": "92cdd4ac724a6da0db2a69ac149820aa220e8518", + "version": "5373af3230028f3e31e9ee39e326228db83710cb", "kind": "direct", "source": { "type": "git", "description": { "url": "../bar.git", "ref": "HEAD", - "resolved-ref": "92cdd4ac724a6da0db2a69ac149820aa220e8518", + "resolved-ref": "5373af3230028f3e31e9ee39e326228db83710cb", "path": "." } }, - "latest": "92cdd4ac724a6da0db2a69ac149820aa220e8518", + "latest": "5373af3230028f3e31e9ee39e326228db83710cb", "constraint": "any", "compatible": [], "singleBreaking": [],
diff --git a/test/testdata/goldens/deps/executables_test/applies formatting before printing executables.txt b/test/testdata/goldens/deps/executables_test/applies formatting before printing executables.txt index d8448df..4c8b2b9 100644 --- a/test/testdata/goldens/deps/executables_test/applies formatting before printing executables.txt +++ b/test/testdata/goldens/deps/executables_test/applies formatting before printing executables.txt
@@ -2,20 +2,20 @@ ## Section 0 $ tree -|-- bar -| |-- bin -| | '-- qux.dart -| '-- pubspec.yaml -|-- foo -| |-- bin -| | |-- baz.dart -| | '-- foo.dart -| '-- pubspec.yaml -'-- myapp - |-- bin - | '-- myapp.dart - |-- pubspec.lock - '-- pubspec.yaml +├── bar +│ ├── bin +│ │ └── qux.dart +│ └── pubspec.yaml +├── foo +│ ├── bin +│ │ ├── baz.dart +│ │ └── foo.dart +│ └── pubspec.yaml +└── myapp + ├── bin + │ └── myapp.dart + ├── pubspec.lock + └── pubspec.yaml -------------------------------- END OF OUTPUT ---------------------------------
diff --git a/test/testdata/goldens/deps/executables_test/dev dependencies.txt b/test/testdata/goldens/deps/executables_test/dev dependencies.txt index 2d72b44..8493d62 100644 --- a/test/testdata/goldens/deps/executables_test/dev dependencies.txt +++ b/test/testdata/goldens/deps/executables_test/dev dependencies.txt
@@ -2,13 +2,13 @@ ## Section 0 $ tree -|-- foo -| |-- bin -| | '-- bar.dart -| '-- pubspec.yaml -'-- myapp - |-- pubspec.lock - '-- pubspec.yaml +├── foo +│ ├── bin +│ │ └── bar.dart +│ └── pubspec.yaml +└── myapp + ├── pubspec.lock + └── pubspec.yaml -------------------------------- END OF OUTPUT ---------------------------------
diff --git a/test/testdata/goldens/deps/executables_test/lists Dart executables, without entrypoints.txt b/test/testdata/goldens/deps/executables_test/lists Dart executables, without entrypoints.txt index a12d363..3b46fb0 100644 --- a/test/testdata/goldens/deps/executables_test/lists Dart executables, without entrypoints.txt +++ b/test/testdata/goldens/deps/executables_test/lists Dart executables, without entrypoints.txt
@@ -2,12 +2,12 @@ ## Section 0 $ tree -'-- myapp - |-- bin - | |-- bar.dart - | '-- foo.dart - |-- pubspec.lock - '-- pubspec.yaml +└── myapp + ├── bin + │ ├── bar.dart + │ └── foo.dart + ├── pubspec.lock + └── pubspec.yaml -------------------------------- END OF OUTPUT ---------------------------------
diff --git a/test/testdata/goldens/deps/executables_test/lists executables from a dependency.txt b/test/testdata/goldens/deps/executables_test/lists executables from a dependency.txt index 75a1b33..b78f99f 100644 --- a/test/testdata/goldens/deps/executables_test/lists executables from a dependency.txt +++ b/test/testdata/goldens/deps/executables_test/lists executables from a dependency.txt
@@ -2,13 +2,13 @@ ## Section 0 $ tree -|-- foo -| |-- bin -| | '-- bar.dart -| '-- pubspec.yaml -'-- myapp - |-- pubspec.lock - '-- pubspec.yaml +├── foo +│ ├── bin +│ │ └── bar.dart +│ └── pubspec.yaml +└── myapp + ├── pubspec.lock + └── pubspec.yaml -------------------------------- END OF OUTPUT ---------------------------------
diff --git a/test/testdata/goldens/deps/executables_test/lists executables only from immediate dependencies.txt b/test/testdata/goldens/deps/executables_test/lists executables only from immediate dependencies.txt index de70643..5d4cf00 100644 --- a/test/testdata/goldens/deps/executables_test/lists executables only from immediate dependencies.txt +++ b/test/testdata/goldens/deps/executables_test/lists executables only from immediate dependencies.txt
@@ -2,17 +2,17 @@ ## Section 0 $ tree -|-- baz -| |-- bin -| | '-- qux.dart -| '-- pubspec.yaml -|-- foo -| |-- bin -| | '-- bar.dart -| '-- pubspec.yaml -'-- myapp - |-- pubspec.lock - '-- pubspec.yaml +├── baz +│ ├── bin +│ │ └── qux.dart +│ └── pubspec.yaml +├── foo +│ ├── bin +│ │ └── bar.dart +│ └── pubspec.yaml +└── myapp + ├── pubspec.lock + └── pubspec.yaml -------------------------------- END OF OUTPUT ---------------------------------
diff --git a/test/testdata/goldens/deps/executables_test/overriden dependencies executables.txt b/test/testdata/goldens/deps/executables_test/overriden dependencies executables.txt index 910210e..edb3098 100644 --- a/test/testdata/goldens/deps/executables_test/overriden dependencies executables.txt +++ b/test/testdata/goldens/deps/executables_test/overriden dependencies executables.txt
@@ -2,18 +2,18 @@ ## Section 0 $ tree -|-- foo-1.0 -| |-- bin -| | '-- bar.dart -| '-- pubspec.yaml -|-- foo-2.0 -| |-- bin -| | |-- bar.dart -| | '-- baz.dart -| '-- pubspec.yaml -'-- myapp - |-- pubspec.lock - '-- pubspec.yaml +├── foo-1.0 +│ ├── bin +│ │ └── bar.dart +│ └── pubspec.yaml +├── foo-2.0 +│ ├── bin +│ │ ├── bar.dart +│ │ └── baz.dart +│ └── pubspec.yaml +└── myapp + ├── pubspec.lock + └── pubspec.yaml -------------------------------- END OF OUTPUT ---------------------------------
diff --git a/test/testdata/goldens/deps/executables_test/skips executables in sub directories.txt b/test/testdata/goldens/deps/executables_test/skips executables in sub directories.txt index 289f7c8..514e5ef 100644 --- a/test/testdata/goldens/deps/executables_test/skips executables in sub directories.txt +++ b/test/testdata/goldens/deps/executables_test/skips executables in sub directories.txt
@@ -2,13 +2,13 @@ ## Section 0 $ tree -'-- myapp - |-- bin - | |-- foo.dart - | '-- sub - | '-- bar.dart - |-- pubspec.lock - '-- pubspec.yaml +└── myapp + ├── bin + │ ├── foo.dart + │ └── sub + │ └── bar.dart + ├── pubspec.lock + └── pubspec.yaml -------------------------------- END OF OUTPUT ---------------------------------
diff --git a/test/testdata/goldens/deps/executables_test/skips non-Dart executables.txt b/test/testdata/goldens/deps/executables_test/skips non-Dart executables.txt index d4a2db3..8cf6076 100644 --- a/test/testdata/goldens/deps/executables_test/skips non-Dart executables.txt +++ b/test/testdata/goldens/deps/executables_test/skips non-Dart executables.txt
@@ -2,12 +2,12 @@ ## Section 0 $ tree -'-- myapp - |-- bin - | |-- bar.sh - | '-- foo.py - |-- pubspec.lock - '-- pubspec.yaml +└── myapp + ├── bin + │ ├── bar.sh + │ └── foo.py + ├── pubspec.lock + └── pubspec.yaml -------------------------------- END OF OUTPUT ---------------------------------
diff --git a/test/testdata/goldens/directory_option_test/commands taking a --directory~-C parameter work.txt b/test/testdata/goldens/directory_option_test/commands taking a --directory~-C parameter work.txt index 9585341..577a0a5 100644 --- a/test/testdata/goldens/directory_option_test/commands taking a --directory~-C parameter work.txt +++ b/test/testdata/goldens/directory_option_test/commands taking a --directory~-C parameter work.txt
@@ -53,7 +53,7 @@ Resolving dependencies in myapp/example2... [STDERR] Error on line 1, column 9 of myapp/pubspec.yaml: "name" field doesn't match expected name "myapp". [STDERR] ╷ -[STDERR] 1 │ {"name":"test_pkg","version":"1.0.0","homepage":"http://pub.dartlang.org","description":"A package, I guess.","environment":{"sdk":">=1.8.0 <=2.0.0"}, dependencies: { foo: ^1.0.0}} +[STDERR] 1 │ {"name":"test_pkg","version":"1.0.0","homepage":"http://pub.dev","description":"A package, I guess.","environment":{"sdk":">=0.1.2 <=0.2.0"}, dependencies: { foo: ^1.0.0}} [STDERR] │ ^^^^^^^^^^ [STDERR] ╵ [EXIT CODE] 65 @@ -93,22 +93,30 @@ ## Section 11 $ pub publish -C myapp --dry-run +Resolving dependencies in myapp... +Got dependencies in myapp! Publishing test_pkg 1.0.0 to http://localhost:$PORT: -|-- CHANGELOG.md -|-- LICENSE -|-- README.md -|-- bin -| '-- app.dart -|-- example -| '-- pubspec.yaml -|-- example2 -| '-- pubspec.yaml -|-- lib -| '-- test_pkg.dart -'-- pubspec.yaml -The server may enforce additional checks. -[STDERR] -[STDERR] Package has 0 warnings. +├── CHANGELOG.md (<1 KB) +├── LICENSE (<1 KB) +├── README.md (<1 KB) +├── bin +│ └── app.dart (<1 KB) +├── example +│ └── pubspec.yaml (<1 KB) +├── example2 +│ └── pubspec.yaml (<1 KB) +├── lib +│ └── test_pkg.dart (<1 KB) +└── pubspec.yaml (<1 KB) +[STDERR] Package validation found the following error: +[STDERR] * Older versions of pub don't support ^ version constraints. +[STDERR] Make sure your SDK constraint excludes old versions: +[STDERR] +[STDERR] environment: +[STDERR] sdk: ">=1.8.0 <2.0.0" +[STDERR] Sorry, your package is missing a requirement and can't be published yet. +[STDERR] For more information, see: https://dart.dev/tools/pub/cmd/pub-lish. +[EXIT CODE] 65 -------------------------------- END OF OUTPUT --------------------------------- @@ -124,7 +132,7 @@ ## Section 13 $ pub deps -C myapp -Dart SDK 1.12.0 +Dart SDK 0.1.2+3 test_pkg 1.0.0 -'-- foo 1.0.0 +└── foo 1.0.0
diff --git a/test/testdata/goldens/embedding/embedding_test/--help.txt b/test/testdata/goldens/embedding/embedding_test/--help.txt index ea8fd3f..13647a0 100644 --- a/test/testdata/goldens/embedding/embedding_test/--help.txt +++ b/test/testdata/goldens/embedding/embedding_test/--help.txt
@@ -23,7 +23,7 @@ login Log into pub.dev. logout Log out of pub.dev. outdated Analyze your dependencies to find which ones can be upgraded. - publish Publish the current package to pub.dartlang.org. + publish Publish the current package to pub.dev. remove Removes a dependency from the current package. token Manage authentication tokens for hosted pub repositories. upgrade Upgrade the current package's dependencies to latest versions.
diff --git a/test/testdata/goldens/embedding/embedding_test/logfile is written with --verbose and on unexpected exceptions.txt b/test/testdata/goldens/embedding/embedding_test/logfile is written with --verbose and on unexpected exceptions.txt index 14ea76f..dc09988 100644 --- a/test/testdata/goldens/embedding/embedding_test/logfile is written with --verbose and on unexpected exceptions.txt +++ b/test/testdata/goldens/embedding/embedding_test/logfile is written with --verbose and on unexpected exceptions.txt
@@ -27,7 +27,7 @@ [E] | date: $TIME [E] | content-length: 197 [E] | x-frame-options: SAMEORIGIN -[E] | content-type: text/plain; charset=utf-8 +[E] | content-type: application/vnd.pub.v2+json [E] | x-xss-protection: 1; mode=block [E] | x-content-type-options: nosniff [E] IO : Writing $N characters to text file $SANDBOX/cache/hosted/localhost%58$PORT/.cache/foo-versions.json. @@ -49,13 +49,15 @@ [E] IO : HTTP response 200 OK for GET http://localhost:$PORT/packages/foo/versions/1.0.0.tar.gz [E] | took: $TIME [E] | x-powered-by: Dart with package:shelf -[E] | transfer-encoding: chunked [E] | date: $TIME +[E] | transfer-encoding: chunked +[E] | x-goog-hash: $CHECKSUM_HEADER [E] | x-frame-options: SAMEORIGIN -[E] | content-type: text/plain; charset=utf-8 +[E] | content-type: application/octet-stream [E] | x-xss-protection: 1; mode=block [E] | x-content-type-options: nosniff [E] IO : Creating $FILE from stream +[E] FINE: Computed checksum $CRC32C for foo 1.0.0 with expected CRC32C of $CRC32C. [E] FINE: Created $FILE from stream [E] IO : Created temp directory $DIR [E] IO : Reading binary file $FILE. @@ -163,7 +165,7 @@ | date: $TIME | content-length: 197 | x-frame-options: SAMEORIGIN - | content-type: text/plain; charset=utf-8 + | content-type: application/vnd.pub.v2+json | x-xss-protection: 1; mode=block | x-content-type-options: nosniff IO : Writing $N characters to text file $SANDBOX/cache/hosted/localhost%58$PORT/.cache/foo-versions.json. @@ -187,13 +189,15 @@ IO : HTTP response 200 OK for GET http://localhost:$PORT/packages/foo/versions/1.0.0.tar.gz | took: $TIME | x-powered-by: Dart with package:shelf - | transfer-encoding: chunked | date: $TIME + | transfer-encoding: chunked + | x-goog-hash: $CHECKSUM_HEADER | x-frame-options: SAMEORIGIN - | content-type: text/plain; charset=utf-8 + | content-type: application/octet-stream | x-xss-protection: 1; mode=block | x-content-type-options: nosniff IO : Creating $FILE from stream +FINE: Computed checksum $CRC32C for foo 1.0.0 with expected CRC32C of $CRC32C. FINE: Created $FILE from stream IO : Created temp directory $DIR IO : Reading binary file $FILE.
diff --git a/test/testdata/goldens/help_test/pub publish --help.txt b/test/testdata/goldens/help_test/pub publish --help.txt index 2977db0..14e83e2 100644 --- a/test/testdata/goldens/help_test/pub publish --help.txt +++ b/test/testdata/goldens/help_test/pub publish --help.txt
@@ -2,7 +2,7 @@ ## Section 0 $ pub publish --help -Publish the current package to pub.dartlang.org. +Publish the current package to pub.dev. Usage: pub publish [options] -h, --help Print this usage information.
diff --git a/test/testdata/goldens/lish/many_files_test/displays all files.txt b/test/testdata/goldens/lish/many_files_test/displays all files.txt index 09a01da..7171f66 100644 --- a/test/testdata/goldens/lish/many_files_test/displays all files.txt +++ b/test/testdata/goldens/lish/many_files_test/displays all files.txt
@@ -1,32 +1,34 @@ # GENERATED BY: test/lish/many_files_test.dart +Resolving dependencies... +Got dependencies! Publishing test_pkg 1.0.0 to http://localhost:$PORT: -|-- CHANGELOG.md -|-- LICENSE -|-- README.md -|-- lib -| |-- file_0.dart -| |-- file_1.dart -| |-- file_10.dart -| |-- file_11.dart -| |-- file_12.dart -| |-- file_13.dart -| |-- file_14.dart -| |-- file_15.dart -| |-- file_16.dart -| |-- file_17.dart -| |-- file_18.dart -| |-- file_19.dart -| |-- file_2.dart -| |-- file_3.dart -| |-- file_4.dart -| |-- file_5.dart -| |-- file_6.dart -| |-- file_7.dart -| |-- file_8.dart -| |-- file_9.dart -| '-- test_pkg.dart -'-- pubspec.yaml +├── CHANGELOG.md (<1 KB) +├── LICENSE (<1 KB) +├── README.md (<1 KB) +├── lib +│ ├── file_0.dart (<1 KB) +│ ├── file_1.dart (<1 KB) +│ ├── file_10.dart (<1 KB) +│ ├── file_11.dart (<1 KB) +│ ├── file_12.dart (<1 KB) +│ ├── file_13.dart (<1 KB) +│ ├── file_14.dart (<1 KB) +│ ├── file_15.dart (<1 KB) +│ ├── file_16.dart (<1 KB) +│ ├── file_17.dart (<1 KB) +│ ├── file_18.dart (<1 KB) +│ ├── file_19.dart (<1 KB) +│ ├── file_2.dart (<1 KB) +│ ├── file_3.dart (<1 KB) +│ ├── file_4.dart (<1 KB) +│ ├── file_5.dart (<1 KB) +│ ├── file_6.dart (<1 KB) +│ ├── file_7.dart (<1 KB) +│ ├── file_8.dart (<1 KB) +│ ├── file_9.dart (<1 KB) +│ └── test_pkg.dart (<1 KB) +└── pubspec.yaml (<1 KB) Publishing is forever; packages cannot be unpublished. Policy details are available at https://pub.dev/policy
diff --git a/test/testdata/goldens/outdated/outdated_test/no lockfile.txt b/test/testdata/goldens/outdated/outdated_test/no lockfile.txt index 87a8997..70ce88f 100644 --- a/test/testdata/goldens/outdated/outdated_test/no lockfile.txt +++ b/test/testdata/goldens/outdated/outdated_test/no lockfile.txt
@@ -49,7 +49,7 @@ foo - 1.2.3 1.2.3 1.2.3 No pubspec.lock found. There are no Current versions. -Run `pub get` to create a pubspec.lock with versions matching your pubspec.yaml. +Run `dart pub get` to create a pubspec.lock with versions matching your pubspec.yaml. 1 dependency is constrained to a version that is older than a resolvable version. To update it, edit pubspec.yaml, or run `dart pub upgrade --major-versions`. @@ -68,7 +68,7 @@ foo - 1.2.3 1.2.3 1.2.3 No pubspec.lock found. There are no Current versions. -Run `pub get` to create a pubspec.lock with versions matching your pubspec.yaml. +Run `dart pub get` to create a pubspec.lock with versions matching your pubspec.yaml. 1 dependency is constrained to a version that is older than a resolvable version. To update it, edit pubspec.yaml, or run `dart pub upgrade --major-versions`. @@ -87,7 +87,7 @@ foo - 1.2.3 1.2.3 1.2.3 No pubspec.lock found. There are no Current versions. -Run `pub get` to create a pubspec.lock with versions matching your pubspec.yaml. +Run `dart pub get` to create a pubspec.lock with versions matching your pubspec.yaml. 1 dependency is constrained to a version that is older than a resolvable version. To update it, edit pubspec.yaml, or run `dart pub upgrade --major-versions`. @@ -106,7 +106,7 @@ foo - 1.2.3 1.2.3 1.2.3 No pubspec.lock found. There are no Current versions. -Run `pub get` to create a pubspec.lock with versions matching your pubspec.yaml. +Run `dart pub get` to create a pubspec.lock with versions matching your pubspec.yaml. 1 dependency is constrained to a version that is older than a resolvable version. To update it, edit pubspec.yaml, or run `dart pub upgrade --major-versions`. @@ -125,7 +125,7 @@ foo - 1.2.3 1.2.3 1.2.3 No pubspec.lock found. There are no Current versions. -Run `pub get` to create a pubspec.lock with versions matching your pubspec.yaml. +Run `dart pub get` to create a pubspec.lock with versions matching your pubspec.yaml. 1 dependency is constrained to a version that is older than a resolvable version. To update it, edit pubspec.yaml, or run `dart pub upgrade --major-versions`. @@ -144,7 +144,7 @@ foo - 1.2.3 1.2.3 1.2.3 No pubspec.lock found. There are no Current versions. -Run `pub get` to create a pubspec.lock with versions matching your pubspec.yaml. +Run `dart pub get` to create a pubspec.lock with versions matching your pubspec.yaml. 1 dependency is constrained to a version that is older than a resolvable version. To update it, edit pubspec.yaml, or run `dart pub upgrade --major-versions`. @@ -164,7 +164,7 @@ foo - ✗1.2.3 ✗1.2.3 ✗1.2.3 No pubspec.lock found. There are no Current versions. -Run `pub get` to create a pubspec.lock with versions matching your pubspec.yaml. +Run `dart pub get` to create a pubspec.lock with versions matching your pubspec.yaml. 1 dependency is constrained to a version that is older than a resolvable version. To update it, edit pubspec.yaml, or run `dart pub upgrade --null-safety`. @@ -184,7 +184,7 @@ foo - ✗1.2.3 ✗1.2.3 ✗1.2.3 No pubspec.lock found. There are no Current versions. -Run `pub get` to create a pubspec.lock with versions matching your pubspec.yaml. +Run `dart pub get` to create a pubspec.lock with versions matching your pubspec.yaml. 1 dependency is constrained to a version that is older than a resolvable version. To update it, edit pubspec.yaml, or run `dart pub upgrade --null-safety`. @@ -204,7 +204,7 @@ foo - ✗1.2.3 ✗1.2.3 ✗1.2.3 No pubspec.lock found. There are no Current versions. -Run `pub get` to create a pubspec.lock with versions matching your pubspec.yaml. +Run `dart pub get` to create a pubspec.lock with versions matching your pubspec.yaml. 1 dependency is constrained to a version that is older than a resolvable version. To update it, edit pubspec.yaml, or run `dart pub upgrade --null-safety`.
diff --git a/test/testdata/goldens/upgrade/example_warns_about_major_versions_test/pub upgrade --major-versions does not update major versions in example~.txt b/test/testdata/goldens/upgrade/example_warns_about_major_versions_test/pub upgrade --major-versions does not update major versions in example~.txt index ae304ea..2cc96c0 100644 --- a/test/testdata/goldens/upgrade/example_warns_about_major_versions_test/pub upgrade --major-versions does not update major versions in example~.txt +++ b/test/testdata/goldens/upgrade/example_warns_about_major_versions_test/pub upgrade --major-versions does not update major versions in example~.txt
@@ -17,7 +17,9 @@ ## Section 1 $ pub upgrade --major-versions --directory example Resolving dependencies in example... + bar 2.0.0 > foo 2.0.0 (was 1.0.0) + myapp 0.0.0 from path . Changed 1 dependency in example! Changed 1 constraint in pubspec.yaml:
diff --git a/test/token/token_authentication_test.dart b/test/token/token_authentication_test.dart index 350bca4..d0b89c9 100644 --- a/test/token/token_authentication_test.dart +++ b/test/token/token_authentication_test.dart
@@ -9,10 +9,9 @@ import '../test_pub.dart'; void main() { - setUp(d.validPackage.create); - test('with a pre existing environment token authenticates', () async { await servePackages(); + await d.validPackage.create(); await d.tokensFile({ 'version': 1, 'hosted': [ @@ -33,6 +32,7 @@ test('with a pre existing opaque token authenticates', () async { await servePackages(); + await d.validPackage.create(); await d.tokensFile({ 'version': 1, 'hosted': [
diff --git a/test/token/when_receives_401_removes_token_test.dart b/test/token/when_receives_401_removes_token_test.dart index add619b..6ab0700 100644 --- a/test/token/when_receives_401_removes_token_test.dart +++ b/test/token/when_receives_401_removes_token_test.dart
@@ -9,10 +9,9 @@ import '../test_pub.dart'; void main() { - setUp(d.validPackage.create); - test('when receives 401 response removes saved token', () async { final server = await servePackages(); + await d.validPackage.create(); await d.tokensFile({ 'version': 1, 'hosted': [
diff --git a/test/token/when_receives_403_persists_saved_token_test.dart b/test/token/when_receives_403_persists_saved_token_test.dart index 6db5538..3fef9a5 100644 --- a/test/token/when_receives_403_persists_saved_token_test.dart +++ b/test/token/when_receives_403_persists_saved_token_test.dart
@@ -9,9 +9,8 @@ import '../test_pub.dart'; void main() { - setUp(d.validPackage.create); - test('when receives 403 response persists saved token', () async { + await d.validPackage.create(); final server = await servePackages(); await d.tokensFile({ 'version': 1,
diff --git a/test/validator/dependency_override_test.dart b/test/validator/dependency_override_test.dart index b801ad9..60af87d 100644 --- a/test/validator/dependency_override_test.dart +++ b/test/validator/dependency_override_test.dart
@@ -36,7 +36,7 @@ }) ]).create(); - await expectValidation(dependencyOverride, errors: isNotEmpty); + await expectValidation(dependencyOverride, warnings: isNotEmpty); }); test('it has any non-dev dependency overrides', () async { @@ -51,7 +51,7 @@ }) ]).create(); - await expectValidation(dependencyOverride, errors: isNotEmpty); + await expectValidation(dependencyOverride, warnings: isNotEmpty); }); }); }
diff --git a/test/validator/dependency_test.dart b/test/validator/dependency_test.dart index f90ed1d..69fc4a8 100644 --- a/test/validator/dependency_test.dart +++ b/test/validator/dependency_test.dart
@@ -25,29 +25,38 @@ ]); } -Future<void> expectValidation({error, int exitCode = 0}) async { +Future<void> expectValidation( + {error, + int exitCode = 0, + Map<String, String> environment = const {}}) async { await runPub( error: error ?? contains('Package has 0 warnings.'), args: ['publish', '--dry-run'], // workingDirectory: d.path(appPath), exitCode: exitCode, + environment: environment, ); } -Future<void> expectValidationWarning(error) async { +Future<void> expectValidationWarning(error, + {Map<String, String> environment = const {}}) async { if (error is String) error = contains(error); await expectValidation( - error: allOf([error, contains('Package has 1 warning.')]), - exitCode: DATA); + error: allOf([error, contains('Package has 1 warning.')]), + exitCode: DATA, + environment: environment, + ); } -Future<void> expectValidationError(String text) async { +Future<void> expectValidationError(String text, + {Map<String, String> environment = const {}}) async { await expectValidation( error: allOf([ contains(text), contains('Package validation found the following error:') ]), - exitCode: DATA); + exitCode: DATA, + environment: environment); } Future<void> setUpDependency(dep, @@ -56,6 +65,7 @@ for (final version in hostedVersions) { server.serve('foo', version); } + await package(deps: {'foo': dep}).create(); } @@ -63,212 +73,186 @@ group('should consider a package valid if it', () { test('looks normal', () async { await package().create(); - await expectValidation(); + await expectValidation(environment: {'_PUB_TEST_SDK_VERSION': '1.18.0'}); }); test('has a ^ constraint with an appropriate SDK constraint', () async { + (await servePackages()).serve('foo', '1.2.3'); await package(deps: {'foo': '^1.2.3'}).create(); - await expectValidation(); + await expectValidation(environment: {'_PUB_TEST_SDK_VERSION': '1.18.0'}); }); test('with a dependency on a pre-release while being one', () async { + (await servePackages()).serve('foo', '1.2.3-dev'); await package(version: '1.0.0-dev', deps: {'foo': '^1.2.3-dev'}).create(); - await expectValidation(); + await expectValidation(environment: {'_PUB_TEST_SDK_VERSION': '1.18.0'}); }); test('has a git path dependency with an appropriate SDK constraint', () async { await servePackages(); + await d.git('foo', [ + d.dir('subdir', [d.libPubspec('foo', '1.0.0', sdk: '^2.0.0')]), + ]).create(); await package(deps: { 'foo': { - 'git': {'url': 'git://github.com/dart-lang/foo', 'path': 'subdir'} + 'git': {'url': '../foo', 'path': 'subdir'} } }, sdk: '>=2.0.0 <3.0.0') .create(); // We should get a warning for using a git dependency, but not an error. - await expectValidationWarning(' foo: any'); + await expectValidationWarning( + allOf([ + contains(' foo: any'), + ]), + environment: {'_PUB_TEST_SDK_VERSION': '2.0.0'}); }); test('depends on Flutter from an SDK source', () async { + await d.dir('flutter', [d.file('version', '1.2.3')]).create(); + await flutterPackage('flutter', sdk: '>=1.19.0 <2.0.0').create(); await package(deps: { 'flutter': {'sdk': 'flutter'} }, sdk: '>=1.19.0 <2.0.0') .create(); - await expectValidation(); - }); - - test( - 'depends on a package from Flutter with an appropriate Dart SDK ' - 'constraint', () async { - await package( - deps: { - 'foo': {'sdk': 'flutter', 'version': '>=1.2.3 <2.0.0'}, + await expectValidation( + environment: { + '_PUB_TEST_SDK_VERSION': '1.19.0', + 'FLUTTER_ROOT': path.join(d.sandbox, 'flutter') }, - sdk: '>=1.19.0 <2.0.0', - ).create(); - - await expectValidation(); + ); }); test( - 'depends on a package from Fuchsia with an appropriate Dart SDK ' - 'constraint', () async { - await package(sdk: '>=2.0.0-dev.51.0 <2.0.0', deps: { - 'foo': {'sdk': 'fuchsia', 'version': '>=1.2.3 <2.0.0'} - }).create(); - await d.validPackage.create(); + 'depends on a package from Flutter with an appropriate Dart SDK constraint', + () async { + await d.dir('flutter', [d.file('version', '1.2.3')]).create(); + await flutterPackage('foo', sdk: '^1.0.0').create(); + await d.dir('flutter', [d.file('version', '1.2.3')]).create(); + await package( + deps: { + 'foo': {'sdk': 'flutter', 'version': '>=1.2.3 <2.0.0'}, + }, + sdk: '>=1.19.0 <2.0.0', + ).create(); - await expectValidation(); - }); + await expectValidation( + environment: { + '_PUB_TEST_SDK_VERSION': '1.19.0', + 'FLUTTER_ROOT': path.join(d.sandbox, 'flutter'), + }, + ); + }, + ); + + test( + 'depends on a package from Fuchsia with an appropriate Dart SDK constraint', + () async { + await fuschiaPackage('foo', sdk: '>=2.0.0 <3.0.0').create(); + await package(sdk: '>=2.0.0 <3.0.0', deps: { + 'foo': {'sdk': 'fuchsia', 'version': '>=1.2.3 <2.0.0'} + }).create(); + + await expectValidation(environment: { + 'FUCHSIA_DART_SDK_ROOT': path.join(d.sandbox, 'fuchsia'), + '_PUB_TEST_SDK_VERSION': '2.12.0', + }); + }, + ); }); group('should consider a package invalid if it', () { setUp(package().create); - group('has a git dependency', () { - group('where a hosted version exists', () { - test('and should suggest the hosted primary version', () async { - await setUpDependency({'git': 'git://github.com/dart-lang/foo'}, - hostedVersions: ['3.0.0-pre', '2.0.0', '1.0.0']); - await expectValidationWarning(' foo: ^2.0.0'); - }); - - test( - 'and should suggest the hosted prerelease version if ' - "it's the only version available", () async { - await setUpDependency({'git': 'git://github.com/dart-lang/foo'}, - hostedVersions: ['3.0.0-pre', '2.0.0-pre']); - await expectValidationWarning(' foo: ^3.0.0-pre'); - }); - - test( - 'and should suggest a tighter constraint if primary is ' - 'pre-1.0.0', () async { - await setUpDependency({'git': 'git://github.com/dart-lang/foo'}, - hostedVersions: ['0.0.1', '0.0.2']); - await expectValidationWarning(' foo: ^0.0.2'); - }); - }); - - group('where no hosted version exists', () { - test("and should use the other source's version", () async { - await setUpDependency({ - 'git': 'git://github.com/dart-lang/foo', - 'version': '>=1.0.0 <2.0.0' - }); - await expectValidationWarning(' foo: ">=1.0.0 <2.0.0"'); - }); - - test( - "and should use the other source's unquoted version if " - 'concrete', () async { - await setUpDependency( - {'git': 'git://github.com/dart-lang/foo', 'version': '0.2.3'}); - await expectValidationWarning(' foo: 0.2.3'); - }); - }); - }); group('has a path dependency', () { group('where a hosted version exists', () { test('and should suggest the hosted primary version', () async { + await d.dir('foo', [ + d.libPubspec('foo', '1.2.3', sdk: 'any'), + ]).create(); await setUpDependency({'path': path.join(d.sandbox, 'foo')}, hostedVersions: ['3.0.0-pre', '2.0.0', '1.0.0']); - await expectValidationError(' foo: ^2.0.0'); + await expectValidationError( + ' foo: ^2.0.0', + environment: {'_PUB_TEST_SDK_VERSION': '1.8.0'}, + ); }); test( 'and should suggest the hosted prerelease version if ' "it's the only version available", () async { - await setUpDependency({'path': path.join(d.sandbox, 'foo')}, - hostedVersions: ['3.0.0-pre', '2.0.0-pre']); - await expectValidationError(' foo: ^3.0.0-pre'); + await d.dir('foo', [ + d.libPubspec('foo', '1.2.3', sdk: 'any'), + ]).create(); + await setUpDependency( + {'path': path.join(d.sandbox, 'foo')}, + hostedVersions: ['3.0.0-pre', '2.0.0-pre'], + ); + await expectValidationError( + ' foo: ^3.0.0-pre', + environment: {'_PUB_TEST_SDK_VERSION': '1.8.0'}, + ); }); test( 'and should suggest a tighter constraint if primary is ' 'pre-1.0.0', () async { - await setUpDependency({'path': path.join(d.sandbox, 'foo')}, - hostedVersions: ['0.0.1', '0.0.2']); - await expectValidationError(' foo: ^0.0.2'); + await d.dir('foo', [ + d.libPubspec('foo', '1.2.3', sdk: 'any'), + ]).create(); + await setUpDependency( + {'path': path.join(d.sandbox, 'foo')}, + hostedVersions: ['0.0.1', '0.0.2'], + ); + await expectValidationError( + ' foo: ^0.0.2', + environment: {'_PUB_TEST_SDK_VERSION': '1.8.0'}, + ); }); }); group('where no hosted version exists', () { test("and should use the other source's version", () async { + await d.dir('foo', [ + d.libPubspec('foo', '1.2.3', sdk: 'any'), + ]).create(); await setUpDependency({ 'path': path.join(d.sandbox, 'foo'), 'version': '>=1.0.0 <2.0.0' }); - await expectValidationError(' foo: ">=1.0.0 <2.0.0"'); + await expectValidationError( + ' foo: ">=1.0.0 <2.0.0"', + environment: {'_PUB_TEST_SDK_VERSION': '1.8.0'}, + ); }); test( "and should use the other source's unquoted version if " 'concrete', () async { + await d.dir('foo', [ + d.libPubspec('foo', '0.2.3', sdk: '^1.8.0'), + ]).create(); await setUpDependency( {'path': path.join(d.sandbox, 'foo'), 'version': '0.2.3'}); - await expectValidationError(' foo: 0.2.3'); + await expectValidationError( + ' foo: 0.2.3', + environment: {'_PUB_TEST_SDK_VERSION': '1.8.0'}, + ); }); }); }); group('has an unconstrained dependency', () { - group('and it should not suggest a version', () { - test("if there's no lockfile", () async { - await d.dir(appPath, [ - d.libPubspec('test_pkg', '1.0.0', deps: {'foo': 'any'}) - ]).create(); - - await expectValidationWarning(isNot(contains('\n foo:'))); - }); - - test("if the lockfile doesn't have an entry for the dependency", - () async { - await d.dir(appPath, [ - d.libPubspec('test_pkg', '1.0.0', deps: {'foo': 'any'}), - d.file( - 'pubspec.lock', - jsonEncode({ - 'packages': { - 'bar': { - 'version': '1.2.3', - 'source': 'hosted', - 'description': { - 'name': 'bar', - 'url': 'http://pub.dartlang.org' - } - } - } - })) - ]).create(); - - await expectValidationWarning(isNot(contains('\n foo:'))); - }); - }); - group('with a lockfile', () { test( 'and it should suggest a constraint based on the locked ' 'version', () async { + (await servePackages()).serve('foo', '1.2.3'); await d.dir(appPath, [ d.libPubspec('test_pkg', '1.0.0', deps: {'foo': 'any'}), - d.file( - 'pubspec.lock', - jsonEncode({ - 'packages': { - 'foo': { - 'version': '1.2.3', - 'source': 'hosted', - 'description': { - 'name': 'foo', - 'url': 'http://pub.dartlang.org' - } - } - } - })) ]).create(); await expectValidationWarning(' foo: ^1.2.3'); @@ -277,6 +261,8 @@ test( 'and it should suggest a concrete constraint if the locked ' 'version is pre-1.0.0', () async { + (await servePackages()).serve('foo', '0.1.2'); + await d.dir(appPath, [ d.libPubspec('test_pkg', '1.0.0', deps: {'foo': 'any'}), d.file( @@ -286,10 +272,7 @@ 'foo': { 'version': '0.1.2', 'source': 'hosted', - 'description': { - 'name': 'foo', - 'url': 'http://pub.dartlang.org' - } + 'description': {'name': 'foo', 'url': 'https://pub.dev'} } } })) @@ -301,20 +284,24 @@ }); test('with a dependency on a pre-release without being one', () async { + (await servePackages()).serve('foo', '1.2.3-dev'); + await d.dir(appPath, [ d.libPubspec( 'test_pkg', '1.0.0', deps: {'foo': '^1.2.3-dev'}, - sdk: '>=1.19.0 <2.0.0', + sdk: '>=1.8.0 <2.0.0', ) ]).create(); - await expectValidationWarning('Packages dependent on a pre-release'); + await expectValidationWarning('Packages dependent on a pre-release', + environment: {'_PUB_TEST_SDK_VERSION': '1.8.0'}); }); test( 'with a single-version dependency and it should suggest a ' 'constraint based on the version', () async { + (await servePackages()).serve('foo', '1.2.3'); await d.dir(appPath, [ d.libPubspec('test_pkg', '1.0.0', deps: {'foo': '1.2.3'}) ]).create(); @@ -323,116 +310,69 @@ }); group('has a dependency without a lower bound', () { - group('and it should not suggest a version', () { - test("if there's no lockfile", () async { - await d.dir(appPath, [ - d.libPubspec('test_pkg', '1.0.0', deps: {'foo': '<3.0.0'}) - ]).create(); + test( + 'and it should suggest a constraint based on the locked ' + 'version', () async { + (await servePackages()).serve('foo', '1.2.3'); - await expectValidationWarning(isNot(contains('\n foo:'))); - }); + await d.dir(appPath, [ + d.libPubspec('test_pkg', '1.0.0', deps: {'foo': '<3.0.0'}), + d.file( + 'pubspec.lock', + ) + ]).create(); - test( - "if the lockfile doesn't have an entry for the " - 'dependency', () async { - await d.dir(appPath, [ - d.libPubspec('test_pkg', '1.0.0', deps: {'foo': '<3.0.0'}), - d.file( - 'pubspec.lock', - jsonEncode({ - 'packages': { - 'bar': { - 'version': '1.2.3', - 'source': 'hosted', - 'description': { - 'name': 'bar', - 'url': 'http://pub.dartlang.org' - } - } - } - })) - ]).create(); - - await expectValidationWarning(isNot(contains('\n foo:'))); - }); + await expectValidationWarning(' foo: ">=1.2.3 <3.0.0"'); }); - group('with a lockfile', () { - test( - 'and it should suggest a constraint based on the locked ' - 'version', () async { - await d.dir(appPath, [ - d.libPubspec('test_pkg', '1.0.0', deps: {'foo': '<3.0.0'}), - d.file( - 'pubspec.lock', - jsonEncode({ - 'packages': { - 'foo': { - 'version': '1.2.3', - 'source': 'hosted', - 'description': { - 'name': 'foo', - 'url': 'http://pub.dartlang.org' - } - } + test('and it should preserve the upper-bound operator', () async { + (await servePackages()).serve('foo', '1.2.3'); + await d.dir(appPath, [ + d.libPubspec('test_pkg', '1.0.0', deps: {'foo': '<=3.0.0'}), + d.file( + 'pubspec.lock', + jsonEncode({ + 'packages': { + 'foo': { + 'version': '1.2.3', + 'source': 'hosted', + 'description': {'name': 'foo', 'url': 'https://pub.dev'} } - })) - ]).create(); + } + })) + ]).create(); - await expectValidationWarning(' foo: ">=1.2.3 <3.0.0"'); - }); + await expectValidationWarning(' foo: ">=1.2.3 <=3.0.0"'); + }); - test('and it should preserve the upper-bound operator', () async { - await d.dir(appPath, [ - d.libPubspec('test_pkg', '1.0.0', deps: {'foo': '<=3.0.0'}), - d.file( - 'pubspec.lock', - jsonEncode({ - 'packages': { - 'foo': { - 'version': '1.2.3', - 'source': 'hosted', - 'description': { - 'name': 'foo', - 'url': 'http://pub.dartlang.org' - } - } + test( + 'and it should expand the suggested constraint if the ' + 'locked version matches the upper bound', () async { + (await servePackages()).serve('foo', '1.2.3'); + + await d.dir(appPath, [ + d.libPubspec('test_pkg', '1.0.0', deps: {'foo': '<=1.2.3'}), + d.file( + 'pubspec.lock', + jsonEncode({ + 'packages': { + 'foo': { + 'version': '1.2.3', + 'source': 'hosted', + 'description': {'name': 'foo', 'url': 'https://pub.dev'} } - })) - ]).create(); + } + })) + ]).create(); - await expectValidationWarning(' foo: ">=1.2.3 <=3.0.0"'); - }); - - test( - 'and it should expand the suggested constraint if the ' - 'locked version matches the upper bound', () async { - await d.dir(appPath, [ - d.libPubspec('test_pkg', '1.0.0', deps: {'foo': '<=1.2.3'}), - d.file( - 'pubspec.lock', - jsonEncode({ - 'packages': { - 'foo': { - 'version': '1.2.3', - 'source': 'hosted', - 'description': { - 'name': 'foo', - 'url': 'http://pub.dartlang.org' - } - } - } - })) - ]).create(); - - await expectValidationWarning(' foo: ^1.2.3'); - }); + await expectValidationWarning(' foo: ^1.2.3'); }); }); group('with a dependency without an upper bound', () { test('and it should suggest a constraint based on the lower bound', () async { + (await servePackages()).serve('foo', '1.2.3'); await d.dir(appPath, [ d.libPubspec('test_pkg', '1.0.0', deps: {'foo': '>=1.2.3'}) ]).create(); @@ -441,6 +381,7 @@ }); test('and it should preserve the lower-bound operator', () async { + (await servePackages()).serve('foo', '1.2.4'); await d.dir(appPath, [ d.libPubspec('test_pkg', '1.0.0', deps: {'foo': '>1.2.3'}) ]).create(); @@ -450,21 +391,17 @@ }); group('has a ^ dependency', () { - test('without an SDK constraint', () async { - await d.dir(appPath, [ - d.libPubspec('integration_pkg', '1.0.0', deps: {'foo': '^1.2.3'}) - ]).create(); - - await expectValidationError(' sdk: ">=1.8.0 <2.0.0"'); - }); - test('with a too-broad SDK constraint', () async { + (await servePackages()).serve('foo', '1.2.3'); await d.dir(appPath, [ d.libPubspec('test_pkg', '1.0.0', deps: {'foo': '^1.2.3'}, sdk: '>=1.5.0 <2.0.0') ]).create(); - await expectValidationError(' sdk: ">=1.8.0 <2.0.0"'); + await expectValidationError( + ' sdk: ">=1.8.0 <2.0.0"', + environment: {'_PUB_TEST_SDK_VERSION': '1.5.0'}, + ); }); }); @@ -472,17 +409,22 @@ test('without an SDK constraint', () async { // Ensure we don't report anything from the real pub.dev. await setUpDependency({}); + await d.git('foo', [ + d.dir('subdir', [d.libPubspec('foo', '1.0.0')]), + ]).create(); await d.dir(appPath, [ d.libPubspec('integration_pkg', '1.0.0', deps: { 'foo': { - 'git': {'url': 'git://github.com/dart-lang/foo', 'path': 'subdir'} + 'git': {'url': d.path('foo'), 'path': 'subdir'} } }) ]).create(); await expectValidation( error: allOf( - contains(' sdk: ">=2.0.0 <3.0.0"'), contains(' foo: any')), + contains(' sdk: ">=2.0.0 <3.0.0"'), + contains(' foo: any'), + ), exitCode: DATA, ); }); @@ -490,14 +432,15 @@ test('with a too-broad SDK constraint', () async { // Ensure we don't report anything from the real pub.dev. await setUpDependency({}); + await d.git('foo', [ + d.dir( + 'subdir', [d.libPubspec('foo', '1.0.0', sdk: '>=0.0.0 <3.0.0')]), + ]).create(); await d.dir(appPath, [ d.libPubspec('integration_pkg', '1.0.0', deps: { 'foo': { - 'git': { - 'url': 'git://github.com/dart-lang/foo', - 'path': 'subdir' - } + 'git': {'url': d.path('foo'), 'path': 'subdir'} } }, sdk: '>=1.24.0 <3.0.0') @@ -508,12 +451,14 @@ contains(' sdk: ">=2.0.0 <3.0.0"'), contains(' foo: any'), ]), + environment: {'_PUB_TEST_SDK_VERSION': '1.24.0'}, exitCode: DATA, ); }); }); test('depends on Flutter from a non-SDK source', () async { + (await servePackages()).serve('flutter', '1.5.0'); await d.dir(appPath, [ d.libPubspec('test_pkg', '1.0.0', deps: {'flutter': '>=1.2.3 <2.0.0'}) ]).create(); @@ -521,16 +466,10 @@ await expectValidationError('sdk: >=1.2.3 <2.0.0'); }); - test('depends on a Flutter package from an unknown SDK', () async { - await package(deps: { - 'foo': {'sdk': 'fblthp', 'version': '>=1.2.3 <2.0.0'} - }).create(); - - await expectValidationError('Unknown SDK "fblthp" for dependency "foo".'); - }); - test('depends on a Flutter package with a too-broad SDK constraint', () async { + await d.dir('flutter', [d.file('version', '1.2.3')]).create(); + await flutterPackage('foo', sdk: 'any').create(); await package( deps: { 'foo': {'sdk': 'flutter', 'version': '>=1.2.3 <2.0.0'} @@ -538,35 +477,79 @@ sdk: '>=1.18.0 <2.0.0', ).create(); - await expectValidationError('sdk: ">=1.19.0 <2.0.0"'); + await expectValidationError('sdk: ">=1.19.0 <2.0.0"', environment: { + '_PUB_TEST_SDK_VERSION': '1.19.0', + 'FLUTTER_ROOT': path.join(d.sandbox, 'flutter'), + }); }); test('depends on a Flutter package with no SDK constraint', () async { + await d.dir('flutter', [d.file('version', '1.2.3')]).create(); + await flutterPackage('foo', sdk: '>=0.0.0 <1.0.0').create(); await package(sdk: '>=0.0.0 <=0.0.1', deps: { 'foo': {'sdk': 'flutter', 'version': '>=1.2.3 <2.0.0'} }).create(); - await expectValidationError('sdk: ">=1.19.0 <2.0.0"'); + await expectValidationError( + 'sdk: ">=1.19.0 <2.0.0"', + environment: { + '_PUB_TEST_SDK_VERSION': '0.0.0', + 'FLUTTER_ROOT': path.join(d.sandbox, 'flutter'), + }, + ); }); test('depends on a Fuchsia package with a too-broad SDK constraint', () async { + await fuschiaPackage('foo', sdk: '>=0.0.0 <3.0.0').create(); + await package( - sdk: '>=2.0.0-dev.50.0 <2.0.0', + sdk: '>=2.0.0-dev.50.0 <2.0.1', deps: { 'foo': {'sdk': 'fuchsia', 'version': '>=1.2.3 <2.0.0'} }, ).create(); - await expectValidationError('sdk: ">=2.0.0 <3.0.0"'); + await expectValidationError('sdk: ">=2.0.0 <2.0.1"', environment: { + 'FUCHSIA_DART_SDK_ROOT': path.join(d.sandbox, 'fuchsia'), + '_PUB_TEST_SDK_VERSION': '2.0.1-dev.51.0', + }); }); test('depends on a Fuchsia package with no SDK constraint', () async { + await fuschiaPackage('foo').create(); await package(sdk: '>=0.0.0 <1.0.0', deps: { 'foo': {'sdk': 'fuchsia', 'version': '>=1.2.3 <2.0.0'} }).create(); - await expectValidationError('sdk: ">=2.0.0 <3.0.0"'); + await expectValidationError( + 'sdk: ">=2.0.0 <3.0.0"', + environment: {'FUCHSIA_DART_SDK_ROOT': path.join(d.sandbox, 'fuchsia')}, + ); }); }); } + +d.Descriptor fuschiaPackage(String name, + {Map<String, String> deps = const {}, String? sdk}) { + return d.dir('fuchsia', [ + d.dir('packages', [ + d.dir(name, [ + d.libDir(name, 'f(x) => 2 * x;'), + d.libPubspec(name, '1.5.0', deps: deps, sdk: sdk), + ]), + ]), + ]); +} + +d.Descriptor flutterPackage(String name, + {Map<String, String> deps = const {}, String? sdk}) { + return d.dir('flutter', [ + d.dir('packages', [ + d.dir(name, [ + d.libDir(name, 'f(x) => 2 * x;'), + d.libPubspec(name, '1.5.0', deps: deps, sdk: sdk), + ]), + ]), + ]); +}
diff --git a/test/validator/flutter_constraint_test.dart b/test/validator/flutter_constraint_test.dart index b7bdf7c..9e73ef6 100644 --- a/test/validator/flutter_constraint_test.dart +++ b/test/validator/flutter_constraint_test.dart
@@ -11,17 +11,21 @@ await runPub( error: error, args: ['publish', '--dry-run'], - environment: {'_PUB_TEST_SDK_VERSION': '2.12.0'}, + environment: { + '_PUB_TEST_SDK_VERSION': '2.12.0', + 'FLUTTER_ROOT': fakeFlutterRoot.io.path, + }, workingDirectory: d.path(appPath), exitCode: exitCode, ); } +late d.DirectoryDescriptor fakeFlutterRoot; + Future<void> setup({ String? flutterConstraint, }) async { - final fakeFlutterRoot = - d.dir('fake_flutter_root', [d.file('version', '1.23.0')]); + fakeFlutterRoot = d.dir('fake_flutter_root', [d.file('version', '1.23.0')]); await fakeFlutterRoot.create(); await d.validPackage.create(); await d.dir(appPath, [
diff --git a/test/validator/gitignore_test.dart b/test/validator/gitignore_test.dart index 3cd4a11..f4ec65c 100644 --- a/test/validator/gitignore_test.dart +++ b/test/validator/gitignore_test.dart
@@ -20,7 +20,7 @@ await runPub( error: error, args: ['publish', '--dry-run'], - environment: {'_PUB_TEST_SDK_VERSION': '2.12.0', ...environment}, + environment: environment, workingDirectory: workingDirectory ?? d.path(appPath), exitCode: exitCode, ); @@ -35,8 +35,6 @@ d.file('foo.txt'), ]).create(); - await pubGet(environment: {'_PUB_TEST_SDK_VERSION': '1.12.0'}); - await expectValidation(contains('Package has 0 warnings.'), 0); await d.dir('myapp', [ @@ -60,7 +58,7 @@ d.file('foo.txt'), ]).create(); - await pubGet(environment: {'_PUB_TEST_SDK_VERSION': '1.12.0'}); + await pubGet(); await setUpFakeGitScript(bash: 'echo "Not git"', batch: 'echo "Not git"'); await expectValidation( allOf([contains('Package has 0 warnings.')]), exit_codes.SUCCESS, @@ -78,9 +76,7 @@ ), ]).create(); final packageRoot = p.join(d.sandbox, 'reporoot', 'myapp'); - await pubGet( - environment: {'_PUB_TEST_SDK_VERSION': '1.12.0'}, - workingDirectory: packageRoot); + await pubGet(workingDirectory: packageRoot); await expectValidation(contains('Package has 0 warnings.'), 0, workingDirectory: packageRoot); @@ -105,9 +101,7 @@ ...d.validPackage.contents, ]).create(); final packageRoot = p.join(d.sandbox, 'myapp'); - await pubGet( - environment: {'_PUB_TEST_SDK_VERSION': '1.12.0'}, - workingDirectory: packageRoot); + await pubGet(workingDirectory: packageRoot); Link(p.join(packageRoot, '.abc', 'itself')).createSync( packageRoot,
diff --git a/test/validator/pubspec_field_test.dart b/test/validator/pubspec_field_test.dart index 43db483..cd205ef 100644 --- a/test/validator/pubspec_field_test.dart +++ b/test/validator/pubspec_field_test.dart
@@ -20,7 +20,7 @@ test('has an HTTPS homepage URL', () async { var pkg = packageMap('test_pkg', '1.0.0'); - pkg['homepage'] = 'https://pub.dartlang.org'; + pkg['homepage'] = 'https://pub.dev'; await d.dir(appPath, [d.pubspec(pkg)]).create(); await expectValidation(pubspecField); @@ -29,7 +29,7 @@ test('has an HTTPS repository URL instead of homepage', () async { var pkg = packageMap('test_pkg', '1.0.0'); pkg.remove('homepage'); - pkg['repository'] = 'https://pub.dartlang.org'; + pkg['repository'] = 'https://pub.dev'; await d.dir(appPath, [d.pubspec(pkg)]).create(); await expectValidation(pubspecField); @@ -37,7 +37,7 @@ test('has an HTTPS documentation URL', () async { var pkg = packageMap('test_pkg', '1.0.0'); - pkg['documentation'] = 'https://pub.dartlang.org'; + pkg['documentation'] = 'https://pub.dev'; await d.dir(appPath, [d.pubspec(pkg)]).create(); await expectValidation(pubspecField);
diff --git a/test/validator/readme_test.dart b/test/validator/readme_test.dart index 17b0602..934feae 100644 --- a/test/validator/readme_test.dart +++ b/test/validator/readme_test.dart
@@ -15,8 +15,6 @@ Validator readme() => ReadmeValidator(); void main() { - setUp(d.validPackage.create); - group('should consider a package valid if it', () { test('looks normal', () async { await d.validPackage.create();
diff --git a/test/version_solver_test.dart b/test/version_solver_test.dart index 128ed73..69489a3 100644 --- a/test/version_solver_test.dart +++ b/test/version_solver_test.dart
@@ -2911,7 +2911,7 @@ if (description is HostedDescription && (description.url == SystemCache().hosted.defaultUrl)) { // If the dep uses the default hosted source, grab it from the test - // package server rather than pub.dartlang.org. + // package server rather than pub.dev. dep = cache.hosted .refFor(dep.name, url: globalServer.url) .withConstraint(dep.constraint);