blob: 8c6b3868241863df989450d9b13101d83a3ac8e5 [file] [log] [blame]
// 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 'dart:io';
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/plugin_command.dart';
import 'package:flutter_plugin_tools/src/common/process_runner.dart';
import 'package:git/git.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
import 'package:platform/platform.dart';
import 'package:test/test.dart';
import '../mocks.dart';
import '../util.dart';
import 'plugin_command_test.mocks.dart';
@GenerateMocks(<Type>[GitDir])
void main() {
late RecordingProcessRunner processRunner;
late SamplePluginCommand 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 = SamplePluginCommand(
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(
'explicitly specifying the plugin (group) name of a federated plugin '
'should include all plugins in the group', () async {
processRunner.mockProcessesForExecutable['git-diff'] = <Process>[
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'] = <Process>[
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'] = <Process>[
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 SamplePluginCommand localCommand = SamplePluginCommand(
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 --packages, --run-on-changed-packages, or '
'--packages-for-branch can be provided.')
]));
});
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 --packages, --run-on-changed-packages, or '
'--packages-for-branch can be provided.')
]));
});
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 --packages, --run-on-changed-packages, or '
'--packages-for-branch can be provided.')
]));
});
});
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'] = <Process>[
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 .cirrus.yml changes.', () async {
processRunner.mockProcessesForExecutable['git-diff'] = <Process>[
MockProcess(stdout: '''
.cirrus.yml
packages/plugin1/CHANGELOG
'''),
];
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'] = <Process>[
MockProcess(stdout: '''
.ci.yaml
packages/plugin1/CHANGELOG
'''),
];
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 anything in .ci/ changes',
() async {
processRunner.mockProcessesForExecutable['git-diff'] = <Process>[
MockProcess(stdout: '''
.ci/Dockerfile
packages/plugin1/CHANGELOG
'''),
];
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 anything in script changes.',
() async {
processRunner.mockProcessesForExecutable['git-diff'] = <Process>[
MockProcess(stdout: '''
script/tool_runner.sh
packages/plugin1/CHANGELOG
'''),
];
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 the root analysis options change.',
() async {
processRunner.mockProcessesForExecutable['git-diff'] = <Process>[
MockProcess(stdout: '''
analysis_options.yaml
packages/plugin1/CHANGELOG
'''),
];
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 formatting options change.',
() async {
processRunner.mockProcessesForExecutable['git-diff'] = <Process>[
MockProcess(stdout: '''
.clang-format
packages/plugin1/CHANGELOG
'''),
];
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('Only changed plugin should be tested.', () async {
processRunner.mockProcessesForExecutable['git-diff'] = <Process>[
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 changed 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'] = <Process>[
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'] = <Process>[
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'] = <Process>[
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'] = <Process>[
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('--exclude flag works with --run-on-changed-packages', () async {
processRunner.mockProcessesForExecutable['git-diff'] = <Process>[
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]));
});
});
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'] = <Process>[
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'] = <Process>[
MockProcess(stdout: '''
.cirrus.yml
.ci.yaml
.ci/Dockerfile
.clang-format
analysis_options.yaml
script/tool_runner.sh
'''),
];
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'] = <Process>[
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'] = <Process>[
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'] = <Process>[
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 on a branch', () async {
processRunner.mockProcessesForExecutable['git-diff'] = <Process>[
MockProcess(stdout: 'packages/plugin1/plugin1.dart'),
];
processRunner.mockProcessesForExecutable['git-rev-parse'] = <Process>[
MockProcess(stdout: 'a-branch'),
];
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 changed packages'),
]));
});
test('tests all packages on main', () async {
processRunner.mockProcessesForExecutable['git-diff'] = <Process>[
MockProcess(stdout: 'packages/plugin1/plugin1.dart'),
];
processRunner.mockProcessesForExecutable['git-rev-parse'] = <Process>[
MockProcess(stdout: 'main'),
];
final RepositoryPackage plugin1 =
createFakePlugin('plugin1', packagesDir);
final RepositoryPackage plugin2 =
createFakePlugin('plugin2', packagesDir);
final List<String> output = await runCapturingPrint(
runner, <String>['sample', '--packages-for-branch']);
expect(command.plugins,
unorderedEquals(<String>[plugin1.path, plugin2.path]));
expect(
output,
containsAllInOrder(<Matcher>[
contains('--packages-for-branch: running on all packages'),
]));
});
test('tests all packages on master', () async {
processRunner.mockProcessesForExecutable['git-diff'] = <Process>[
MockProcess(stdout: 'packages/plugin1/plugin1.dart'),
];
processRunner.mockProcessesForExecutable['git-rev-parse'] = <Process>[
MockProcess(stdout: 'master'),
];
final RepositoryPackage plugin1 =
createFakePlugin('plugin1', packagesDir);
final RepositoryPackage plugin2 =
createFakePlugin('plugin2', packagesDir);
final List<String> output = await runCapturingPrint(
runner, <String>['sample', '--packages-for-branch']);
expect(command.plugins,
unorderedEquals(<String>[plugin1.path, plugin2.path]));
expect(
output,
containsAllInOrder(<Matcher>[
contains('--packages-for-branch: running on all packages'),
]));
});
test('throws if getting the branch fails', () async {
processRunner.mockProcessesForExecutable['git-diff'] = <Process>[
MockProcess(stdout: 'packages/plugin1/plugin1.dart'),
];
processRunner.mockProcessesForExecutable['git-rev-parse'] = <Process>[
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('Unabled 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 SamplePluginCommand localCommand = SamplePluginCommand(
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 SamplePluginCommand localCommand = SamplePluginCommand(
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 SamplePluginCommand localCommand = SamplePluginCommand(
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 SamplePluginCommand extends PluginCommand {
SamplePluginCommand(
Directory packagesDir, {
ProcessRunner processRunner = const ProcessRunner(),
Platform platform = const LocalPlatform(),
GitDir? gitDir,
this.includeSubpackages = false,
}) : super(packagesDir,
processRunner: processRunner, platform: platform, gitDir: gitDir);
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);
}
}
}