| // Copyright 2013 The Flutter Authors |
| // 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:flutter_plugin_tools/src/analyze_command.dart'; |
| import 'package:flutter_plugin_tools/src/common/core.dart'; |
| import 'package:flutter_plugin_tools/src/common/plugin_utils.dart'; |
| import 'package:git/git.dart'; |
| import 'package:test/test.dart'; |
| |
| import 'mocks.dart'; |
| import 'util.dart'; |
| |
| void main() { |
| late MockPlatform mockPlatform; |
| late Directory packagesDir; |
| late RecordingProcessRunner processRunner; |
| late RecordingProcessRunner gitProcessRunner; |
| late CommandRunner<void> runner; |
| |
| setUp(() { |
| mockPlatform = MockPlatform(); |
| final GitDir gitDir; |
| (:packagesDir, :processRunner, :gitProcessRunner, :gitDir) = |
| configureBaseCommandMocks(platform: mockPlatform); |
| final analyzeCommand = AnalyzeCommand( |
| packagesDir, |
| processRunner: processRunner, |
| gitDir: gitDir, |
| platform: mockPlatform, |
| ); |
| |
| runner = CommandRunner<void>('analyze_command', 'Test for analyze_command'); |
| runner.addCommand(analyzeCommand); |
| }); |
| |
| test('throws if no analysis options are included', () async { |
| createFakePackage('a', packagesDir); |
| |
| await expectLater( |
| () => runCapturingPrint(runner, <String>['analyze', '--no-dart']), |
| throwsA(isA<ToolExit>()), |
| ); |
| }); |
| |
| group('result aggregation', () { |
| test('repeorts failure if any analysis fails', () async { |
| final RepositoryPackage plugin = createFakePlugin( |
| 'plugin1', |
| packagesDir, |
| extraFiles: <String>['example/android/gradlew'], |
| platformSupport: <String, PlatformDetails>{ |
| platformAndroid: const PlatformDetails(PlatformSupport.inline), |
| platformIOS: const PlatformDetails(PlatformSupport.inline), |
| platformMacOS: const PlatformDetails(PlatformSupport.inline), |
| }, |
| ); |
| |
| // Simulate Android analysis failure only. |
| final String gradlewPath = plugin |
| .getExamples() |
| .first |
| .platformDirectory(FlutterPlatform.android) |
| .childFile('gradlew') |
| .path; |
| processRunner.mockProcessesForExecutable[gradlewPath] = <FakeProcessInfo>[ |
| FakeProcessInfo(MockProcess(exitCode: 1)), |
| ]; |
| |
| Error? commandError; |
| final List<String> output = await runCapturingPrint( |
| runner, |
| <String>['analyze', '--android', '--ios', '--macos'], |
| errorHandler: (Error e) { |
| commandError = e; |
| }, |
| ); |
| |
| expect(commandError, isA<ToolExit>()); |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains('The following packages had errors:'), |
| ]), |
| ); |
| }); |
| |
| test('reports skip if everything is skipped', () async { |
| createFakePlugin('plugin', packagesDir); |
| |
| final List<String> output = await runCapturingPrint(runner, <String>[ |
| 'analyze', |
| '--no-dart', |
| '--android', |
| '--ios', |
| '--macos', |
| ]); |
| |
| expect(output, containsAllInOrder(<Matcher>[contains('SKIPPING:')])); |
| |
| expect(processRunner.recordedCalls, orderedEquals(<ProcessCall>[])); |
| }); |
| |
| test('reports success for a mixture of skip and success', () async { |
| createFakePlugin( |
| 'plugin', |
| packagesDir, |
| platformSupport: <String, PlatformDetails>{ |
| platformIOS: const PlatformDetails(PlatformSupport.inline), |
| }, |
| ); |
| |
| final List<String> output = await runCapturingPrint(runner, <String>[ |
| 'analyze', |
| '--no-dart', |
| '--android', |
| '--ios', |
| '--macos', |
| ]); |
| |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[contains('No issues found')]), |
| ); |
| expect( |
| output, |
| isNot(containsAllInOrder(<Matcher>[contains('SKIPPING:')])), |
| ); |
| }); |
| }); |
| |
| group('dart analyze', () { |
| test('analyzes all packages', () async { |
| final RepositoryPackage package1 = createFakePackage('a', packagesDir); |
| final RepositoryPackage plugin2 = createFakePlugin('b', packagesDir); |
| |
| await runCapturingPrint(runner, <String>['analyze']); |
| |
| expect( |
| processRunner.recordedCalls, |
| orderedEquals(<ProcessCall>[ |
| ProcessCall('flutter', const <String>['pub', 'get'], package1.path), |
| ProcessCall('dart', const <String>[ |
| 'analyze', |
| '--fatal-infos', |
| ], package1.path), |
| ProcessCall('flutter', const <String>['pub', 'get'], plugin2.path), |
| ProcessCall('dart', const <String>[ |
| 'analyze', |
| '--fatal-infos', |
| ], plugin2.path), |
| ]), |
| ); |
| }); |
| |
| test('skips flutter pub get for examples', () async { |
| final RepositoryPackage plugin1 = createFakePlugin('a', packagesDir); |
| |
| await runCapturingPrint(runner, <String>['analyze']); |
| |
| expect( |
| processRunner.recordedCalls, |
| orderedEquals(<ProcessCall>[ |
| ProcessCall('flutter', const <String>['pub', 'get'], plugin1.path), |
| ProcessCall('dart', const <String>[ |
| 'analyze', |
| '--fatal-infos', |
| ], plugin1.path), |
| ]), |
| ); |
| }); |
| |
| test('runs flutter pub get for non-example subpackages', () async { |
| final RepositoryPackage mainPackage = createFakePackage('a', packagesDir); |
| final Directory otherPackagesDir = mainPackage.directory.childDirectory( |
| 'other_packages', |
| ); |
| final RepositoryPackage subpackage1 = createFakePackage( |
| 'subpackage1', |
| otherPackagesDir, |
| ); |
| final RepositoryPackage subpackage2 = createFakePackage( |
| 'subpackage2', |
| otherPackagesDir, |
| ); |
| |
| await runCapturingPrint(runner, <String>['analyze']); |
| |
| expect( |
| processRunner.recordedCalls, |
| orderedEquals(<ProcessCall>[ |
| ProcessCall('flutter', const <String>[ |
| 'pub', |
| 'get', |
| ], mainPackage.path), |
| ProcessCall('flutter', const <String>[ |
| 'pub', |
| 'get', |
| ], subpackage1.path), |
| ProcessCall('flutter', const <String>[ |
| 'pub', |
| 'get', |
| ], subpackage2.path), |
| ProcessCall('dart', const <String>[ |
| 'analyze', |
| '--fatal-infos', |
| ], mainPackage.path), |
| ]), |
| ); |
| }); |
| |
| test('passes lib/ directory with --lib-only', () async { |
| final RepositoryPackage package = createFakePackage( |
| 'a_package', |
| packagesDir, |
| ); |
| |
| await runCapturingPrint(runner, <String>['analyze', '--lib-only']); |
| |
| expect( |
| processRunner.recordedCalls, |
| orderedEquals(<ProcessCall>[ |
| ProcessCall('flutter', const <String>['pub', 'get'], package.path), |
| ProcessCall('dart', const <String>[ |
| 'analyze', |
| '--fatal-infos', |
| 'lib', |
| ], package.path), |
| ]), |
| ); |
| }); |
| |
| test('skips when missing lib/ directory with --lib-only', () async { |
| final RepositoryPackage package = createFakePackage( |
| 'a_package', |
| packagesDir, |
| ); |
| package.libDirectory.deleteSync(); |
| |
| final List<String> output = await runCapturingPrint(runner, <String>[ |
| 'analyze', |
| '--lib-only', |
| ]); |
| |
| expect(processRunner.recordedCalls, isEmpty); |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[contains('SKIPPING: No lib/ directory')]), |
| ); |
| }); |
| |
| test( |
| 'does not run flutter pub get for non-example subpackages with --lib-only', |
| () async { |
| final RepositoryPackage mainPackage = createFakePackage( |
| 'a', |
| packagesDir, |
| ); |
| final Directory otherPackagesDir = mainPackage.directory.childDirectory( |
| 'other_packages', |
| ); |
| createFakePackage('subpackage1', otherPackagesDir); |
| createFakePackage('subpackage2', otherPackagesDir); |
| |
| await runCapturingPrint(runner, <String>['analyze', '--lib-only']); |
| |
| expect( |
| processRunner.recordedCalls, |
| orderedEquals(<ProcessCall>[ |
| ProcessCall('flutter', const <String>[ |
| 'pub', |
| 'get', |
| ], mainPackage.path), |
| ProcessCall('dart', const <String>[ |
| 'analyze', |
| '--fatal-infos', |
| 'lib', |
| ], mainPackage.path), |
| ]), |
| ); |
| }, |
| ); |
| |
| test("don't elide a non-contained example package", () async { |
| final RepositoryPackage plugin1 = createFakePlugin('a', packagesDir); |
| final RepositoryPackage plugin2 = createFakePlugin( |
| 'example', |
| packagesDir, |
| ); |
| |
| await runCapturingPrint(runner, <String>['analyze']); |
| |
| expect( |
| processRunner.recordedCalls, |
| orderedEquals(<ProcessCall>[ |
| ProcessCall('flutter', const <String>['pub', 'get'], plugin1.path), |
| ProcessCall('dart', const <String>[ |
| 'analyze', |
| '--fatal-infos', |
| ], plugin1.path), |
| ProcessCall('flutter', const <String>['pub', 'get'], plugin2.path), |
| ProcessCall('dart', const <String>[ |
| 'analyze', |
| '--fatal-infos', |
| ], plugin2.path), |
| ]), |
| ); |
| }); |
| |
| test('uses a separate analysis sdk', () async { |
| final RepositoryPackage plugin = createFakePlugin('a', packagesDir); |
| |
| await runCapturingPrint(runner, <String>[ |
| 'analyze', |
| '--analysis-sdk', |
| 'foo/bar/baz', |
| ]); |
| |
| expect( |
| processRunner.recordedCalls, |
| orderedEquals(<ProcessCall>[ |
| ProcessCall('flutter', const <String>['pub', 'get'], plugin.path), |
| ProcessCall('foo/bar/baz/bin/dart', const <String>[ |
| 'analyze', |
| '--fatal-infos', |
| ], plugin.path), |
| ]), |
| ); |
| }); |
| |
| test('downgrades first when requested', () async { |
| final RepositoryPackage plugin = createFakePlugin('a', packagesDir); |
| |
| await runCapturingPrint(runner, <String>['analyze', '--downgrade']); |
| |
| expect( |
| processRunner.recordedCalls, |
| orderedEquals(<ProcessCall>[ |
| ProcessCall('flutter', const <String>[ |
| 'pub', |
| 'downgrade', |
| ], plugin.path), |
| ProcessCall('flutter', const <String>['pub', 'get'], plugin.path), |
| ProcessCall('dart', const <String>[ |
| 'analyze', |
| '--fatal-infos', |
| ], plugin.path), |
| ]), |
| ); |
| }); |
| |
| group('verifies analysis settings', () { |
| test('fails analysis_options.yaml', () async { |
| createFakePlugin( |
| 'foo', |
| packagesDir, |
| extraFiles: <String>['analysis_options.yaml'], |
| ); |
| |
| Error? commandError; |
| final List<String> output = await runCapturingPrint( |
| runner, |
| <String>['analyze'], |
| errorHandler: (Error e) { |
| commandError = e; |
| }, |
| ); |
| |
| expect(commandError, isA<ToolExit>()); |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains( |
| 'Found an extra analysis_options.yaml at /packages/foo/analysis_options.yaml', |
| ), |
| contains( |
| ' foo:\n' |
| ' Unexpected local analysis options', |
| ), |
| ]), |
| ); |
| }); |
| |
| test('fails .analysis_options', () async { |
| createFakePlugin( |
| 'foo', |
| packagesDir, |
| extraFiles: <String>['.analysis_options'], |
| ); |
| |
| Error? commandError; |
| final List<String> output = await runCapturingPrint( |
| runner, |
| <String>['analyze'], |
| errorHandler: (Error e) { |
| commandError = e; |
| }, |
| ); |
| |
| expect(commandError, isA<ToolExit>()); |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains( |
| 'Found an extra analysis_options.yaml at /packages/foo/.analysis_options', |
| ), |
| contains( |
| ' foo:\n' |
| ' Unexpected local analysis options', |
| ), |
| ]), |
| ); |
| }); |
| |
| test('takes an allow list', () async { |
| final RepositoryPackage plugin = createFakePlugin( |
| 'foo', |
| packagesDir, |
| extraFiles: <String>['analysis_options.yaml'], |
| ); |
| |
| await runCapturingPrint(runner, <String>[ |
| 'analyze', |
| '--custom-analysis', |
| 'foo', |
| ]); |
| |
| expect( |
| processRunner.recordedCalls, |
| orderedEquals(<ProcessCall>[ |
| ProcessCall('flutter', const <String>['pub', 'get'], plugin.path), |
| ProcessCall('dart', const <String>[ |
| 'analyze', |
| '--fatal-infos', |
| ], plugin.path), |
| ]), |
| ); |
| }); |
| |
| test( |
| 'ignores analysis options in the plugin .symlinks directory', |
| () async { |
| final RepositoryPackage plugin = createFakePlugin( |
| 'foo', |
| packagesDir, |
| extraFiles: <String>['analysis_options.yaml'], |
| ); |
| final RepositoryPackage includingPackage = createFakePlugin( |
| 'bar', |
| packagesDir, |
| ); |
| // Simulate the local state of having built 'bar' if it includes 'foo'. |
| includingPackage.directory |
| .childDirectory('example') |
| .childDirectory('ios') |
| .childLink('.symlinks') |
| .createSync(plugin.directory.path, recursive: true); |
| |
| await runCapturingPrint(runner, <String>[ |
| 'analyze', |
| '--custom-analysis', |
| 'foo', |
| ]); |
| }, |
| ); |
| |
| test('takes an allow config file', () async { |
| final RepositoryPackage plugin = createFakePlugin( |
| 'foo', |
| packagesDir, |
| extraFiles: <String>['analysis_options.yaml'], |
| ); |
| final File allowFile = packagesDir.childFile('custom.yaml'); |
| allowFile.writeAsStringSync('- foo'); |
| |
| await runCapturingPrint(runner, <String>[ |
| 'analyze', |
| '--custom-analysis', |
| allowFile.path, |
| ]); |
| |
| expect( |
| processRunner.recordedCalls, |
| orderedEquals(<ProcessCall>[ |
| ProcessCall('flutter', const <String>['pub', 'get'], plugin.path), |
| ProcessCall('dart', const <String>[ |
| 'analyze', |
| '--fatal-infos', |
| ], plugin.path), |
| ]), |
| ); |
| }); |
| |
| test('allows an empty config file', () async { |
| createFakePlugin( |
| 'foo', |
| packagesDir, |
| extraFiles: <String>['analysis_options.yaml'], |
| ); |
| final File allowFile = packagesDir.childFile('custom.yaml'); |
| allowFile.createSync(); |
| |
| await expectLater( |
| () => runCapturingPrint(runner, <String>[ |
| 'analyze', |
| '--custom-analysis', |
| allowFile.path, |
| ]), |
| throwsA(isA<ToolExit>()), |
| ); |
| }); |
| |
| // See: https://github.com/flutter/flutter/issues/78994 |
| test('takes an empty allow list', () async { |
| createFakePlugin( |
| 'foo', |
| packagesDir, |
| extraFiles: <String>['analysis_options.yaml'], |
| ); |
| |
| await expectLater( |
| () => runCapturingPrint(runner, <String>[ |
| 'analyze', |
| '--custom-analysis', |
| '', |
| ]), |
| throwsA(isA<ToolExit>()), |
| ); |
| }); |
| }); |
| |
| test('skips if requested if "pub get" fails in the resolver', () async { |
| final RepositoryPackage plugin = createFakePlugin('foo', packagesDir); |
| |
| final failingPubGet = FakeProcessInfo( |
| MockProcess( |
| exitCode: 1, |
| stderr: |
| 'So, because foo depends on both thing_one ^1.0.0 and ' |
| 'thing_two from path, version solving failed.', |
| ), |
| <String>['pub', 'get'], |
| ); |
| processRunner.mockProcessesForExecutable['flutter'] = <FakeProcessInfo>[ |
| failingPubGet, |
| // The command re-runs failures when --skip-if-resolver-fails is passed |
| // to check the output, so provide the same failing outcome. |
| failingPubGet, |
| ]; |
| |
| final List<String> output = await runCapturingPrint(runner, <String>[ |
| 'analyze', |
| '--skip-if-resolving-fails', |
| ]); |
| |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains('Skipping package due to pub resolution failure.'), |
| ]), |
| ); |
| expect( |
| processRunner.recordedCalls, |
| orderedEquals(<ProcessCall>[ |
| ProcessCall('flutter', const <String>['pub', 'get'], plugin.path), |
| ProcessCall('flutter', const <String>['pub', 'get'], plugin.path), |
| ]), |
| ); |
| }); |
| |
| test('fails if "pub get" fails', () async { |
| createFakePlugin('foo', packagesDir); |
| |
| processRunner.mockProcessesForExecutable['flutter'] = <FakeProcessInfo>[ |
| FakeProcessInfo(MockProcess(exitCode: 1), <String>['pub', 'get']), |
| ]; |
| |
| Error? commandError; |
| final List<String> output = await runCapturingPrint( |
| runner, |
| <String>['analyze'], |
| errorHandler: (Error e) { |
| commandError = e; |
| }, |
| ); |
| |
| expect(commandError, isA<ToolExit>()); |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[contains('Unable to get dependencies')]), |
| ); |
| }); |
| |
| test('fails if "pub downgrade" fails', () async { |
| createFakePlugin('foo', packagesDir); |
| |
| processRunner.mockProcessesForExecutable['flutter'] = <FakeProcessInfo>[ |
| FakeProcessInfo(MockProcess(exitCode: 1), <String>['pub', 'downgrade']), |
| ]; |
| |
| Error? commandError; |
| final List<String> output = await runCapturingPrint( |
| runner, |
| <String>['analyze', '--downgrade'], |
| errorHandler: (Error e) { |
| commandError = e; |
| }, |
| ); |
| |
| expect(commandError, isA<ToolExit>()); |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains('Unable to downgrade dependencies'), |
| ]), |
| ); |
| }); |
| |
| test('fails if "analyze" fails', () async { |
| createFakePlugin('foo', packagesDir); |
| |
| processRunner.mockProcessesForExecutable['dart'] = <FakeProcessInfo>[ |
| FakeProcessInfo(MockProcess(exitCode: 1), <String>['analyze']), |
| ]; |
| |
| Error? commandError; |
| final List<String> output = await runCapturingPrint( |
| runner, |
| <String>['analyze'], |
| errorHandler: (Error e) { |
| commandError = e; |
| }, |
| ); |
| |
| expect(commandError, isA<ToolExit>()); |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains('The following packages had errors:'), |
| contains(' foo'), |
| ]), |
| ); |
| }); |
| |
| // Ensure that the command used to analyze flutter/plugins in the Dart repo: |
| // https://github.com/dart-lang/sdk/blob/main/tools/bots/flutter/analyze_flutter_plugins.sh |
| // continues to work. |
| // |
| // DO NOT remove or modify this test without a coordination plan in place to |
| // modify the script above, as it is run from source, but out-of-repo. |
| // Contact stuartmorgan or devoncarew for assistance. |
| test('Dart repo analyze command works', () async { |
| final RepositoryPackage plugin = createFakePlugin( |
| 'foo', |
| packagesDir, |
| extraFiles: <String>['analysis_options.yaml'], |
| ); |
| final File allowFile = packagesDir.childFile('custom.yaml'); |
| allowFile.writeAsStringSync('- foo'); |
| |
| await runCapturingPrint(runner, <String>[ |
| // DO NOT change this call; see comment above. |
| 'analyze', |
| '--analysis-sdk', |
| 'foo/bar/baz', |
| '--custom-analysis', |
| allowFile.path, |
| ]); |
| |
| expect( |
| processRunner.recordedCalls, |
| orderedEquals(<ProcessCall>[ |
| ProcessCall('flutter', const <String>['pub', 'get'], plugin.path), |
| ProcessCall('foo/bar/baz/bin/dart', const <String>[ |
| 'analyze', |
| '--fatal-infos', |
| ], plugin.path), |
| ]), |
| ); |
| }); |
| |
| group('file filtering', () { |
| test('runs command for changes to Dart source', () async { |
| createFakePackage('package_a', packagesDir); |
| |
| gitProcessRunner.mockProcessesForExecutable['git-diff'] = |
| <FakeProcessInfo>[ |
| FakeProcessInfo( |
| MockProcess( |
| stdout: ''' |
| packages/package_a/foo.dart |
| ''', |
| ), |
| ), |
| ]; |
| |
| final List<String> output = await runCapturingPrint(runner, <String>[ |
| 'analyze', |
| ]); |
| |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[contains('Running for package_a')]), |
| ); |
| }); |
| |
| const files = <String>[ |
| 'foo.java', |
| 'foo.kt', |
| 'foo.m', |
| 'foo.swift', |
| 'foo.c', |
| 'foo.cc', |
| 'foo.cpp', |
| 'foo.h', |
| ]; |
| for (final file in files) { |
| test('skips command for changes to non-Dart source $file', () async { |
| createFakePackage('package_a', packagesDir); |
| |
| gitProcessRunner.mockProcessesForExecutable['git-diff'] = |
| <FakeProcessInfo>[ |
| FakeProcessInfo( |
| MockProcess( |
| stdout: |
| ''' |
| packages/package_a/$file |
| ''', |
| ), |
| ), |
| ]; |
| |
| final List<String> output = await runCapturingPrint(runner, <String>[ |
| 'analyze', |
| ]); |
| |
| expect( |
| output, |
| isNot( |
| containsAllInOrder(<Matcher>[contains('Running for package_a')]), |
| ), |
| ); |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[contains('SKIPPING ALL PACKAGES')]), |
| ); |
| }); |
| } |
| |
| test('skips commands if all files should be ignored', () async { |
| createFakePackage('package_a', packagesDir); |
| |
| gitProcessRunner.mockProcessesForExecutable['git-diff'] = |
| <FakeProcessInfo>[ |
| FakeProcessInfo( |
| MockProcess( |
| stdout: ''' |
| README.md |
| CODEOWNERS |
| packages/package_a/CHANGELOG.md |
| ''', |
| ), |
| ), |
| ]; |
| |
| final List<String> output = await runCapturingPrint(runner, <String>[ |
| 'analyze', |
| ]); |
| |
| expect( |
| output, |
| isNot( |
| containsAllInOrder(<Matcher>[contains('Running for package_a')]), |
| ), |
| ); |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[contains('SKIPPING ALL PACKAGES')]), |
| ); |
| }); |
| }); |
| }); |
| |
| group('gradle lint', () { |
| test('runs gradle lint', () async { |
| final RepositoryPackage plugin = createFakePlugin( |
| 'plugin1', |
| packagesDir, |
| extraFiles: <String>['example/android/gradlew'], |
| platformSupport: <String, PlatformDetails>{ |
| platformAndroid: const PlatformDetails(PlatformSupport.inline), |
| }, |
| ); |
| |
| final Directory androidDir = plugin.getExamples().first.platformDirectory( |
| FlutterPlatform.android, |
| ); |
| |
| final List<String> output = await runCapturingPrint(runner, <String>[ |
| 'analyze', |
| '--android', |
| '--no-dart', |
| ]); |
| |
| expect( |
| processRunner.recordedCalls, |
| orderedEquals(<ProcessCall>[ |
| ProcessCall(androidDir.childFile('gradlew').path, const <String>[ |
| 'plugin1:lintDebug', |
| ], androidDir.path), |
| ]), |
| ); |
| |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains('Running for plugin1'), |
| contains('No issues found!'), |
| ]), |
| ); |
| }); |
| |
| test('runs on all examples', () async { |
| final examples = <String>['example1', 'example2']; |
| final RepositoryPackage plugin = createFakePlugin( |
| 'plugin1', |
| packagesDir, |
| examples: examples, |
| extraFiles: <String>[ |
| 'example/example1/android/gradlew', |
| 'example/example2/android/gradlew', |
| ], |
| platformSupport: <String, PlatformDetails>{ |
| platformAndroid: const PlatformDetails(PlatformSupport.inline), |
| }, |
| ); |
| |
| final Iterable<Directory> exampleAndroidDirs = plugin.getExamples().map( |
| (RepositoryPackage example) => |
| example.platformDirectory(FlutterPlatform.android), |
| ); |
| |
| final List<String> output = await runCapturingPrint(runner, <String>[ |
| 'analyze', |
| '--android', |
| '--no-dart', |
| ]); |
| |
| expect( |
| processRunner.recordedCalls, |
| orderedEquals(<ProcessCall>[ |
| for (final Directory directory in exampleAndroidDirs) |
| ProcessCall(directory.childFile('gradlew').path, const <String>[ |
| 'plugin1:lintDebug', |
| ], directory.path), |
| ]), |
| ); |
| |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains('Running for plugin1'), |
| contains('No issues found!'), |
| ]), |
| ); |
| }); |
| |
| test('runs --config-only build if gradlew is missing', () async { |
| final RepositoryPackage plugin = createFakePlugin( |
| 'plugin1', |
| packagesDir, |
| platformSupport: <String, PlatformDetails>{ |
| platformAndroid: const PlatformDetails(PlatformSupport.inline), |
| }, |
| ); |
| |
| final Directory androidDir = plugin.getExamples().first.platformDirectory( |
| FlutterPlatform.android, |
| ); |
| |
| final List<String> output = await runCapturingPrint(runner, <String>[ |
| 'analyze', |
| '--android', |
| '--no-dart', |
| ]); |
| |
| expect( |
| processRunner.recordedCalls, |
| orderedEquals(<ProcessCall>[ |
| ProcessCall( |
| getFlutterCommand(mockPlatform), |
| const <String>['build', 'apk', '--config-only'], |
| plugin.getExamples().first.directory.path, |
| ), |
| ProcessCall(androidDir.childFile('gradlew').path, const <String>[ |
| 'plugin1:lintDebug', |
| ], androidDir.path), |
| ]), |
| ); |
| |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains('Running for plugin1'), |
| contains('No issues found!'), |
| ]), |
| ); |
| }); |
| |
| test('fails if gradlew generation fails', () async { |
| createFakePlugin( |
| 'plugin1', |
| packagesDir, |
| platformSupport: <String, PlatformDetails>{ |
| platformAndroid: const PlatformDetails(PlatformSupport.inline), |
| }, |
| ); |
| |
| processRunner.mockProcessesForExecutable[getFlutterCommand( |
| mockPlatform, |
| )] = <FakeProcessInfo>[ |
| FakeProcessInfo(MockProcess(exitCode: 1)), |
| ]; |
| |
| Error? commandError; |
| final List<String> output = await runCapturingPrint( |
| runner, |
| <String>['analyze', '--android', '--no-dart'], |
| errorHandler: (Error e) { |
| commandError = e; |
| }, |
| ); |
| |
| expect(commandError, isA<ToolExit>()); |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains('Unable to configure Gradle project'), |
| ]), |
| ); |
| }); |
| |
| test('fails if linting finds issues', () async { |
| final RepositoryPackage plugin = createFakePlugin( |
| 'plugin1', |
| packagesDir, |
| extraFiles: <String>['example/android/gradlew'], |
| platformSupport: <String, PlatformDetails>{ |
| platformAndroid: const PlatformDetails(PlatformSupport.inline), |
| }, |
| ); |
| |
| final String gradlewPath = plugin |
| .getExamples() |
| .first |
| .platformDirectory(FlutterPlatform.android) |
| .childFile('gradlew') |
| .path; |
| processRunner.mockProcessesForExecutable[gradlewPath] = <FakeProcessInfo>[ |
| FakeProcessInfo(MockProcess(exitCode: 1)), |
| ]; |
| |
| Error? commandError; |
| final List<String> output = await runCapturingPrint( |
| runner, |
| <String>['analyze', '--android', '--no-dart'], |
| errorHandler: (Error e) { |
| commandError = e; |
| }, |
| ); |
| |
| expect(commandError, isA<ToolExit>()); |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains('The following packages had errors:'), |
| ]), |
| ); |
| }); |
| |
| test('skips non-Android plugins', () async { |
| createFakePlugin('plugin1', packagesDir); |
| |
| final List<String> output = await runCapturingPrint(runner, <String>[ |
| 'analyze', |
| '--android', |
| '--no-dart', |
| ]); |
| |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains( |
| 'SKIPPING: Package does not contain native Android plugin code', |
| ), |
| ]), |
| ); |
| }); |
| |
| test('skips non-inline plugins', () async { |
| createFakePlugin( |
| 'plugin1', |
| packagesDir, |
| platformSupport: <String, PlatformDetails>{ |
| platformAndroid: const PlatformDetails(PlatformSupport.federated), |
| }, |
| ); |
| |
| final List<String> output = await runCapturingPrint(runner, <String>[ |
| 'analyze', |
| '--android', |
| '--no-dart', |
| ]); |
| |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains( |
| 'SKIPPING: Package does not contain native Android plugin code', |
| ), |
| ]), |
| ); |
| }); |
| |
| group('file filtering', () { |
| const files = <String>['foo.java', 'foo.kt']; |
| for (final file in files) { |
| test('runs command for changes to $file', () async { |
| createFakePackage('package_a', packagesDir); |
| |
| gitProcessRunner.mockProcessesForExecutable['git-diff'] = |
| <FakeProcessInfo>[ |
| FakeProcessInfo( |
| MockProcess( |
| stdout: |
| ''' |
| packages/package_a/$file |
| ''', |
| ), |
| ), |
| ]; |
| |
| final List<String> output = await runCapturingPrint(runner, <String>[ |
| 'analyze', |
| '--android', |
| '--no-dart', |
| ]); |
| |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[contains('Running for package_a')]), |
| ); |
| }); |
| } |
| |
| test('skips commands if all files should be ignored', () async { |
| createFakePackage('package_a', packagesDir); |
| |
| gitProcessRunner.mockProcessesForExecutable['git-diff'] = |
| <FakeProcessInfo>[ |
| FakeProcessInfo( |
| MockProcess( |
| stdout: ''' |
| README.md |
| CODEOWNERS |
| packages/package_a/CHANGELOG.md |
| packages/package_a/lib/foo.dart |
| ''', |
| ), |
| ), |
| ]; |
| |
| final List<String> output = await runCapturingPrint(runner, <String>[ |
| 'analyze', |
| '--android', |
| '--no-dart', |
| ]); |
| |
| expect( |
| output, |
| isNot( |
| containsAllInOrder(<Matcher>[contains('Running for package_a')]), |
| ), |
| ); |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[contains('SKIPPING ALL PACKAGES')]), |
| ); |
| }); |
| }); |
| }); |
| |
| group('Xcode analyze', () { |
| test('temporarily disables Swift Package Manager', () async { |
| final RepositoryPackage plugin = createFakePlugin( |
| 'plugin', |
| packagesDir, |
| platformSupport: <String, PlatformDetails>{ |
| platformIOS: const PlatformDetails(PlatformSupport.inline), |
| }, |
| ); |
| |
| final RepositoryPackage example = plugin.getExamples().first; |
| final String originalPubspecContents = example.pubspecFile |
| .readAsStringSync(); |
| String? buildTimePubspecContents; |
| processRunner.mockProcessesForExecutable['xcrun'] = <FakeProcessInfo>[ |
| FakeProcessInfo(MockProcess(), <String>[], () { |
| buildTimePubspecContents = example.pubspecFile.readAsStringSync(); |
| }), |
| ]; |
| |
| await runCapturingPrint(runner, <String>[ |
| 'analyze', |
| '--no-dart', |
| '--ios', |
| ]); |
| |
| // Ensure that SwiftPM was disabled for the package. |
| expect( |
| originalPubspecContents, |
| isNot(contains('enable-swift-package-manager: false')), |
| ); |
| expect( |
| buildTimePubspecContents, |
| contains('enable-swift-package-manager: false'), |
| ); |
| // And that it was undone after. |
| expect(example.pubspecFile.readAsStringSync(), originalPubspecContents); |
| }); |
| |
| group('iOS', () { |
| test('skip if iOS is not supported', () async { |
| createFakePlugin( |
| 'plugin', |
| packagesDir, |
| platformSupport: <String, PlatformDetails>{ |
| platformMacOS: const PlatformDetails(PlatformSupport.inline), |
| }, |
| ); |
| |
| final List<String> output = await runCapturingPrint(runner, <String>[ |
| 'analyze', |
| '--no-dart', |
| '--ios', |
| ]); |
| expect( |
| output, |
| contains(contains('Package does not contain native iOS plugin code')), |
| ); |
| expect(processRunner.recordedCalls, orderedEquals(<ProcessCall>[])); |
| }); |
| |
| test('skip if iOS is implemented in a federated package', () async { |
| createFakePlugin( |
| 'plugin', |
| packagesDir, |
| platformSupport: <String, PlatformDetails>{ |
| platformIOS: const PlatformDetails(PlatformSupport.federated), |
| }, |
| ); |
| |
| final List<String> output = await runCapturingPrint(runner, <String>[ |
| 'analyze', |
| '--no-dart', |
| '--ios', |
| ]); |
| expect( |
| output, |
| contains(contains('Package does not contain native iOS plugin code')), |
| ); |
| expect(processRunner.recordedCalls, orderedEquals(<ProcessCall>[])); |
| }); |
| |
| test('runs for iOS plugin', () async { |
| final RepositoryPackage plugin = createFakePlugin( |
| 'plugin', |
| packagesDir, |
| platformSupport: <String, PlatformDetails>{ |
| platformIOS: const PlatformDetails(PlatformSupport.inline), |
| }, |
| ); |
| |
| final Directory pluginExampleDirectory = getExampleDir(plugin); |
| |
| final List<String> output = await runCapturingPrint(runner, <String>[ |
| 'analyze', |
| '--no-dart', |
| '--ios', |
| ]); |
| |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains('Running for plugin'), |
| contains('plugin/example (iOS) passed analysis.'), |
| ]), |
| ); |
| |
| expect( |
| processRunner.recordedCalls, |
| orderedEquals(<ProcessCall>[ |
| ProcessCall('flutter', const <String>[ |
| 'build', |
| 'ios', |
| '--debug', |
| '--config-only', |
| ], pluginExampleDirectory.path), |
| ProcessCall('xcrun', const <String>[ |
| 'xcodebuild', |
| 'clean', |
| 'analyze', |
| '-workspace', |
| 'ios/Runner.xcworkspace', |
| '-scheme', |
| 'Runner', |
| '-configuration', |
| 'Debug', |
| '-destination', |
| 'generic/platform=iOS Simulator', |
| 'GCC_TREAT_WARNINGS_AS_ERRORS=YES', |
| ], pluginExampleDirectory.path), |
| ]), |
| ); |
| }); |
| |
| test('passes min iOS deployment version when requested', () async { |
| final RepositoryPackage plugin = createFakePlugin( |
| 'plugin', |
| packagesDir, |
| platformSupport: <String, PlatformDetails>{ |
| platformIOS: const PlatformDetails(PlatformSupport.inline), |
| }, |
| ); |
| |
| final Directory pluginExampleDirectory = getExampleDir(plugin); |
| |
| final List<String> output = await runCapturingPrint(runner, <String>[ |
| 'analyze', |
| '--no-dart', |
| '--ios', |
| '--ios-min-version=14.0', |
| ]); |
| |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains('Running for plugin'), |
| contains('plugin/example (iOS) passed analysis.'), |
| ]), |
| ); |
| |
| expect( |
| processRunner.recordedCalls, |
| orderedEquals(<ProcessCall>[ |
| ProcessCall('flutter', const <String>[ |
| 'build', |
| 'ios', |
| '--debug', |
| '--config-only', |
| ], pluginExampleDirectory.path), |
| ProcessCall('xcrun', const <String>[ |
| 'xcodebuild', |
| 'clean', |
| 'analyze', |
| '-workspace', |
| 'ios/Runner.xcworkspace', |
| '-scheme', |
| 'Runner', |
| '-configuration', |
| 'Debug', |
| '-destination', |
| 'generic/platform=iOS Simulator', |
| 'IPHONEOS_DEPLOYMENT_TARGET=14.0', |
| 'GCC_TREAT_WARNINGS_AS_ERRORS=YES', |
| ], pluginExampleDirectory.path), |
| ]), |
| ); |
| }); |
| |
| test('fails if xcrun fails', () async { |
| createFakePlugin( |
| 'plugin', |
| packagesDir, |
| platformSupport: <String, PlatformDetails>{ |
| platformIOS: const PlatformDetails(PlatformSupport.inline), |
| }, |
| ); |
| |
| processRunner.mockProcessesForExecutable['xcrun'] = <FakeProcessInfo>[ |
| FakeProcessInfo(MockProcess(exitCode: 1)), |
| ]; |
| |
| Error? commandError; |
| final List<String> output = await runCapturingPrint( |
| runner, |
| <String>['analyze', '--no-dart', '--ios'], |
| errorHandler: (Error e) { |
| commandError = e; |
| }, |
| ); |
| |
| expect(commandError, isA<ToolExit>()); |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains('The following packages had errors:'), |
| contains(' plugin'), |
| ]), |
| ); |
| }); |
| }); |
| |
| group('macOS', () { |
| test('skip if macOS is not supported', () async { |
| createFakePlugin('plugin', packagesDir); |
| |
| final List<String> output = await runCapturingPrint(runner, <String>[ |
| 'analyze', |
| '--no-dart', |
| '--macos', |
| ]); |
| expect( |
| output, |
| contains( |
| contains('Package does not contain native macOS plugin code'), |
| ), |
| ); |
| expect(processRunner.recordedCalls, orderedEquals(<ProcessCall>[])); |
| }); |
| |
| test('skip if macOS is implemented in a federated package', () async { |
| createFakePlugin( |
| 'plugin', |
| packagesDir, |
| platformSupport: <String, PlatformDetails>{ |
| platformMacOS: const PlatformDetails(PlatformSupport.federated), |
| }, |
| ); |
| |
| final List<String> output = await runCapturingPrint(runner, <String>[ |
| 'analyze', |
| '--no-dart', |
| '--macos', |
| ]); |
| expect( |
| output, |
| contains( |
| contains('Package does not contain native macOS plugin code'), |
| ), |
| ); |
| expect(processRunner.recordedCalls, orderedEquals(<ProcessCall>[])); |
| }); |
| |
| test('runs for macOS plugin', () async { |
| final RepositoryPackage plugin = createFakePlugin( |
| 'plugin', |
| packagesDir, |
| platformSupport: <String, PlatformDetails>{ |
| platformMacOS: const PlatformDetails(PlatformSupport.inline), |
| }, |
| ); |
| |
| final Directory pluginExampleDirectory = getExampleDir(plugin); |
| |
| final List<String> output = await runCapturingPrint(runner, <String>[ |
| 'analyze', |
| '--no-dart', |
| '--macos', |
| ]); |
| |
| expect( |
| output, |
| contains(contains('plugin/example (macOS) passed analysis.')), |
| ); |
| |
| expect( |
| processRunner.recordedCalls, |
| orderedEquals(<ProcessCall>[ |
| ProcessCall('flutter', const <String>[ |
| 'build', |
| 'macos', |
| '--debug', |
| '--config-only', |
| ], pluginExampleDirectory.path), |
| ProcessCall('xcrun', const <String>[ |
| 'xcodebuild', |
| 'clean', |
| 'analyze', |
| '-workspace', |
| 'macos/Runner.xcworkspace', |
| '-scheme', |
| 'Runner', |
| '-configuration', |
| 'Debug', |
| 'GCC_TREAT_WARNINGS_AS_ERRORS=YES', |
| ], pluginExampleDirectory.path), |
| ]), |
| ); |
| }); |
| |
| test('passes min macOS deployment version when requested', () async { |
| final RepositoryPackage plugin = createFakePlugin( |
| 'plugin', |
| packagesDir, |
| platformSupport: <String, PlatformDetails>{ |
| platformMacOS: const PlatformDetails(PlatformSupport.inline), |
| }, |
| ); |
| |
| final Directory pluginExampleDirectory = getExampleDir(plugin); |
| |
| final List<String> output = await runCapturingPrint(runner, <String>[ |
| 'analyze', |
| '--no-dart', |
| '--macos', |
| '--macos-min-version=12.0', |
| ]); |
| |
| expect( |
| output, |
| contains(contains('plugin/example (macOS) passed analysis.')), |
| ); |
| |
| expect( |
| processRunner.recordedCalls, |
| orderedEquals(<ProcessCall>[ |
| ProcessCall('flutter', const <String>[ |
| 'build', |
| 'macos', |
| '--debug', |
| '--config-only', |
| ], pluginExampleDirectory.path), |
| ProcessCall('xcrun', const <String>[ |
| 'xcodebuild', |
| 'clean', |
| 'analyze', |
| '-workspace', |
| 'macos/Runner.xcworkspace', |
| '-scheme', |
| 'Runner', |
| '-configuration', |
| 'Debug', |
| 'MACOSX_DEPLOYMENT_TARGET=12.0', |
| 'GCC_TREAT_WARNINGS_AS_ERRORS=YES', |
| ], pluginExampleDirectory.path), |
| ]), |
| ); |
| }); |
| |
| test('fails if xcrun fails', () async { |
| createFakePlugin( |
| 'plugin', |
| packagesDir, |
| platformSupport: <String, PlatformDetails>{ |
| platformMacOS: const PlatformDetails(PlatformSupport.inline), |
| }, |
| ); |
| |
| processRunner.mockProcessesForExecutable['xcrun'] = <FakeProcessInfo>[ |
| FakeProcessInfo(MockProcess(exitCode: 1)), |
| ]; |
| |
| Error? commandError; |
| final List<String> output = await runCapturingPrint( |
| runner, |
| <String>['analyze', '--no-dart', '--macos'], |
| errorHandler: (Error e) { |
| commandError = e; |
| }, |
| ); |
| |
| expect(commandError, isA<ToolExit>()); |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains('The following packages had errors:'), |
| contains(' plugin'), |
| ]), |
| ); |
| }); |
| }); |
| |
| group('combined', () { |
| test('runs both iOS and macOS when supported', () async { |
| final RepositoryPackage plugin = createFakePlugin( |
| 'plugin', |
| packagesDir, |
| platformSupport: <String, PlatformDetails>{ |
| platformIOS: const PlatformDetails(PlatformSupport.inline), |
| platformMacOS: const PlatformDetails(PlatformSupport.inline), |
| }, |
| ); |
| |
| final Directory pluginExampleDirectory = getExampleDir(plugin); |
| |
| final List<String> output = await runCapturingPrint(runner, <String>[ |
| 'analyze', |
| '--no-dart', |
| '--ios', |
| '--macos', |
| ]); |
| |
| expect( |
| output, |
| containsAll(<Matcher>[ |
| contains('plugin/example (iOS) passed analysis.'), |
| contains('plugin/example (macOS) passed analysis.'), |
| ]), |
| ); |
| |
| expect( |
| processRunner.recordedCalls, |
| orderedEquals(<ProcessCall>[ |
| ProcessCall('flutter', const <String>[ |
| 'build', |
| 'ios', |
| '--debug', |
| '--config-only', |
| ], pluginExampleDirectory.path), |
| ProcessCall('xcrun', const <String>[ |
| 'xcodebuild', |
| 'clean', |
| 'analyze', |
| '-workspace', |
| 'ios/Runner.xcworkspace', |
| '-scheme', |
| 'Runner', |
| '-configuration', |
| 'Debug', |
| '-destination', |
| 'generic/platform=iOS Simulator', |
| 'GCC_TREAT_WARNINGS_AS_ERRORS=YES', |
| ], pluginExampleDirectory.path), |
| ProcessCall('flutter', const <String>[ |
| 'build', |
| 'macos', |
| '--debug', |
| '--config-only', |
| ], pluginExampleDirectory.path), |
| ProcessCall('xcrun', const <String>[ |
| 'xcodebuild', |
| 'clean', |
| 'analyze', |
| '-workspace', |
| 'macos/Runner.xcworkspace', |
| '-scheme', |
| 'Runner', |
| '-configuration', |
| 'Debug', |
| 'GCC_TREAT_WARNINGS_AS_ERRORS=YES', |
| ], pluginExampleDirectory.path), |
| ]), |
| ); |
| }); |
| |
| test('runs only macOS for a macOS plugin', () async { |
| final RepositoryPackage plugin = createFakePlugin( |
| 'plugin', |
| packagesDir, |
| platformSupport: <String, PlatformDetails>{ |
| platformMacOS: const PlatformDetails(PlatformSupport.inline), |
| }, |
| ); |
| |
| final Directory pluginExampleDirectory = getExampleDir(plugin); |
| |
| final List<String> output = await runCapturingPrint(runner, <String>[ |
| 'analyze', |
| '--no-dart', |
| '--ios', |
| '--macos', |
| ]); |
| |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains('plugin/example (macOS) passed analysis.'), |
| ]), |
| ); |
| |
| expect( |
| processRunner.recordedCalls, |
| orderedEquals(<ProcessCall>[ |
| ProcessCall('flutter', const <String>[ |
| 'build', |
| 'macos', |
| '--debug', |
| '--config-only', |
| ], pluginExampleDirectory.path), |
| ProcessCall('xcrun', const <String>[ |
| 'xcodebuild', |
| 'clean', |
| 'analyze', |
| '-workspace', |
| 'macos/Runner.xcworkspace', |
| '-scheme', |
| 'Runner', |
| '-configuration', |
| 'Debug', |
| 'GCC_TREAT_WARNINGS_AS_ERRORS=YES', |
| ], pluginExampleDirectory.path), |
| ]), |
| ); |
| }); |
| |
| test('runs only iOS for a iOS plugin', () async { |
| final RepositoryPackage plugin = createFakePlugin( |
| 'plugin', |
| packagesDir, |
| platformSupport: <String, PlatformDetails>{ |
| platformIOS: const PlatformDetails(PlatformSupport.inline), |
| }, |
| ); |
| |
| final Directory pluginExampleDirectory = getExampleDir(plugin); |
| |
| final List<String> output = await runCapturingPrint(runner, <String>[ |
| 'analyze', |
| '--no-dart', |
| '--ios', |
| '--macos', |
| ]); |
| |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains('plugin/example (iOS) passed analysis.'), |
| ]), |
| ); |
| |
| expect( |
| processRunner.recordedCalls, |
| orderedEquals(<ProcessCall>[ |
| ProcessCall('flutter', const <String>[ |
| 'build', |
| 'ios', |
| '--debug', |
| '--config-only', |
| ], pluginExampleDirectory.path), |
| ProcessCall('xcrun', const <String>[ |
| 'xcodebuild', |
| 'clean', |
| 'analyze', |
| '-workspace', |
| 'ios/Runner.xcworkspace', |
| '-scheme', |
| 'Runner', |
| '-configuration', |
| 'Debug', |
| '-destination', |
| 'generic/platform=iOS Simulator', |
| 'GCC_TREAT_WARNINGS_AS_ERRORS=YES', |
| ], pluginExampleDirectory.path), |
| ]), |
| ); |
| }); |
| }); |
| |
| group('file filtering', () { |
| const files = <String>[ |
| 'foo.m', |
| 'foo.swift', |
| 'foo.cc', |
| 'foo.cpp', |
| 'foo.h', |
| ]; |
| for (final file in files) { |
| test('runs command for changes to $file', () async { |
| createFakePackage('package_a', packagesDir); |
| |
| gitProcessRunner.mockProcessesForExecutable['git-diff'] = |
| <FakeProcessInfo>[ |
| FakeProcessInfo( |
| MockProcess( |
| stdout: |
| ''' |
| packages/package_a/$file |
| ''', |
| ), |
| ), |
| ]; |
| |
| final List<String> output = await runCapturingPrint(runner, <String>[ |
| 'analyze', |
| '--no-dart', |
| '--ios', |
| ]); |
| |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[contains('Running for package_a')]), |
| ); |
| }); |
| } |
| |
| test('skips commands if all files should be ignored', () async { |
| createFakePackage('package_a', packagesDir); |
| |
| gitProcessRunner.mockProcessesForExecutable['git-diff'] = |
| <FakeProcessInfo>[ |
| FakeProcessInfo( |
| MockProcess( |
| stdout: ''' |
| .gemini/config.yaml |
| AGENTS.md |
| README.md |
| CODEOWNERS |
| packages/package_a/CHANGELOG.md |
| packages/package_a/lib/foo.dart |
| ''', |
| ), |
| ), |
| ]; |
| |
| final List<String> output = await runCapturingPrint(runner, <String>[ |
| 'analyze', |
| '--no-dart', |
| '--ios', |
| ]); |
| |
| expect( |
| output, |
| isNot( |
| containsAllInOrder(<Matcher>[contains('Running for package_a')]), |
| ), |
| ); |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[contains('SKIPPING ALL PACKAGES')]), |
| ); |
| }); |
| }); |
| }); |
| } |