| // 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/common/core.dart'; |
| import 'package:flutter_plugin_tools/src/common/plugin_utils.dart'; |
| import 'package:flutter_plugin_tools/src/validate_command.dart'; |
| import 'package:flutter_plugin_tools/src/validators/gradle_validator.dart'; |
| import 'package:git/git.dart'; |
| import 'package:test/test.dart'; |
| |
| import 'util.dart'; |
| |
| const String _defaultFakeNamespace = 'dev.flutter.foo'; |
| |
| void main() { |
| late CommandRunner<void> runner; |
| late Directory packagesDir; |
| const javaIncompatabilityIndicator = |
| 'build.gradle.kts must set an explicit Java compatibility version.'; |
| |
| setUp(() { |
| final GitDir gitDir; |
| (:packagesDir, processRunner: _, gitProcessRunner: _, :gitDir) = configureBaseCommandMocks(); |
| final command = ValidateCommand( |
| packagesDir, |
| gitDir: gitDir, |
| targetedValidators: {Validator.gradle}, |
| ); |
| |
| runner = CommandRunner<void>('validate_gradle_test', 'Test for gradle validations'); |
| runner.addCommand(command); |
| }); |
| |
| /// Writes a fake android/build.gradle.kts file for plugin [package] with the |
| /// given options. |
| void writeFakePluginBuildGradle( |
| RepositoryPackage package, { |
| bool includeLanguageVersion = false, |
| bool includeSourceCompat = false, |
| bool includeTargetCompat = false, |
| bool commentSourceLanguage = false, |
| bool includeNamespace = true, |
| bool commentNamespace = false, |
| bool warningsConfigured = true, |
| String compileSdk = '36', |
| bool includeKotlinCompilerOptions = true, |
| bool commentKotlinCompilerOptions = false, |
| bool useDeprecatedJvmTargetStyle = false, |
| bool useJavaVersionStringForJvmTarget = false, |
| int jvmTargetValue = 17, |
| int kotlinJvmValue = 17, |
| bool includeKotlinGradlePlugin = false, |
| bool includeDeprecatedKotlinOptionsInsideAndroid = false, |
| bool includeDeprecatedKotlinOptionsInsideKotlin = false, |
| }) { |
| final File buildGradle = package |
| .platformDirectory(FlutterPlatform.android) |
| .childFile('build.gradle.kts'); |
| buildGradle.createSync(recursive: true); |
| |
| const warningConfig = ''' |
| lint { |
| checkAllWarnings = true |
| warningsAsErrors = true |
| disable.addAll(setOf("AndroidGradlePluginVersion", "InvalidPackage", "GradleDependency", "NewerVersionAvailable")) |
| baseline = file("lint-baseline.xml") |
| } |
| '''; |
| final javaSection = |
| ''' |
| java { |
| toolchain { |
| ${commentSourceLanguage ? '// ' : ''}languageVersion = JavaLanguageVersion.of(8) |
| } |
| } |
| |
| '''; |
| final sourceCompat = |
| '${commentSourceLanguage ? '// ' : ''}sourceCompatibility = JavaVersion.VERSION_$jvmTargetValue'; |
| final targetCompat = |
| '${commentSourceLanguage ? '// ' : ''}targetCompatibility = JavaVersion.VERSION_$jvmTargetValue'; |
| final namespace = ' ${commentNamespace ? '// ' : ''}namespace = "$_defaultFakeNamespace"'; |
| final _KotlinConfigParts kotlinConfigParts = _generateKotlinConfigParts( |
| commentKotlinCompilerOptions: commentKotlinCompilerOptions, |
| useJavaVersionStringForJvmTarget: useJavaVersionStringForJvmTarget, |
| useDeprecatedJvmTargetStyle: useDeprecatedJvmTargetStyle, |
| jvmTargetValue: jvmTargetValue, |
| kotlinJvmValue: kotlinJvmValue, |
| ); |
| |
| buildGradle.writeAsStringSync(''' |
| group = "dev.flutter.plugins.fake" |
| version = "1.0-SNAPSHOT" |
| |
| buildscript { |
| repositories { |
| google() |
| mavenCentral() |
| } |
| } |
| |
| allprojects { |
| repositories { |
| google() |
| mavenCentral() |
| } |
| } |
| |
| plugins { |
| id("com.android.library") |
| ${includeKotlinGradlePlugin ? 'id("kotlin-android")' : ''} |
| } |
| |
| ${includeLanguageVersion ? javaSection : ''} |
| ${includeKotlinCompilerOptions ? kotlinConfigParts.kotlinConfig : ''} |
| ${includeDeprecatedKotlinOptionsInsideKotlin ? kotlinConfigParts.kotlinDeprecatedInKotlinConfig : ''} |
| |
| android { |
| ${includeNamespace ? namespace : ''} |
| compileSdk = $compileSdk |
| |
| defaultConfig { |
| minSdk = 30 |
| } |
| ${warningsConfigured ? warningConfig : ''} |
| compileOptions { |
| ${includeSourceCompat ? sourceCompat : ''} |
| ${includeTargetCompat ? targetCompat : ''} |
| } |
| ${includeDeprecatedKotlinOptionsInsideAndroid ? kotlinConfigParts.kotlinDeprecatedInAndroidConfig : ''} |
| testOptions { |
| unitTests { |
| isIncludeAndroidResources = true |
| } |
| } |
| } |
| |
| dependencies { |
| implementation("fake.package:fake:1.0.0") |
| } |
| '''); |
| } |
| |
| /// Writes a fake android/build.gradle.kts file for an example [package] with |
| /// the given options. |
| void writeFakeExampleTopLevelBuildGradle( |
| RepositoryPackage package, { |
| required String pluginName, |
| bool warningsConfigured = true, |
| bool includeArtifactHub = true, |
| }) { |
| final File buildGradle = package |
| .platformDirectory(FlutterPlatform.android) |
| .childFile('build.gradle.kts'); |
| buildGradle.createSync(recursive: true); |
| |
| final warningConfig = |
| ''' |
| gradle.projectsEvaluated { |
| project(":$pluginName") { |
| tasks.withType<JavaCompile> { |
| options.compilerArgs.addAll(listOf("-Xlint:all", "-Werror")) |
| } |
| } |
| } |
| '''; |
| |
| buildGradle.writeAsStringSync(''' |
| allprojects { |
| repositories { |
| ${includeArtifactHub ? GradleValidator.exampleRootGradleArtifactHubString : ''} |
| google() |
| mavenCentral() |
| } |
| } |
| |
| val newBuildDir: Directory = |
| rootProject.layout.buildDirectory |
| .dir("../../build") |
| .get() |
| rootProject.layout.buildDirectory.value(newBuildDir) |
| |
| subprojects { |
| val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name) |
| project.layout.buildDirectory.value(newSubprojectBuildDir) |
| } |
| subprojects { |
| project.evaluationDependsOn(":app") |
| } |
| |
| tasks.register<Delete>("clean") { |
| delete(rootProject.layout.buildDirectory) |
| } |
| |
| ${warningsConfigured ? warningConfig : ''} |
| '''); |
| } |
| |
| /// Writes a fake android/settings.gradle.kts file for an example [package] |
| /// with the given options. |
| void writeFakeExampleSettingsGradle( |
| RepositoryPackage package, { |
| required String kotlinVersion, |
| bool includeArtifactHub = true, |
| bool includeArtifactDocumentation = true, |
| }) { |
| final File settingsGradle = package |
| .platformDirectory(FlutterPlatform.android) |
| .childFile('settings.gradle.kts'); |
| settingsGradle.createSync(recursive: true); |
| |
| settingsGradle.writeAsStringSync(''' |
| pluginManagement { |
| val flutterSdkPath = |
| run { |
| val properties = java.util.Properties() |
| file("local.properties").inputStream().use { properties.load(it) } |
| val flutterSdkPath = properties.getProperty("flutter.sdk") |
| require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" } |
| flutterSdkPath |
| } |
| |
| includeBuild("\$flutterSdkPath/packages/flutter_tools/gradle") |
| |
| repositories { |
| google() |
| mavenCentral() |
| gradlePluginPortal() |
| } |
| } |
| |
| ${includeArtifactDocumentation ? '// See ${GradleValidator.artifactHubDocumentationString} for more info.' : ''} |
| plugins { |
| id("dev.flutter.flutter-plugin-loader") version "1.0.0" |
| id("com.android.application") version "8.11.1" apply false |
| id("org.jetbrains.kotlin.android") version "$kotlinVersion" apply false |
| ${includeArtifactHub ? 'id("com.google.cloud.artifactregistry.gradle-plugin") version "2.2.1"' : ''} |
| } |
| |
| include ":app" |
| '''); |
| } |
| |
| /// Writes a fake android/app/build.gradle.kts file for an example [package] |
| /// with the given options. |
| void writeFakeExampleAppBuildGradle( |
| RepositoryPackage package, { |
| required bool includeNamespace, |
| required bool commentNamespace, |
| bool includeKotlinCompilerOptions = true, |
| bool commentKotlinCompilerOptions = false, |
| bool useDeprecatedJvmTargetStyle = false, |
| bool useJavaVersionStringForJvmTarget = false, |
| int jvmTargetValue = 17, |
| int kotlinJvmValue = 17, |
| bool includeKotlinGradlePlugin = false, |
| bool includeDeprecatedKotlinOptionsInsideAndroid = false, |
| bool includeDeprecatedKotlinOptionsInsideKotlin = false, |
| }) { |
| final File buildGradle = package |
| .platformDirectory(FlutterPlatform.android) |
| .childDirectory('app') |
| .childFile('build.gradle.kts'); |
| buildGradle.createSync(recursive: true); |
| |
| final namespace = '${commentNamespace ? '// ' : ''}namespace = "$_defaultFakeNamespace"'; |
| |
| final _KotlinConfigParts kotlinConfigParts = _generateKotlinConfigParts( |
| commentKotlinCompilerOptions: commentKotlinCompilerOptions, |
| useJavaVersionStringForJvmTarget: useJavaVersionStringForJvmTarget, |
| useDeprecatedJvmTargetStyle: useDeprecatedJvmTargetStyle, |
| jvmTargetValue: jvmTargetValue, |
| kotlinJvmValue: kotlinJvmValue, |
| ); |
| |
| buildGradle.writeAsStringSync(''' |
| plugins { |
| id("com.android.application") |
| ${includeKotlinGradlePlugin ? 'id("kotlin-android")' : ''} |
| // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. |
| id("dev.flutter.flutter-gradle-plugin") |
| } |
| |
| ${includeKotlinCompilerOptions ? kotlinConfigParts.kotlinConfig : ''} |
| ${includeDeprecatedKotlinOptionsInsideKotlin ? kotlinConfigParts.kotlinDeprecatedInKotlinConfig : ''} |
| |
| android { |
| ${includeNamespace ? namespace : ''} |
| compileSdk = flutter.compileSdkVersion |
| ndkVersion = flutter.ndkVersion |
| |
| compileOptions { |
| sourceCompatibility = JavaVersion.VERSION_$jvmTargetValue |
| targetCompatibility = JavaVersion.VERSION_$jvmTargetValue |
| } |
| |
| defaultConfig { |
| applicationId = "$_defaultFakeNamespace.example" |
| minSdk = flutter.minSdkVersion |
| targetSdk = flutter.targetSdkVersion |
| versionCode = flutter.versionCode |
| versionName = flutter.versionName |
| } |
| |
| buildTypes { |
| release { |
| signingConfig = signingConfigs.getByName("debug") |
| } |
| } |
| ${includeDeprecatedKotlinOptionsInsideAndroid ? kotlinConfigParts.kotlinDeprecatedInAndroidConfig : ''} |
| } |
| |
| flutter { |
| source = "../.." |
| } |
| '''); |
| } |
| |
| void writeFakeExampleBuildGradles( |
| RepositoryPackage package, { |
| required String pluginName, |
| String? kotlinVersion, |
| bool includeNamespace = true, |
| bool commentNamespace = false, |
| bool warningsConfigured = true, |
| bool includeBuildArtifactHub = true, |
| bool includeSettingsArtifactHub = true, |
| bool includeSettingsDocumentationArtifactHub = true, |
| bool includeKotlinCompilerOptions = true, |
| bool commentKotlinCompilerOptions = false, |
| bool useDeprecatedJvmTargetStyle = false, |
| bool useJavaVersionStringForJvmTarget = false, |
| int jvmTargetValue = 17, |
| int kotlinJvmValue = 17, |
| bool includeKotlinGradlePlugin = false, |
| bool includeDeprecatedKotlinOptionsInsideAndroid = false, |
| bool includeDeprecatedKotlinOptionsInsideKotlin = false, |
| }) { |
| writeFakeExampleTopLevelBuildGradle( |
| package, |
| pluginName: pluginName, |
| warningsConfigured: warningsConfigured, |
| includeArtifactHub: includeBuildArtifactHub, |
| ); |
| writeFakeExampleAppBuildGradle( |
| package, |
| includeNamespace: includeNamespace, |
| commentNamespace: commentNamespace, |
| includeKotlinCompilerOptions: includeKotlinCompilerOptions, |
| commentKotlinCompilerOptions: commentKotlinCompilerOptions, |
| useDeprecatedJvmTargetStyle: useDeprecatedJvmTargetStyle, |
| useJavaVersionStringForJvmTarget: useJavaVersionStringForJvmTarget, |
| jvmTargetValue: jvmTargetValue, |
| kotlinJvmValue: kotlinJvmValue, |
| includeKotlinGradlePlugin: includeKotlinGradlePlugin, |
| includeDeprecatedKotlinOptionsInsideAndroid: includeDeprecatedKotlinOptionsInsideAndroid, |
| includeDeprecatedKotlinOptionsInsideKotlin: includeDeprecatedKotlinOptionsInsideKotlin, |
| ); |
| writeFakeExampleSettingsGradle( |
| package, |
| kotlinVersion: kotlinVersion ?? '2.2.20', |
| includeArtifactHub: includeSettingsArtifactHub, |
| includeArtifactDocumentation: includeSettingsDocumentationArtifactHub, |
| ); |
| } |
| |
| void writeFakeManifest( |
| RepositoryPackage package, { |
| bool isApp = false, |
| String packageName = _defaultFakeNamespace, |
| }) { |
| final Directory androidDir = package.platformDirectory(FlutterPlatform.android); |
| final Directory startDir = isApp ? androidDir.childDirectory('app') : androidDir; |
| final File manifest = startDir |
| .childDirectory('src') |
| .childDirectory('main') |
| .childFile('AndroidManifest.xml'); |
| manifest.createSync(recursive: true); |
| manifest.writeAsString(''' |
| <manifest xmlns:android="http://schemas.android.com/apk/res/android" |
| package="$packageName"> |
| </manifest>'''); |
| } |
| |
| test('passes when package has no Android directory', () async { |
| createFakePackage('a_package', packagesDir, examples: <String>[]); |
| |
| final List<String> output = await runCapturingPrint(runner, <String>['validate']); |
| |
| expect(output, containsAllInOrder(<Matcher>[contains('Running for a_package')])); |
| }); |
| |
| test('fails when build.gradle.kts has no java compatibility version', () async { |
| final RepositoryPackage package = createFakePlugin( |
| 'a_plugin', |
| packagesDir, |
| examples: <String>[], |
| ); |
| writeFakePluginBuildGradle(package); |
| writeFakeManifest(package); |
| |
| Error? commandError; |
| final List<String> output = await runCapturingPrint( |
| runner, |
| <String>['validate'], |
| errorHandler: (Error e) { |
| commandError = e; |
| }, |
| ); |
| |
| expect(commandError, isA<ToolExit>()); |
| expect(output, containsAllInOrder(<Matcher>[contains(javaIncompatabilityIndicator)])); |
| }); |
| |
| test('fails when sourceCompatibility is provided with out targetCompatibility', () async { |
| final RepositoryPackage package = createFakePlugin( |
| 'a_plugin', |
| packagesDir, |
| examples: <String>[], |
| ); |
| writeFakePluginBuildGradle(package, includeSourceCompat: true); |
| writeFakeManifest(package); |
| |
| Error? commandError; |
| final List<String> output = await runCapturingPrint( |
| runner, |
| <String>['validate'], |
| errorHandler: (Error e) { |
| commandError = e; |
| }, |
| ); |
| |
| expect(commandError, isA<ToolExit>()); |
| expect(output, containsAllInOrder(<Matcher>[contains(javaIncompatabilityIndicator)])); |
| }); |
| |
| test('fails when sourceCompatibility/targetCompatibility are below minimum', () async { |
| final RepositoryPackage package = createFakePlugin( |
| 'a_plugin', |
| packagesDir, |
| examples: <String>[], |
| ); |
| writeFakePluginBuildGradle( |
| package, |
| includeSourceCompat: true, |
| includeTargetCompat: true, |
| jvmTargetValue: 11, |
| kotlinJvmValue: 11, |
| ); |
| writeFakeManifest(package); |
| |
| Error? commandError; |
| final List<String> output = await runCapturingPrint( |
| runner, |
| <String>['validate'], |
| errorHandler: (Error e) { |
| commandError = e; |
| }, |
| ); |
| |
| expect(commandError, isA<ToolExit>()); |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains('Which is below the minimum required. Use at least "JavaVersion.VERSION_'), |
| ]), |
| ); |
| }); |
| |
| test('fails when compatibility values do not match kotlin compiler options', () async { |
| final RepositoryPackage package = createFakePlugin( |
| 'a_plugin', |
| packagesDir, |
| examples: <String>[], |
| ); |
| writeFakePluginBuildGradle( |
| package, |
| includeSourceCompat: true, |
| includeTargetCompat: true, |
| jvmTargetValue: 21, |
| // ignore: avoid_redundant_argument_values |
| kotlinJvmValue: 17, |
| ); |
| writeFakeManifest(package); |
| |
| Error? commandError; |
| final List<String> output = await runCapturingPrint( |
| runner, |
| <String>['validate'], |
| errorHandler: (Error e) { |
| commandError = e; |
| }, |
| ); |
| |
| expect(commandError, isA<ToolExit>()); |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains( |
| 'If build.gradle.kts uses JavaVersion.* and JvmTarget.*, the versions must be the same.', |
| ), |
| ]), |
| ); |
| }); |
| |
| test('passes when jvmValues are higher than minimim', () async { |
| final RepositoryPackage package = createFakePlugin( |
| 'a_plugin', |
| packagesDir, |
| examples: <String>[], |
| ); |
| writeFakePluginBuildGradle( |
| package, |
| includeSourceCompat: true, |
| includeTargetCompat: true, |
| jvmTargetValue: 21, |
| kotlinJvmValue: 21, |
| ); |
| writeFakeManifest(package); |
| |
| final List<String> output = await runCapturingPrint(runner, <String>['validate']); |
| |
| expect(output, containsAllInOrder(<Matcher>[contains('Validating android/build.gradle.kts')])); |
| }); |
| |
| test('passes when sourceCompatibility and targetCompatibility are specified', () async { |
| final RepositoryPackage package = createFakePlugin( |
| 'a_plugin', |
| packagesDir, |
| examples: <String>[], |
| ); |
| writeFakePluginBuildGradle(package, includeSourceCompat: true, includeTargetCompat: true); |
| writeFakeManifest(package); |
| |
| final List<String> output = await runCapturingPrint(runner, <String>['validate']); |
| |
| expect(output, containsAllInOrder(<Matcher>[contains('Validating android/build.gradle.kts')])); |
| }); |
| |
| test('passes when toolchain languageVersion is specified', () async { |
| final RepositoryPackage package = createFakePlugin( |
| 'a_plugin', |
| packagesDir, |
| examples: <String>[], |
| ); |
| writeFakePluginBuildGradle(package, includeLanguageVersion: true); |
| writeFakeManifest(package); |
| |
| final List<String> output = await runCapturingPrint(runner, <String>['validate']); |
| |
| expect(output, containsAllInOrder(<Matcher>[contains('Validating android/build.gradle.kts')])); |
| }); |
| |
| test('does not require java version in examples', () async { |
| const pluginName = 'a_plugin'; |
| final RepositoryPackage package = createFakePackage(pluginName, packagesDir); |
| final RepositoryPackage example = package.getExamples().first; |
| writeFakeExampleBuildGradles(example, pluginName: pluginName); |
| writeFakeManifest(example, isApp: true); |
| |
| final List<String> output = await runCapturingPrint(runner, <String>['validate']); |
| |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains('Running for a_plugin/example'), |
| contains('Validating android/build.gradle.kts'), |
| ]), |
| ); |
| }); |
| |
| test('fails when java compatibility version is commented out', () async { |
| final RepositoryPackage package = createFakePlugin( |
| 'a_plugin', |
| packagesDir, |
| examples: <String>[], |
| ); |
| writeFakePluginBuildGradle( |
| package, |
| includeSourceCompat: true, |
| includeTargetCompat: true, |
| commentSourceLanguage: true, |
| ); |
| writeFakeManifest(package); |
| |
| Error? commandError; |
| final List<String> output = await runCapturingPrint( |
| runner, |
| <String>['validate'], |
| errorHandler: (Error e) { |
| commandError = e; |
| }, |
| ); |
| |
| expect(commandError, isA<ToolExit>()); |
| expect(output, containsAllInOrder(<Matcher>[contains(javaIncompatabilityIndicator)])); |
| }); |
| |
| test('fails when languageVersion is commented out', () async { |
| final RepositoryPackage package = createFakePlugin( |
| 'a_plugin', |
| packagesDir, |
| examples: <String>[], |
| ); |
| writeFakePluginBuildGradle(package, includeLanguageVersion: true, commentSourceLanguage: true); |
| writeFakeManifest(package); |
| |
| Error? commandError; |
| final List<String> output = await runCapturingPrint( |
| runner, |
| <String>['validate'], |
| errorHandler: (Error e) { |
| commandError = e; |
| }, |
| ); |
| |
| expect(commandError, isA<ToolExit>()); |
| expect(output, containsAllInOrder(<Matcher>[contains(javaIncompatabilityIndicator)])); |
| }); |
| |
| test('fails when plugin namespace does not match AndroidManifest.xml', () async { |
| final RepositoryPackage package = createFakePlugin( |
| 'a_plugin', |
| packagesDir, |
| examples: <String>[], |
| ); |
| writeFakePluginBuildGradle(package, includeLanguageVersion: true); |
| writeFakeManifest(package, packageName: 'wrong.package.name'); |
| |
| Error? commandError; |
| final List<String> output = await runCapturingPrint( |
| runner, |
| <String>['validate'], |
| errorHandler: (Error e) { |
| commandError = e; |
| }, |
| ); |
| |
| expect(commandError, isA<ToolExit>()); |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains( |
| 'build.gradle.kts "namespace" must match the "package" attribute in AndroidManifest.xml', |
| ), |
| ]), |
| ); |
| }); |
| |
| test('fails when namespace is missing', () async { |
| final RepositoryPackage package = createFakePlugin( |
| 'a_plugin', |
| packagesDir, |
| examples: <String>[], |
| ); |
| writeFakePluginBuildGradle(package, includeLanguageVersion: true, includeNamespace: false); |
| writeFakeManifest(package); |
| |
| Error? commandError; |
| final List<String> output = await runCapturingPrint( |
| runner, |
| <String>['validate'], |
| errorHandler: (Error e) { |
| commandError = e; |
| }, |
| ); |
| |
| expect(commandError, isA<ToolExit>()); |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[contains('build.gradle.kts must set a "namespace"')]), |
| ); |
| }); |
| |
| test('fails when namespace is missing from example', () async { |
| const pluginName = 'a_plugin'; |
| final RepositoryPackage package = createFakePlugin(pluginName, packagesDir); |
| writeFakePluginBuildGradle(package, includeLanguageVersion: true); |
| writeFakeManifest(package); |
| final RepositoryPackage example = package.getExamples().first; |
| writeFakeExampleBuildGradles(example, pluginName: pluginName, includeNamespace: false); |
| writeFakeManifest(example, isApp: true); |
| |
| Error? commandError; |
| final List<String> output = await runCapturingPrint( |
| runner, |
| <String>['validate'], |
| errorHandler: (Error e) { |
| commandError = e; |
| }, |
| ); |
| |
| expect(commandError, isA<ToolExit>()); |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[contains('build.gradle.kts must set a "namespace"')]), |
| ); |
| }); |
| |
| // TODO(stuartmorgan): Consider removing this in the future; we may at some |
| // point decide that we have a use case of example apps having different |
| // app IDs and namespaces. For now, it's enforced for consistency so they |
| // don't just accidentally diverge. |
| test('fails when namespace in example does not match AndroidManifest.xml', () async { |
| const pluginName = 'a_plugin'; |
| final RepositoryPackage package = createFakePlugin(pluginName, packagesDir); |
| writeFakePluginBuildGradle(package, includeLanguageVersion: true); |
| writeFakeManifest(package); |
| final RepositoryPackage example = package.getExamples().first; |
| writeFakeExampleBuildGradles(example, pluginName: pluginName); |
| writeFakeManifest(example, isApp: true, packageName: 'wrong.package.name'); |
| |
| Error? commandError; |
| final List<String> output = await runCapturingPrint( |
| runner, |
| <String>['validate'], |
| errorHandler: (Error e) { |
| commandError = e; |
| }, |
| ); |
| |
| expect(commandError, isA<ToolExit>()); |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains( |
| 'build.gradle.kts "namespace" must match the "package" attribute in AndroidManifest.xml', |
| ), |
| ]), |
| ); |
| }); |
| |
| test('fails when namespace is commented out', () async { |
| final RepositoryPackage package = createFakePlugin( |
| 'a_plugin', |
| packagesDir, |
| examples: <String>[], |
| ); |
| writeFakePluginBuildGradle(package, includeLanguageVersion: true, commentNamespace: true); |
| writeFakeManifest(package); |
| |
| Error? commandError; |
| final List<String> output = await runCapturingPrint( |
| runner, |
| <String>['validate'], |
| errorHandler: (Error e) { |
| commandError = e; |
| }, |
| ); |
| |
| expect(commandError, isA<ToolExit>()); |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[contains('build.gradle.kts must set a "namespace"')]), |
| ); |
| }); |
| |
| test('fails if gradle-driven lint-warnings-as-errors is missing', () async { |
| const pluginName = 'a_plugin'; |
| final RepositoryPackage plugin = createFakePlugin( |
| pluginName, |
| packagesDir, |
| examples: <String>[], |
| ); |
| writeFakePluginBuildGradle(plugin, includeLanguageVersion: true, warningsConfigured: false); |
| writeFakeManifest(plugin); |
| |
| Error? commandError; |
| final List<String> output = await runCapturingPrint( |
| runner, |
| <String>['validate'], |
| errorHandler: (Error e) { |
| commandError = e; |
| }, |
| ); |
| |
| expect(commandError, isA<ToolExit>()); |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains( |
| 'This package is not configured to enable all ' |
| 'Gradle-driven lint warnings and treat them as errors.', |
| ), |
| contains('The following packages had errors:'), |
| ]), |
| ); |
| }); |
| |
| test('fails if plugin example javac lint-warnings-as-errors is missing', () async { |
| const pluginName = 'a_plugin'; |
| final RepositoryPackage plugin = createFakePlugin( |
| pluginName, |
| packagesDir, |
| platformSupport: <String, PlatformDetails>{ |
| platformAndroid: const PlatformDetails(PlatformSupport.inline), |
| }, |
| ); |
| writeFakePluginBuildGradle(plugin, includeLanguageVersion: true); |
| writeFakeManifest(plugin); |
| final RepositoryPackage example = plugin.getExamples().first; |
| writeFakeExampleBuildGradles(example, pluginName: pluginName, warningsConfigured: false); |
| writeFakeManifest(example, isApp: true); |
| |
| Error? commandError; |
| final List<String> output = await runCapturingPrint( |
| runner, |
| <String>['validate'], |
| errorHandler: (Error e) { |
| commandError = e; |
| }, |
| ); |
| |
| expect(commandError, isA<ToolExit>()); |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains( |
| 'The example "example" is not configured to treat javac ' |
| 'lints and warnings as errors.', |
| ), |
| contains('The following packages had errors:'), |
| ]), |
| ); |
| }); |
| |
| test('passes if non-plugin package example javac lint-warnings-as-errors is missing', () async { |
| const packageName = 'a_package'; |
| final RepositoryPackage plugin = createFakePackage(packageName, packagesDir); |
| final RepositoryPackage example = plugin.getExamples().first; |
| writeFakeExampleBuildGradles(example, pluginName: packageName, warningsConfigured: false); |
| writeFakeManifest(example, isApp: true); |
| |
| final List<String> output = await runCapturingPrint(runner, <String>['validate']); |
| |
| expect(output, containsAllInOrder(<Matcher>[contains('Validating android/build.gradle.kts')])); |
| }); |
| |
| group('Artifact Hub check', () { |
| test('passes build.gradle.kts artifact hub check when set', () async { |
| const packageName = 'a_package'; |
| final RepositoryPackage package = createFakePackage('a_package', packagesDir); |
| final RepositoryPackage example = package.getExamples().first; |
| writeFakeExampleBuildGradles( |
| example, |
| pluginName: packageName, |
| // ignore: avoid_redundant_argument_values |
| includeBuildArtifactHub: true, |
| // ignore: avoid_redundant_argument_values |
| includeSettingsArtifactHub: true, |
| // ignore: avoid_redundant_argument_values |
| includeSettingsDocumentationArtifactHub: true, |
| ); |
| writeFakeManifest(example, isApp: true); |
| |
| final List<String> output = await runCapturingPrint(runner, <String>['validate']); |
| |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains('Validating android/build.gradle.kts'), |
| contains('Validating android/settings.gradle.kts'), |
| ]), |
| ); |
| }); |
| |
| test('fails artifact hub check when build and settings sections missing', () async { |
| const packageName = 'a_package'; |
| final RepositoryPackage package = createFakePackage('a_package', packagesDir); |
| final RepositoryPackage example = package.getExamples().first; |
| writeFakeExampleBuildGradles( |
| example, |
| pluginName: packageName, |
| includeBuildArtifactHub: false, |
| includeSettingsArtifactHub: false, |
| ); |
| writeFakeManifest(example, isApp: true); |
| |
| Error? commandError; |
| final List<String> output = await runCapturingPrint( |
| runner, |
| <String>['validate'], |
| errorHandler: (Error e) { |
| commandError = e; |
| }, |
| ); |
| |
| expect(commandError, isA<ToolExit>()); |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains(GradleValidator.exampleRootGradleArtifactHubString), |
| contains(GradleValidator.exampleSettingsArtifactHubString), |
| ]), |
| ); |
| }); |
| |
| test('fails build.gradle.kts artifact hub check when missing', () async { |
| const packageName = 'a_package'; |
| final RepositoryPackage package = createFakePackage('a_package', packagesDir); |
| final RepositoryPackage example = package.getExamples().first; |
| writeFakeExampleBuildGradles( |
| example, |
| pluginName: packageName, |
| includeBuildArtifactHub: false, |
| // ignore: avoid_redundant_argument_values |
| includeSettingsArtifactHub: true, |
| ); |
| writeFakeManifest(example, isApp: true); |
| |
| Error? commandError; |
| final List<String> output = await runCapturingPrint( |
| runner, |
| <String>['validate'], |
| errorHandler: (Error e) { |
| commandError = e; |
| }, |
| ); |
| |
| expect(commandError, isA<ToolExit>()); |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[contains(GradleValidator.exampleRootGradleArtifactHubString)]), |
| ); |
| }); |
| |
| test('fails settings.gradle.kts artifact hub check when missing', () async { |
| const packageName = 'a_package'; |
| final RepositoryPackage package = createFakePackage('a_package', packagesDir); |
| final RepositoryPackage example = package.getExamples().first; |
| writeFakeExampleBuildGradles( |
| example, |
| pluginName: packageName, |
| // ignore: avoid_redundant_argument_values |
| includeBuildArtifactHub: true, |
| includeSettingsArtifactHub: false, |
| ); |
| writeFakeManifest(example, isApp: true); |
| |
| Error? commandError; |
| final List<String> output = await runCapturingPrint( |
| runner, |
| <String>['validate'], |
| errorHandler: (Error e) { |
| commandError = e; |
| }, |
| ); |
| |
| expect(commandError, isA<ToolExit>()); |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[contains(GradleValidator.exampleSettingsArtifactHubString)]), |
| ); |
| expect(output, isNot(contains(GradleValidator.exampleRootGradleArtifactHubString))); |
| }); |
| |
| test('error message is printed when documentation link is missing', () async { |
| const packageName = 'a_package'; |
| final RepositoryPackage package = createFakePackage('a_package', packagesDir); |
| final RepositoryPackage example = package.getExamples().first; |
| writeFakeExampleBuildGradles( |
| example, |
| pluginName: packageName, |
| // ignore: avoid_redundant_argument_values |
| includeBuildArtifactHub: true, |
| // ignore: avoid_redundant_argument_values |
| includeSettingsArtifactHub: true, |
| includeSettingsDocumentationArtifactHub: false, |
| ); |
| writeFakeManifest(example, isApp: true); |
| |
| Error? commandError; |
| final List<String> output = await runCapturingPrint( |
| runner, |
| <String>['validate'], |
| errorHandler: (Error e) { |
| commandError = e; |
| }, |
| ); |
| |
| expect(commandError, isA<ToolExit>()); |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[contains(GradleValidator.artifactHubDocumentationString)]), |
| ); |
| }); |
| }); |
| |
| group('Kotlin version check', () { |
| test('passes if not set', () async { |
| const packageName = 'a_package'; |
| final RepositoryPackage package = createFakePackage('a_package', packagesDir); |
| final RepositoryPackage example = package.getExamples().first; |
| writeFakeExampleBuildGradles(example, pluginName: packageName); |
| writeFakeManifest(example, isApp: true); |
| |
| final List<String> output = await runCapturingPrint(runner, <String>['validate']); |
| |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[contains('Validating android/build.gradle.kts')]), |
| ); |
| }); |
| |
| test('passes if at the minimum allowed version', () async { |
| const packageName = 'a_package'; |
| final RepositoryPackage package = createFakePackage('a_package', packagesDir); |
| final RepositoryPackage example = package.getExamples().first; |
| writeFakeExampleBuildGradles( |
| example, |
| pluginName: packageName, |
| kotlinVersion: minKotlinVersion.toString(), |
| ); |
| writeFakeManifest(example, isApp: true); |
| |
| final List<String> output = await runCapturingPrint(runner, <String>['validate']); |
| |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[contains('Validating android/build.gradle.kts')]), |
| ); |
| }); |
| |
| test('passes if above the minimum allowed version', () async { |
| const packageName = 'a_package'; |
| final RepositoryPackage package = createFakePackage('a_package', packagesDir); |
| final RepositoryPackage example = package.getExamples().first; |
| writeFakeExampleBuildGradles(example, pluginName: packageName, kotlinVersion: '99.99.0'); |
| writeFakeManifest(example, isApp: true); |
| |
| final List<String> output = await runCapturingPrint(runner, <String>['validate']); |
| |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[contains('Validating android/build.gradle.kts')]), |
| ); |
| }); |
| |
| test('fails if below the minimum allowed version', () async { |
| const packageName = 'a_package'; |
| final RepositoryPackage package = createFakePackage('a_package', packagesDir); |
| final RepositoryPackage example = package.getExamples().first; |
| writeFakeExampleBuildGradles(example, pluginName: packageName, kotlinVersion: '1.9.20'); |
| writeFakeManifest(example, isApp: true); |
| |
| Error? commandError; |
| final List<String> output = await runCapturingPrint( |
| runner, |
| <String>['validate'], |
| errorHandler: (Error e) { |
| commandError = e; |
| }, |
| ); |
| |
| expect(commandError, isA<ToolExit>()); |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains( |
| 'settings.gradle.kts sets the "org.jetbrains.kotlin.android" plugin ' |
| 'version to "1.9.20". The minimum Kotlin version that can be ' |
| 'specified is $minKotlinVersion, for compatibility with modern ' |
| 'dependencies.', |
| ), |
| ]), |
| ); |
| }); |
| }); |
| |
| group('compileSdk check', () { |
| test('passes if set to a version higher than flutter.compileSdkVersion', () async { |
| const packageName = 'a_package'; |
| final RepositoryPackage package = createFakePackage( |
| packageName, |
| packagesDir, |
| isFlutter: true, |
| ); |
| // Current flutter.compileSdkVersion is 36. |
| writeFakePluginBuildGradle(package, includeLanguageVersion: true, compileSdk: '37'); |
| writeFakeManifest(package); |
| final RepositoryPackage example = package.getExamples().first; |
| writeFakeExampleBuildGradles(example, pluginName: packageName); |
| writeFakeManifest(example, isApp: true); |
| |
| final List<String> output = await runCapturingPrint(runner, <String>['validate']); |
| |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[contains('Validating android/build.gradle.kts')]), |
| ); |
| }); |
| |
| test('passes if set to flutter.compileSdkVersion with Flutter 3.27+', () async { |
| const packageName = 'a_package'; |
| final RepositoryPackage package = createFakePackage( |
| packageName, |
| packagesDir, |
| isFlutter: true, |
| flutterConstraint: '>=3.27.0', |
| ); |
| writeFakePluginBuildGradle( |
| package, |
| includeLanguageVersion: true, |
| compileSdk: 'flutter.compileSdkVersion', |
| ); |
| writeFakeManifest(package); |
| final RepositoryPackage example = package.getExamples().first; |
| writeFakeExampleBuildGradles(example, pluginName: packageName); |
| writeFakeManifest(example, isApp: true); |
| |
| final List<String> output = await runCapturingPrint(runner, <String>['validate']); |
| |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[contains('Validating android/build.gradle.kts')]), |
| ); |
| }); |
| |
| test('fails if set to a version lower than flutter.compileSdkVersion', () async { |
| const packageName = 'a_package'; |
| final RepositoryPackage package = createFakePackage( |
| packageName, |
| packagesDir, |
| isFlutter: true, |
| ); |
| // Current flutter.compileSdkVersion is 36. |
| const minCompileSdkVersion = '36'; |
| const testCompileSdkVersion = '35'; |
| writeFakePluginBuildGradle( |
| package, |
| includeLanguageVersion: true, |
| compileSdk: testCompileSdkVersion, |
| ); |
| writeFakeManifest(package); |
| final RepositoryPackage example = package.getExamples().first; |
| writeFakeExampleBuildGradles(example, pluginName: packageName); |
| writeFakeManifest(example, isApp: true); |
| |
| Error? commandError; |
| final List<String> output = await runCapturingPrint( |
| runner, |
| <String>['validate'], |
| errorHandler: (Error e) { |
| commandError = e; |
| }, |
| ); |
| |
| expect(commandError, isA<ToolExit>()); |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains( |
| 'compileSdk version $testCompileSdkVersion is too low. ' |
| 'Minimum required version is $minCompileSdkVersion.', |
| ), |
| ]), |
| ); |
| }); |
| |
| test('fails if set to flutter.compileSdkVersion with Flutter <3.27', () async { |
| const packageName = 'a_package'; |
| final RepositoryPackage package = createFakePackage( |
| packageName, |
| packagesDir, |
| isFlutter: true, |
| flutterConstraint: '>=3.24.0', |
| ); |
| writeFakePluginBuildGradle( |
| package, |
| includeLanguageVersion: true, |
| compileSdk: 'flutter.compileSdkVersion', |
| ); |
| writeFakeManifest(package); |
| final RepositoryPackage example = package.getExamples().first; |
| writeFakeExampleBuildGradles(example, pluginName: packageName); |
| writeFakeManifest(example, isApp: true); |
| |
| Error? commandError; |
| final List<String> output = await runCapturingPrint( |
| runner, |
| <String>['validate'], |
| errorHandler: (Error e) { |
| commandError = e; |
| }, |
| ); |
| |
| expect(commandError, isA<ToolExit>()); |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains( |
| 'Use of flutter.compileSdkVersion requires a minimum ' |
| 'Flutter version of 3.27, but this package currently supports ' |
| '3.24.0', |
| ), |
| ]), |
| ); |
| }); |
| }); |
| |
| group('kotlin gradle plugin check', () { |
| test('passes when kotlin gradle plugin is not applied', () async { |
| final RepositoryPackage package = createFakePlugin( |
| 'a_plugin', |
| packagesDir, |
| examples: <String>[], |
| ); |
| writeFakePluginBuildGradle(package, includeLanguageVersion: true); |
| writeFakeManifest(package); |
| |
| final List<String> output = await runCapturingPrint(runner, <String>['validate']); |
| |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[contains('Validating android/build.gradle.kts')]), |
| ); |
| }); |
| |
| test('fails when kotlin gradle plugin is applied', () async { |
| final RepositoryPackage package = createFakePlugin( |
| 'a_plugin', |
| packagesDir, |
| examples: <String>[], |
| ); |
| writeFakePluginBuildGradle( |
| package, |
| includeLanguageVersion: true, |
| includeKotlinGradlePlugin: true, |
| ); |
| writeFakeManifest(package); |
| |
| Error? commandError; |
| final List<String> output = await runCapturingPrint( |
| runner, |
| <String>['validate'], |
| errorHandler: (Error e) { |
| commandError = e; |
| }, |
| ); |
| |
| expect(commandError, isA<ToolExit>()); |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains( |
| "The kotlin-android plugin should not be applied in the plugin module's build.gradle.kts", |
| ), |
| ]), |
| ); |
| }); |
| |
| test('passes when Kotlin Gradle plugin is not applied in app/build.gradle.kts', () async { |
| const pluginName = 'a_plugin'; |
| final RepositoryPackage package = createFakePackage(pluginName, packagesDir); |
| final RepositoryPackage example = package.getExamples().first; |
| writeFakeExampleBuildGradles(example, pluginName: pluginName); |
| writeFakeManifest(example, isApp: true); |
| |
| final List<String> output = await runCapturingPrint(runner, <String>['validate']); |
| |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains('Running for a_plugin/example'), |
| contains('Validating android/app/build.gradle.kts'), |
| ]), |
| ); |
| }); |
| |
| test('fails when Kotlin Gradle plugin is applied in app/build.gradle.kts', () async { |
| const pluginName = 'a_plugin'; |
| final RepositoryPackage package = createFakePackage(pluginName, packagesDir); |
| final RepositoryPackage example = package.getExamples().first; |
| writeFakeExampleBuildGradles( |
| example, |
| pluginName: pluginName, |
| includeKotlinGradlePlugin: true, |
| ); |
| writeFakeManifest(example, isApp: true); |
| |
| Error? commandError; |
| final List<String> output = await runCapturingPrint( |
| runner, |
| <String>['validate'], |
| errorHandler: (Error e) { |
| commandError = e; |
| }, |
| ); |
| |
| expect(commandError, isA<ToolExit>()); |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains( |
| "The kotlin-android plugin should not be applied in the app module's build.gradle.kts", |
| ), |
| ]), |
| ); |
| }); |
| }); |
| |
| group('kotlin compiler options check', () { |
| test('passes when kotlin compiler options are specified', () async { |
| final RepositoryPackage package = createFakePlugin( |
| 'a_plugin', |
| packagesDir, |
| examples: <String>[], |
| ); |
| writeFakePluginBuildGradle( |
| package, |
| includeLanguageVersion: true, |
| // ignore: avoid_redundant_argument_values ensure codepath is tested if defaults change. |
| includeKotlinCompilerOptions: true, |
| ); |
| writeFakeManifest(package); |
| |
| final List<String> output = await runCapturingPrint(runner, <String>['validate']); |
| |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[contains('Validating android/build.gradle.kts')]), |
| ); |
| }); |
| |
| test('passes when kotlin compiler options are not specified', () async { |
| final RepositoryPackage package = createFakePlugin( |
| 'a_plugin', |
| packagesDir, |
| examples: <String>[], |
| ); |
| writeFakePluginBuildGradle( |
| package, |
| includeLanguageVersion: true, |
| includeKotlinCompilerOptions: false, |
| ); |
| writeFakeManifest(package); |
| |
| final List<String> output = await runCapturingPrint(runner, <String>['validate']); |
| |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[contains('Validating android/build.gradle.kts')]), |
| ); |
| }); |
| |
| test('passes when kotlin compiler options is commented out', () async { |
| final RepositoryPackage package = createFakePlugin( |
| 'a_plugin', |
| packagesDir, |
| examples: <String>[], |
| ); |
| writeFakePluginBuildGradle( |
| package, |
| includeLanguageVersion: true, |
| commentKotlinCompilerOptions: true, |
| ); |
| writeFakeManifest(package); |
| |
| final List<String> output = await runCapturingPrint(runner, <String>['validate']); |
| |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[contains('Validating android/build.gradle.kts')]), |
| ); |
| }); |
| |
| test('fails when kotlin compiler options uses string jvm version', () async { |
| final RepositoryPackage package = createFakePlugin( |
| 'a_plugin', |
| packagesDir, |
| examples: <String>[], |
| ); |
| writeFakePluginBuildGradle( |
| package, |
| includeLanguageVersion: true, |
| useDeprecatedJvmTargetStyle: true, |
| ); |
| writeFakeManifest(package); |
| |
| Error? commandError; |
| final List<String> output = await runCapturingPrint( |
| runner, |
| <String>['validate'], |
| errorHandler: (Error e) { |
| commandError = e; |
| }, |
| ); |
| |
| expect(commandError, isA<ToolExit>()); |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains( |
| 'If build.gradle.kts sets jvmTarget inside kotlin.compilerOptions, it must use JvmTarget syntax.', |
| ), |
| ]), |
| ); |
| }); |
| |
| test('fails when kotlin compiler options uses JavaVersion string', () async { |
| final RepositoryPackage package = createFakePlugin( |
| 'a_plugin', |
| packagesDir, |
| examples: <String>[], |
| ); |
| writeFakePluginBuildGradle( |
| package, |
| includeLanguageVersion: true, |
| useJavaVersionStringForJvmTarget: true, |
| ); |
| writeFakeManifest(package); |
| |
| Error? commandError; |
| final List<String> output = await runCapturingPrint( |
| runner, |
| <String>['validate'], |
| errorHandler: (Error e) { |
| commandError = e; |
| }, |
| ); |
| |
| expect(commandError, isA<ToolExit>()); |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains( |
| 'If build.gradle.kts sets jvmTarget inside kotlin.compilerOptions, it must use JvmTarget syntax.', |
| ), |
| ]), |
| ); |
| }); |
| |
| test('fails when there is a kotlin compiler options DSL block in the android block', () async { |
| final RepositoryPackage package = createFakePlugin( |
| 'a_plugin', |
| packagesDir, |
| examples: <String>[], |
| ); |
| writeFakePluginBuildGradle( |
| package, |
| includeLanguageVersion: true, |
| includeKotlinCompilerOptions: false, |
| ); |
| |
| final File buildGradle = package |
| .platformDirectory(FlutterPlatform.android) |
| .childFile('build.gradle.kts'); |
| final String contents = buildGradle.readAsStringSync(); |
| final String updatedContents = contents.replaceFirst( |
| 'android {', |
| 'android {\n kotlin {\n compilerOptions {\n jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17\n }\n }', |
| ); |
| buildGradle.writeAsStringSync(updatedContents); |
| writeFakeManifest(package); |
| |
| Error? commandError; |
| final List<String> output = await runCapturingPrint( |
| runner, |
| <String>['validate'], |
| errorHandler: (Error e) { |
| commandError = e; |
| }, |
| ); |
| |
| expect(commandError, isA<ToolExit>()); |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains( |
| 'build.gradle.kts must not nest "kotlin" or "compilerOptions" inside the "android" block. It must be at the top-level', |
| ), |
| ]), |
| ); |
| }); |
| |
| test('fails when kotlinOptions is used in the android block', () async { |
| final RepositoryPackage package = createFakePlugin( |
| 'a_plugin', |
| packagesDir, |
| examples: <String>[], |
| ); |
| writeFakePluginBuildGradle( |
| package, |
| includeLanguageVersion: true, |
| includeDeprecatedKotlinOptionsInsideAndroid: true, |
| ); |
| writeFakeManifest(package); |
| |
| Error? commandError; |
| final List<String> output = await runCapturingPrint( |
| runner, |
| <String>['validate'], |
| errorHandler: (Error e) { |
| commandError = e; |
| }, |
| ); |
| |
| expect(commandError, isA<ToolExit>()); |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains( |
| 'build.gradle.kts must not use the deprecated "kotlinOptions" DSL. Use "kotlin.compilerOptions" instead', |
| ), |
| ]), |
| ); |
| }); |
| |
| test('fails when kotlinOptions is used in the kotlin block', () async { |
| final RepositoryPackage package = createFakePlugin( |
| 'a_plugin', |
| packagesDir, |
| examples: <String>[], |
| ); |
| writeFakePluginBuildGradle( |
| package, |
| includeLanguageVersion: true, |
| includeDeprecatedKotlinOptionsInsideKotlin: true, |
| ); |
| writeFakeManifest(package); |
| |
| Error? commandError; |
| final List<String> output = await runCapturingPrint( |
| runner, |
| <String>['validate'], |
| errorHandler: (Error e) { |
| commandError = e; |
| }, |
| ); |
| |
| expect(commandError, isA<ToolExit>()); |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains( |
| 'build.gradle.kts must not use the deprecated "kotlinOptions" DSL. Use "kotlin.compilerOptions" instead', |
| ), |
| ]), |
| ); |
| }); |
| |
| test('fails when kotlinOptions is used in both the android and kotlin blocks', () async { |
| final RepositoryPackage package = createFakePlugin( |
| 'a_plugin', |
| packagesDir, |
| examples: <String>[], |
| ); |
| writeFakePluginBuildGradle( |
| package, |
| includeLanguageVersion: true, |
| includeDeprecatedKotlinOptionsInsideAndroid: true, |
| includeDeprecatedKotlinOptionsInsideKotlin: true, |
| ); |
| writeFakeManifest(package); |
| |
| Error? commandError; |
| final List<String> output = await runCapturingPrint( |
| runner, |
| <String>['validate'], |
| errorHandler: (Error e) { |
| commandError = e; |
| }, |
| ); |
| |
| expect(commandError, isA<ToolExit>()); |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains( |
| 'build.gradle.kts must not use the deprecated "kotlinOptions" DSL. Use "kotlin.compilerOptions" instead', |
| ), |
| ]), |
| ); |
| }); |
| }); |
| |
| group('example app kotlin compiler options check', () { |
| test('passes when kotlin compiler options are specified', () async { |
| const pluginName = 'a_plugin'; |
| final RepositoryPackage package = createFakePackage(pluginName, packagesDir); |
| final RepositoryPackage example = package.getExamples().first; |
| writeFakeExampleBuildGradles(example, pluginName: pluginName); |
| writeFakeManifest(example, isApp: true); |
| |
| final List<String> output = await runCapturingPrint(runner, <String>['validate']); |
| |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains('Running for a_plugin/example'), |
| contains('Validating android/app/build.gradle.kts'), |
| ]), |
| ); |
| }); |
| |
| test('passes when kotlin compiler options are not specified', () async { |
| const pluginName = 'a_plugin'; |
| final RepositoryPackage package = createFakePackage(pluginName, packagesDir); |
| final RepositoryPackage example = package.getExamples().first; |
| writeFakeExampleBuildGradles( |
| example, |
| pluginName: pluginName, |
| includeKotlinCompilerOptions: false, |
| ); |
| writeFakeManifest(example, isApp: true); |
| |
| final List<String> output = await runCapturingPrint(runner, <String>['validate']); |
| |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains('Running for a_plugin/example'), |
| contains('Validating android/app/build.gradle.kts'), |
| ]), |
| ); |
| }); |
| |
| test('passes when kotlin compiler options is commented out', () async { |
| const pluginName = 'a_plugin'; |
| final RepositoryPackage package = createFakePackage(pluginName, packagesDir); |
| final RepositoryPackage example = package.getExamples().first; |
| writeFakeExampleBuildGradles( |
| example, |
| pluginName: pluginName, |
| commentKotlinCompilerOptions: true, |
| ); |
| writeFakeManifest(example, isApp: true); |
| |
| final List<String> output = await runCapturingPrint(runner, <String>['validate']); |
| |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains('Running for a_plugin/example'), |
| contains('Validating android/app/build.gradle.kts'), |
| ]), |
| ); |
| }); |
| |
| test('fails when kotlin compiler options uses string jvm version', () async { |
| const pluginName = 'a_plugin'; |
| final RepositoryPackage package = createFakePackage(pluginName, packagesDir); |
| final RepositoryPackage example = package.getExamples().first; |
| writeFakeExampleBuildGradles( |
| example, |
| pluginName: pluginName, |
| useDeprecatedJvmTargetStyle: true, |
| ); |
| writeFakeManifest(example, isApp: true); |
| |
| Error? commandError; |
| final List<String> output = await runCapturingPrint( |
| runner, |
| <String>['validate'], |
| errorHandler: (Error e) { |
| commandError = e; |
| }, |
| ); |
| |
| expect(commandError, isA<ToolExit>()); |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains( |
| 'If build.gradle.kts sets jvmTarget inside kotlin.compilerOptions, it must use JvmTarget syntax.', |
| ), |
| ]), |
| ); |
| }); |
| |
| test('fails when kotlin compiler options uses JavaVersion string', () async { |
| const pluginName = 'a_plugin'; |
| final RepositoryPackage package = createFakePackage(pluginName, packagesDir); |
| final RepositoryPackage example = package.getExamples().first; |
| writeFakeExampleBuildGradles( |
| example, |
| pluginName: pluginName, |
| useJavaVersionStringForJvmTarget: true, |
| ); |
| writeFakeManifest(example, isApp: true); |
| |
| Error? commandError; |
| final List<String> output = await runCapturingPrint( |
| runner, |
| <String>['validate'], |
| errorHandler: (Error e) { |
| commandError = e; |
| }, |
| ); |
| |
| expect(commandError, isA<ToolExit>()); |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains( |
| 'If build.gradle.kts sets jvmTarget inside kotlin.compilerOptions, it must use JvmTarget syntax.', |
| ), |
| ]), |
| ); |
| }); |
| |
| test('fails when there is a kotlin compiler options DSL block in the android block', () async { |
| const pluginName = 'a_plugin'; |
| final RepositoryPackage package = createFakePackage(pluginName, packagesDir); |
| final RepositoryPackage example = package.getExamples().first; |
| writeFakeExampleBuildGradles( |
| example, |
| pluginName: pluginName, |
| includeKotlinCompilerOptions: false, |
| ); |
| |
| final File buildGradle = example |
| .platformDirectory(FlutterPlatform.android) |
| .childDirectory('app') |
| .childFile('build.gradle.kts'); |
| final String contents = buildGradle.readAsStringSync(); |
| final String updatedContents = contents.replaceFirst( |
| 'android {', |
| 'android {\n kotlin {\n compilerOptions {\n jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17\n }\n }', |
| ); |
| buildGradle.writeAsStringSync(updatedContents); |
| writeFakeManifest(example, isApp: true); |
| |
| Error? commandError; |
| final List<String> output = await runCapturingPrint( |
| runner, |
| <String>['validate'], |
| errorHandler: (Error e) { |
| commandError = e; |
| }, |
| ); |
| |
| expect(commandError, isA<ToolExit>()); |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains( |
| 'build.gradle.kts must not nest "kotlin" or "compilerOptions" inside the "android" block. It must be at the top-level', |
| ), |
| ]), |
| ); |
| }); |
| |
| test('fails when kotlinOptions is used in the android block', () async { |
| const pluginName = 'a_plugin'; |
| final RepositoryPackage package = createFakePackage(pluginName, packagesDir); |
| final RepositoryPackage example = package.getExamples().first; |
| writeFakeExampleBuildGradles( |
| example, |
| pluginName: pluginName, |
| includeKotlinCompilerOptions: false, |
| includeDeprecatedKotlinOptionsInsideAndroid: true, |
| ); |
| writeFakeManifest(example, isApp: true); |
| |
| Error? commandError; |
| final List<String> output = await runCapturingPrint( |
| runner, |
| <String>['validate'], |
| errorHandler: (Error e) { |
| commandError = e; |
| }, |
| ); |
| |
| expect(commandError, isA<ToolExit>()); |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains( |
| 'build.gradle.kts must not use the deprecated "kotlinOptions" DSL. Use "kotlin.compilerOptions" instead', |
| ), |
| ]), |
| ); |
| }); |
| |
| test('fails when kotlinOptions is used in the kotlin block', () async { |
| const pluginName = 'a_plugin'; |
| final RepositoryPackage package = createFakePackage(pluginName, packagesDir); |
| final RepositoryPackage example = package.getExamples().first; |
| writeFakeExampleBuildGradles( |
| example, |
| pluginName: pluginName, |
| includeKotlinCompilerOptions: false, |
| includeDeprecatedKotlinOptionsInsideKotlin: true, |
| ); |
| writeFakeManifest(example, isApp: true); |
| |
| Error? commandError; |
| final List<String> output = await runCapturingPrint( |
| runner, |
| <String>['validate'], |
| errorHandler: (Error e) { |
| commandError = e; |
| }, |
| ); |
| |
| expect(commandError, isA<ToolExit>()); |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains( |
| 'build.gradle.kts must not use the deprecated "kotlinOptions" DSL. Use "kotlin.compilerOptions" instead', |
| ), |
| ]), |
| ); |
| }); |
| |
| test('fails when kotlinOptions is used in both the android and kotlin blocks', () async { |
| const pluginName = 'a_plugin'; |
| final RepositoryPackage package = createFakePackage(pluginName, packagesDir); |
| final RepositoryPackage example = package.getExamples().first; |
| writeFakeExampleBuildGradles( |
| example, |
| pluginName: pluginName, |
| includeKotlinCompilerOptions: false, |
| includeDeprecatedKotlinOptionsInsideAndroid: true, |
| includeDeprecatedKotlinOptionsInsideKotlin: true, |
| ); |
| writeFakeManifest(example, isApp: true); |
| |
| Error? commandError; |
| final List<String> output = await runCapturingPrint( |
| runner, |
| <String>['validate'], |
| errorHandler: (Error e) { |
| commandError = e; |
| }, |
| ); |
| |
| expect(commandError, isA<ToolExit>()); |
| expect( |
| output, |
| containsAllInOrder(<Matcher>[ |
| contains( |
| 'build.gradle.kts must not use the deprecated "kotlinOptions" DSL. Use "kotlin.compilerOptions" instead', |
| ), |
| ]), |
| ); |
| }); |
| }); |
| } |
| |
| class _KotlinConfigParts { |
| const _KotlinConfigParts({ |
| required this.kotlinConfig, |
| required this.kotlinDeprecatedInAndroidConfig, |
| required this.kotlinDeprecatedInKotlinConfig, |
| }); |
| |
| final String kotlinConfig; |
| final String kotlinDeprecatedInAndroidConfig; |
| final String kotlinDeprecatedInKotlinConfig; |
| } |
| |
| _KotlinConfigParts _generateKotlinConfigParts({ |
| required bool commentKotlinCompilerOptions, |
| required bool useJavaVersionStringForJvmTarget, |
| required bool useDeprecatedJvmTargetStyle, |
| required int jvmTargetValue, |
| required int kotlinJvmValue, |
| }) { |
| final String kotlinJvmTarget; |
| if (useJavaVersionStringForJvmTarget) { |
| kotlinJvmTarget = 'JavaVersion.VERSION_$kotlinJvmValue.toString()'; |
| } else if (useDeprecatedJvmTargetStyle) { |
| kotlinJvmTarget = '"$jvmTargetValue"'; |
| } else { |
| kotlinJvmTarget = 'org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_$kotlinJvmValue'; |
| } |
| |
| final kotlinConfig = |
| ''' |
| ${commentKotlinCompilerOptions ? '// ' : ''}kotlin { |
| ${commentKotlinCompilerOptions ? '// ' : ''} compilerOptions { |
| ${commentKotlinCompilerOptions ? '// ' : ''} jvmTarget = $kotlinJvmTarget |
| ${commentKotlinCompilerOptions ? '// ' : ''} } |
| ${commentKotlinCompilerOptions ? '// ' : ''}}'''; |
| |
| final kotlinDeprecatedInAndroidConfig = |
| ''' |
| kotlinOptions { |
| jvmTarget = JavaVersion.VERSION_$kotlinJvmValue.toString() |
| }'''; |
| |
| final kotlinDeprecatedInKotlinConfig = |
| ''' |
| kotlin { |
| $kotlinDeprecatedInAndroidConfig |
| }'''; |
| |
| return _KotlinConfigParts( |
| kotlinConfig: kotlinConfig, |
| kotlinDeprecatedInAndroidConfig: kotlinDeprecatedInAndroidConfig, |
| kotlinDeprecatedInKotlinConfig: kotlinDeprecatedInKotlinConfig, |
| ); |
| } |