mirror of
https://github.com/flutter/packages.git
synced 2025-06-08 04:18:49 +08:00

`script/tool_runner.sh` has only been intended for CI usage for quite a while, but it still lives in a prominent non-CI location which has been a source of confusion for developers (e.g., people looking at how CI is running things, and reasonably assuming that since the script isn't in a CI-specific location, it's for general use). While running it for simple cases works, it conflicts with any package selection, which is common thing to want to control when running locally. This makes the following changes: - Move the script into the .ci directory - Adds explicit comments that it's not designed for local use, with a pointer to the corresponding local command - Removes a stale reference to external-to-the-repo use of tool_runner.sh, as that usage no longer exists - Also consolidates and updates the links to the external scripts - Updates a couple of CONTRIBUTING.md comments that still said to run tool_runner.sh - Opportunistically cleans up some tooling references to .cirrus.yml, found while cleaning up comments in tool_runner.sh and checking for other references.
1383 lines
50 KiB
Dart
1383 lines
50 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/common/package_command.dart';
|
|
import 'package:git/git.dart';
|
|
import 'package:mockito/annotations.dart';
|
|
import 'package:mockito/mockito.dart';
|
|
import 'package:test/test.dart';
|
|
|
|
import '../mocks.dart';
|
|
import '../util.dart';
|
|
import 'package_command_test.mocks.dart';
|
|
|
|
@GenerateMocks(<Type>[GitDir])
|
|
void main() {
|
|
late RecordingProcessRunner processRunner;
|
|
late SamplePackageCommand command;
|
|
late CommandRunner<void> runner;
|
|
late FileSystem fileSystem;
|
|
late MockPlatform mockPlatform;
|
|
late Directory packagesDir;
|
|
late Directory thirdPartyPackagesDir;
|
|
|
|
setUp(() {
|
|
fileSystem = MemoryFileSystem();
|
|
mockPlatform = MockPlatform();
|
|
packagesDir = createPackagesDirectory(fileSystem: fileSystem);
|
|
thirdPartyPackagesDir = packagesDir.parent
|
|
.childDirectory('third_party')
|
|
.childDirectory('packages');
|
|
|
|
final MockGitDir gitDir = MockGitDir();
|
|
when(gitDir.runCommand(any, throwOnError: anyNamed('throwOnError')))
|
|
.thenAnswer((Invocation invocation) {
|
|
final List<String> arguments =
|
|
invocation.positionalArguments[0]! as List<String>;
|
|
// 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();
|
|
command = SamplePackageCommand(
|
|
packagesDir,
|
|
processRunner: processRunner,
|
|
platform: mockPlatform,
|
|
gitDir: gitDir,
|
|
);
|
|
runner =
|
|
CommandRunner<void>('common_command', 'Test for common functionality');
|
|
runner.addCommand(command);
|
|
});
|
|
|
|
group('plugin iteration', () {
|
|
test('all plugins from file system', () async {
|
|
final RepositoryPackage plugin1 =
|
|
createFakePlugin('plugin1', packagesDir);
|
|
final RepositoryPackage plugin2 =
|
|
createFakePlugin('plugin2', packagesDir);
|
|
await runCapturingPrint(runner, <String>['sample']);
|
|
expect(command.plugins,
|
|
unorderedEquals(<String>[plugin1.path, plugin2.path]));
|
|
});
|
|
|
|
test('includes both plugins and packages', () async {
|
|
final RepositoryPackage plugin1 =
|
|
createFakePlugin('plugin1', packagesDir);
|
|
final RepositoryPackage plugin2 =
|
|
createFakePlugin('plugin2', packagesDir);
|
|
final RepositoryPackage package3 =
|
|
createFakePackage('package3', packagesDir);
|
|
final RepositoryPackage package4 =
|
|
createFakePackage('package4', packagesDir);
|
|
await runCapturingPrint(runner, <String>['sample']);
|
|
expect(
|
|
command.plugins,
|
|
unorderedEquals(<String>[
|
|
plugin1.path,
|
|
plugin2.path,
|
|
package3.path,
|
|
package4.path,
|
|
]));
|
|
});
|
|
|
|
test('includes packages without source', () async {
|
|
final RepositoryPackage package =
|
|
createFakePackage('package', packagesDir);
|
|
package.libDirectory.deleteSync(recursive: true);
|
|
|
|
await runCapturingPrint(runner, <String>['sample']);
|
|
expect(
|
|
command.plugins,
|
|
unorderedEquals(<String>[
|
|
package.path,
|
|
]));
|
|
});
|
|
|
|
test('all plugins includes third_party/packages', () async {
|
|
final RepositoryPackage plugin1 =
|
|
createFakePlugin('plugin1', packagesDir);
|
|
final RepositoryPackage plugin2 =
|
|
createFakePlugin('plugin2', packagesDir);
|
|
final RepositoryPackage plugin3 =
|
|
createFakePlugin('plugin3', thirdPartyPackagesDir);
|
|
await runCapturingPrint(runner, <String>['sample']);
|
|
expect(command.plugins,
|
|
unorderedEquals(<String>[plugin1.path, plugin2.path, plugin3.path]));
|
|
});
|
|
|
|
test('--packages limits packages', () async {
|
|
final RepositoryPackage plugin1 =
|
|
createFakePlugin('plugin1', packagesDir);
|
|
createFakePlugin('plugin2', packagesDir);
|
|
createFakePackage('package3', packagesDir);
|
|
final RepositoryPackage package4 =
|
|
createFakePackage('package4', packagesDir);
|
|
await runCapturingPrint(
|
|
runner, <String>['sample', '--packages=plugin1,package4']);
|
|
expect(
|
|
command.plugins,
|
|
unorderedEquals(<String>[
|
|
plugin1.path,
|
|
package4.path,
|
|
]));
|
|
});
|
|
|
|
test('--plugins acts as an alias to --packages', () async {
|
|
final RepositoryPackage plugin1 =
|
|
createFakePlugin('plugin1', packagesDir);
|
|
createFakePlugin('plugin2', packagesDir);
|
|
createFakePackage('package3', packagesDir);
|
|
final RepositoryPackage package4 =
|
|
createFakePackage('package4', packagesDir);
|
|
await runCapturingPrint(
|
|
runner, <String>['sample', '--plugins=plugin1,package4']);
|
|
expect(
|
|
command.plugins,
|
|
unorderedEquals(<String>[
|
|
plugin1.path,
|
|
package4.path,
|
|
]));
|
|
});
|
|
|
|
test('exclude packages when packages flag is specified', () async {
|
|
createFakePlugin('plugin1', packagesDir);
|
|
final RepositoryPackage plugin2 =
|
|
createFakePlugin('plugin2', packagesDir);
|
|
await runCapturingPrint(runner, <String>[
|
|
'sample',
|
|
'--packages=plugin1,plugin2',
|
|
'--exclude=plugin1'
|
|
]);
|
|
expect(command.plugins, unorderedEquals(<String>[plugin2.path]));
|
|
});
|
|
|
|
test("exclude packages when packages flag isn't specified", () async {
|
|
createFakePlugin('plugin1', packagesDir);
|
|
createFakePlugin('plugin2', packagesDir);
|
|
await runCapturingPrint(
|
|
runner, <String>['sample', '--exclude=plugin1,plugin2']);
|
|
expect(command.plugins, unorderedEquals(<String>[]));
|
|
});
|
|
|
|
test('exclude federated plugins when packages flag is specified', () async {
|
|
createFakePlugin('plugin1', packagesDir.childDirectory('federated'));
|
|
final RepositoryPackage plugin2 =
|
|
createFakePlugin('plugin2', packagesDir);
|
|
await runCapturingPrint(runner, <String>[
|
|
'sample',
|
|
'--packages=federated/plugin1,plugin2',
|
|
'--exclude=federated/plugin1'
|
|
]);
|
|
expect(command.plugins, unorderedEquals(<String>[plugin2.path]));
|
|
});
|
|
|
|
test('exclude entire federated plugins when packages flag is specified',
|
|
() async {
|
|
createFakePlugin('plugin1', packagesDir.childDirectory('federated'));
|
|
final RepositoryPackage plugin2 =
|
|
createFakePlugin('plugin2', packagesDir);
|
|
await runCapturingPrint(runner, <String>[
|
|
'sample',
|
|
'--packages=federated/plugin1,plugin2',
|
|
'--exclude=federated'
|
|
]);
|
|
expect(command.plugins, unorderedEquals(<String>[plugin2.path]));
|
|
});
|
|
|
|
test('exclude accepts config files', () async {
|
|
createFakePlugin('plugin1', packagesDir);
|
|
final File configFile = packagesDir.childFile('exclude.yaml');
|
|
configFile.writeAsStringSync('- plugin1');
|
|
|
|
await runCapturingPrint(runner, <String>[
|
|
'sample',
|
|
'--packages=plugin1',
|
|
'--exclude=${configFile.path}'
|
|
]);
|
|
expect(command.plugins, unorderedEquals(<String>[]));
|
|
});
|
|
|
|
test('filter-packages-to accepts config files', () async {
|
|
final RepositoryPackage plugin1 =
|
|
createFakePlugin('plugin1', packagesDir);
|
|
createFakePlugin('plugin2', packagesDir);
|
|
final File configFile = packagesDir.childFile('exclude.yaml');
|
|
configFile.writeAsStringSync('- plugin1');
|
|
|
|
await runCapturingPrint(runner, <String>[
|
|
'sample',
|
|
'--packages=plugin1,plugin2',
|
|
'--filter-packages-to=${configFile.path}'
|
|
]);
|
|
expect(command.plugins, unorderedEquals(<String>[plugin1.path]));
|
|
});
|
|
|
|
test(
|
|
'explicitly specifying the plugin (group) name of a federated plugin '
|
|
'should include all plugins in the group', () async {
|
|
processRunner.mockProcessesForExecutable['git-diff'] = <FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: '''
|
|
packages/plugin1/plugin1/plugin1.dart
|
|
''')),
|
|
];
|
|
final Directory pluginGroup = packagesDir.childDirectory('plugin1');
|
|
final RepositoryPackage appFacingPackage =
|
|
createFakePlugin('plugin1', pluginGroup);
|
|
final RepositoryPackage platformInterfacePackage =
|
|
createFakePlugin('plugin1_platform_interface', pluginGroup);
|
|
final RepositoryPackage implementationPackage =
|
|
createFakePlugin('plugin1_web', pluginGroup);
|
|
|
|
await runCapturingPrint(
|
|
runner, <String>['sample', '--base-sha=main', '--packages=plugin1']);
|
|
|
|
expect(
|
|
command.plugins,
|
|
unorderedEquals(<String>[
|
|
appFacingPackage.path,
|
|
platformInterfacePackage.path,
|
|
implementationPackage.path
|
|
]));
|
|
});
|
|
|
|
test(
|
|
'specifying the app-facing package of a federated plugin using its '
|
|
'fully qualified name should include only that package', () async {
|
|
processRunner.mockProcessesForExecutable['git-diff'] = <FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: '''
|
|
packages/plugin1/plugin1/plugin1.dart
|
|
''')),
|
|
];
|
|
final Directory pluginGroup = packagesDir.childDirectory('plugin1');
|
|
final RepositoryPackage appFacingPackage =
|
|
createFakePlugin('plugin1', pluginGroup);
|
|
createFakePlugin('plugin1_platform_interface', pluginGroup);
|
|
createFakePlugin('plugin1_web', pluginGroup);
|
|
|
|
await runCapturingPrint(runner,
|
|
<String>['sample', '--base-sha=main', '--packages=plugin1/plugin1']);
|
|
|
|
expect(command.plugins, unorderedEquals(<String>[appFacingPackage.path]));
|
|
});
|
|
|
|
test(
|
|
'specifying a package of a federated plugin by its name should '
|
|
'include only that package', () async {
|
|
processRunner.mockProcessesForExecutable['git-diff'] = <FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: '''
|
|
packages/plugin1/plugin1/plugin1.dart
|
|
''')),
|
|
];
|
|
final Directory pluginGroup = packagesDir.childDirectory('plugin1');
|
|
|
|
createFakePlugin('plugin1', pluginGroup);
|
|
final RepositoryPackage platformInterfacePackage =
|
|
createFakePlugin('plugin1_platform_interface', pluginGroup);
|
|
createFakePlugin('plugin1_web', pluginGroup);
|
|
|
|
await runCapturingPrint(runner, <String>[
|
|
'sample',
|
|
'--base-sha=main',
|
|
'--packages=plugin1_platform_interface'
|
|
]);
|
|
|
|
expect(command.plugins,
|
|
unorderedEquals(<String>[platformInterfacePackage.path]));
|
|
});
|
|
|
|
test('returns subpackages after the enclosing package', () async {
|
|
final SamplePackageCommand localCommand = SamplePackageCommand(
|
|
packagesDir,
|
|
processRunner: processRunner,
|
|
platform: mockPlatform,
|
|
gitDir: MockGitDir(),
|
|
includeSubpackages: true,
|
|
);
|
|
final CommandRunner<void> localRunner =
|
|
CommandRunner<void>('common_command', 'subpackage testing');
|
|
localRunner.addCommand(localCommand);
|
|
|
|
final RepositoryPackage package =
|
|
createFakePackage('apackage', packagesDir);
|
|
|
|
await runCapturingPrint(localRunner, <String>['sample']);
|
|
expect(
|
|
localCommand.plugins,
|
|
containsAllInOrder(<String>[
|
|
package.path,
|
|
getExampleDir(package).path,
|
|
]));
|
|
});
|
|
|
|
group('conflicting package selection', () {
|
|
test('does not allow --packages with --run-on-changed-packages',
|
|
() async {
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'sample',
|
|
'--run-on-changed-packages',
|
|
'--packages=plugin1',
|
|
], errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Only one of the package selection arguments')
|
|
]));
|
|
});
|
|
|
|
test('does not allow --packages with --packages-for-branch', () async {
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'sample',
|
|
'--packages-for-branch',
|
|
'--packages=plugin1',
|
|
], errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Only one of the package selection arguments')
|
|
]));
|
|
});
|
|
|
|
test('does not allow --packages with --current-package', () async {
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'sample',
|
|
'--current-package',
|
|
'--packages=plugin1',
|
|
], errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Only one of the package selection arguments')
|
|
]));
|
|
});
|
|
|
|
test(
|
|
'does not allow --run-on-changed-packages with --packages-for-branch',
|
|
() async {
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'sample',
|
|
'--packages-for-branch',
|
|
'--packages=plugin1',
|
|
], errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Only one of the package selection arguments')
|
|
]));
|
|
});
|
|
});
|
|
|
|
group('current-package', () {
|
|
test('throws when run from outside of the packages directory', () async {
|
|
fileSystem.currentDirectory = packagesDir.parent;
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'sample',
|
|
'--current-package',
|
|
], errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('--current-package can only be used within a repository '
|
|
'package or package group')
|
|
]));
|
|
});
|
|
|
|
test('throws when run directly in the packages directory', () async {
|
|
fileSystem.currentDirectory = packagesDir;
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'sample',
|
|
'--current-package',
|
|
], errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('--current-package can only be used within a repository '
|
|
'package or package group')
|
|
]));
|
|
});
|
|
|
|
test('runs on a package when run from the package directory', () async {
|
|
final RepositoryPackage package =
|
|
createFakePlugin('a_package', packagesDir);
|
|
createFakePlugin('another_package', packagesDir);
|
|
fileSystem.currentDirectory = package.directory;
|
|
|
|
await runCapturingPrint(
|
|
runner, <String>['sample', '--current-package']);
|
|
|
|
expect(command.plugins, unorderedEquals(<String>[package.path]));
|
|
});
|
|
|
|
test('runs only app-facing package of a federated plugin', () async {
|
|
const String pluginName = 'foo';
|
|
final Directory groupDir = packagesDir.childDirectory(pluginName);
|
|
final RepositoryPackage package =
|
|
createFakePlugin(pluginName, groupDir);
|
|
createFakePlugin('${pluginName}_someplatform', groupDir);
|
|
createFakePackage('${pluginName}_platform_interface', groupDir);
|
|
fileSystem.currentDirectory = package.directory;
|
|
|
|
await runCapturingPrint(
|
|
runner, <String>['sample', '--current-package']);
|
|
|
|
expect(command.plugins, unorderedEquals(<String>[package.path]));
|
|
});
|
|
|
|
test('runs on a package when run from a package example directory',
|
|
() async {
|
|
final RepositoryPackage package = createFakePlugin(
|
|
'a_package', packagesDir,
|
|
examples: <String>['a', 'b', 'c']);
|
|
createFakePlugin('another_package', packagesDir);
|
|
fileSystem.currentDirectory = package.getExamples().first.directory;
|
|
|
|
await runCapturingPrint(
|
|
runner, <String>['sample', '--current-package']);
|
|
|
|
expect(command.plugins, unorderedEquals(<String>[package.path]));
|
|
});
|
|
|
|
test('runs on a package group when run from the group directory',
|
|
() async {
|
|
final Directory pluginGroup = packagesDir.childDirectory('a_plugin');
|
|
final RepositoryPackage plugin1 =
|
|
createFakePlugin('a_plugin_foo', pluginGroup);
|
|
final RepositoryPackage plugin2 =
|
|
createFakePlugin('a_plugin_bar', pluginGroup);
|
|
createFakePlugin('unrelated_plugin', packagesDir);
|
|
fileSystem.currentDirectory = pluginGroup;
|
|
|
|
await runCapturingPrint(
|
|
runner, <String>['sample', '--current-package']);
|
|
|
|
expect(command.plugins,
|
|
unorderedEquals(<String>[plugin1.path, plugin2.path]));
|
|
});
|
|
});
|
|
|
|
group('test run-on-changed-packages', () {
|
|
test('all plugins should be tested if there are no changes.', () async {
|
|
final RepositoryPackage plugin1 =
|
|
createFakePlugin('plugin1', packagesDir);
|
|
final RepositoryPackage plugin2 =
|
|
createFakePlugin('plugin2', packagesDir);
|
|
await runCapturingPrint(runner,
|
|
<String>['sample', '--base-sha=main', '--run-on-changed-packages']);
|
|
|
|
expect(command.plugins,
|
|
unorderedEquals(<String>[plugin1.path, plugin2.path]));
|
|
});
|
|
|
|
test(
|
|
'all plugins should be tested if there are no plugin related changes.',
|
|
() async {
|
|
processRunner.mockProcessesForExecutable['git-diff'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'AUTHORS')),
|
|
];
|
|
final RepositoryPackage plugin1 =
|
|
createFakePlugin('plugin1', packagesDir);
|
|
final RepositoryPackage plugin2 =
|
|
createFakePlugin('plugin2', packagesDir);
|
|
await runCapturingPrint(runner,
|
|
<String>['sample', '--base-sha=main', '--run-on-changed-packages']);
|
|
|
|
expect(command.plugins,
|
|
unorderedEquals(<String>[plugin1.path, plugin2.path]));
|
|
});
|
|
|
|
test('all plugins should be tested if .ci.yaml changes', () async {
|
|
processRunner.mockProcessesForExecutable['git-diff'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: '''
|
|
.ci.yaml
|
|
packages/plugin1/CHANGELOG
|
|
''')),
|
|
];
|
|
final RepositoryPackage plugin1 =
|
|
createFakePlugin('plugin1', packagesDir);
|
|
final RepositoryPackage plugin2 =
|
|
createFakePlugin('plugin2', packagesDir);
|
|
final List<String> output = await runCapturingPrint(runner,
|
|
<String>['sample', '--base-sha=main', '--run-on-changed-packages']);
|
|
|
|
expect(command.plugins,
|
|
unorderedEquals(<String>[plugin1.path, plugin2.path]));
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for all packages, since a file has changed '
|
|
'that could affect the entire repository.')
|
|
]));
|
|
});
|
|
|
|
test('all plugins should be tested if anything in .ci/ changes',
|
|
() async {
|
|
processRunner.mockProcessesForExecutable['git-diff'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: '''
|
|
.ci/Dockerfile
|
|
packages/plugin1/CHANGELOG
|
|
''')),
|
|
];
|
|
final RepositoryPackage plugin1 =
|
|
createFakePlugin('plugin1', packagesDir);
|
|
final RepositoryPackage plugin2 =
|
|
createFakePlugin('plugin2', packagesDir);
|
|
final List<String> output = await runCapturingPrint(runner,
|
|
<String>['sample', '--base-sha=main', '--run-on-changed-packages']);
|
|
|
|
expect(command.plugins,
|
|
unorderedEquals(<String>[plugin1.path, plugin2.path]));
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for all packages, since a file has changed '
|
|
'that could affect the entire repository.')
|
|
]));
|
|
});
|
|
|
|
test('all plugins should be tested if anything in script/ changes.',
|
|
() async {
|
|
processRunner.mockProcessesForExecutable['git-diff'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: '''
|
|
script/tool/bin/flutter_plugin_tools.dart
|
|
packages/plugin1/CHANGELOG
|
|
''')),
|
|
];
|
|
final RepositoryPackage plugin1 =
|
|
createFakePlugin('plugin1', packagesDir);
|
|
final RepositoryPackage plugin2 =
|
|
createFakePlugin('plugin2', packagesDir);
|
|
final List<String> output = await runCapturingPrint(runner,
|
|
<String>['sample', '--base-sha=main', '--run-on-changed-packages']);
|
|
|
|
expect(command.plugins,
|
|
unorderedEquals(<String>[plugin1.path, plugin2.path]));
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for all packages, since a file has changed '
|
|
'that could affect the entire repository.')
|
|
]));
|
|
});
|
|
|
|
test('all plugins should be tested if the root analysis options change.',
|
|
() async {
|
|
processRunner.mockProcessesForExecutable['git-diff'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: '''
|
|
analysis_options.yaml
|
|
packages/plugin1/CHANGELOG
|
|
''')),
|
|
];
|
|
final RepositoryPackage plugin1 =
|
|
createFakePlugin('plugin1', packagesDir);
|
|
final RepositoryPackage plugin2 =
|
|
createFakePlugin('plugin2', packagesDir);
|
|
final List<String> output = await runCapturingPrint(runner,
|
|
<String>['sample', '--base-sha=main', '--run-on-changed-packages']);
|
|
|
|
expect(command.plugins,
|
|
unorderedEquals(<String>[plugin1.path, plugin2.path]));
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for all packages, since a file has changed '
|
|
'that could affect the entire repository.')
|
|
]));
|
|
});
|
|
|
|
test('all plugins should be tested if formatting options change.',
|
|
() async {
|
|
processRunner.mockProcessesForExecutable['git-diff'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: '''
|
|
.clang-format
|
|
packages/plugin1/CHANGELOG
|
|
''')),
|
|
];
|
|
final RepositoryPackage plugin1 =
|
|
createFakePlugin('plugin1', packagesDir);
|
|
final RepositoryPackage plugin2 =
|
|
createFakePlugin('plugin2', packagesDir);
|
|
final List<String> output = await runCapturingPrint(runner,
|
|
<String>['sample', '--base-sha=main', '--run-on-changed-packages']);
|
|
|
|
expect(command.plugins,
|
|
unorderedEquals(<String>[plugin1.path, plugin2.path]));
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for all packages, since a file has changed '
|
|
'that could affect the entire repository.')
|
|
]));
|
|
});
|
|
|
|
test('Only changed plugin should be tested.', () async {
|
|
processRunner.mockProcessesForExecutable['git-diff'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'packages/plugin1/plugin1.dart')),
|
|
];
|
|
final RepositoryPackage plugin1 =
|
|
createFakePlugin('plugin1', packagesDir);
|
|
createFakePlugin('plugin2', packagesDir);
|
|
final List<String> output = await runCapturingPrint(runner,
|
|
<String>['sample', '--base-sha=main', '--run-on-changed-packages']);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains(
|
|
'Running for all packages that have diffs relative to "main"'),
|
|
]));
|
|
|
|
expect(command.plugins, unorderedEquals(<String>[plugin1.path]));
|
|
});
|
|
|
|
test('multiple files in one plugin should also test the plugin',
|
|
() async {
|
|
processRunner.mockProcessesForExecutable['git-diff'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: '''
|
|
packages/plugin1/plugin1.dart
|
|
packages/plugin1/ios/plugin1.m
|
|
''')),
|
|
];
|
|
final RepositoryPackage plugin1 =
|
|
createFakePlugin('plugin1', packagesDir);
|
|
createFakePlugin('plugin2', packagesDir);
|
|
await runCapturingPrint(runner,
|
|
<String>['sample', '--base-sha=main', '--run-on-changed-packages']);
|
|
|
|
expect(command.plugins, unorderedEquals(<String>[plugin1.path]));
|
|
});
|
|
|
|
test('multiple plugins changed should test all the changed plugins',
|
|
() async {
|
|
processRunner.mockProcessesForExecutable['git-diff'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: '''
|
|
packages/plugin1/plugin1.dart
|
|
packages/plugin2/ios/plugin2.m
|
|
''')),
|
|
];
|
|
final RepositoryPackage plugin1 =
|
|
createFakePlugin('plugin1', packagesDir);
|
|
final RepositoryPackage plugin2 =
|
|
createFakePlugin('plugin2', packagesDir);
|
|
createFakePlugin('plugin3', packagesDir);
|
|
await runCapturingPrint(runner,
|
|
<String>['sample', '--base-sha=main', '--run-on-changed-packages']);
|
|
|
|
expect(command.plugins,
|
|
unorderedEquals(<String>[plugin1.path, plugin2.path]));
|
|
});
|
|
|
|
test(
|
|
'multiple plugins inside the same plugin group changed should output the plugin group name',
|
|
() async {
|
|
processRunner.mockProcessesForExecutable['git-diff'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: '''
|
|
packages/plugin1/plugin1/plugin1.dart
|
|
packages/plugin1/plugin1_platform_interface/plugin1_platform_interface.dart
|
|
packages/plugin1/plugin1_web/plugin1_web.dart
|
|
''')),
|
|
];
|
|
final RepositoryPackage plugin1 =
|
|
createFakePlugin('plugin1', packagesDir.childDirectory('plugin1'));
|
|
createFakePlugin('plugin2', packagesDir);
|
|
createFakePlugin('plugin3', packagesDir);
|
|
await runCapturingPrint(runner,
|
|
<String>['sample', '--base-sha=main', '--run-on-changed-packages']);
|
|
|
|
expect(command.plugins, unorderedEquals(<String>[plugin1.path]));
|
|
});
|
|
|
|
test(
|
|
'changing one plugin in a federated group should only include that plugin',
|
|
() async {
|
|
processRunner.mockProcessesForExecutable['git-diff'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: '''
|
|
packages/plugin1/plugin1/plugin1.dart
|
|
''')),
|
|
];
|
|
final RepositoryPackage plugin1 =
|
|
createFakePlugin('plugin1', packagesDir.childDirectory('plugin1'));
|
|
createFakePlugin('plugin1_platform_interface',
|
|
packagesDir.childDirectory('plugin1'));
|
|
createFakePlugin('plugin1_web', packagesDir.childDirectory('plugin1'));
|
|
await runCapturingPrint(runner,
|
|
<String>['sample', '--base-sha=main', '--run-on-changed-packages']);
|
|
|
|
expect(command.plugins, unorderedEquals(<String>[plugin1.path]));
|
|
});
|
|
|
|
test('honors --exclude flag', () async {
|
|
processRunner.mockProcessesForExecutable['git-diff'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: '''
|
|
packages/plugin1/plugin1.dart
|
|
packages/plugin2/ios/plugin2.m
|
|
packages/plugin3/plugin3.dart
|
|
''')),
|
|
];
|
|
final RepositoryPackage plugin1 =
|
|
createFakePlugin('plugin1', packagesDir.childDirectory('plugin1'));
|
|
createFakePlugin('plugin2', packagesDir);
|
|
createFakePlugin('plugin3', packagesDir);
|
|
await runCapturingPrint(runner, <String>[
|
|
'sample',
|
|
'--exclude=plugin2,plugin3',
|
|
'--base-sha=main',
|
|
'--run-on-changed-packages'
|
|
]);
|
|
|
|
expect(command.plugins, unorderedEquals(<String>[plugin1.path]));
|
|
});
|
|
|
|
test('honors --filter-packages-to flag', () async {
|
|
processRunner.mockProcessesForExecutable['git-diff'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: '''
|
|
packages/plugin1/plugin1.dart
|
|
packages/plugin2/ios/plugin2.m
|
|
packages/plugin3/plugin3.dart
|
|
''')),
|
|
];
|
|
final RepositoryPackage plugin1 =
|
|
createFakePlugin('plugin1', packagesDir.childDirectory('plugin1'));
|
|
createFakePlugin('plugin2', packagesDir);
|
|
createFakePlugin('plugin3', packagesDir);
|
|
await runCapturingPrint(runner, <String>[
|
|
'sample',
|
|
'--filter-packages-to=plugin1',
|
|
'--base-sha=main',
|
|
'--run-on-changed-packages'
|
|
]);
|
|
|
|
expect(command.plugins, unorderedEquals(<String>[plugin1.path]));
|
|
});
|
|
|
|
test(
|
|
'honors --filter-packages-to flag when a file is changed that makes '
|
|
'all packages potentially changed', () async {
|
|
processRunner.mockProcessesForExecutable['git-diff'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: '''
|
|
.ci.yaml
|
|
''')),
|
|
];
|
|
final RepositoryPackage plugin1 =
|
|
createFakePlugin('plugin1', packagesDir.childDirectory('plugin1'));
|
|
createFakePlugin('plugin2', packagesDir);
|
|
createFakePlugin('plugin3', packagesDir);
|
|
await runCapturingPrint(runner, <String>[
|
|
'sample',
|
|
'--filter-packages-to=plugin1',
|
|
'--base-sha=main',
|
|
'--run-on-changed-packages'
|
|
]);
|
|
|
|
expect(command.plugins, unorderedEquals(<String>[plugin1.path]));
|
|
});
|
|
|
|
test('--filter-packages-to handles federated plugin groups', () async {
|
|
processRunner.mockProcessesForExecutable['git-diff'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: '''
|
|
packages/a_plugin/a_plugin/lib/foo.dart
|
|
packages/a_plugin/a_plugin_impl/lib/foo.dart
|
|
packages/a_plugin/a_plugin_platform_interface/lib/foo.dart
|
|
''')),
|
|
];
|
|
final Directory groupDir = packagesDir.childDirectory('a_plugin');
|
|
final RepositoryPackage plugin1 =
|
|
createFakePlugin('a_plugin', groupDir);
|
|
final RepositoryPackage plugin2 =
|
|
createFakePlugin('a_plugin_impl', groupDir);
|
|
final RepositoryPackage plugin3 =
|
|
createFakePlugin('a_plugin_platform_interface', groupDir);
|
|
await runCapturingPrint(runner, <String>[
|
|
'sample',
|
|
'--filter-packages-to=a_plugin',
|
|
'--base-sha=main',
|
|
'--run-on-changed-packages'
|
|
]);
|
|
|
|
expect(
|
|
command.plugins,
|
|
unorderedEquals(
|
|
<String>[plugin1.path, plugin2.path, plugin3.path]));
|
|
});
|
|
|
|
test('--filter-packages-to and --exclude work together', () async {
|
|
processRunner.mockProcessesForExecutable['git-diff'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: '''
|
|
.ci.yaml
|
|
''')),
|
|
];
|
|
final RepositoryPackage plugin1 =
|
|
createFakePlugin('plugin1', packagesDir.childDirectory('plugin1'));
|
|
createFakePlugin('plugin2', packagesDir);
|
|
createFakePlugin('plugin3', packagesDir);
|
|
await runCapturingPrint(runner, <String>[
|
|
'sample',
|
|
'--filter-packages-to=plugin1,plugin2',
|
|
'--exclude=plugin2',
|
|
'--base-sha=main',
|
|
'--run-on-changed-packages'
|
|
]);
|
|
|
|
expect(command.plugins, unorderedEquals(<String>[plugin1.path]));
|
|
});
|
|
});
|
|
|
|
group('test run-on-dirty-packages', () {
|
|
test('no packages should be tested if there are no changes.', () async {
|
|
createFakePackage('a_package', packagesDir);
|
|
await runCapturingPrint(
|
|
runner, <String>['sample', '--run-on-dirty-packages']);
|
|
|
|
expect(command.plugins, unorderedEquals(<String>[]));
|
|
});
|
|
|
|
test(
|
|
'no packages should be tested if there are no plugin related changes.',
|
|
() async {
|
|
processRunner.mockProcessesForExecutable['git-diff'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'AUTHORS')),
|
|
];
|
|
createFakePackage('a_package', packagesDir);
|
|
await runCapturingPrint(
|
|
runner, <String>['sample', '--run-on-dirty-packages']);
|
|
|
|
expect(command.plugins, unorderedEquals(<String>[]));
|
|
});
|
|
|
|
test('no packages should be tested even if special repo files change.',
|
|
() async {
|
|
processRunner.mockProcessesForExecutable['git-diff'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: '''
|
|
.ci.yaml
|
|
.ci/Dockerfile
|
|
.clang-format
|
|
analysis_options.yaml
|
|
script/tool/bin/flutter_plugin_tools.dart
|
|
''')),
|
|
];
|
|
createFakePackage('a_package', packagesDir);
|
|
await runCapturingPrint(
|
|
runner, <String>['sample', '--run-on-dirty-packages']);
|
|
|
|
expect(command.plugins, unorderedEquals(<String>[]));
|
|
});
|
|
|
|
test('Only changed packages should be tested.', () async {
|
|
processRunner.mockProcessesForExecutable['git-diff'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(
|
|
MockProcess(stdout: 'packages/a_package/lib/a_package.dart')),
|
|
];
|
|
final RepositoryPackage packageA =
|
|
createFakePackage('a_package', packagesDir);
|
|
createFakePlugin('b_package', packagesDir);
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['sample', '--run-on-dirty-packages']);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains(
|
|
'Running for all packages that have uncommitted changes'),
|
|
]));
|
|
|
|
expect(command.plugins, unorderedEquals(<String>[packageA.path]));
|
|
});
|
|
|
|
test('multiple packages changed should test all the changed packages',
|
|
() async {
|
|
processRunner.mockProcessesForExecutable['git-diff'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: '''
|
|
packages/a_package/lib/a_package.dart
|
|
packages/b_package/lib/src/foo.dart
|
|
''')),
|
|
];
|
|
final RepositoryPackage packageA =
|
|
createFakePackage('a_package', packagesDir);
|
|
final RepositoryPackage packageB =
|
|
createFakePackage('b_package', packagesDir);
|
|
createFakePackage('c_package', packagesDir);
|
|
await runCapturingPrint(
|
|
runner, <String>['sample', '--run-on-dirty-packages']);
|
|
|
|
expect(command.plugins,
|
|
unorderedEquals(<String>[packageA.path, packageB.path]));
|
|
});
|
|
|
|
test('honors --exclude flag', () async {
|
|
processRunner.mockProcessesForExecutable['git-diff'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: '''
|
|
packages/a_package/lib/a_package.dart
|
|
packages/b_package/lib/src/foo.dart
|
|
''')),
|
|
];
|
|
final RepositoryPackage packageA =
|
|
createFakePackage('a_package', packagesDir);
|
|
createFakePackage('b_package', packagesDir);
|
|
createFakePackage('c_package', packagesDir);
|
|
await runCapturingPrint(runner, <String>[
|
|
'sample',
|
|
'--exclude=b_package',
|
|
'--run-on-dirty-packages'
|
|
]);
|
|
|
|
expect(command.plugins, unorderedEquals(<String>[packageA.path]));
|
|
});
|
|
});
|
|
});
|
|
|
|
group('--packages-for-branch', () {
|
|
test('only tests changed packages relative to the merge base on a branch',
|
|
() async {
|
|
processRunner.mockProcessesForExecutable['git-diff'] = <FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'packages/plugin1/plugin1.dart')),
|
|
];
|
|
processRunner.mockProcessesForExecutable['git-rev-parse'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'a-branch')),
|
|
];
|
|
processRunner.mockProcessesForExecutable['git-merge-base'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(exitCode: 1), <String>['--is-ancestor']),
|
|
FakeProcessInfo(MockProcess(stdout: 'abc123'),
|
|
<String>['--fork-point']), // finding merge base
|
|
];
|
|
final RepositoryPackage plugin1 =
|
|
createFakePlugin('plugin1', packagesDir);
|
|
createFakePlugin('plugin2', packagesDir);
|
|
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['sample', '--packages-for-branch']);
|
|
|
|
expect(command.plugins, unorderedEquals(<String>[plugin1.path]));
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('--packages-for-branch: running on branch "a-branch"'),
|
|
contains(
|
|
'Running for all packages that have diffs relative to "abc123"'),
|
|
]));
|
|
// Ensure that it's diffing against the merge-base.
|
|
expect(
|
|
processRunner.recordedCalls,
|
|
contains(
|
|
const ProcessCall(
|
|
'git-diff', <String>['--name-only', 'abc123', 'HEAD'], null),
|
|
));
|
|
});
|
|
|
|
test('only tests changed packages relative to the previous commit on main',
|
|
() async {
|
|
processRunner.mockProcessesForExecutable['git-diff'] = <FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'packages/plugin1/plugin1.dart')),
|
|
];
|
|
processRunner.mockProcessesForExecutable['git-rev-parse'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'main')),
|
|
];
|
|
final RepositoryPackage plugin1 =
|
|
createFakePlugin('plugin1', packagesDir);
|
|
createFakePlugin('plugin2', packagesDir);
|
|
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['sample', '--packages-for-branch']);
|
|
|
|
expect(command.plugins, unorderedEquals(<String>[plugin1.path]));
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('--packages-for-branch: running on default branch.'),
|
|
contains(
|
|
'--packages-for-branch: using parent commit as the diff base'),
|
|
contains(
|
|
'Running for all packages that have diffs relative to "HEAD~"'),
|
|
]));
|
|
// Ensure that it's diffing against the prior commit.
|
|
expect(
|
|
processRunner.recordedCalls,
|
|
contains(
|
|
const ProcessCall(
|
|
'git-diff', <String>['--name-only', 'HEAD~', 'HEAD'], null),
|
|
));
|
|
});
|
|
|
|
test(
|
|
'only tests changed packages relative to the previous commit if '
|
|
'running on a specific hash from main', () async {
|
|
processRunner.mockProcessesForExecutable['git-diff'] = <FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'packages/plugin1/plugin1.dart')),
|
|
];
|
|
processRunner.mockProcessesForExecutable['git-rev-parse'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'HEAD')),
|
|
];
|
|
final RepositoryPackage plugin1 =
|
|
createFakePlugin('plugin1', packagesDir);
|
|
createFakePlugin('plugin2', packagesDir);
|
|
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['sample', '--packages-for-branch']);
|
|
|
|
expect(command.plugins, unorderedEquals(<String>[plugin1.path]));
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains(
|
|
'--packages-for-branch: running on a commit from default branch.'),
|
|
contains(
|
|
'--packages-for-branch: using parent commit as the diff base'),
|
|
contains(
|
|
'Running for all packages that have diffs relative to "HEAD~"'),
|
|
]));
|
|
// Ensure that it's diffing against the prior commit.
|
|
expect(
|
|
processRunner.recordedCalls,
|
|
contains(
|
|
const ProcessCall(
|
|
'git-diff', <String>['--name-only', 'HEAD~', 'HEAD'], null),
|
|
));
|
|
});
|
|
|
|
test(
|
|
'only tests changed packages relative to the previous commit if '
|
|
'running on a specific hash from origin/main', () async {
|
|
processRunner.mockProcessesForExecutable['git-diff'] = <FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'packages/plugin1/plugin1.dart')),
|
|
];
|
|
processRunner.mockProcessesForExecutable['git-rev-parse'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'HEAD')),
|
|
];
|
|
processRunner.mockProcessesForExecutable['git-merge-base'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(exitCode: 128), <String>[
|
|
'--is-ancestor',
|
|
'HEAD',
|
|
'main'
|
|
]), // Fail with a non-1 exit code for 'main'
|
|
FakeProcessInfo(MockProcess(), <String>[
|
|
'--is-ancestor',
|
|
'HEAD',
|
|
'origin/main'
|
|
]), // Succeed for the variant.
|
|
];
|
|
final RepositoryPackage plugin1 =
|
|
createFakePlugin('plugin1', packagesDir);
|
|
createFakePlugin('plugin2', packagesDir);
|
|
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['sample', '--packages-for-branch']);
|
|
|
|
expect(command.plugins, unorderedEquals(<String>[plugin1.path]));
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains(
|
|
'--packages-for-branch: running on a commit from default branch.'),
|
|
contains(
|
|
'--packages-for-branch: using parent commit as the diff base'),
|
|
contains(
|
|
'Running for all packages that have diffs relative to "HEAD~"'),
|
|
]));
|
|
// Ensure that it's diffing against the prior commit.
|
|
expect(
|
|
processRunner.recordedCalls,
|
|
contains(
|
|
const ProcessCall(
|
|
'git-diff', <String>['--name-only', 'HEAD~', 'HEAD'], null),
|
|
));
|
|
});
|
|
|
|
test(
|
|
'only tests changed packages relative to the previous commit on master',
|
|
() async {
|
|
processRunner.mockProcessesForExecutable['git-diff'] = <FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'packages/plugin1/plugin1.dart')),
|
|
];
|
|
processRunner.mockProcessesForExecutable['git-rev-parse'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'master')),
|
|
];
|
|
final RepositoryPackage plugin1 =
|
|
createFakePlugin('plugin1', packagesDir);
|
|
createFakePlugin('plugin2', packagesDir);
|
|
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['sample', '--packages-for-branch']);
|
|
|
|
expect(command.plugins, unorderedEquals(<String>[plugin1.path]));
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('--packages-for-branch: running on default branch.'),
|
|
contains(
|
|
'--packages-for-branch: using parent commit as the diff base'),
|
|
contains(
|
|
'Running for all packages that have diffs relative to "HEAD~"'),
|
|
]));
|
|
// Ensure that it's diffing against the prior commit.
|
|
expect(
|
|
processRunner.recordedCalls,
|
|
contains(
|
|
const ProcessCall(
|
|
'git-diff', <String>['--name-only', 'HEAD~', 'HEAD'], null),
|
|
));
|
|
});
|
|
|
|
test('throws if getting the branch fails', () async {
|
|
processRunner.mockProcessesForExecutable['git-diff'] = <FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'packages/plugin1/plugin1.dart')),
|
|
];
|
|
processRunner.mockProcessesForExecutable['git-rev-parse'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(exitCode: 1)),
|
|
];
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['sample', '--packages-for-branch'],
|
|
errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Unable to determine branch'),
|
|
]));
|
|
});
|
|
});
|
|
|
|
group('sharding', () {
|
|
test('distributes evenly when evenly divisible', () async {
|
|
final List<List<RepositoryPackage>> expectedShards =
|
|
<List<RepositoryPackage>>[
|
|
<RepositoryPackage>[
|
|
createFakePackage('package1', packagesDir),
|
|
createFakePackage('package2', packagesDir),
|
|
createFakePackage('package3', packagesDir),
|
|
],
|
|
<RepositoryPackage>[
|
|
createFakePackage('package4', packagesDir),
|
|
createFakePackage('package5', packagesDir),
|
|
createFakePackage('package6', packagesDir),
|
|
],
|
|
<RepositoryPackage>[
|
|
createFakePackage('package7', packagesDir),
|
|
createFakePackage('package8', packagesDir),
|
|
createFakePackage('package9', packagesDir),
|
|
],
|
|
];
|
|
|
|
for (int i = 0; i < expectedShards.length; ++i) {
|
|
final SamplePackageCommand localCommand = SamplePackageCommand(
|
|
packagesDir,
|
|
processRunner: processRunner,
|
|
platform: mockPlatform,
|
|
gitDir: MockGitDir(),
|
|
);
|
|
final CommandRunner<void> localRunner =
|
|
CommandRunner<void>('common_command', 'Shard testing');
|
|
localRunner.addCommand(localCommand);
|
|
|
|
await runCapturingPrint(localRunner, <String>[
|
|
'sample',
|
|
'--shardIndex=$i',
|
|
'--shardCount=3',
|
|
]);
|
|
expect(
|
|
localCommand.plugins,
|
|
unorderedEquals(expectedShards[i]
|
|
.map((RepositoryPackage package) => package.path)
|
|
.toList()));
|
|
}
|
|
});
|
|
|
|
test('distributes as evenly as possible when not evenly divisible',
|
|
() async {
|
|
final List<List<RepositoryPackage>> expectedShards =
|
|
<List<RepositoryPackage>>[
|
|
<RepositoryPackage>[
|
|
createFakePackage('package1', packagesDir),
|
|
createFakePackage('package2', packagesDir),
|
|
createFakePackage('package3', packagesDir),
|
|
],
|
|
<RepositoryPackage>[
|
|
createFakePackage('package4', packagesDir),
|
|
createFakePackage('package5', packagesDir),
|
|
createFakePackage('package6', packagesDir),
|
|
],
|
|
<RepositoryPackage>[
|
|
createFakePackage('package7', packagesDir),
|
|
createFakePackage('package8', packagesDir),
|
|
],
|
|
];
|
|
|
|
for (int i = 0; i < expectedShards.length; ++i) {
|
|
final SamplePackageCommand localCommand = SamplePackageCommand(
|
|
packagesDir,
|
|
processRunner: processRunner,
|
|
platform: mockPlatform,
|
|
gitDir: MockGitDir(),
|
|
);
|
|
final CommandRunner<void> localRunner =
|
|
CommandRunner<void>('common_command', 'Shard testing');
|
|
localRunner.addCommand(localCommand);
|
|
|
|
await runCapturingPrint(localRunner, <String>[
|
|
'sample',
|
|
'--shardIndex=$i',
|
|
'--shardCount=3',
|
|
]);
|
|
expect(
|
|
localCommand.plugins,
|
|
unorderedEquals(expectedShards[i]
|
|
.map((RepositoryPackage package) => package.path)
|
|
.toList()));
|
|
}
|
|
});
|
|
|
|
// In CI (which is the use case for sharding) we often want to run muliple
|
|
// commands on the same set of packages, but the exclusion lists for those
|
|
// commands may be different. In those cases we still want all the commands
|
|
// to operate on a consistent set of plugins.
|
|
//
|
|
// E.g., some commands require running build-examples in a previous step;
|
|
// excluding some plugins from the later step shouldn't change what's tested
|
|
// in each shard, as it may no longer align with what was built.
|
|
test('counts excluded plugins when sharding', () async {
|
|
final List<List<RepositoryPackage>> expectedShards =
|
|
<List<RepositoryPackage>>[
|
|
<RepositoryPackage>[
|
|
createFakePackage('package1', packagesDir),
|
|
createFakePackage('package2', packagesDir),
|
|
createFakePackage('package3', packagesDir),
|
|
],
|
|
<RepositoryPackage>[
|
|
createFakePackage('package4', packagesDir),
|
|
createFakePackage('package5', packagesDir),
|
|
createFakePackage('package6', packagesDir),
|
|
],
|
|
<RepositoryPackage>[
|
|
createFakePackage('package7', packagesDir),
|
|
],
|
|
];
|
|
// These would be in the last shard, but are excluded.
|
|
createFakePackage('package8', packagesDir);
|
|
createFakePackage('package9', packagesDir);
|
|
|
|
for (int i = 0; i < expectedShards.length; ++i) {
|
|
final SamplePackageCommand localCommand = SamplePackageCommand(
|
|
packagesDir,
|
|
processRunner: processRunner,
|
|
platform: mockPlatform,
|
|
gitDir: MockGitDir(),
|
|
);
|
|
final CommandRunner<void> localRunner =
|
|
CommandRunner<void>('common_command', 'Shard testing');
|
|
localRunner.addCommand(localCommand);
|
|
|
|
await runCapturingPrint(localRunner, <String>[
|
|
'sample',
|
|
'--shardIndex=$i',
|
|
'--shardCount=3',
|
|
'--exclude=package8,package9',
|
|
]);
|
|
expect(
|
|
localCommand.plugins,
|
|
unorderedEquals(expectedShards[i]
|
|
.map((RepositoryPackage package) => package.path)
|
|
.toList()));
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
class SamplePackageCommand extends PackageCommand {
|
|
SamplePackageCommand(
|
|
super.packagesDir, {
|
|
super.processRunner,
|
|
super.platform,
|
|
super.gitDir,
|
|
this.includeSubpackages = false,
|
|
});
|
|
|
|
final List<String> plugins = <String>[];
|
|
|
|
final bool includeSubpackages;
|
|
|
|
@override
|
|
final String name = 'sample';
|
|
|
|
@override
|
|
final String description = 'sample command';
|
|
|
|
@override
|
|
Future<void> run() async {
|
|
final Stream<PackageEnumerationEntry> packages = includeSubpackages
|
|
? getTargetPackagesAndSubpackages()
|
|
: getTargetPackages();
|
|
await for (final PackageEnumerationEntry entry in packages) {
|
|
plugins.add(entry.package.path);
|
|
}
|
|
}
|
|
}
|