blob: 2cb6b15b12c613b619a9a21a3da760702b524324 [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: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);
});
});
});
}