Unverified Commit 23e7449a authored by Greg Spencer's avatar Greg Spencer Committed by GitHub

Warm cache with all transitive dependencies in `flutter update-packages` command (#96258)

parent 44849ab4
......@@ -153,7 +153,7 @@ class Cache {
platform ??= FakePlatform(environment: <String, String>{});
logger ??= BufferLogger.test();
return Cache(
rootOverride: rootOverride ??= fileSystem.directory('cache'),
rootOverride: rootOverride ?? fileSystem.directory('cache'),
artifacts: artifacts ?? <ArtifactSet>[],
logger: logger,
fileSystem: fileSystem,
......
......@@ -97,6 +97,12 @@ class UpdatePackagesCommand extends FlutterCommand {
help: 'For Flutter CLI testing only, forces this command to throw an unhandled exception.',
defaultsTo: false,
negatable: false,
)
..addOption(
'jobs',
abbr: 'j',
help: 'Causes the "pub get" runs to happen concurrently on this many '
'CPUs. Defaults to the number of CPUs that this machine has.',
);
}
......@@ -141,19 +147,19 @@ class UpdatePackagesCommand extends FlutterCommand {
Future<FlutterCommandResult> runCommand() async {
final List<Directory> packages = runner.getRepoPackages();
final bool upgrade = boolArg('force-upgrade');
final bool forceUpgrade = boolArg('force-upgrade');
final bool isPrintPaths = boolArg('paths');
final bool isPrintTransitiveClosure = boolArg('transitive-closure');
final bool isVerifyOnly = boolArg('verify-only');
final bool isConsumerOnly = boolArg('consumer-only');
final bool offline = boolArg('offline');
final bool crash = boolArg('crash');
final bool doUpgrade = forceUpgrade || isPrintPaths || isPrintTransitiveClosure;
if (crash) {
if (boolArg('crash')) {
throw StateError('test crash please ignore.');
}
if (upgrade && offline) {
if (forceUpgrade && offline) {
throwToolExit(
'--force-upgrade cannot be used with the --offline flag'
);
......@@ -177,6 +183,69 @@ class UpdatePackagesCommand extends FlutterCommand {
}
if (isVerifyOnly) {
_verifyPubspecs(packages);
return FlutterCommandResult.success();
}
if (doUpgrade) {
// This feature attempts to collect all the packages used across all the
// pubspec.yamls in the repo (including via transitive dependencies), and
// find the latest version of each that can be used while keeping each
// such package fixed at a single version across all the pubspec.yamls.
globals.printStatus('Upgrading packages...');
}
// First, collect the dependencies:
final List<PubspecYaml> pubspecs = <PubspecYaml>[];
final Map<String, PubspecDependency> explicitDependencies = <String, PubspecDependency>{};
final Map<String, PubspecDependency> allDependencies = <String, PubspecDependency>{};
final Set<String> specialDependencies = <String>{};
_collectDependencies(
packages: packages,
pubspecs: pubspecs,
explicitDependencies: explicitDependencies,
allDependencies: allDependencies,
specialDependencies: specialDependencies,
doUpgrade: doUpgrade,
);
// Now that we have all the dependencies we care about, we are going to
// create a fake package and then run either "pub upgrade", if requested,
// followed by "pub get" on it. If upgrading, the pub tool will attempt to
// bring these dependencies up to the most recent possible versions while
// honoring all their constraints. If not upgrading the pub tool will only
// attempt to download any necessary package versions to the pub cache to
// warm the cache.
final PubDependencyTree tree = PubDependencyTree(); // object to collect results
final Directory tempDir = globals.fs.systemTempDirectory.createTempSync('flutter_update_packages.');
await _generateFakePackage(
tempDir: tempDir,
dependencies: doUpgrade ? explicitDependencies.values : allDependencies.values,
pubspecs: pubspecs,
tree: tree,
doUpgrade: doUpgrade,
);
if (doUpgrade) {
final bool done = _upgradePubspecs(
tree: tree,
pubspecs: pubspecs,
explicitDependencies: explicitDependencies,
specialDependencies: specialDependencies,
);
if (done) {
// Complete early if we were just printing data.
return FlutterCommandResult.success();
}
}
await _runPubGetOnPackages(packages);
return FlutterCommandResult.success();
}
void _verifyPubspecs(List<Directory> packages) {
bool needsUpdate = false;
globals.printStatus('Verifying pubspecs...');
for (final Directory directory in packages) {
......@@ -226,22 +295,16 @@ class UpdatePackagesCommand extends FlutterCommand {
);
}
globals.printStatus('All pubspecs were up to date.');
return FlutterCommandResult.success();
}
final Map<String, PubspecDependency> dependencies = <String, PubspecDependency>{};
final bool doUpgrade = upgrade || isPrintPaths || isPrintTransitiveClosure;
if (doUpgrade) {
// This feature attempts to collect all the packages used across all the
// pubspec.yamls in the repo (including via transitive dependencies), and
// find the latest version of each that can be used while keeping each
// such package fixed at a single version across all the pubspec.yamls.
globals.printStatus('Upgrading packages...');
}
// First, collect up the explicit dependencies:
final List<PubspecYaml> pubspecs = <PubspecYaml>[];
final Set<String> specialDependencies = <String>{};
void _collectDependencies({
@required List<Directory> packages,
@required List<PubspecYaml> pubspecs,
@required Set<String> specialDependencies,
@required Map<String, PubspecDependency> explicitDependencies,
@required Map<String, PubspecDependency> allDependencies,
@required bool doUpgrade,
}) {
// Visit all the directories with pubspec.yamls we care about.
for (final Directory directory in packages) {
if (doUpgrade) {
......@@ -254,8 +317,38 @@ class UpdatePackagesCommand extends FlutterCommand {
throwToolExit(message);
}
pubspecs.add(pubspec); // remember it for later
for (final PubspecDependency dependency in pubspec.allDependencies) { // this is all the explicit dependencies
if (dependencies.containsKey(dependency.name)) {
for (final PubspecDependency dependency in pubspec.allDependencies) {
if (allDependencies.containsKey(dependency.name)) {
// If we've seen the dependency before, make sure that we are
// importing it the same way. There's several ways to import a
// dependency. Hosted (from pub via version number), by path (e.g.
// pointing at the version of a package we get from the Dart SDK
// that we download with Flutter), by SDK (e.g. the "flutter"
// package is explicitly from "sdk: flutter").
//
// This makes sure that we don't import a package in two different
// ways, e.g. by saying "sdk: flutter" in one pubspec.yaml and
// saying "path: ../../..." in another.
final PubspecDependency previous = allDependencies[dependency.name];
if (dependency.kind != previous.kind || dependency.lockTarget != previous.lockTarget) {
throwToolExit(
'Inconsistent requirements around ${dependency.name}; '
'saw ${dependency.kind} (${dependency.lockTarget}) in "${dependency.sourcePath}" '
'and ${previous.kind} (${previous.lockTarget}) in "${previous.sourcePath}".'
);
}
if (dependency.version != previous.version) {
globals.printError(
'Requiring multiple versions: multiple versions required by ${dependency.name}; '
'saw ${dependency.version} in "${dependency.sourcePath}" '
'and ${previous.version} in "${previous.sourcePath}".'
);
}
}
allDependencies[dependency.name] = dependency;
}
for (final PubspecDependency dependency in pubspec.allExplicitDependencies) {
if (explicitDependencies.containsKey(dependency.name)) {
// If we've seen the dependency before, make sure that we are
// importing it the same way. There's several ways to import a
// dependency. Hosted (from pub via version number), by path (e.g.
......@@ -266,7 +359,7 @@ class UpdatePackagesCommand extends FlutterCommand {
// This makes sure that we don't import a package in two different
// ways, e.g. by saying "sdk: flutter" in one pubspec.yaml and
// saying "path: ../../..." in another.
final PubspecDependency previous = dependencies[dependency.name];
final PubspecDependency previous = explicitDependencies[dependency.name];
if (dependency.kind != previous.kind || dependency.lockTarget != previous.lockTarget) {
throwToolExit(
'Inconsistent requirements around ${dependency.name}; '
......@@ -276,7 +369,7 @@ class UpdatePackagesCommand extends FlutterCommand {
}
}
// Remember this dependency by name so we can look it up again.
dependencies[dependency.name] = dependency;
explicitDependencies[dependency.name] = dependency;
// Normal dependencies are those we get from pub. The others we
// already implicitly pin since we pull down one version of the
// Flutter and Dart SDKs, so we track which those are here so that we
......@@ -286,29 +379,28 @@ class UpdatePackagesCommand extends FlutterCommand {
}
}
}
}
// Now that we have all the dependencies we explicitly care about, we are
// going to create a fake package and then run either "pub upgrade" or "pub
// get" on it, depending on whether we are upgrading or not. If upgrading,
// the pub tool will attempt to bring these dependencies up to the most
// recent possible versions while honoring all their constraints. If not
// upgrading the pub tool will attempt to download any necessary package
// versions to the pub cache to warm the cache.
final PubDependencyTree tree = PubDependencyTree(); // object to collect results
final Directory tempDir = globals.fs.systemTempDirectory.createTempSync('flutter_update_packages.');
Future<void> _generateFakePackage({
Directory tempDir,
Iterable<PubspecDependency> dependencies,
List<PubspecYaml> pubspecs,
PubDependencyTree tree,
bool doUpgrade,
}) async {
try {
final File fakePackage = _pubspecFor(tempDir);
fakePackage.createSync();
fakePackage.writeAsStringSync(
_generateFakePubspec(
dependencies.values,
dependencies,
useAnyVersion: doUpgrade,
),
);
// Create a synthetic flutter SDK so that transitive flutter SDK
// constraints are not affected by this upgrade.
Directory temporaryFlutterSdk;
if (upgrade) {
if (doUpgrade) {
temporaryFlutterSdk = createTemporaryFlutterSdk(
globals.logger,
globals.fs,
......@@ -317,17 +409,14 @@ class UpdatePackagesCommand extends FlutterCommand {
);
}
// Next we run "pub upgrade" on this generated package, if we're doing
// an upgrade. Otherwise, we just run a regular "pub get" on it in order
// to force the download of any needed packages to the pub cache.
// Next we run "pub get" on it in order to force the download of any
// needed packages to the pub cache, upgrading if requested.
await pub.get(
context: PubContext.updatePackages,
directory: tempDir.path,
upgrade: doUpgrade,
offline: offline,
flutterRootOverride: upgrade
? temporaryFlutterSdk.path
: null,
offline: boolArg('offline'),
flutterRootOverride: doUpgrade ? temporaryFlutterSdk.path : null,
generateSyntheticPackage: false,
);
// Cleanup the temporary SDK
......@@ -354,8 +443,14 @@ class UpdatePackagesCommand extends FlutterCommand {
} finally {
tempDir.deleteSync(recursive: true);
}
}
if (doUpgrade) {
bool _upgradePubspecs({
@required PubDependencyTree tree,
@required List<PubspecYaml> pubspecs,
@required Set<String> specialDependencies,
@required Map<String, PubspecDependency> explicitDependencies,
}) {
// The transitive dependency tree for the fake package does not contain
// dependencies between Flutter SDK packages and pub packages. We add them
// here.
......@@ -372,16 +467,16 @@ class UpdatePackagesCommand extends FlutterCommand {
}
}
if (isPrintTransitiveClosure) {
if (boolArg('transitive-closure')) {
tree._dependencyTree.forEach((String from, Set<String> to) {
globals.printStatus('$from -> $to');
});
return FlutterCommandResult.success();
return true;
}
if (isPrintPaths) {
if (boolArg('paths')) {
showDependencyPaths(from: stringArg('from'), to: stringArg('to'), tree: tree);
return FlutterCommandResult.success();
return true;
}
// Now that we have collected all the data, we can apply our dependency
......@@ -395,13 +490,10 @@ class UpdatePackagesCommand extends FlutterCommand {
for (final PubspecYaml pubspec in pubspecs) {
pubspec.apply(tree, specialDependencies);
}
// Now that the pubspec.yamls are updated, we run "pub get" on each one so
// that the various packages are ready to use. This is what "flutter
// update-packages" does without --force-upgrade, so we can just fall into
// the regular code path.
return false;
}
Future<void> _runPubGetOnPackages(List<Directory> packages) async {
final Stopwatch timer = Stopwatch()..start();
int count = 0;
......@@ -416,7 +508,7 @@ class UpdatePackagesCommand extends FlutterCommand {
'Running "flutter pub get" in affected packages...',
);
try {
final TaskQueue<void> queue = TaskQueue<void>();
final TaskQueue<void> queue = TaskQueue<void>(maxJobs: intArg('jobs'));
for (final Directory dir in packages) {
unawaited(queue.add(() async {
final Stopwatch stopwatch = Stopwatch();
......@@ -424,7 +516,9 @@ class UpdatePackagesCommand extends FlutterCommand {
await pub.get(
context: PubContext.updatePackages,
directory: dir.path,
offline: offline,
// All dependencies should already have been downloaded by the fake
// package, so the concurrent checks can all happen offline.
offline: true,
generateSyntheticPackage: false,
printProgress: false,
);
......@@ -452,8 +546,6 @@ class UpdatePackagesCommand extends FlutterCommand {
final double seconds = timer.elapsedMilliseconds / 1000.0;
globals.printStatus("\nRan 'pub get' $count time${count == 1 ? "" : "s"} and fetched coverage data in ${seconds.toStringAsFixed(1)}s.");
return FlutterCommandResult.success();
}
void showDependencyPaths({
......@@ -752,12 +844,17 @@ class PubspecYaml {
}
/// This returns all regular dependencies and all dev dependencies.
Iterable<PubspecDependency> get allDependencies {
Iterable<PubspecDependency> get allExplicitDependencies {
return inputData
.whereType<PubspecDependency>()
.where((PubspecDependency data) => data.kind != DependencyKind.overridden && !data.isTransitive);
}
/// This returns all dependencies.
Iterable<PubspecDependency> get allDependencies {
return inputData.whereType<PubspecDependency>();
}
/// Take a dependency graph with explicit version numbers, and apply them to
/// the pubspec.yaml, ignoring any that we know are special dependencies (those
/// that depend on the Flutter or Dart SDK directly and are thus automatically
......@@ -814,7 +911,8 @@ class PubspecYaml {
// We output data that matches the format that
// PubspecDependency.parse can handle. The data.suffix is any
// previously-specified trailing comment.
assert(versions.contains(data.name));
assert(versions.contains(data.name),
"versions doesn't contain ${data.name}");
output.add(' ${data.name}: ${versions.versionFor(data.name)}${data.suffix}');
} else {
// If it wasn't a regular dependency, then we output the line
......
......@@ -1473,12 +1473,15 @@ abstract class FlutterCommand extends Command<void> {
ApplicationPackageFactory? applicationPackages;
/// Gets the parsed command-line option named [name] as `bool`.
/// Gets the parsed command-line option named [name] as a `bool`.
bool boolArg(String name) => argResults?[name] as bool? ?? false;
/// Gets the parsed command-line option named [name] as `String`.
/// Gets the parsed command-line option named [name] as a `String`.
String? stringArg(String name) => argResults?[name] as String?;
/// Gets the parsed command-line option named [name] as an `int`.
int? intArg(String name) => argResults?[name] as int?;
/// Gets the parsed command-line option named [name] as `List<String>`.
List<String> stringsArg(String name) => argResults?[name] as List<String>? ?? <String>[];
}
......
......@@ -4,9 +4,75 @@
// @dart = 2.8
import 'package:file/file.dart';
import 'package:file/memory.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/commands/update_packages.dart';
import 'package:flutter_tools/src/dart/pub.dart';
import 'package:meta/meta.dart';
import 'package:test/fake.dart';
import '../../src/common.dart';
import '../../src/context.dart';
import '../../src/test_flutter_command_runner.dart';
// An example pubspec.yaml from flutter, not necessary for it to be up to date.
const String kFlutterPubspecYaml = r'''
name: flutter
description: A framework for writing Flutter applications
homepage: http://flutter.dev
environment:
sdk: ">=2.2.2 <3.0.0"
dependencies:
# To update these, use "flutter update-packages --force-upgrade".
collection: 1.14.11
meta: 1.1.8
typed_data: 1.1.6
vector_math: 2.0.8
sky_engine:
sdk: flutter
gallery:
git:
url: https://github.com/flutter/gallery.git
ref: d00362e6bdd0f9b30bba337c358b9e4a6e4ca950
dev_dependencies:
flutter_test:
sdk: flutter
flutter_goldens:
sdk: flutter
archive: 2.0.11 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
# PUBSPEC CHECKSUM: 1234
''';
// An example pubspec.yaml, not necessary for it to be up to date.
const String kExamplesPubspecYaml = r'''
name: examples
description: Examples for flutter
homepage: http://flutter.dev
version: 1.0.0
environment:
sdk: ">=2.14.0-383.0.dev <3.0.0"
flutter: ">=2.5.0-6.0.pre.30 <3.0.0"
dependencies:
cupertino_icons: 1.0.4
flutter:
sdk: flutter
archive: 2.0.11 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
# PUBSPEC CHECKSUM: 6543
''';
void main() {
testWithoutContext('kManuallyPinnedDependencies pins are actually pins', () {
......@@ -16,4 +82,148 @@ void main() {
reason: 'Version pins in kManuallyPinnedDependencies must be specific pins, not ranges.',
);
});
group('update-packages', () {
FileSystem fileSystem;
Directory flutterSdk;
Directory flutter;
FakePub pub;
setUpAll(() {
Cache.disableLocking();
});
setUp(() {
fileSystem = MemoryFileSystem.test();
flutterSdk = fileSystem.directory('flutter')..createSync();
flutterSdk.childFile('version').writeAsStringSync('1.2.3');
flutter = flutterSdk.childDirectory('packages').childDirectory('flutter')
..createSync(recursive: true);
flutterSdk.childDirectory('dev').createSync(recursive: true);
flutterSdk.childDirectory('examples').childFile('pubspec.yaml')
..createSync(recursive: true)
..writeAsStringSync(kExamplesPubspecYaml);
flutter.childFile('pubspec.yaml').writeAsStringSync(kFlutterPubspecYaml);
Cache.flutterRoot = flutterSdk.absolute.path;
pub = FakePub(fileSystem);
});
testUsingContext('updates packages', () async {
final UpdatePackagesCommand command = UpdatePackagesCommand();
await createTestCommandRunner(command).run(<String>['update-packages']);
expect(pub.pubGetDirectories, equals(<String>[
'/.tmp_rand0/flutter_update_packages.rand0',
'/flutter/examples',
'/flutter/packages/flutter',
]));
expect(pub.pubBatchDirectories, isEmpty);
}, overrides: <Type, Generator>{
Pub: () => pub,
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(),
Cache: () => Cache.test(
processManager: FakeProcessManager.any(),
),
});
testUsingContext('force updates packages', () async {
final UpdatePackagesCommand command = UpdatePackagesCommand();
await createTestCommandRunner(command).run(<String>[
'update-packages',
'--force-upgrade',
]);
expect(pub.pubGetDirectories, equals(<String>[
'/.tmp_rand0/flutter_update_packages.rand0',
'/flutter/examples',
'/flutter/packages/flutter',
]));
expect(pub.pubBatchDirectories, equals(<String>[
'/.tmp_rand0/flutter_update_packages.rand0',
]));
}, overrides: <Type, Generator>{
Pub: () => pub,
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(),
Cache: () => Cache.test(
processManager: FakeProcessManager.any(),
),
});
});
}
class FakePub extends Fake implements Pub {
FakePub(this.fileSystem);
final FileSystem fileSystem;
final List<String> pubGetDirectories = <String>[];
final List<String> pubBatchDirectories = <String>[];
@override
Future<void> get({
@required PubContext context,
String directory,
bool skipIfAbsent = false,
bool upgrade = false,
bool offline = false,
bool generateSyntheticPackage = false,
String flutterRootOverride,
bool checkUpToDate = false,
bool shouldSkipThirdPartyGenerator = true,
bool printProgress = true,
}) async {
pubGetDirectories.add(directory);
fileSystem.directory(directory).childFile('pubspec.lock')
..createSync(recursive: true)
..writeAsStringSync('''
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
async:
dependency: "direct dev"
description:
name: async
url: "https://pub.dartlang.org"
source: hosted
version: "2.8.2"
sdks:
dart: ">=2.14.0 <3.0.0"
''');
fileSystem.currentDirectory
.childDirectory('.dart_tool')
.childFile('package_config.json')
..createSync(recursive: true)
..writeAsStringSync('{"configVersion":2,"packages":[]}');
}
@override
Future<void> batch(
List<String> arguments, {
@required PubContext context,
String directory,
MessageFilter filter,
String failureMessage = 'pub failed',
@required bool retry,
bool showTraceForErrors,
}) async {
pubBatchDirectories.add(directory);
'''
Dart SDK 2.16.0-144.0.dev
Flutter SDK 2.9.0-1.0.pre.263
flutter_api_samples 1.0.0
dependencies:
- cupertino_icons 1.0.4
- collection 1.15.0
- meta 1.7.0
- typed_data 1.3.0 [collection]
- vector_math 2.1.1
dev dependencies:
transitive dependencies:
- platform 3.1.0
- process 4.2.4 [file path platform]
'''.split('\n').forEach(filter);
}
}
......@@ -79,36 +79,36 @@ dependencies:
''';
void main() {
testWithoutContext('createTemporaryFlutterSdk creates an unpinned flutter SDK', () {
final FileSystem fileSystem = MemoryFileSystem.test();
FileSystem fileSystem;
Directory flutterSdk;
Directory flutter;
setUp(() {
fileSystem = MemoryFileSystem.test();
// Setup simplified Flutter SDK.
final Directory flutterSdk = fileSystem.directory('flutter')
..createSync();
flutterSdk = fileSystem.directory('flutter')..createSync();
// Create version file
flutterSdk.childFile('version').writeAsStringSync('1.2.3');
// Create a pubspec file
final Directory flutter = flutterSdk
.childDirectory('packages')
.childDirectory('flutter')
flutter = flutterSdk.childDirectory('packages').childDirectory('flutter')
..createSync(recursive: true);
flutter
.childFile('pubspec.yaml')
.writeAsStringSync(kFlutterPubspecYaml);
flutter.childFile('pubspec.yaml').writeAsStringSync(kFlutterPubspecYaml);
});
testWithoutContext(
'createTemporaryFlutterSdk creates an unpinned flutter SDK', () {
// A stray extra package should not cause a crash.
final Directory extra = flutterSdk
.childDirectory('packages')
.childDirectory('extra')
..createSync(recursive: true);
extra
.childFile('pubspec.yaml')
.writeAsStringSync(kExtraPubspecYaml);
extra.childFile('pubspec.yaml').writeAsStringSync(kExtraPubspecYaml);
// Create already parsed pubspecs.
final PubspecYaml flutterPubspec = PubspecYaml(flutter);
final PubspecDependency gitDependency = flutterPubspec.dependencies.firstWhere((PubspecDependency dep) => dep.kind == DependencyKind.git);
final PubspecDependency gitDependency = flutterPubspec.dependencies
.firstWhere((PubspecDependency dep) => dep.kind == DependencyKind.git);
expect(
gitDependency.lockLine,
'''
......@@ -152,25 +152,57 @@ void main() {
});
testWithoutContext('Throws a StateError on a malformed git: reference', () {
final FileSystem fileSystem = MemoryFileSystem.test();
// Setup simplified Flutter SDK.
final Directory flutterSdk = fileSystem.directory('flutter')
..createSync();
// Create version file
flutterSdk.childFile('version').writeAsStringSync('1.2.3');
// Create a pubspec file
final Directory flutter = flutterSdk
.childDirectory('packages')
.childDirectory('flutter')
..createSync(recursive: true);
flutter
.childFile('pubspec.yaml')
.writeAsStringSync(kInvalidGitPubspec);
// Create an invalid pubspec file.
flutter.childFile('pubspec.yaml').writeAsStringSync(kInvalidGitPubspec);
expect(
() => PubspecYaml(flutter),
throwsStateError,
);
});
testWithoutContext('PubspecYaml Loads dependencies', () async {
final PubspecYaml pubspecYaml = PubspecYaml(flutter);
expect(
pubspecYaml.allDependencies
.map<String>((PubspecDependency dependency) => '${dependency.name}: ${dependency.version}')
.toSet(),
equals(<String>{
'collection: 1.14.11',
'meta: 1.1.8',
'typed_data: 1.1.6',
'vector_math: 2.0.8',
'sky_engine: ',
'gallery: ',
'flutter_test: ',
'flutter_goldens: ',
'archive: 2.0.11',
}));
expect(
pubspecYaml.allExplicitDependencies
.map<String>((PubspecDependency dependency) => '${dependency.name}: ${dependency.version}')
.toSet(),
equals(<String>{
'collection: 1.14.11',
'meta: 1.1.8',
'typed_data: 1.1.6',
'vector_math: 2.0.8',
'sky_engine: ',
'gallery: ',
'flutter_test: ',
'flutter_goldens: '
}));
expect(
pubspecYaml.dependencies
.map<String>((PubspecDependency dependency) => '${dependency.name}: ${dependency.version}')
.toSet(),
equals(<String>{
'collection: 1.14.11',
'meta: 1.1.8',
'typed_data: 1.1.6',
'vector_math: 2.0.8',
'sky_engine: ',
'gallery: '
}));
});
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment