flutter/packages/flutter_tools/test/commands.shard/hermetic/update_packages_test.dart
auto-submit[bot] 4372bfbc6c
Reverts "Add workspace (#169451)" (#169468)
<!-- start_original_pr_link -->
Reverts: flutter/flutter#169451
<!-- end_original_pr_link -->
<!-- start_initiating_author -->
Initiated by: matanlurey
<!-- end_initiating_author -->
<!-- start_revert_reason -->
Reason for reverting: Broke a number of post-submit tests
(ios_app_extension, packages_autoroller).
<!-- end_revert_reason -->
<!-- start_original_pr_author -->
Original PR Author: mosuem
<!-- end_original_pr_author -->

<!-- start_reviewers -->
Reviewed By: {matanlurey}
<!-- end_reviewers -->

<!-- start_revert_body -->
This change reverts the following previous change:
Reland after #169357.

Switch Flutter to use pub workspaces as a preparation to unpin selected
packages.

Assumptions:

1. No packages in this repository are published to pub.dev --> We can
use `any` dependencies in most local pubspecs, as the global constraint
defines the version. An exception are the packages used outside of this
repo with an `sdk` dependency, namely `flutter_localizations`,
`flutter_test`, and `flutter`.
2. The "universes" `{flutter_tools}` and `{flutter,
flutter_localizations, flutter_goldens}` can use different packages
versions, as they are not resolved together. --> We do not need to
upgrade them in sync, we can first do one "universe", then the other.

Based on these assumptions, we use
https://github.com/mosuem/pubspec_merger.dart to merge all packages in
the `flutter` universe into a top-level pub workspace.

The `flutter` and `flutter_tools` workspaces being separate also ensures
that changes to `flutter` will not inadvertently break `flutter_tools`,
with not-so-nice consequences for our users which would be unable to run
`flutter upgrade`.

There is a third "top-level" pubspec besides `./pubspec.yaml` and
`packages/flutter_tools/pubspec.yaml`, namely
`packages/flutter_tools/.../widget_preview_scaffold/pubspec.yaml`. This
is an artifact due to it living under `flutter_tools`, so it can't be
part of the `./pubspec.yaml` workspace. Moving it would be a larger
change, and out of the scope of this PR.

This required a rewrite of the update-packages tool, but the main
functionality stays the same, as well as the argument names, to ensure a
seamless transition.

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel
on [Discord].

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md

<!-- end_revert_body -->

Co-authored-by: auto-submit[bot] <flutter-engprod-team@google.com>
2025-05-26 14:07:27 +00:00

443 lines
14 KiB
Dart

// Copyright 2014 The Flutter Authors. 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:file/file.dart';
import 'package:file/memory.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/logger.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:flutter_tools/src/project.dart';
import 'package:test/fake.dart';
import 'package:yaml/yaml.dart';
import '../../src/context.dart';
import '../../src/package_config.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: ^3.7.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: '>=3.2.0-0 <4.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
''';
const String kVersionJson = '''
{
"frameworkVersion": "1.2.3",
"channel": "[user-branch]",
"repositoryUrl": "git@github.com:flutter/flutter.git",
"frameworkRevision": "1234567812345678123456781234567812345678",
"frameworkCommitDate": "2024-02-06 22:26:52 +0100",
"engineRevision": "abcdef01abcdef01abcdef01abcdef01abcdef01",
"dartSdkVersion": "1.2.3",
"devToolsVersion": "1.2.3",
"flutterVersion": "1.2.3"
}
''';
void main() {
group('update-packages', () {
late FileSystem fileSystem;
late Directory flutterSdk;
late Directory flutter;
late FakePub pub;
late FakeProcessManager processManager;
late BufferLogger logger;
setUpAll(() {
Cache.disableLocking();
logger = BufferLogger.test();
});
setUp(() {
fileSystem = MemoryFileSystem.test();
flutterSdk = fileSystem.directory('flutter')..createSync();
flutterSdk.childFile('version').writeAsStringSync('1.2.3');
flutterSdk.childDirectory('bin').childDirectory('cache').childFile('flutter.version.json')
..createSync(recursive: true)
..writeAsStringSync(kVersionJson);
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();
processManager = FakeProcessManager.empty();
});
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/synthetic_package',
'/flutter/examples',
'/flutter/packages/flutter',
]),
);
expect(pub.pubBatchDirectories, isEmpty);
},
overrides: <Type, Generator>{
Pub: () => pub,
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
Cache: () => Cache.test(processManager: processManager),
},
);
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/synthetic_package',
'/flutter/examples',
'/flutter/packages/flutter',
]),
);
expect(
pub.pubBatchDirectories,
equals(<String>['/.tmp_rand0/flutter_update_packages.rand0/synthetic_package']),
);
},
overrides: <Type, Generator>{
Pub: () => pub,
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
Cache: () => Cache.test(processManager: processManager),
},
);
testUsingContext(
'force updates packages --jobs=1',
() async {
final UpdatePackagesCommand command = UpdatePackagesCommand();
await createTestCommandRunner(
command,
).run(<String>['update-packages', '--force-upgrade', '--jobs=1']);
expect(
pub.pubGetDirectories,
equals(<String>[
'/.tmp_rand0/flutter_update_packages.rand0/synthetic_package',
'/flutter/examples',
'/flutter/packages/flutter',
]),
);
expect(
pub.pubBatchDirectories,
equals(<String>['/.tmp_rand0/flutter_update_packages.rand0/synthetic_package']),
);
},
overrides: <Type, Generator>{
Pub: () => pub,
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
Cache: () => Cache.test(processManager: processManager),
},
);
testUsingContext(
'--transitive-closure --consumer-only',
() async {
final UpdatePackagesCommand command = UpdatePackagesCommand();
await createTestCommandRunner(
command,
).run(<String>['update-packages', '--transitive-closure', '--consumer-only']);
expect(
pub.pubGetDirectories,
equals(<String>['/.tmp_rand0/flutter_update_packages.rand0/synthetic_package']),
);
expect(
pub.pubBatchDirectories,
equals(<String>['/.tmp_rand0/flutter_update_packages.rand0/synthetic_package']),
);
// Expecting a line like:
// 'flutter -> {collection, meta, typed_data, vector_math}'
expect(logger.statusText, contains(RegExp(r'flutter -> {([a-z_]+, )*([a-z_]+)+}')));
},
overrides: <Type, Generator>{
Pub: () => pub,
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
Cache: () => Cache.test(processManager: processManager),
Logger: () => logger,
},
);
testUsingContext(
'--cherry-pick-package',
() async {
final UpdatePackagesCommand command = UpdatePackagesCommand();
await createTestCommandRunner(command).run(<String>[
'update-packages',
'--cherry-pick-package=vector_math',
'--cherry-pick-version=2.0.9',
]);
expect(
pub.pubGetDirectories,
equals(<String>[
'/.tmp_rand0/flutter_update_packages.rand0/synthetic_package',
'/flutter/examples',
'/flutter/packages/flutter',
]),
);
expect(
pub.pubBatchDirectories,
equals(<String>['/.tmp_rand0/flutter_update_packages.rand0/synthetic_package']),
);
expect(pub.pubspecYamls, hasLength(3));
final String output = pub.pubspecYamls.first;
expect(output, isNotNull);
expect(output, contains('collection: 1.14.11\n'));
expect(output, contains('meta: 1.1.8\n'));
expect(output, contains('typed_data: 1.1.6\n'));
expect(output, contains('vector_math: 2.0.9\n'));
expect(output, isNot(contains('vector_math: 2.0.8')));
expect(output, isNot(contains('vector_math: ">= 2.0.8"')));
expect(output, isNot(contains("vector_math: '>= 2.0.8'")));
},
overrides: <Type, Generator>{
Pub: () => pub,
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
Cache: () => Cache.test(processManager: processManager),
Logger: () => logger,
},
);
testUsingContext(
'--force-upgrade',
() 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/synthetic_package',
'/flutter/examples',
'/flutter/packages/flutter',
]),
);
expect(
pub.pubBatchDirectories,
equals(<String>['/.tmp_rand0/flutter_update_packages.rand0/synthetic_package']),
);
expect(pub.pubspecYamls, hasLength(3));
final String output = pub.pubspecYamls.first;
expect(output, isNotNull);
expect(output, contains("collection: '>= 1.14.11'\n"));
expect(output, contains("meta: '>= 1.1.8'\n"));
expect(output, contains("typed_data: '>= 1.1.6'\n"));
expect(output, contains("vector_math: '>= 2.0.8'\n"));
expect(output, isNot(contains('vector_math: 2.0.8')));
},
overrides: <Type, Generator>{
Pub: () => pub,
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
Cache: () => Cache.test(processManager: processManager),
Logger: () => logger,
},
);
testUsingContext(
'force updates packages --synthetic-package-path',
() async {
final UpdatePackagesCommand command = UpdatePackagesCommand();
const String dir = '/path/to/synthetic/package';
await createTestCommandRunner(
command,
).run(<String>['update-packages', '--force-upgrade', '--synthetic-package-path=$dir']);
expect(
pub.pubGetDirectories,
equals(<String>[
'$dir/synthetic_package',
'/flutter/examples',
'/flutter/packages/flutter',
]),
);
expect(pub.pubBatchDirectories, equals(<String>['$dir/synthetic_package']));
},
overrides: <Type, Generator>{
Pub: () => pub,
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
Cache: () => Cache.test(processManager: processManager),
},
);
});
group('generateFakePubspec', () {
const String prevVersion = '1.2.0';
testUsingContext('constrains package versions to >= previous version if doUpgrade: true', () {
final String pubspecSource = generateFakePubspec(<PubspecDependency>[
PubspecDependency(
' foo: $prevVersion',
'foo',
'',
version: prevVersion,
sourcePath: '/path/to/pubspec.yaml',
kind: DependencyKind.normal,
isTransitive: false,
isDevDependency: false,
),
], doUpgrade: true);
final YamlMap pubspec = loadYaml(pubspecSource) as YamlMap;
expect((pubspec['dependencies'] as YamlMap)['foo'], '>= $prevVersion');
});
testUsingContext('uses previous package versions doUpgrade: false', () {
final String pubspecSource = generateFakePubspec(<PubspecDependency>[
PubspecDependency(
' foo: $prevVersion',
'foo',
'',
version: prevVersion,
sourcePath: '/path/to/pubspec.yaml',
kind: DependencyKind.normal,
isTransitive: false,
isDevDependency: false,
),
]);
final YamlMap pubspec = loadYaml(pubspecSource) as YamlMap;
expect((pubspec['dependencies'] as YamlMap)['foo'], prevVersion);
});
});
}
class FakePub extends Fake implements Pub {
FakePub();
final List<String> pubGetDirectories = <String>[];
final List<String> pubBatchDirectories = <String>[];
final List<String> pubspecYamls = <String>[];
@override
Future<void> get({
required PubContext context,
required FlutterProject project,
bool upgrade = false,
bool offline = false,
bool generateSyntheticPackage = false,
bool generateSyntheticPackageForExample = false,
String? flutterRootOverride,
bool checkUpToDate = false,
bool shouldSkipThirdPartyGenerator = true,
PubOutputMode outputMode = PubOutputMode.all,
}) async {
pubGetDirectories.add(project.directory.path);
pubspecYamls.add(project.directory.childFile('pubspec.yaml').readAsStringSync());
project.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"
''');
writePackageConfigFiles(directory: project.directory, mainLibName: 'my_app');
}
@override
Future<void> batch(
List<String> arguments, {
required PubContext context,
String? directory,
MessageFilter? filter,
String failureMessage = 'pub failed',
}) async {
if (directory != null) {
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!);
}
}