mirror of
https://github.com/flutter/packages.git
synced 2025-08-06 17:28:42 +08:00

Currently if a PR follows the recommended combo PR process for a federated plugin, the main PR will have CI errors that say the PR isn't allowed to do what it is doing, which is confusing, especially to new contributors or reviewers. This updates the tooling to detect the temporary overrides created by the tooling, and uses that to trigger a different error message that explains that the error is expected, and exists only to prevent accidental landing. Fixes https://github.com/flutter/flutter/issues/129303
652 lines
24 KiB
Dart
652 lines
24 KiB
Dart
// Copyright 2013 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:args/command_runner.dart';
|
|
import 'package:file/file.dart';
|
|
import 'package:file/memory.dart';
|
|
import 'package:flutter_plugin_tools/src/common/core.dart';
|
|
import 'package:flutter_plugin_tools/src/federation_safety_check_command.dart';
|
|
import 'package:mockito/mockito.dart';
|
|
import 'package:test/test.dart';
|
|
|
|
import 'common/package_command_test.mocks.dart';
|
|
import 'mocks.dart';
|
|
import 'util.dart';
|
|
|
|
void main() {
|
|
FileSystem fileSystem;
|
|
late MockPlatform mockPlatform;
|
|
late Directory packagesDir;
|
|
late CommandRunner<void> runner;
|
|
late RecordingProcessRunner processRunner;
|
|
|
|
setUp(() {
|
|
fileSystem = MemoryFileSystem();
|
|
mockPlatform = MockPlatform();
|
|
packagesDir = createPackagesDirectory(fileSystem: fileSystem);
|
|
|
|
final MockGitDir gitDir = MockGitDir();
|
|
when(gitDir.path).thenReturn(packagesDir.parent.path);
|
|
when(gitDir.runCommand(any, throwOnError: anyNamed('throwOnError')))
|
|
.thenAnswer((Invocation invocation) {
|
|
final List<String> arguments =
|
|
invocation.positionalArguments[0]! as List<String>;
|
|
// Route git calls through the process runner, to make mock output
|
|
// consistent with other processes. Attach the first argument to the
|
|
// command to make targeting the mock results easier.
|
|
final String gitCommand = arguments.removeAt(0);
|
|
return processRunner.run('git-$gitCommand', arguments);
|
|
});
|
|
|
|
processRunner = RecordingProcessRunner();
|
|
final FederationSafetyCheckCommand command = FederationSafetyCheckCommand(
|
|
packagesDir,
|
|
processRunner: processRunner,
|
|
platform: mockPlatform,
|
|
gitDir: gitDir);
|
|
|
|
runner = CommandRunner<void>('federation_safety_check_command',
|
|
'Test for $FederationSafetyCheckCommand');
|
|
runner.addCommand(command);
|
|
});
|
|
|
|
test('skips non-plugin packages', () async {
|
|
final RepositoryPackage package = createFakePackage('foo', packagesDir);
|
|
|
|
final String changedFileOutput = <File>[
|
|
package.libDirectory.childFile('foo.dart'),
|
|
].map((File file) => file.path).join('\n');
|
|
processRunner.mockProcessesForExecutable['git-diff'] = <FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: changedFileOutput)),
|
|
];
|
|
|
|
final List<String> output =
|
|
await runCapturingPrint(runner, <String>['federation-safety-check']);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for foo...'),
|
|
contains('Not a plugin'),
|
|
contains('Skipped 1 package(s)'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('skips unfederated plugins', () async {
|
|
final RepositoryPackage package = createFakePlugin('foo', packagesDir);
|
|
|
|
final String changedFileOutput = <File>[
|
|
package.libDirectory.childFile('foo.dart'),
|
|
].map((File file) => file.path).join('\n');
|
|
processRunner.mockProcessesForExecutable['git-diff'] = <FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: changedFileOutput)),
|
|
];
|
|
|
|
final List<String> output =
|
|
await runCapturingPrint(runner, <String>['federation-safety-check']);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for foo...'),
|
|
contains('Not a federated plugin'),
|
|
contains('Skipped 1 package(s)'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('skips interface packages', () async {
|
|
final Directory pluginGroupDir = packagesDir.childDirectory('foo');
|
|
final RepositoryPackage platformInterface =
|
|
createFakePlugin('foo_platform_interface', pluginGroupDir);
|
|
|
|
final String changedFileOutput = <File>[
|
|
platformInterface.libDirectory.childFile('foo.dart'),
|
|
].map((File file) => file.path).join('\n');
|
|
processRunner.mockProcessesForExecutable['git-diff'] = <FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: changedFileOutput)),
|
|
];
|
|
|
|
final List<String> output =
|
|
await runCapturingPrint(runner, <String>['federation-safety-check']);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for foo_platform_interface...'),
|
|
contains('Platform interface changes are not validated.'),
|
|
contains('Skipped 1 package(s)'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('allows changes to just an interface package', () async {
|
|
final Directory pluginGroupDir = packagesDir.childDirectory('foo');
|
|
final RepositoryPackage platformInterface =
|
|
createFakePlugin('foo_platform_interface', pluginGroupDir);
|
|
createFakePlugin('foo', pluginGroupDir);
|
|
createFakePlugin('foo_ios', pluginGroupDir);
|
|
createFakePlugin('foo_android', pluginGroupDir);
|
|
|
|
final String changedFileOutput = <File>[
|
|
platformInterface.libDirectory.childFile('foo.dart'),
|
|
platformInterface.pubspecFile,
|
|
].map((File file) => file.path).join('\n');
|
|
processRunner.mockProcessesForExecutable['git-diff'] = <FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: changedFileOutput)),
|
|
];
|
|
|
|
final List<String> output =
|
|
await runCapturingPrint(runner, <String>['federation-safety-check']);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for foo/foo...'),
|
|
contains('No Dart changes.'),
|
|
contains('Running for foo_android...'),
|
|
contains('No Dart changes.'),
|
|
contains('Running for foo_ios...'),
|
|
contains('No Dart changes.'),
|
|
contains('Running for foo_platform_interface...'),
|
|
contains('Ran for 3 package(s)'),
|
|
contains('Skipped 1 package(s)'),
|
|
]),
|
|
);
|
|
expect(
|
|
output,
|
|
isNot(contains(<Matcher>[
|
|
contains('No published changes for foo_platform_interface'),
|
|
])),
|
|
);
|
|
});
|
|
|
|
test('allows changes to multiple non-interface packages', () async {
|
|
final Directory pluginGroupDir = packagesDir.childDirectory('foo');
|
|
final RepositoryPackage appFacing = createFakePlugin('foo', pluginGroupDir);
|
|
final RepositoryPackage implementation =
|
|
createFakePlugin('foo_bar', pluginGroupDir);
|
|
createFakePlugin('foo_platform_interface', pluginGroupDir);
|
|
|
|
final String changedFileOutput = <File>[
|
|
appFacing.libDirectory.childFile('foo.dart'),
|
|
implementation.libDirectory.childFile('foo.dart'),
|
|
].map((File file) => file.path).join('\n');
|
|
processRunner.mockProcessesForExecutable['git-diff'] = <FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: changedFileOutput)),
|
|
];
|
|
|
|
final List<String> output =
|
|
await runCapturingPrint(runner, <String>['federation-safety-check']);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for foo/foo...'),
|
|
contains('No published changes for foo_platform_interface.'),
|
|
contains('Running for foo_bar...'),
|
|
contains('No published changes for foo_platform_interface.'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test(
|
|
'fails on changes to interface and non-interface packages in the same plugin',
|
|
() async {
|
|
final Directory pluginGroupDir = packagesDir.childDirectory('foo');
|
|
final RepositoryPackage appFacing = createFakePlugin('foo', pluginGroupDir);
|
|
final RepositoryPackage implementation =
|
|
createFakePlugin('foo_bar', pluginGroupDir);
|
|
final RepositoryPackage platformInterface =
|
|
createFakePlugin('foo_platform_interface', pluginGroupDir);
|
|
|
|
const String appFacingChanges = '''
|
|
diff --git a/packages/foo/foo/lib/foo.dart b/packages/foo/foo/lib/foo.dart
|
|
index abc123..def456 100644
|
|
--- a/packages/foo/foo/lib/foo.dart
|
|
+++ b/packages/foo/foo/lib/foo.dart
|
|
@@ -51,6 +51,9 @@ Future<bool> launchUrl(
|
|
return true;
|
|
}
|
|
|
|
+// This is a new method
|
|
+bool foo() => true;
|
|
+
|
|
// This in an existing method
|
|
void aMethod() {
|
|
// Do things.
|
|
''';
|
|
|
|
final String changedFileOutput = <File>[
|
|
appFacing.libDirectory.childFile('foo.dart'),
|
|
implementation.libDirectory.childFile('foo.dart'),
|
|
platformInterface.pubspecFile,
|
|
platformInterface.libDirectory.childFile('foo.dart'),
|
|
].map((File file) => file.path).join('\n');
|
|
processRunner.mockProcessesForExecutable['git-diff'] = <FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: changedFileOutput)),
|
|
// Ensure that a change with both a comment and non-comment addition is
|
|
// counted, to validate change analysis.
|
|
FakeProcessInfo(MockProcess(stdout: appFacingChanges),
|
|
<String>['', 'HEAD', '--', '/packages/foo/foo/lib/foo.dart']),
|
|
// The others diffs don't need to be specified, since empty diff is also
|
|
// treated as a non-comment change.
|
|
];
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['federation-safety-check'], errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for foo/foo...'),
|
|
contains('Dart changes are not allowed to other packages in foo in the '
|
|
'same PR as changes to public Dart code in foo_platform_interface, '
|
|
'as this can cause accidental breaking changes to be missed by '
|
|
'automated checks. Please split the changes to these two packages '
|
|
'into separate PRs.'),
|
|
contains('Running for foo_bar...'),
|
|
contains('Dart changes are not allowed to other packages in foo'),
|
|
contains('The following packages had errors:'),
|
|
contains('foo/foo:\n'
|
|
' foo_platform_interface changed.'),
|
|
contains('foo_bar:\n'
|
|
' foo_platform_interface changed.'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('fails with specific text for combo PRs using the recommended tooling',
|
|
() async {
|
|
final Directory pluginGroupDir = packagesDir.childDirectory('foo');
|
|
final RepositoryPackage appFacing = createFakePlugin('foo', pluginGroupDir);
|
|
final RepositoryPackage implementation =
|
|
createFakePlugin('foo_bar', pluginGroupDir);
|
|
final RepositoryPackage platformInterface =
|
|
createFakePlugin('foo_platform_interface', pluginGroupDir);
|
|
|
|
void addFakeTempPubspecOverrides(RepositoryPackage package) {
|
|
final String contents = package.pubspecFile.readAsStringSync();
|
|
package.pubspecFile.writeAsStringSync('''
|
|
$contents
|
|
|
|
# FOR TESTING AND INITIAL REVIEW ONLY. $kDoNotLandWarning.
|
|
dependency_overrides:
|
|
foo_platform_interface:
|
|
path: ../../../foo/foo_platform_interface
|
|
''');
|
|
}
|
|
|
|
addFakeTempPubspecOverrides(appFacing.getExamples().first);
|
|
addFakeTempPubspecOverrides(implementation.getExamples().first);
|
|
|
|
const String appFacingChanges = '''
|
|
diff --git a/packages/foo/foo/lib/foo.dart b/packages/foo/foo/lib/foo.dart
|
|
index abc123..def456 100644
|
|
--- a/packages/foo/foo/lib/foo.dart
|
|
+++ b/packages/foo/foo/lib/foo.dart
|
|
@@ -51,6 +51,9 @@ Future<bool> launchUrl(
|
|
return true;
|
|
}
|
|
|
|
+// This is a new method
|
|
+bool foo() => true;
|
|
+
|
|
// This in an existing method
|
|
void aMethod() {
|
|
// Do things.
|
|
''';
|
|
|
|
final String changedFileOutput = <File>[
|
|
appFacing.libDirectory.childFile('foo.dart'),
|
|
implementation.libDirectory.childFile('foo.dart'),
|
|
platformInterface.pubspecFile,
|
|
platformInterface.libDirectory.childFile('foo.dart'),
|
|
].map((File file) => file.path).join('\n');
|
|
processRunner.mockProcessesForExecutable['git-diff'] = <FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: changedFileOutput)),
|
|
FakeProcessInfo(MockProcess(stdout: appFacingChanges),
|
|
<String>['', 'HEAD', '--', '/packages/foo/foo/lib/foo.dart']),
|
|
// The others diffs don't need to be specified, since empty diff is also
|
|
// treated as a non-comment change.
|
|
];
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['federation-safety-check'], errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for foo/foo...'),
|
|
contains('"DO NOT MERGE" found in pubspec.yaml, so this is assumed to '
|
|
'be the initial combination PR for a federated change, following '
|
|
'the standard repository procedure. This failure is expected, in '
|
|
'order to prevent accidentally landing the temporary overrides, '
|
|
'and will automatically be resolved when the temporary overrides '
|
|
'are replaced by dependency version bumps later in the process.'),
|
|
contains('Running for foo_bar...'),
|
|
contains('"DO NOT MERGE" found in pubspec.yaml'),
|
|
contains('The following packages had errors:'),
|
|
contains('foo/foo:\n'
|
|
' Unresolved combo PR.'),
|
|
contains('foo_bar:\n'
|
|
' Unresolved combo PR.'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('ignores test-only changes to interface packages', () async {
|
|
final Directory pluginGroupDir = packagesDir.childDirectory('foo');
|
|
final RepositoryPackage appFacing = createFakePlugin('foo', pluginGroupDir);
|
|
final RepositoryPackage implementation =
|
|
createFakePlugin('foo_bar', pluginGroupDir);
|
|
final RepositoryPackage platformInterface =
|
|
createFakePlugin('foo_platform_interface', pluginGroupDir);
|
|
|
|
final String changedFileOutput = <File>[
|
|
appFacing.libDirectory.childFile('foo.dart'),
|
|
implementation.libDirectory.childFile('foo.dart'),
|
|
platformInterface.pubspecFile,
|
|
platformInterface.testDirectory.childFile('foo.dart'),
|
|
].map((File file) => file.path).join('\n');
|
|
processRunner.mockProcessesForExecutable['git-diff'] = <FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: changedFileOutput)),
|
|
];
|
|
|
|
final List<String> output =
|
|
await runCapturingPrint(runner, <String>['federation-safety-check']);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for foo/foo...'),
|
|
contains('No public code changes for foo_platform_interface.'),
|
|
contains('Running for foo_bar...'),
|
|
contains('No public code changes for foo_platform_interface.'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('ignores unpublished changes to interface packages', () async {
|
|
final Directory pluginGroupDir = packagesDir.childDirectory('foo');
|
|
final RepositoryPackage appFacing = createFakePlugin('foo', pluginGroupDir);
|
|
final RepositoryPackage implementation =
|
|
createFakePlugin('foo_bar', pluginGroupDir);
|
|
final RepositoryPackage platformInterface =
|
|
createFakePlugin('foo_platform_interface', pluginGroupDir);
|
|
|
|
final String changedFileOutput = <File>[
|
|
appFacing.libDirectory.childFile('foo.dart'),
|
|
implementation.libDirectory.childFile('foo.dart'),
|
|
platformInterface.pubspecFile,
|
|
platformInterface.libDirectory.childFile('foo.dart'),
|
|
].map((File file) => file.path).join('\n');
|
|
processRunner.mockProcessesForExecutable['git-diff'] = <FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: changedFileOutput)),
|
|
];
|
|
// Simulate no change to the version in the interface's pubspec.yaml.
|
|
processRunner.mockProcessesForExecutable['git-show'] = <FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(
|
|
stdout: platformInterface.pubspecFile.readAsStringSync())),
|
|
];
|
|
|
|
final List<String> output =
|
|
await runCapturingPrint(runner, <String>['federation-safety-check']);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for foo/foo...'),
|
|
contains('No published changes for foo_platform_interface.'),
|
|
contains('Running for foo_bar...'),
|
|
contains('No published changes for foo_platform_interface.'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('ignores comment-only changes in implementation packages', () async {
|
|
final Directory pluginGroupDir = packagesDir.childDirectory('foo');
|
|
final RepositoryPackage implementation =
|
|
createFakePlugin('foo_bar', pluginGroupDir);
|
|
final RepositoryPackage platformInterface =
|
|
createFakePlugin('foo_platform_interface', pluginGroupDir);
|
|
|
|
final String changedFileOutput = <File>[
|
|
implementation.libDirectory.childFile('foo.dart'),
|
|
platformInterface.pubspecFile,
|
|
platformInterface.libDirectory.childFile('foo.dart'),
|
|
].map((File file) => file.path).join('\n');
|
|
|
|
const String platformInterfaceChanges = '''
|
|
diff --git a/packages/foo/foo_platform_interface/lib/foo.dart b/packages/foo/foo_platform_interface/lib/foo.dart
|
|
index abc123..def456 100644
|
|
--- a/packages/foo/foo_platform_interface/lib/foo.dart
|
|
+++ b/packages/foo/foo_platform_interface/lib/foo.dart
|
|
@@ -51,6 +51,7 @@ Future<bool> launchUrl(
|
|
enum Foo {
|
|
a,
|
|
b,
|
|
+ c,
|
|
d,
|
|
e,
|
|
}
|
|
''';
|
|
const String implementationChanges = '''
|
|
diff --git a/packages/foo/foo_bar/lib/foo.dart b/packages/foo/foo_bar/lib/foo.dart
|
|
index abc123..def456 100644
|
|
--- a/packages/foo/foo_bar/lib/foo.dart
|
|
+++ b/packages/foo/foo_bar/lib/foo.dart
|
|
@@ -51,6 +51,7 @@ Future<bool> launchUrl(
|
|
}
|
|
|
|
void foo() {
|
|
+ // ignore: exhaustive_cases
|
|
switch(a_foo) {
|
|
case a:
|
|
// Do things
|
|
''';
|
|
|
|
processRunner.mockProcessesForExecutable['git-diff'] = <FakeProcessInfo>[
|
|
FakeProcessInfo(
|
|
MockProcess(stdout: changedFileOutput), <String>['--name-only']),
|
|
FakeProcessInfo(MockProcess(stdout: implementationChanges),
|
|
<String>['', 'HEAD', '--', '/packages/foo/foo_bar/lib/foo.dart']),
|
|
FakeProcessInfo(MockProcess(stdout: platformInterfaceChanges), <String>[
|
|
'',
|
|
'HEAD',
|
|
'--',
|
|
'/packages/foo/foo_platform_interface/lib/foo.dart'
|
|
]),
|
|
];
|
|
|
|
final List<String> output =
|
|
await runCapturingPrint(runner, <String>['federation-safety-check']);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for foo_bar...'),
|
|
contains('No Dart changes.'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('ignores comment-only changes in platform interface packages', () async {
|
|
final Directory pluginGroupDir = packagesDir.childDirectory('foo');
|
|
final RepositoryPackage implementation =
|
|
createFakePlugin('foo_bar', pluginGroupDir);
|
|
final RepositoryPackage platformInterface =
|
|
createFakePlugin('foo_platform_interface', pluginGroupDir);
|
|
|
|
final String changedFileOutput = <File>[
|
|
implementation.libDirectory.childFile('foo.dart'),
|
|
platformInterface.pubspecFile,
|
|
platformInterface.libDirectory.childFile('foo.dart'),
|
|
].map((File file) => file.path).join('\n');
|
|
|
|
const String platformInterfaceChanges = '''
|
|
diff --git a/packages/foo/foo_platform_interface/lib/foo.dart b/packages/foo/foo_platform_interface/lib/foo.dart
|
|
index abc123..def456 100644
|
|
--- a/packages/foo/foo_platform_interface/lib/foo.dart
|
|
+++ b/packages/foo/foo_platform_interface/lib/foo.dart
|
|
@@ -51,6 +51,8 @@ Future<bool> launchUrl(
|
|
// existing comment
|
|
// existing comment
|
|
// existing comment
|
|
+ //
|
|
+ // additional comment
|
|
void foo() {
|
|
some code;
|
|
}
|
|
''';
|
|
const String implementationChanges = '''
|
|
diff --git a/packages/foo/foo_bar/lib/foo.dart b/packages/foo/foo_bar/lib/foo.dart
|
|
index abc123..def456 100644
|
|
--- a/packages/foo/foo_bar/lib/foo.dart
|
|
+++ b/packages/foo/foo_bar/lib/foo.dart
|
|
@@ -51,6 +51,7 @@ Future<bool> launchUrl(
|
|
}
|
|
|
|
void foo() {
|
|
+ new code;
|
|
existing code;
|
|
...
|
|
...
|
|
''';
|
|
|
|
processRunner.mockProcessesForExecutable['git-diff'] = <FakeProcessInfo>[
|
|
FakeProcessInfo(
|
|
MockProcess(stdout: changedFileOutput), <String>['--name-only']),
|
|
FakeProcessInfo(MockProcess(stdout: implementationChanges),
|
|
<String>['', 'HEAD', '--', '/packages/foo/foo_bar/lib/foo.dart']),
|
|
FakeProcessInfo(MockProcess(stdout: platformInterfaceChanges), <String>[
|
|
'',
|
|
'HEAD',
|
|
'--',
|
|
'/packages/foo/foo_platform_interface/lib/foo.dart'
|
|
]),
|
|
];
|
|
|
|
final List<String> output =
|
|
await runCapturingPrint(runner, <String>['federation-safety-check']);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for foo_bar...'),
|
|
contains('No public code changes for foo_platform_interface.'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('allows things that look like mass changes, with warning', () async {
|
|
final Directory pluginGroupDir = packagesDir.childDirectory('foo');
|
|
final RepositoryPackage appFacing = createFakePlugin('foo', pluginGroupDir);
|
|
final RepositoryPackage implementation =
|
|
createFakePlugin('foo_bar', pluginGroupDir);
|
|
final RepositoryPackage platformInterface =
|
|
createFakePlugin('foo_platform_interface', pluginGroupDir);
|
|
|
|
final RepositoryPackage otherPlugin1 = createFakePlugin('bar', packagesDir);
|
|
final RepositoryPackage otherPlugin2 = createFakePlugin('baz', packagesDir);
|
|
|
|
final String changedFileOutput = <File>[
|
|
appFacing.libDirectory.childFile('foo.dart'),
|
|
implementation.libDirectory.childFile('foo.dart'),
|
|
platformInterface.pubspecFile,
|
|
platformInterface.libDirectory.childFile('foo.dart'),
|
|
otherPlugin1.libDirectory.childFile('bar.dart'),
|
|
otherPlugin2.libDirectory.childFile('baz.dart'),
|
|
].map((File file) => file.path).join('\n');
|
|
processRunner.mockProcessesForExecutable['git-diff'] = <FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: changedFileOutput)),
|
|
];
|
|
|
|
final List<String> output =
|
|
await runCapturingPrint(runner, <String>['federation-safety-check']);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for foo/foo...'),
|
|
contains(
|
|
'Ignoring potentially dangerous change, as this appears to be a mass change.'),
|
|
contains('Running for foo_bar...'),
|
|
contains(
|
|
'Ignoring potentially dangerous change, as this appears to be a mass change.'),
|
|
contains('Ran for 2 package(s) (2 with warnings)'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('handles top-level files that match federated package heuristics',
|
|
() async {
|
|
final RepositoryPackage plugin = createFakePlugin('foo', packagesDir);
|
|
|
|
final String changedFileOutput = <File>[
|
|
// This should be picked up as a change to 'foo', and not crash.
|
|
plugin.directory.childFile('foo_bar.baz'),
|
|
].map((File file) => file.path).join('\n');
|
|
processRunner.mockProcessesForExecutable['git-diff'] = <FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: changedFileOutput)),
|
|
];
|
|
|
|
final List<String> output =
|
|
await runCapturingPrint(runner, <String>['federation-safety-check']);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for foo...'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('handles deletion of an entire plugin', () async {
|
|
// Simulate deletion, in the form of diffs for packages that don't exist in
|
|
// the filesystem.
|
|
final String changedFileOutput = <File>[
|
|
packagesDir.childDirectory('foo').childFile('pubspec.yaml'),
|
|
packagesDir
|
|
.childDirectory('foo')
|
|
.childDirectory('lib')
|
|
.childFile('foo.dart'),
|
|
packagesDir
|
|
.childDirectory('foo_platform_interface')
|
|
.childFile('pubspec.yaml'),
|
|
packagesDir
|
|
.childDirectory('foo_platform_interface')
|
|
.childDirectory('lib')
|
|
.childFile('foo.dart'),
|
|
packagesDir.childDirectory('foo_web').childFile('pubspec.yaml'),
|
|
packagesDir
|
|
.childDirectory('foo_web')
|
|
.childDirectory('lib')
|
|
.childFile('foo.dart'),
|
|
].map((File file) => file.path).join('\n');
|
|
processRunner.mockProcessesForExecutable['git-diff'] = <FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: changedFileOutput)),
|
|
];
|
|
|
|
final List<String> output =
|
|
await runCapturingPrint(runner, <String>['federation-safety-check']);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Ran for 0 package(s)'),
|
|
]),
|
|
);
|
|
});
|
|
}
|