| // 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:convert'; |
| |
| 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/update_dependency_command.dart'; |
| import 'package:http/http.dart' as http; |
| import 'package:http/testing.dart'; |
| import 'package:pubspec_parse/pubspec_parse.dart'; |
| import 'package:test/test.dart'; |
| |
| import 'mocks.dart'; |
| import 'util.dart'; |
| |
| void main() { |
| FileSystem fileSystem; |
| late Directory packagesDir; |
| late RecordingProcessRunner processRunner; |
| late CommandRunner<void> runner; |
| Future<http.Response> Function(http.Request request)? mockHttpResponse; |
| |
| setUp(() { |
| fileSystem = MemoryFileSystem(); |
| processRunner = RecordingProcessRunner(); |
| packagesDir = createPackagesDirectory(fileSystem: fileSystem); |
| final UpdateDependencyCommand command = UpdateDependencyCommand( |
| packagesDir, |
| processRunner: processRunner, |
| httpClient: |
| MockClient((http.Request request) => mockHttpResponse!(request)), |
| ); |
| |
| runner = CommandRunner<void>( |
| 'update_dependency_command', 'Test for update-dependency command.'); |
| runner.addCommand(command); |
| }); |
| |
| /// Adds a dummy 'dependencies:' entries for [dependency] to [package]. |
| void addDependency(RepositoryPackage package, String dependency, |
| {String version = '^1.0.0'}) { |
| final List<String> lines = package.pubspecFile.readAsLinesSync(); |
| final int dependenciesStartIndex = lines.indexOf('dependencies:'); |
| assert(dependenciesStartIndex != -1); |
| lines.insert(dependenciesStartIndex + 1, ' $dependency: $version'); |
| package.pubspecFile.writeAsStringSync(lines.join('\n')); |
| } |
| |
| /// Adds a 'dev_dependencies:' section with an entry for [dependency] to |
| /// [package]. |
| void addDevDependency(RepositoryPackage package, String dependency, |
| {String version = '^1.0.0'}) { |
| final String originalContent = package.pubspecFile.readAsStringSync(); |
| package.pubspecFile.writeAsStringSync(''' |
| $originalContent |
| |
| dev_dependencies: |
| $dependency: $version |
| '''); |
| } |
| |
| test('throws if no target is provided', () async { |
| Error? commandError; |
| final List<String> output = await runCapturingPrint( |
| runner, <String>['update-dependency'], errorHandler: (Error e) { |
| commandError = e; |
| }); |
| |
| expect(commandError, isA<ToolExit>()); |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains('Exactly one of the target flags must be provided:'), |
| ]), |
| ); |
| }); |
| |
| test('throws if multiple dependencies specified', () async { |
| Error? commandError; |
| final List<String> output = await runCapturingPrint(runner, <String>[ |
| 'update-dependency', |
| '--pub-package', |
| 'target_package', |
| '--android-dependency', |
| 'gradle' |
| ], errorHandler: (Error e) { |
| commandError = e; |
| }); |
| |
| expect(commandError, isA<ToolExit>()); |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains('Exactly one of the target flags must be provided:'), |
| ]), |
| ); |
| }); |
| |
| group('pub dependencies', () { |
| test('throws if no version is given for an unpublished target', () async { |
| mockHttpResponse = (http.Request request) async { |
| return http.Response('', 404); |
| }; |
| |
| Error? commandError; |
| final List<String> output = await runCapturingPrint(runner, <String>[ |
| 'update-dependency', |
| '--pub-package', |
| 'target_package' |
| ], errorHandler: (Error e) { |
| commandError = e; |
| }); |
| |
| expect(commandError, isA<ToolExit>()); |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains('target_package does not exist on pub'), |
| ]), |
| ); |
| }); |
| |
| test('skips if there is no dependency', () async { |
| createFakePackage('a_package', packagesDir, examples: <String>[]); |
| |
| final List<String> output = await runCapturingPrint(runner, <String>[ |
| 'update-dependency', |
| '--pub-package', |
| 'target_package', |
| '--version', |
| '1.5.0' |
| ]); |
| |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains('SKIPPING: Does not depend on target_package'), |
| ]), |
| ); |
| }); |
| |
| test('skips if the dependency is already the target version', () async { |
| final RepositoryPackage package = |
| createFakePackage('a_package', packagesDir, examples: <String>[]); |
| addDependency(package, 'target_package', version: '^1.5.0'); |
| |
| final List<String> output = await runCapturingPrint(runner, <String>[ |
| 'update-dependency', |
| '--pub-package', |
| 'target_package', |
| '--version', |
| '1.5.0' |
| ]); |
| |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains('SKIPPING: Already depends on ^1.5.0'), |
| ]), |
| ); |
| }); |
| |
| test('logs updates', () async { |
| final RepositoryPackage package = |
| createFakePackage('a_package', packagesDir, examples: <String>[]); |
| addDependency(package, 'target_package'); |
| |
| final List<String> output = await runCapturingPrint(runner, <String>[ |
| 'update-dependency', |
| '--pub-package', |
| 'target_package', |
| '--version', |
| '1.5.0' |
| ]); |
| |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains('Updating to "^1.5.0"'), |
| ]), |
| ); |
| }); |
| |
| test('updates normal dependency', () async { |
| final RepositoryPackage package = |
| createFakePackage('a_package', packagesDir, examples: <String>[]); |
| addDependency(package, 'target_package'); |
| |
| await runCapturingPrint(runner, <String>[ |
| 'update-dependency', |
| '--pub-package', |
| 'target_package', |
| '--version', |
| '1.5.0' |
| ]); |
| |
| final Dependency? dep = |
| package.parsePubspec().dependencies['target_package']; |
| expect(dep, isA<HostedDependency>()); |
| expect((dep! as HostedDependency).version.toString(), '^1.5.0'); |
| }); |
| |
| test('updates dev dependency', () async { |
| final RepositoryPackage package = |
| createFakePackage('a_package', packagesDir, examples: <String>[]); |
| addDevDependency(package, 'target_package'); |
| |
| await runCapturingPrint(runner, <String>[ |
| 'update-dependency', |
| '--pub-package', |
| 'target_package', |
| '--version', |
| '1.5.0' |
| ]); |
| |
| final Dependency? dep = |
| package.parsePubspec().devDependencies['target_package']; |
| expect(dep, isA<HostedDependency>()); |
| expect((dep! as HostedDependency).version.toString(), '^1.5.0'); |
| }); |
| |
| test('updates dependency in example', () async { |
| final RepositoryPackage package = |
| createFakePackage('a_package', packagesDir); |
| final RepositoryPackage example = package.getExamples().first; |
| addDevDependency(example, 'target_package'); |
| |
| await runCapturingPrint(runner, <String>[ |
| 'update-dependency', |
| '--pub-package', |
| 'target_package', |
| '--version', |
| '1.5.0' |
| ]); |
| |
| final Dependency? dep = |
| example.parsePubspec().devDependencies['target_package']; |
| expect(dep, isA<HostedDependency>()); |
| expect((dep! as HostedDependency).version.toString(), '^1.5.0'); |
| }); |
| |
| test('uses provided constraint as-is', () async { |
| final RepositoryPackage package = |
| createFakePackage('a_package', packagesDir, examples: <String>[]); |
| addDependency(package, 'target_package'); |
| |
| const String providedConstraint = '>=1.6.0 <3.0.0'; |
| await runCapturingPrint(runner, <String>[ |
| 'update-dependency', |
| '--pub-package', |
| 'target_package', |
| '--version', |
| providedConstraint |
| ]); |
| |
| final Dependency? dep = |
| package.parsePubspec().dependencies['target_package']; |
| expect(dep, isA<HostedDependency>()); |
| expect((dep! as HostedDependency).version.toString(), providedConstraint); |
| }); |
| |
| test('uses provided version as lower bound for unpinned', () async { |
| final RepositoryPackage package = |
| createFakePackage('a_package', packagesDir, examples: <String>[]); |
| addDependency(package, 'target_package'); |
| |
| await runCapturingPrint(runner, <String>[ |
| 'update-dependency', |
| '--pub-package', |
| 'target_package', |
| '--version', |
| '1.5.0' |
| ]); |
| |
| final Dependency? dep = |
| package.parsePubspec().dependencies['target_package']; |
| expect(dep, isA<HostedDependency>()); |
| expect((dep! as HostedDependency).version.toString(), '^1.5.0'); |
| }); |
| |
| test('uses provided version as exact version for pinned', () async { |
| final RepositoryPackage package = |
| createFakePackage('a_package', packagesDir, examples: <String>[]); |
| addDependency(package, 'target_package', version: '1.0.0'); |
| |
| await runCapturingPrint(runner, <String>[ |
| 'update-dependency', |
| '--pub-package', |
| 'target_package', |
| '--version', |
| '1.5.0' |
| ]); |
| |
| final Dependency? dep = |
| package.parsePubspec().dependencies['target_package']; |
| expect(dep, isA<HostedDependency>()); |
| expect((dep! as HostedDependency).version.toString(), '1.5.0'); |
| }); |
| |
| test('uses latest pub version as lower bound for unpinned', () async { |
| final RepositoryPackage package = |
| createFakePackage('a_package', packagesDir, examples: <String>[]); |
| addDependency(package, 'target_package'); |
| |
| const Map<String, dynamic> targetPackagePubResponse = <String, dynamic>{ |
| 'name': 'a', |
| 'versions': <String>[ |
| '0.0.1', |
| '1.0.0', |
| '1.5.0', |
| ], |
| }; |
| mockHttpResponse = (http.Request request) async { |
| if (request.url.pathSegments.last == 'target_package.json') { |
| return http.Response(json.encode(targetPackagePubResponse), 200); |
| } |
| return http.Response('', 500); |
| }; |
| |
| await runCapturingPrint(runner, <String>[ |
| 'update-dependency', |
| '--pub-package', |
| 'target_package', |
| ]); |
| |
| final Dependency? dep = |
| package.parsePubspec().dependencies['target_package']; |
| expect(dep, isA<HostedDependency>()); |
| expect((dep! as HostedDependency).version.toString(), '^1.5.0'); |
| }); |
| |
| test('uses latest pub version as exact version for pinned', () async { |
| final RepositoryPackage package = |
| createFakePackage('a_package', packagesDir, examples: <String>[]); |
| addDependency(package, 'target_package', version: '1.0.0'); |
| |
| const Map<String, dynamic> targetPackagePubResponse = <String, dynamic>{ |
| 'name': 'a', |
| 'versions': <String>[ |
| '0.0.1', |
| '1.0.0', |
| '1.5.0', |
| ], |
| }; |
| mockHttpResponse = (http.Request request) async { |
| if (request.url.pathSegments.last == 'target_package.json') { |
| return http.Response(json.encode(targetPackagePubResponse), 200); |
| } |
| return http.Response('', 500); |
| }; |
| |
| await runCapturingPrint(runner, <String>[ |
| 'update-dependency', |
| '--pub-package', |
| 'target_package', |
| ]); |
| |
| final Dependency? dep = |
| package.parsePubspec().dependencies['target_package']; |
| expect(dep, isA<HostedDependency>()); |
| expect((dep! as HostedDependency).version.toString(), '1.5.0'); |
| }); |
| |
| test('regenerates all pigeon files when updating pigeon', () async { |
| final RepositoryPackage package = |
| createFakePackage('a_package', packagesDir, extraFiles: <String>[ |
| 'pigeons/foo.dart', |
| 'pigeons/bar.dart', |
| ]); |
| addDependency(package, 'pigeon', version: '1.0.0'); |
| |
| await runCapturingPrint(runner, <String>[ |
| 'update-dependency', |
| '--pub-package', |
| 'pigeon', |
| '--version', |
| '1.5.0', |
| ]); |
| |
| expect( |
| processRunner.recordedCalls, |
| orderedEquals(<ProcessCall>[ |
| ProcessCall( |
| 'dart', |
| const <String>['pub', 'get'], |
| package.path, |
| ), |
| ProcessCall( |
| 'dart', |
| const <String>['run', 'pigeon', '--input', 'pigeons/foo.dart'], |
| package.path, |
| ), |
| ProcessCall( |
| 'dart', |
| const <String>['run', 'pigeon', '--input', 'pigeons/bar.dart'], |
| package.path, |
| ), |
| ]), |
| ); |
| }); |
| |
| test('warns when regenerating pigeon if there are no pigeon files', |
| () async { |
| final RepositoryPackage package = |
| createFakePackage('a_package', packagesDir); |
| addDependency(package, 'pigeon', version: '1.0.0'); |
| |
| final List<String> output = await runCapturingPrint(runner, <String>[ |
| 'update-dependency', |
| '--pub-package', |
| 'pigeon', |
| '--version', |
| '1.5.0', |
| ]); |
| |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains('No pigeon input files found'), |
| ]), |
| ); |
| }); |
| |
| test('updating pigeon fails if pub get fails', () async { |
| final RepositoryPackage package = createFakePackage( |
| 'a_package', packagesDir, |
| extraFiles: <String>['pigeons/foo.dart']); |
| addDependency(package, 'pigeon', version: '1.0.0'); |
| |
| processRunner.mockProcessesForExecutable['dart'] = <FakeProcessInfo>[ |
| FakeProcessInfo(MockProcess(exitCode: 1), <String>['pub', 'get']) |
| ]; |
| |
| Error? commandError; |
| final List<String> output = await runCapturingPrint(runner, <String>[ |
| 'update-dependency', |
| '--pub-package', |
| 'pigeon', |
| '--version', |
| '1.5.0', |
| ], errorHandler: (Error e) { |
| commandError = e; |
| }); |
| |
| expect(commandError, isA<ToolExit>()); |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains('Fetching dependencies failed'), |
| contains('Failed to update pigeon files'), |
| ]), |
| ); |
| }); |
| |
| test('updating pigeon fails if running pigeon fails', () async { |
| final RepositoryPackage package = createFakePackage( |
| 'a_package', packagesDir, |
| extraFiles: <String>['pigeons/foo.dart']); |
| addDependency(package, 'pigeon', version: '1.0.0'); |
| |
| processRunner.mockProcessesForExecutable['dart'] = <FakeProcessInfo>[ |
| FakeProcessInfo(MockProcess(), <String>['pub', 'get']), |
| FakeProcessInfo(MockProcess(exitCode: 1), <String>['run', 'pigeon']), |
| ]; |
| |
| Error? commandError; |
| final List<String> output = await runCapturingPrint(runner, <String>[ |
| 'update-dependency', |
| '--pub-package', |
| 'pigeon', |
| '--version', |
| '1.5.0', |
| ], errorHandler: (Error e) { |
| commandError = e; |
| }); |
| |
| expect(commandError, isA<ToolExit>()); |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains('dart run pigeon failed'), |
| contains('Failed to update pigeon files'), |
| ]), |
| ); |
| }); |
| |
| test('regenerates mocks when updating mockito if necessary', () async { |
| final RepositoryPackage package = |
| createFakePackage('a_package', packagesDir); |
| addDependency(package, 'mockito', version: '1.0.0'); |
| addDevDependency(package, 'build_runner'); |
| |
| await runCapturingPrint(runner, <String>[ |
| 'update-dependency', |
| '--pub-package', |
| 'mockito', |
| '--version', |
| '1.5.0', |
| ]); |
| |
| expect( |
| processRunner.recordedCalls, |
| orderedEquals(<ProcessCall>[ |
| ProcessCall( |
| 'dart', |
| const <String>['pub', 'get'], |
| package.path, |
| ), |
| ProcessCall( |
| 'dart', |
| const <String>[ |
| 'run', |
| 'build_runner', |
| 'build', |
| '--delete-conflicting-outputs' |
| ], |
| package.path, |
| ), |
| ]), |
| ); |
| }); |
| |
| test('skips regenerating mocks when there is no build_runner dependency', |
| () async { |
| final RepositoryPackage package = |
| createFakePackage('a_package', packagesDir); |
| addDependency(package, 'mockito', version: '1.0.0'); |
| |
| await runCapturingPrint(runner, <String>[ |
| 'update-dependency', |
| '--pub-package', |
| 'mockito', |
| '--version', |
| '1.5.0', |
| ]); |
| |
| expect(processRunner.recordedCalls.isEmpty, true); |
| }); |
| |
| test('updating mockito fails if pub get fails', () async { |
| final RepositoryPackage package = |
| createFakePackage('a_package', packagesDir); |
| addDependency(package, 'mockito', version: '1.0.0'); |
| addDevDependency(package, 'build_runner'); |
| |
| processRunner.mockProcessesForExecutable['dart'] = <FakeProcessInfo>[ |
| FakeProcessInfo(MockProcess(exitCode: 1), <String>['pub', 'get']) |
| ]; |
| |
| Error? commandError; |
| final List<String> output = await runCapturingPrint(runner, <String>[ |
| 'update-dependency', |
| '--pub-package', |
| 'mockito', |
| '--version', |
| '1.5.0', |
| ], errorHandler: (Error e) { |
| commandError = e; |
| }); |
| |
| expect(commandError, isA<ToolExit>()); |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains('Fetching dependencies failed'), |
| contains('Failed to update mocks'), |
| ]), |
| ); |
| }); |
| |
| test('updating mockito fails if running build_runner fails', () async { |
| final RepositoryPackage package = |
| createFakePackage('a_package', packagesDir); |
| addDependency(package, 'mockito', version: '1.0.0'); |
| addDevDependency(package, 'build_runner'); |
| |
| processRunner.mockProcessesForExecutable['dart'] = <FakeProcessInfo>[ |
| FakeProcessInfo(MockProcess(), <String>['pub', 'get']), |
| FakeProcessInfo( |
| MockProcess(exitCode: 1), <String>['run', 'build_runner']), |
| ]; |
| |
| Error? commandError; |
| final List<String> output = await runCapturingPrint(runner, <String>[ |
| 'update-dependency', |
| '--pub-package', |
| 'mockito', |
| '--version', |
| '1.5.0', |
| ], errorHandler: (Error e) { |
| commandError = e; |
| }); |
| |
| expect(commandError, isA<ToolExit>()); |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains('"dart run build_runner build" failed'), |
| contains('Failed to update mocks'), |
| ]), |
| ); |
| }); |
| }); |
| |
| group('Android dependencies', () { |
| group('gradle', () { |
| test('throws if version format is invalid', () async { |
| Error? commandError; |
| final List<String> output = await runCapturingPrint(runner, <String>[ |
| 'update-dependency', |
| '--android-dependency', |
| 'gradle', |
| '--version', |
| '83', |
| ], errorHandler: (Error e) { |
| commandError = e; |
| }); |
| |
| expect(commandError, isA<ToolExit>()); |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains( |
| 'A version with a valid format (maximum 2-3 numbers separated by period) must be provided.'), |
| ]), |
| ); |
| }); |
| |
| test('skips if example app does not run on Android', () async { |
| final RepositoryPackage package = |
| createFakePlugin('fake_plugin', packagesDir); |
| |
| final List<String> output = await runCapturingPrint(runner, <String>[ |
| 'update-dependency', |
| '--packages', |
| package.displayName, |
| '--android-dependency', |
| 'gradle', |
| '--version', |
| '8.8.8', |
| ]); |
| |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains('SKIPPING: No example apps run on Android.'), |
| ]), |
| ); |
| }); |
| |
| test( |
| 'throws if wrapper does not have distribution URL with expected format', |
| () async { |
| final RepositoryPackage package = createFakePlugin( |
| 'fake_plugin', packagesDir, extraFiles: <String>[ |
| 'example/android/app/gradle/wrapper/gradle-wrapper.properties' |
| ]); |
| |
| final File gradleWrapperPropertiesFile = package.directory |
| .childDirectory('example') |
| .childDirectory('android') |
| .childDirectory('app') |
| .childDirectory('gradle') |
| .childDirectory('wrapper') |
| .childFile('gradle-wrapper.properties'); |
| |
| gradleWrapperPropertiesFile.writeAsStringSync(''' |
| How is it even possible that I didn't specify a Gradle distribution? |
| '''); |
| |
| Error? commandError; |
| final List<String> output = await runCapturingPrint(runner, <String>[ |
| 'update-dependency', |
| '--packages', |
| package.displayName, |
| '--android-dependency', |
| 'gradle', |
| '--version', |
| '8.8.8', |
| ], errorHandler: (Error e) { |
| commandError = e; |
| }); |
| |
| expect(commandError, isA<ToolExit>()); |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains( |
| 'Unable to find a gradle version entry to update for ${package.displayName}/example.'), |
| ]), |
| ); |
| }); |
| |
| test('succeeds if example app has android/app/gradle directory structure', |
| () async { |
| final RepositoryPackage package = createFakePlugin( |
| 'fake_plugin', packagesDir, extraFiles: <String>[ |
| 'example/android/app/gradle/wrapper/gradle-wrapper.properties' |
| ]); |
| const String newGradleVersion = '8.8.8'; |
| |
| final File gradleWrapperPropertiesFile = package.directory |
| .childDirectory('example') |
| .childDirectory('android') |
| .childDirectory('app') |
| .childDirectory('gradle') |
| .childDirectory('wrapper') |
| .childFile('gradle-wrapper.properties'); |
| |
| gradleWrapperPropertiesFile.writeAsStringSync(r''' |
| distributionBase=GRADLE_USER_HOME |
| distributionPath=wrapper/dists |
| zipStoreBase=GRADLE_USER_HOME |
| zipStorePath=wrapper/dists |
| distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-all.zip |
| '''); |
| |
| await runCapturingPrint(runner, <String>[ |
| 'update-dependency', |
| '--packages', |
| package.displayName, |
| '--android-dependency', |
| 'gradle', |
| '--version', |
| newGradleVersion, |
| ]); |
| |
| final String updatedGradleWrapperPropertiesContents = |
| gradleWrapperPropertiesFile.readAsStringSync(); |
| expect( |
| updatedGradleWrapperPropertiesContents, |
| contains( |
| r'distributionUrl=https\://services.gradle.org/distributions/' |
| 'gradle-$newGradleVersion-all.zip')); |
| }); |
| |
| test('succeeds if example app has android/gradle directory structure', |
| () async { |
| final RepositoryPackage package = createFakePlugin( |
| 'fake_plugin', packagesDir, extraFiles: <String>[ |
| 'example/android/gradle/wrapper/gradle-wrapper.properties' |
| ]); |
| const String newGradleVersion = '9.9'; |
| |
| final File gradleWrapperPropertiesFile = package.directory |
| .childDirectory('example') |
| .childDirectory('android') |
| .childDirectory('gradle') |
| .childDirectory('wrapper') |
| .childFile('gradle-wrapper.properties'); |
| |
| gradleWrapperPropertiesFile.writeAsStringSync(r''' |
| distributionBase=GRADLE_USER_HOME |
| distributionPath=wrapper/dists |
| zipStoreBase=GRADLE_USER_HOME |
| zipStorePath=wrapper/dists |
| distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-all.zip |
| '''); |
| |
| await runCapturingPrint(runner, <String>[ |
| 'update-dependency', |
| '--packages', |
| package.displayName, |
| '--android-dependency', |
| 'gradle', |
| '--version', |
| newGradleVersion, |
| ]); |
| |
| final String updatedGradleWrapperPropertiesContents = |
| gradleWrapperPropertiesFile.readAsStringSync(); |
| expect( |
| updatedGradleWrapperPropertiesContents, |
| contains( |
| r'distributionUrl=https\://services.gradle.org/distributions/' |
| 'gradle-$newGradleVersion-all.zip')); |
| }); |
| |
| test( |
| 'succeeds if example app has android/gradle and android/app/gradle directory structure', |
| () async { |
| final RepositoryPackage package = |
| createFakePlugin('fake_plugin', packagesDir, extraFiles: <String>[ |
| 'example/android/gradle/wrapper/gradle-wrapper.properties', |
| 'example/android/app/gradle/wrapper/gradle-wrapper.properties' |
| ]); |
| const String newGradleVersion = '9.9'; |
| |
| final File gradleWrapperPropertiesFile = package.directory |
| .childDirectory('example') |
| .childDirectory('android') |
| .childDirectory('gradle') |
| .childDirectory('wrapper') |
| .childFile('gradle-wrapper.properties'); |
| |
| final File gradleAppWrapperPropertiesFile = package.directory |
| .childDirectory('example') |
| .childDirectory('android') |
| .childDirectory('app') |
| .childDirectory('gradle') |
| .childDirectory('wrapper') |
| .childFile('gradle-wrapper.properties'); |
| |
| gradleWrapperPropertiesFile.writeAsStringSync(r''' |
| distributionBase=GRADLE_USER_HOME |
| distributionPath=wrapper/dists |
| zipStoreBase=GRADLE_USER_HOME |
| zipStorePath=wrapper/dists |
| distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-all.zip |
| '''); |
| |
| gradleAppWrapperPropertiesFile.writeAsStringSync(r''' |
| distributionBase=GRADLE_USER_HOME |
| distributionPath=wrapper/dists |
| zipStoreBase=GRADLE_USER_HOME |
| zipStorePath=wrapper/dists |
| distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-all.zip |
| '''); |
| |
| await runCapturingPrint(runner, <String>[ |
| 'update-dependency', |
| '--packages', |
| package.displayName, |
| '--android-dependency', |
| 'gradle', |
| '--version', |
| newGradleVersion, |
| ]); |
| |
| final String updatedGradleWrapperPropertiesContents = |
| gradleWrapperPropertiesFile.readAsStringSync(); |
| final String updatedGradleAppWrapperPropertiesContents = |
| gradleAppWrapperPropertiesFile.readAsStringSync(); |
| expect( |
| updatedGradleWrapperPropertiesContents, |
| contains( |
| r'distributionUrl=https\://services.gradle.org/distributions/' |
| 'gradle-$newGradleVersion-all.zip')); |
| expect( |
| updatedGradleAppWrapperPropertiesContents, |
| contains( |
| r'distributionUrl=https\://services.gradle.org/distributions/' |
| 'gradle-$newGradleVersion-all.zip')); |
| }); |
| |
| test('succeeds if one example app runs on Android and another does not', |
| () async { |
| final RepositoryPackage package = createFakePlugin( |
| 'fake_plugin', packagesDir, examples: <String>[ |
| 'example_1', |
| 'example_2' |
| ], extraFiles: <String>[ |
| 'example/example_2/android/app/gradle/wrapper/gradle-wrapper.properties' |
| ]); |
| const String newGradleVersion = '8.8.8'; |
| |
| final File gradleWrapperPropertiesFile = package.directory |
| .childDirectory('example') |
| .childDirectory('example_2') |
| .childDirectory('android') |
| .childDirectory('app') |
| .childDirectory('gradle') |
| .childDirectory('wrapper') |
| .childFile('gradle-wrapper.properties'); |
| |
| gradleWrapperPropertiesFile.writeAsStringSync(r''' |
| distributionBase=GRADLE_USER_HOME |
| distributionPath=wrapper/dists |
| zipStoreBase=GRADLE_USER_HOME |
| zipStorePath=wrapper/dists |
| distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-all.zip |
| '''); |
| |
| await runCapturingPrint(runner, <String>[ |
| 'update-dependency', |
| '--packages', |
| package.displayName, |
| '--android-dependency', |
| 'gradle', |
| '--version', |
| newGradleVersion, |
| ]); |
| |
| final String updatedGradleWrapperPropertiesContents = |
| gradleWrapperPropertiesFile.readAsStringSync(); |
| expect( |
| updatedGradleWrapperPropertiesContents, |
| contains( |
| r'distributionUrl=https\://services.gradle.org/distributions/' |
| 'gradle-$newGradleVersion-all.zip')); |
| }); |
| }); |
| |
| group('compileSdk/compileSdkForExamples', () { |
| // Tests if the compileSdk version is updated for the provided |
| // build.gradle file and new compileSdk version to update to. |
| Future<void> testCompileSdkVersionUpdated( |
| {required RepositoryPackage package, |
| required File buildGradleFile, |
| required String oldCompileSdkVersion, |
| required String newCompileSdkVersion, |
| bool runForExamples = false, |
| bool checkForDeprecatedCompileSdkVersion = false}) async { |
| buildGradleFile.writeAsStringSync(''' |
| android { |
| // Conditional for compatibility with AGP <4.2. |
| if (project.android.hasProperty("namespace")) { |
| namespace 'io.flutter.plugins.pathprovider' |
| } |
| ${checkForDeprecatedCompileSdkVersion ? 'compileSdkVersion' : 'compileSdk'} $oldCompileSdkVersion |
| '''); |
| |
| await runCapturingPrint(runner, <String>[ |
| 'update-dependency', |
| '--packages', |
| package.displayName, |
| '--android-dependency', |
| if (runForExamples) 'compileSdkForExamples' else 'compileSdk', |
| '--version', |
| newCompileSdkVersion, |
| ]); |
| |
| final String updatedBuildGradleContents = |
| buildGradleFile.readAsStringSync(); |
| // compileSdkVersion is now deprecated, so if the tool finds any |
| // instances of compileSdk OR compileSdkVersion, it should change it |
| // to compileSdk. See https://developer.android.com/reference/tools/gradle-api/7.2/com/android/build/api/dsl/CommonExtension#compileSdkVersion(kotlin.Int). |
| expect(updatedBuildGradleContents, |
| contains('compileSdk $newCompileSdkVersion')); |
| } |
| |
| test('throws if version format is invalid for compileSdk', () async { |
| Error? commandError; |
| final List<String> output = await runCapturingPrint(runner, <String>[ |
| 'update-dependency', |
| '--android-dependency', |
| 'compileSdk', |
| '--version', |
| '834', |
| ], errorHandler: (Error e) { |
| commandError = e; |
| }); |
| |
| expect(commandError, isA<ToolExit>()); |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains( |
| 'A valid Android SDK version number (1-2 digit numbers) must be provided.'), |
| ]), |
| ); |
| }); |
| |
| test('throws if version format is invalid for compileSdkForExamples', |
| () async { |
| Error? commandError; |
| final List<String> output = await runCapturingPrint(runner, <String>[ |
| 'update-dependency', |
| '--android-dependency', |
| 'compileSdkForExamples', |
| '--version', |
| '438', |
| ], errorHandler: (Error e) { |
| commandError = e; |
| }); |
| |
| expect(commandError, isA<ToolExit>()); |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains( |
| 'A valid Android SDK version number (1-2 digit numbers) must be provided.'), |
| ]), |
| ); |
| }); |
| |
| test('skips if plugin does not run on Android', () async { |
| final RepositoryPackage package = |
| createFakePlugin('fake_plugin', packagesDir); |
| |
| final List<String> output = await runCapturingPrint(runner, <String>[ |
| 'update-dependency', |
| '--packages', |
| package.displayName, |
| '--android-dependency', |
| 'compileSdk', |
| '--version', |
| '34', |
| ]); |
| |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains( |
| 'SKIPPING: Package ${package.displayName} does not run on Android.'), |
| ]), |
| ); |
| }); |
| |
| test('skips if plugin example does not run on Android', () async { |
| final RepositoryPackage package = |
| createFakePlugin('fake_plugin', packagesDir); |
| |
| final List<String> output = await runCapturingPrint(runner, <String>[ |
| 'update-dependency', |
| '--packages', |
| package.displayName, |
| '--android-dependency', |
| 'compileSdkForExamples', |
| '--version', |
| '34', |
| ]); |
| |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains('SKIPPING: No example apps run on Android.'), |
| ]), |
| ); |
| }); |
| |
| test( |
| 'throws if build configuration file does not have compileSdk version with expected format for compileSdk', |
| () async { |
| final RepositoryPackage package = createFakePlugin( |
| 'fake_plugin', packagesDir, |
| extraFiles: <String>['android/build.gradle']); |
| |
| final File buildGradleFile = package.directory |
| .childDirectory('android') |
| .childFile('build.gradle'); |
| |
| buildGradleFile.writeAsStringSync(''' |
| How is it even possible that I didn't specify a compileSdk version? |
| '''); |
| |
| Error? commandError; |
| final List<String> output = await runCapturingPrint(runner, <String>[ |
| 'update-dependency', |
| '--packages', |
| package.displayName, |
| '--android-dependency', |
| 'compileSdk', |
| '--version', |
| '34', |
| ], errorHandler: (Error e) { |
| commandError = e; |
| }); |
| |
| expect(commandError, isA<ToolExit>()); |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains( |
| 'Unable to find a compileSdk version entry to update for ${package.displayName}.'), |
| ]), |
| ); |
| }); |
| |
| test( |
| 'throws if build configuration file does not have compileSdk version with expected format for compileSdkForExamples', |
| () async { |
| final RepositoryPackage package = createFakePlugin( |
| 'fake_plugin', packagesDir, |
| extraFiles: <String>['example/android/app/build.gradle']); |
| |
| final File buildGradleFile = package.directory |
| .childDirectory('example') |
| .childDirectory('android') |
| .childDirectory('app') |
| .childFile('build.gradle'); |
| |
| buildGradleFile.writeAsStringSync(''' |
| How is it even possible that I didn't specify a compileSdk version? |
| '''); |
| |
| Error? commandError; |
| final List<String> output = await runCapturingPrint(runner, <String>[ |
| 'update-dependency', |
| '--packages', |
| package.displayName, |
| '--android-dependency', |
| 'compileSdkForExamples', |
| '--version', |
| '34', |
| ], errorHandler: (Error e) { |
| commandError = e; |
| }); |
| |
| expect(commandError, isA<ToolExit>()); |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains( |
| 'Unable to find a compileSdkForExamples version entry to update for ${package.displayName}/example.'), |
| ]), |
| ); |
| }); |
| |
| test( |
| 'succeeds if plugin runs on Android and valid version is supplied for compileSdkVersion entry', |
| () async { |
| final RepositoryPackage package = createFakePlugin( |
| 'fake_plugin', packagesDir, extraFiles: <String>[ |
| 'android/build.gradle', |
| 'example/android/app/build.gradle' |
| ]); |
| final File buildGradleFile = package.directory |
| .childDirectory('android') |
| .childFile('build.gradle'); |
| |
| await testCompileSdkVersionUpdated( |
| package: package, |
| buildGradleFile: buildGradleFile, |
| oldCompileSdkVersion: '8', |
| newCompileSdkVersion: '16', |
| checkForDeprecatedCompileSdkVersion: true); |
| }); |
| |
| test( |
| 'succeeds if plugin example runs on Android and valid version is supplied for compileSdkVersion entry', |
| () async { |
| final RepositoryPackage package = createFakePlugin( |
| 'fake_plugin', packagesDir, extraFiles: <String>[ |
| 'android/build.gradle', |
| 'example/android/app/build.gradle' |
| ]); |
| final File exampleBuildGradleFile = package.directory |
| .childDirectory('example') |
| .childDirectory('android') |
| .childDirectory('app') |
| .childFile('build.gradle'); |
| |
| await testCompileSdkVersionUpdated( |
| package: package, |
| buildGradleFile: exampleBuildGradleFile, |
| oldCompileSdkVersion: '8', |
| newCompileSdkVersion: '16', |
| runForExamples: true, |
| checkForDeprecatedCompileSdkVersion: true); |
| }); |
| |
| test( |
| 'succeeds if plugin runs on Android and valid version is supplied for compileSdk entry', |
| () async { |
| final RepositoryPackage package = createFakePlugin( |
| 'fake_plugin', packagesDir, extraFiles: <String>[ |
| 'android/build.gradle', |
| 'example/android/app/build.gradle' |
| ]); |
| |
| final File buildGradleFile = package.directory |
| .childDirectory('android') |
| .childFile('build.gradle'); |
| await testCompileSdkVersionUpdated( |
| package: package, |
| buildGradleFile: buildGradleFile, |
| oldCompileSdkVersion: '8', |
| newCompileSdkVersion: '16'); |
| }); |
| |
| test( |
| 'succeeds if plugin example runs on Android and valid version is supplied for compileSdk entry', |
| () async { |
| final RepositoryPackage package = createFakePlugin( |
| 'fake_plugin', packagesDir, extraFiles: <String>[ |
| 'android/build.gradle', |
| 'example/android/app/build.gradle' |
| ]); |
| |
| final File exampleBuildGradleFile = package.directory |
| .childDirectory('example') |
| .childDirectory('android') |
| .childDirectory('app') |
| .childFile('build.gradle'); |
| await testCompileSdkVersionUpdated( |
| package: package, |
| buildGradleFile: exampleBuildGradleFile, |
| oldCompileSdkVersion: '33', |
| newCompileSdkVersion: '34', |
| runForExamples: true); |
| }); |
| }); |
| }); |
| } |