Convert BaseFlutterTask From Groovy to Kotlin (#163148)

Converted `BaseFlutterTask` from Groovy to Kotlin.

Note: Since it looks like Gradle prevents us from instantiating and
using an object that extends abstract class BaseFlutterTask, we must use
principles of Test Driven Development (TDD) in order to unit test
`BaseFlutterTask`. `BaseFlutterTaskHelper` was created solely for unit
testing to simulate the behavior of all functions in the original
`BaseFlutterTask`.

Fixes #162111 

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [ ] All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel
on [Discord].

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
This commit is contained in:
jesswrd 2025-03-14 13:36:36 -07:00 committed by GitHub
parent 7ce41abbd2
commit 58a2116d90
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 993 additions and 188 deletions

View File

@ -11,6 +11,7 @@ import com.android.build.gradle.tasks.PackageAndroidArtifact
import com.android.build.gradle.tasks.ProcessAndroidResources
import com.android.builder.model.BuildType
import com.flutter.gradle.BaseApplicationNameHandler
import com.flutter.gradle.BaseFlutterTask
import com.flutter.gradle.Deeplink
import com.flutter.gradle.DependencyVersionChecker
import com.flutter.gradle.FlutterExtension
@ -21,7 +22,6 @@ import org.gradle.api.file.Directory
import java.nio.file.Paths
import org.apache.tools.ant.taskdefs.condition.Os
import org.gradle.api.DefaultTask
import org.gradle.api.GradleException
import org.gradle.api.JavaVersion
import org.gradle.api.Project
@ -30,14 +30,11 @@ import org.gradle.api.Task
import org.gradle.api.UnknownTaskException
import org.gradle.api.file.CopySpec
import org.gradle.api.file.FileCollection
import org.gradle.api.logging.LogLevel
import org.gradle.api.tasks.Copy
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.OutputFiles
import org.gradle.api.tasks.Optional
import org.gradle.api.tasks.TaskAction
import org.gradle.api.tasks.TaskProvider
import org.gradle.api.tasks.bundling.Jar
@ -1459,190 +1456,6 @@ class FlutterPlugin implements Plugin<Project> {
}
abstract class BaseFlutterTask extends DefaultTask {
@Internal
File flutterRoot
@Internal
File flutterExecutable
@Input
String buildMode
@Input
int minSdkVersion
@Optional @Input
String localEngine
@Optional @Input
String localEngineHost
@Optional @Input
String localEngineSrcPath
@Optional @Input
Boolean fastStart
@Input
String targetPath
@Optional @Input
Boolean verbose
@Optional @Input
String[] fileSystemRoots
@Optional @Input
String fileSystemScheme
@Input
Boolean trackWidgetCreation
@Optional @Input
List<String> targetPlatformValues
@Internal
File sourceDir
@Internal
File intermediateDir
@Optional @Input
String frontendServerStarterPath
@Optional @Input
String extraFrontEndOptions
@Optional @Input
String extraGenSnapshotOptions
@Optional @Input
String splitDebugInfo
@Optional @Input
Boolean treeShakeIcons
@Optional @Input
Boolean dartObfuscation
@Optional @Input
String dartDefines
@Optional @Input
String codeSizeDirectory
@Optional @Input
String performanceMeasurementFile
@Optional @Input
Boolean deferredComponents
@Optional @Input
Boolean validateDeferredComponents
@Optional @Input
Boolean skipDependencyChecks
@Optional @Input
String flavor
@OutputFiles
FileCollection getDependenciesFiles() {
FileCollection depfiles = project.files()
// Includes all sources used in the flutter compilation.
depfiles += project.files("${intermediateDir}/flutter_build.d")
return depfiles
}
void buildBundle() {
if (!sourceDir.isDirectory()) {
throw new GradleException("Invalid Flutter source directory: ${sourceDir}")
}
intermediateDir.mkdirs()
// Compute the rule name for flutter assemble. To speed up builds that contain
// multiple ABIs, the target name is used to communicate which ones are required
// rather than the TargetPlatform. This allows multiple builds to share the same
// cache.
String[] ruleNames
if (buildMode == "debug") {
ruleNames = ["debug_android_application"]
} else if (deferredComponents) {
ruleNames = targetPlatformValues.collect { "android_aot_deferred_components_bundle_${buildMode}_$it" }
} else {
ruleNames = targetPlatformValues.collect { "android_aot_bundle_${buildMode}_$it" }
}
project.exec {
logging.captureStandardError(LogLevel.ERROR)
executable(flutterExecutable.absolutePath)
workingDir(sourceDir)
if (localEngine != null) {
args "--local-engine", localEngine
args "--local-engine-src-path", localEngineSrcPath
}
if (localEngineHost != null) {
args "--local-engine-host", localEngineHost
}
if (verbose) {
args "--verbose"
} else {
args "--quiet"
}
args("assemble")
args("--no-version-check")
args("--depfile", "${intermediateDir}/flutter_build.d")
args("--output", "${intermediateDir}")
if (performanceMeasurementFile != null) {
args("--performance-measurement-file=${performanceMeasurementFile}")
}
if (!fastStart || buildMode != "debug") {
args("-dTargetFile=${targetPath}")
} else {
args("-dTargetFile=${Paths.get(flutterRoot.absolutePath, "examples", "splash", "lib", "main.dart")}")
}
args("-dTargetPlatform=android")
args("-dBuildMode=${buildMode}")
if (trackWidgetCreation != null) {
args("-dTrackWidgetCreation=${trackWidgetCreation}")
}
if (splitDebugInfo != null) {
args("-dSplitDebugInfo=${splitDebugInfo}")
}
if (treeShakeIcons == true) {
args("-dTreeShakeIcons=true")
}
if (dartObfuscation == true) {
args("-dDartObfuscation=true")
}
if (dartDefines != null) {
args("--DartDefines=${dartDefines}")
}
if (codeSizeDirectory != null) {
args("-dCodeSizeDirectory=${codeSizeDirectory}")
}
if (flavor != null) {
args("-dFlavor=${flavor}")
}
if (extraGenSnapshotOptions != null) {
args("--ExtraGenSnapshotOptions=${extraGenSnapshotOptions}")
}
if (frontendServerStarterPath != null) {
args("-dFrontendServerStarterPath=${frontendServerStarterPath}")
}
if (extraFrontEndOptions != null) {
args("--ExtraFrontEndOptions=${extraFrontEndOptions}")
}
args("-dAndroidArchs=${targetPlatformValues.join(' ')}")
args("-dMinSdkVersion=${minSdkVersion}")
args(ruleNames)
}
}
}
class FlutterTask extends BaseFlutterTask {
@OutputDirectory

View File

@ -0,0 +1,152 @@
// Copyright 2014 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.
package com.flutter.gradle
import org.gradle.api.DefaultTask
import org.gradle.api.file.FileCollection
import org.gradle.api.logging.LogLevel
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.Optional
import org.gradle.api.tasks.OutputFiles
import java.io.File
abstract class BaseFlutterTask : DefaultTask() {
@Internal
var flutterRoot: File? = null
@Internal
var flutterExecutable: File? = null
@Input
var buildMode: String? = null
@Input
var minSdkVersion: Int? = null
@Optional
@Input
var localEngine: String? = null
@Optional
@Input
var localEngineHost: String? = null
@Optional
@Input
var localEngineSrcPath: String? = null
@Optional
@Input
var fastStart: Boolean? = null
@Input
var targetPath: String? = null
@Optional
@Input
var verbose: Boolean? = null
@Optional
@Input
var fileSystemRoots: Array<String>? = null
@Optional
@Input
var fileSystemScheme: String? = null
@Input
var trackWidgetCreation: Boolean? = null
@Optional
@Input
var targetPlatformValues: List<String>? = null
@Internal
var sourceDir: File? = null
@Internal
var intermediateDir: File? = null
@Optional
@Input
var frontendServerStarterPath: String? = null
@Optional
@Input
var extraFrontEndOptions: String? = null
@Optional
@Input
var extraGenSnapshotOptions: String? = null
@Optional
@Input
var splitDebugInfo: String? = null
@Optional
@Input
var treeShakeIcons: Boolean? = null
@Optional
@Input
var dartObfuscation: Boolean? = null
@Optional
@Input
var dartDefines: String? = null
@Optional
@Input
var bundleSkSLPath: String? = null
@Optional
@Input
var codeSizeDirectory: String? = null
@Optional
@Input
var performanceMeasurementFile: String? = null
@Optional
@Input
var deferredComponents: Boolean? = null
@Optional
@Input
var validateDeferredComponents: Boolean? = null
@Optional
@Input
var skipDependencyChecks: Boolean? = null
@Optional
@Input
var flavor: String? = null
/**
* Gets the dependency file(s) by calling [com.flutter.gradle.BaseFlutterTaskHelper.getDependenciesFiles].
*
* @return the dependency file(s) based on the current intermediate directory path.
*/
@OutputFiles
fun getDependenciesFiles(): FileCollection {
val helper = BaseFlutterTaskHelper(baseFlutterTask = this)
val depFiles = helper.getDependenciesFiles()
return depFiles
}
/**
* Builds a Flutter Android application bundle by verifying the Flutter source directory,
* creating an intermediate build directory if necessary, and running flutter assemble by
* configuring and executing with a set of build configurations.
*/
fun buildBundle() {
val helper = BaseFlutterTaskHelper(baseFlutterTask = this)
helper.checkPreConditions()
logging.captureStandardError(LogLevel.ERROR)
project.exec(helper.createExecSpecActionFromTask())
}
}

View File

@ -0,0 +1,149 @@
package com.flutter.gradle
import androidx.annotation.VisibleForTesting
import org.gradle.api.Action
import org.gradle.api.GradleException
import org.gradle.api.file.FileCollection
import org.gradle.api.tasks.OutputFiles
import org.gradle.process.ExecSpec
import java.nio.file.Paths
class BaseFlutterTaskHelper(
private var baseFlutterTask: BaseFlutterTask
) {
@VisibleForTesting
internal var gradleErrorMessage = "Invalid Flutter source directory: ${baseFlutterTask.sourceDir}"
/**
* Gets the dependency file(s) that tracks the dependencies or input files used for a specific
* Flutter build step based on the current intermediate directory.
*
* @return the dependency file(s) based on the current intermediate directory.
*/
@OutputFiles
@VisibleForTesting
internal fun getDependenciesFiles(): FileCollection {
var depfiles: FileCollection = baseFlutterTask.project.files()
// TODO(jesswon): During cleanup determine if .../flutter_build.d is ever a directory and refactor accordingly
// Includes all sources used in the flutter compilation.
depfiles += baseFlutterTask.project.files("${baseFlutterTask.intermediateDir}/flutter_build.d")
return depfiles
}
/**
* Checks precondition to ensures sourceDir is not null and is a directory. Also checks
* if intermediateDir is valid valid and creates it (and parent directories if needed) if invalid.
*
* @throws GradleException if sourceDir is null or is not a directory
*/
@VisibleForTesting
internal fun checkPreConditions() {
if (baseFlutterTask.sourceDir == null || !baseFlutterTask.sourceDir!!.isDirectory) {
throw GradleException(gradleErrorMessage)
}
baseFlutterTask.intermediateDir!!.mkdirs()
}
/**
* Computes the rule names for flutter assemble. To speed up builds that contain
* multiple ABIs, the target name is used to communicate which ones are required
* rather than the TargetPlatform. This allows multiple builds to share the same
* cache.
*
* @param baseFlutterTask is a BaseFlutterTask to access its properties
* @return the list of rule names for flutter assemble.
*/
@VisibleForTesting
internal fun generateRuleNames(baseFlutterTask: BaseFlutterTask): List<String> {
val ruleNames: List<String> =
when {
baseFlutterTask.buildMode == "debug" -> listOf("debug_android_application")
baseFlutterTask.deferredComponents!! ->
baseFlutterTask.targetPlatformValues!!
.map {
"android_aot_deferred_components_bundle_${baseFlutterTask.buildMode}_$it"
}
else -> baseFlutterTask.targetPlatformValues!!.map { "android_aot_bundle_${baseFlutterTask.buildMode}_$it" }
}
return ruleNames
}
/**
* Creates and configures the build processes of an Android Flutter application to be executed.
* The configuration includes setting the executable to the Flutter command-line tool (Flutter CLI),
* setting the working directory to the Flutter project's source directory, adding command-line arguments and build rules
* to configure various build options.
*
* @return an Action<ExecSpec> of build processes and options to be executed.
*/
@VisibleForTesting
internal fun createExecSpecActionFromTask(): Action<ExecSpec> =
Action<ExecSpec> {
executable(baseFlutterTask.flutterExecutable!!.absolutePath)
workingDir(baseFlutterTask.sourceDir)
baseFlutterTask.localEngine?.let {
args("--local-engine", it)
args("--local-engine-src-path", baseFlutterTask.localEngineSrcPath)
}
baseFlutterTask.localEngineHost?.let {
args("--local-engine-host", it)
}
if (baseFlutterTask.verbose == true) {
args("--verbose")
} else {
args("--quiet")
}
args("assemble")
args("--no-version-check")
args("--depfile", "${baseFlutterTask.intermediateDir}/flutter_build.d")
args("--output", "${baseFlutterTask.intermediateDir}")
baseFlutterTask.performanceMeasurementFile?.let {
args("--performance-measurement-file=$it")
}
if (!baseFlutterTask.fastStart!! || baseFlutterTask.buildMode != "debug") {
args("-dTargetFile=${baseFlutterTask.targetPath}")
} else {
args("-dTargetFile=${Paths.get(baseFlutterTask.flutterRoot!!.absolutePath, "examples", "splash", "lib", "main.dart")}")
}
args("-dTargetPlatform=android")
args("-dBuildMode=${baseFlutterTask.buildMode}")
baseFlutterTask.trackWidgetCreation?.let {
args("-dTrackWidgetCreation=$it")
}
baseFlutterTask.splitDebugInfo?.let {
args("-dSplitDebugInfo=$it")
}
if (baseFlutterTask.treeShakeIcons == true) {
args("-dTreeShakeIcons=true")
}
if (baseFlutterTask.dartObfuscation == true) {
args("-dDartObfuscation=true")
}
baseFlutterTask.dartDefines?.let {
args("--DartDefines=$it")
}
baseFlutterTask.bundleSkSLPath?.let {
args("-dBundleSkSLPath=$it")
}
baseFlutterTask.codeSizeDirectory?.let {
args("-dCodeSizeDirectory=$it")
}
baseFlutterTask.flavor?.let {
args("-dFlavor=$it")
}
baseFlutterTask.extraGenSnapshotOptions?.let {
args("--ExtraGenSnapshotOptions=$it")
}
baseFlutterTask.frontendServerStarterPath?.let {
args("-dFrontendServerStarterPath=$it")
}
baseFlutterTask.extraFrontEndOptions?.let {
args("--ExtraFrontEndOptions=$it")
}
args("-dAndroidArchs=${baseFlutterTask.targetPlatformValues!!.joinToString(" ")}")
args("-dMinSdkVersion=${baseFlutterTask.minSdkVersion}")
args(generateRuleNames(baseFlutterTask))
}
}

View File

@ -0,0 +1,544 @@
package com.flutter.gradle
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import org.gradle.api.GradleException
import org.gradle.process.ExecSpec
import org.gradle.process.ProcessForkOptions
import org.junit.jupiter.api.assertDoesNotThrow
import java.io.File
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
class BaseFlutterTaskHelperTest {
object BaseFlutterTaskPropertiesTest {
internal const val LOCAL_ENGINE_TEST = "android_debug_arm64"
internal const val LOCAL_ENGINE_HOST_TEST = "host_debug"
internal const val DART_DEFINES_TEST = "ENVIRONMENT=development"
internal const val FLAVOR_TEST = "dev"
internal const val EXTRA_FRONTEND_OPTIONS_TEST = "--enable-asserts"
internal const val EXTRA_GEN_SNAPSHOT_OPTIONS_TEST = "--debugger"
internal const val TARGET_PLATFORM_VALUES_JOINED_LIST = "android linux"
val MIN_SDK_VERSION_TEST = DependencyVersionChecker.warnMinSdkVersion
// Using File.separator to ensure all paths use platform-specific separators
internal val FLUTTER_ROOT_ABSOLUTE_PATH_TEST = "/path/to/flutter".replace("/", File.separator)
internal val FLUTTER_EXECUTABLE_ABSOLUTE_PATH_TEST = "/path/to/flutter/bin/flutter".replace("/", File.separator)
internal val LOCAL_ENGINE_SRC_PATH_TEST = "/path/to/flutter/engine/src".replace("/", File.separator)
internal val PERFORMANCE_MEASUREMENT_FILE_TEST = "/path/to/build/performance_file".replace("/", File.separator)
internal val FRONTEND_SERVER_STARTER_PATH_TEST = "/path/to/starter/script_file".replace("/", File.separator)
internal val SPLIT_DEBUG_INFO_TEST = "/path/to/build/debug_info_directory".replace("/", File.separator)
internal val CODE_SIZE_DIRECTORY_TEST = "/path/to/build/code_size_directory".replace("/", File.separator)
internal val BUNDLE_SK_SL_PATH_TEST = "/path/to/custom/shaders".replace("/", File.separator)
internal val FLUTTER_TARGET_FILE_PATH = "/path/to/flutter/examples/splash/lib/main.dart".replace("/", File.separator)
internal val FLUTTER_TARGET_PATH = "/path/to/main.dart".replace("/", File.separator)
internal val sourceDirTest = File("/path/to/working_directory".replace("/", File.separator))
internal val flutterRootTest = File("/path/to/flutter".replace("/", File.separator))
internal val flutterExecutableTest = File("/path/to/flutter/bin/flutter".replace("/", File.separator))
internal val intermediateDirFileTest = File("/path/to/build/app/intermediates/flutter/release".replace("/", File.separator))
internal val targetPlatformValuesList = listOf("android", "linux")
}
@Test
fun `checkPreConditions throws a GradleException when sourceDir is null`() {
val baseFlutterTask = mockk<BaseFlutterTask>()
every { baseFlutterTask.sourceDir } returns null
val helper = BaseFlutterTaskHelper(baseFlutterTask)
val gradleException =
assertFailsWith<GradleException> { helper.checkPreConditions() }
assert(
gradleException.message ==
helper.gradleErrorMessage
)
}
@Test
fun `checkPreConditions throws a GradleException when sourceDir is not a directory`() {
val baseFlutterTask = mockk<BaseFlutterTask>()
every { baseFlutterTask.sourceDir } returns BaseFlutterTaskPropertiesTest.sourceDirTest
every { baseFlutterTask.sourceDir!!.isDirectory } returns false
val helper = BaseFlutterTaskHelper(baseFlutterTask)
val gradleException =
assertFailsWith<GradleException> { helper.checkPreConditions() }
assert(
gradleException.message ==
helper.gradleErrorMessage
)
}
// TODO(jesswon): Add a test for intermediateDir is not valid during cleanup for handling NPEs.
@Test
fun `checkPreConditions does not throw a GradleException and intermediateDir is valid`() {
val baseFlutterTask = mockk<BaseFlutterTask>()
every { baseFlutterTask.sourceDir } returns BaseFlutterTaskPropertiesTest.sourceDirTest
every { baseFlutterTask.sourceDir!!.isDirectory } returns true
every { baseFlutterTask.intermediateDir } returns BaseFlutterTaskPropertiesTest.intermediateDirFileTest
// There is already an intermediate directory, so there is no need to create it.
every { baseFlutterTask.intermediateDir!!.mkdirs() } returns false
val helper = BaseFlutterTaskHelper(baseFlutterTask)
assertDoesNotThrow { helper.checkPreConditions() }
}
@Test
fun `generateRuleNames returns correct rule names when buildMode is debug`() {
val buildModeString = "debug"
val baseFlutterTask = mockk<BaseFlutterTask>()
// When baseFlutterTask.sourceDir is null, an exception is thrown. We mock its return value
// before creating a BaseFlutterTaskHelper object.
every { baseFlutterTask.sourceDir } returns BaseFlutterTaskPropertiesTest.sourceDirTest
every { baseFlutterTask.buildMode } returns buildModeString
val helper = BaseFlutterTaskHelper(baseFlutterTask)
val ruleNamesList = helper.generateRuleNames(baseFlutterTask)
assertEquals(ruleNamesList, listOf("debug_android_application"))
}
@Test
fun `generateRuleNames returns correct rule names when buildMode is not debug and deferredComponents is true`() {
val buildModeString = "release"
val baseFlutterTask = mockk<BaseFlutterTask>()
// When baseFlutterTask.sourceDir is null, an exception is thrown. We mock its return value
// before creating a BaseFlutterTaskHelper object.
every { baseFlutterTask.sourceDir } returns BaseFlutterTaskPropertiesTest.sourceDirTest
every { baseFlutterTask.buildMode } returns buildModeString
every { baseFlutterTask.deferredComponents } returns true
every { baseFlutterTask.targetPlatformValues } returns BaseFlutterTaskPropertiesTest.targetPlatformValuesList
val helper = BaseFlutterTaskHelper(baseFlutterTask)
val ruleNamesList = helper.generateRuleNames(baseFlutterTask)
assertEquals(
ruleNamesList,
listOf(
"android_aot_deferred_components_bundle_release_android",
"android_aot_deferred_components_bundle_release_linux"
)
)
}
@Test
fun `generateRuleNames returns correct rule names when buildMode is not debug and deferredComponents is false`() {
val buildModeString = "release"
val baseFlutterTask = mockk<BaseFlutterTask>()
// When baseFlutterTask.sourceDir is null, an exception is thrown. We mock its return value
// before creating a BaseFlutterTaskHelper object.
every { baseFlutterTask.sourceDir } returns BaseFlutterTaskPropertiesTest.sourceDirTest
every { baseFlutterTask.buildMode } returns buildModeString
every { baseFlutterTask.deferredComponents } returns false
every { baseFlutterTask.targetPlatformValues } returns BaseFlutterTaskPropertiesTest.targetPlatformValuesList
val helper = BaseFlutterTaskHelper(baseFlutterTask)
val ruleNamesList = helper.generateRuleNames(baseFlutterTask)
assertEquals(
ruleNamesList,
listOf(
"android_aot_bundle_release_android",
"android_aot_bundle_release_linux"
)
)
}
@Test
fun `createSpecActionFromTask creates the correct build configurations when properties are non-null`() {
val buildModeString = "debug"
// Create necessary mocks.
val baseFlutterTask = mockk<BaseFlutterTask>()
val mockExecSpec = mockk<ExecSpec>()
val mockProcessForkOptions = mockk<ProcessForkOptions>()
// When baseFlutterTask.sourceDir is null, an exception is thrown. We mock its return value
// before creating a BaseFlutterTaskHelper object.
every { baseFlutterTask.sourceDir } returns BaseFlutterTaskPropertiesTest.sourceDirTest
val helper = BaseFlutterTaskHelper(baseFlutterTask)
val execSpecActionFromTask = helper.createExecSpecActionFromTask()
// Mock return values of properties.
every { baseFlutterTask.flutterExecutable } returns BaseFlutterTaskPropertiesTest.flutterExecutableTest
every {
baseFlutterTask.flutterExecutable!!.absolutePath
} returns BaseFlutterTaskPropertiesTest.FLUTTER_EXECUTABLE_ABSOLUTE_PATH_TEST
every { baseFlutterTask.localEngine } returns BaseFlutterTaskPropertiesTest.LOCAL_ENGINE_TEST
every { baseFlutterTask.localEngineSrcPath } returns BaseFlutterTaskPropertiesTest.LOCAL_ENGINE_SRC_PATH_TEST
every { baseFlutterTask.localEngineHost } returns BaseFlutterTaskPropertiesTest.LOCAL_ENGINE_HOST_TEST
every { baseFlutterTask.verbose } returns true
every { baseFlutterTask.intermediateDir } returns BaseFlutterTaskPropertiesTest.intermediateDirFileTest
every { baseFlutterTask.performanceMeasurementFile } returns BaseFlutterTaskPropertiesTest.PERFORMANCE_MEASUREMENT_FILE_TEST
every { baseFlutterTask.fastStart } returns true
every { baseFlutterTask.buildMode } returns buildModeString
every { baseFlutterTask.flutterRoot } returns BaseFlutterTaskPropertiesTest.flutterRootTest
every { baseFlutterTask.flutterRoot!!.absolutePath } returns BaseFlutterTaskPropertiesTest.FLUTTER_ROOT_ABSOLUTE_PATH_TEST
every { baseFlutterTask.trackWidgetCreation } returns true
every { baseFlutterTask.splitDebugInfo } returns BaseFlutterTaskPropertiesTest.SPLIT_DEBUG_INFO_TEST
every { baseFlutterTask.treeShakeIcons } returns true
every { baseFlutterTask.dartObfuscation } returns true
every { baseFlutterTask.dartDefines } returns BaseFlutterTaskPropertiesTest.DART_DEFINES_TEST
every { baseFlutterTask.bundleSkSLPath } returns BaseFlutterTaskPropertiesTest.BUNDLE_SK_SL_PATH_TEST
every { baseFlutterTask.codeSizeDirectory } returns BaseFlutterTaskPropertiesTest.CODE_SIZE_DIRECTORY_TEST
every { baseFlutterTask.flavor } returns BaseFlutterTaskPropertiesTest.FLAVOR_TEST
every { baseFlutterTask.extraGenSnapshotOptions } returns BaseFlutterTaskPropertiesTest.EXTRA_GEN_SNAPSHOT_OPTIONS_TEST
every { baseFlutterTask.frontendServerStarterPath } returns BaseFlutterTaskPropertiesTest.FRONTEND_SERVER_STARTER_PATH_TEST
every { baseFlutterTask.extraFrontEndOptions } returns BaseFlutterTaskPropertiesTest.EXTRA_FRONTEND_OPTIONS_TEST
every { baseFlutterTask.targetPlatformValues } returns BaseFlutterTaskPropertiesTest.targetPlatformValuesList
every { baseFlutterTask.minSdkVersion } returns BaseFlutterTaskPropertiesTest.MIN_SDK_VERSION_TEST
// Mock the method calls. We collapse all the args mock calls into four calls.
every { mockExecSpec.executable(any<String>()) } returns mockExecSpec
every { mockExecSpec.workingDir(any()) } returns mockProcessForkOptions
every { mockExecSpec.args(any<String>(), any()) } returns mockExecSpec
every { mockExecSpec.args(any<String>(), any()) } returns mockExecSpec
every { mockExecSpec.args(any<String>()) } returns mockExecSpec
every { mockExecSpec.args(any<List<String>>()) } returns mockExecSpec
// Generate rule names for verification and can only be generated after buildMode is mocked.
val ruleNamesList: List<String> = helper.generateRuleNames(baseFlutterTask)
// The exec function will be deprecated in gradle 8.11 and will be removed in gradle 9.0
// https://docs.gradle.org/current/kotlin-dsl/gradle/org.gradle.kotlin.dsl/-kotlin-script/exec.html?query=abstract%20fun%20exec(configuration:%20Action%3CExecSpec%3E):%20ExecResult
// The actions are executed.
execSpecActionFromTask.execute(mockExecSpec)
// After execution, we verify the functions are actually being
// called with the expected argument passed in.
verify { mockExecSpec.executable(BaseFlutterTaskPropertiesTest.FLUTTER_EXECUTABLE_ABSOLUTE_PATH_TEST) }
verify { mockExecSpec.workingDir(BaseFlutterTaskPropertiesTest.sourceDirTest) }
verify { mockExecSpec.args("--local-engine", BaseFlutterTaskPropertiesTest.LOCAL_ENGINE_TEST) }
verify { mockExecSpec.args("--local-engine-src-path", BaseFlutterTaskPropertiesTest.LOCAL_ENGINE_SRC_PATH_TEST) }
verify { mockExecSpec.args("--local-engine-host", BaseFlutterTaskPropertiesTest.LOCAL_ENGINE_HOST_TEST) }
verify { mockExecSpec.args("--verbose") }
verify { mockExecSpec.args("assemble") }
verify { mockExecSpec.args("--no-version-check") }
verify { mockExecSpec.args("--depfile", "${BaseFlutterTaskPropertiesTest.intermediateDirFileTest}/flutter_build.d") }
verify { mockExecSpec.args("--output", "${BaseFlutterTaskPropertiesTest.intermediateDirFileTest}") }
verify { mockExecSpec.args("--performance-measurement-file=${BaseFlutterTaskPropertiesTest.PERFORMANCE_MEASUREMENT_FILE_TEST}") }
verify { mockExecSpec.args("-dTargetFile=${BaseFlutterTaskPropertiesTest.FLUTTER_TARGET_FILE_PATH}") }
verify { mockExecSpec.args("-dTargetPlatform=android") }
verify { mockExecSpec.args("-dBuildMode=$buildModeString") }
verify { mockExecSpec.args("-dTrackWidgetCreation=${true}") }
verify { mockExecSpec.args("-dSplitDebugInfo=${BaseFlutterTaskPropertiesTest.SPLIT_DEBUG_INFO_TEST}") }
verify { mockExecSpec.args("-dTreeShakeIcons=true") }
verify { mockExecSpec.args("-dDartObfuscation=true") }
verify { mockExecSpec.args("--DartDefines=${BaseFlutterTaskPropertiesTest.DART_DEFINES_TEST}") }
verify { mockExecSpec.args("-dBundleSkSLPath=${BaseFlutterTaskPropertiesTest.BUNDLE_SK_SL_PATH_TEST}") }
verify { mockExecSpec.args("-dCodeSizeDirectory=${BaseFlutterTaskPropertiesTest.CODE_SIZE_DIRECTORY_TEST}") }
verify { mockExecSpec.args("-dFlavor=${BaseFlutterTaskPropertiesTest.FLAVOR_TEST}") }
verify { mockExecSpec.args("--ExtraGenSnapshotOptions=${BaseFlutterTaskPropertiesTest.EXTRA_GEN_SNAPSHOT_OPTIONS_TEST}") }
verify { mockExecSpec.args("-dFrontendServerStarterPath=${BaseFlutterTaskPropertiesTest.FRONTEND_SERVER_STARTER_PATH_TEST}") }
verify { mockExecSpec.args("--ExtraFrontEndOptions=${BaseFlutterTaskPropertiesTest.EXTRA_FRONTEND_OPTIONS_TEST}") }
verify { mockExecSpec.args("-dAndroidArchs=${BaseFlutterTaskPropertiesTest.TARGET_PLATFORM_VALUES_JOINED_LIST}") }
verify { mockExecSpec.args("-dMinSdkVersion=${BaseFlutterTaskPropertiesTest.MIN_SDK_VERSION_TEST}") }
verify { mockExecSpec.args(ruleNamesList) }
}
@Test
fun `createSpecActionFromTask creates the correct build configurations when properties are null`() {
val buildModeString = "debug"
// Create necessary mocks.
val baseFlutterTask = mockk<BaseFlutterTask>()
val mockExecSpec = mockk<ExecSpec>()
val mockProcessForkOptions = mockk<ProcessForkOptions>()
// When baseFlutterTask.sourceDir is null, an exception is thrown. We mock its return value
// before creating a BaseFlutterTaskHelper object.
every { baseFlutterTask.sourceDir } returns BaseFlutterTaskPropertiesTest.sourceDirTest
val helper = BaseFlutterTaskHelper(baseFlutterTask)
val execSpecActionFromTask = helper.createExecSpecActionFromTask()
// Mock return values of properties.
every { baseFlutterTask.flutterExecutable } returns BaseFlutterTaskPropertiesTest.flutterExecutableTest
every {
baseFlutterTask.flutterExecutable!!.absolutePath
} returns BaseFlutterTaskPropertiesTest.FLUTTER_EXECUTABLE_ABSOLUTE_PATH_TEST
every { baseFlutterTask.localEngine } returns null
every { baseFlutterTask.localEngineSrcPath } returns null
every { baseFlutterTask.localEngineHost } returns null
every { baseFlutterTask.verbose } returns true
every { baseFlutterTask.intermediateDir } returns BaseFlutterTaskPropertiesTest.intermediateDirFileTest
every { baseFlutterTask.performanceMeasurementFile } returns null
every { baseFlutterTask.fastStart } returns true
every { baseFlutterTask.buildMode } returns buildModeString
every { baseFlutterTask.flutterRoot } returns BaseFlutterTaskPropertiesTest.flutterRootTest
every { baseFlutterTask.flutterRoot!!.absolutePath } returns BaseFlutterTaskPropertiesTest.FLUTTER_ROOT_ABSOLUTE_PATH_TEST
every { baseFlutterTask.trackWidgetCreation } returns null
every { baseFlutterTask.splitDebugInfo } returns null
every { baseFlutterTask.treeShakeIcons } returns null
every { baseFlutterTask.dartObfuscation } returns null
every { baseFlutterTask.dartDefines } returns null
every { baseFlutterTask.bundleSkSLPath } returns null
every { baseFlutterTask.codeSizeDirectory } returns null
every { baseFlutterTask.flavor } returns null
every { baseFlutterTask.extraGenSnapshotOptions } returns null
every { baseFlutterTask.frontendServerStarterPath } returns null
every { baseFlutterTask.extraFrontEndOptions } returns null
every { baseFlutterTask.targetPlatformValues } returns BaseFlutterTaskPropertiesTest.targetPlatformValuesList
every { baseFlutterTask.minSdkVersion } returns BaseFlutterTaskPropertiesTest.MIN_SDK_VERSION_TEST
// Mock the method calls. We collapse all the args mock calls into four calls.
every { mockExecSpec.executable(any<String>()) } returns mockExecSpec
every { mockExecSpec.workingDir(any()) } returns mockProcessForkOptions
every { mockExecSpec.args(any<String>(), any()) } returns mockExecSpec
every { mockExecSpec.args(any<String>(), any()) } returns mockExecSpec
every { mockExecSpec.args(any<String>()) } returns mockExecSpec
every { mockExecSpec.args(any<List<String>>()) } returns mockExecSpec
// Generate rule names for verification and can only be generated after buildMode is mocked.
val ruleNamesList: List<String> = helper.generateRuleNames(baseFlutterTask)
// The exec function will be deprecated in gradle 8.11 and will be removed in gradle 9.0
// https://docs.gradle.org/current/kotlin-dsl/gradle/org.gradle.kotlin.dsl/-kotlin-script/exec.html?query=abstract%20fun%20exec(configuration:%20Action%3CExecSpec%3E):%20ExecResult
// The actions are executed.
execSpecActionFromTask.execute(mockExecSpec)
// After execution, we verify the functions are actually being
// called with the expected argument passed in.
verify { mockExecSpec.executable(BaseFlutterTaskPropertiesTest.FLUTTER_EXECUTABLE_ABSOLUTE_PATH_TEST) }
verify { mockExecSpec.workingDir(BaseFlutterTaskPropertiesTest.sourceDirTest) }
verify { mockExecSpec.args("--verbose") }
verify { mockExecSpec.args("assemble") }
verify { mockExecSpec.args("--no-version-check") }
verify { mockExecSpec.args("--depfile", "${BaseFlutterTaskPropertiesTest.intermediateDirFileTest}/flutter_build.d") }
verify { mockExecSpec.args("--output", "${BaseFlutterTaskPropertiesTest.intermediateDirFileTest}") }
verify { mockExecSpec.args("-dTargetFile=${BaseFlutterTaskPropertiesTest.FLUTTER_TARGET_FILE_PATH}") }
verify { mockExecSpec.args("-dTargetPlatform=android") }
verify { mockExecSpec.args("-dBuildMode=$buildModeString") }
verify { mockExecSpec.args("-dAndroidArchs=${BaseFlutterTaskPropertiesTest.TARGET_PLATFORM_VALUES_JOINED_LIST}") }
verify { mockExecSpec.args("-dMinSdkVersion=${BaseFlutterTaskPropertiesTest.MIN_SDK_VERSION_TEST}") }
verify { mockExecSpec.args(ruleNamesList) }
}
@Test
fun `createSpecActionFromTask creates the correct build configurations when verbose is false and fastStart is false`() {
val buildModeString = "debug"
// Create necessary mocks.
val baseFlutterTask = mockk<BaseFlutterTask>()
val mockExecSpec = mockk<ExecSpec>()
val mockProcessForkOptions = mockk<ProcessForkOptions>()
// When baseFlutterTask.sourceDir is null, an exception is thrown. We mock its return value
// before creating a BaseFlutterTaskHelper object.
every { baseFlutterTask.sourceDir } returns BaseFlutterTaskPropertiesTest.sourceDirTest
val helper = BaseFlutterTaskHelper(baseFlutterTask)
val execSpecActionFromTask = helper.createExecSpecActionFromTask()
// Mock return values of properties.
every { baseFlutterTask.flutterExecutable } returns BaseFlutterTaskPropertiesTest.flutterExecutableTest
every {
baseFlutterTask.flutterExecutable!!.absolutePath
} returns BaseFlutterTaskPropertiesTest.FLUTTER_EXECUTABLE_ABSOLUTE_PATH_TEST
every { baseFlutterTask.localEngine } returns BaseFlutterTaskPropertiesTest.LOCAL_ENGINE_TEST
every { baseFlutterTask.localEngineSrcPath } returns BaseFlutterTaskPropertiesTest.LOCAL_ENGINE_SRC_PATH_TEST
every { baseFlutterTask.localEngineHost } returns BaseFlutterTaskPropertiesTest.LOCAL_ENGINE_HOST_TEST
every { baseFlutterTask.verbose } returns false
every { baseFlutterTask.intermediateDir } returns BaseFlutterTaskPropertiesTest.intermediateDirFileTest
every { baseFlutterTask.performanceMeasurementFile } returns BaseFlutterTaskPropertiesTest.PERFORMANCE_MEASUREMENT_FILE_TEST
every { baseFlutterTask.fastStart } returns false
every { baseFlutterTask.buildMode } returns buildModeString
every { baseFlutterTask.targetPath } returns BaseFlutterTaskPropertiesTest.FLUTTER_TARGET_PATH
every { baseFlutterTask.trackWidgetCreation } returns true
every { baseFlutterTask.splitDebugInfo } returns BaseFlutterTaskPropertiesTest.SPLIT_DEBUG_INFO_TEST
every { baseFlutterTask.treeShakeIcons } returns true
every { baseFlutterTask.dartObfuscation } returns true
every { baseFlutterTask.dartDefines } returns BaseFlutterTaskPropertiesTest.DART_DEFINES_TEST
every { baseFlutterTask.bundleSkSLPath } returns BaseFlutterTaskPropertiesTest.BUNDLE_SK_SL_PATH_TEST
every { baseFlutterTask.codeSizeDirectory } returns BaseFlutterTaskPropertiesTest.CODE_SIZE_DIRECTORY_TEST
every { baseFlutterTask.flavor } returns BaseFlutterTaskPropertiesTest.FLAVOR_TEST
every { baseFlutterTask.extraGenSnapshotOptions } returns BaseFlutterTaskPropertiesTest.EXTRA_GEN_SNAPSHOT_OPTIONS_TEST
every { baseFlutterTask.frontendServerStarterPath } returns BaseFlutterTaskPropertiesTest.FRONTEND_SERVER_STARTER_PATH_TEST
every { baseFlutterTask.extraFrontEndOptions } returns BaseFlutterTaskPropertiesTest.EXTRA_FRONTEND_OPTIONS_TEST
every { baseFlutterTask.targetPlatformValues } returns BaseFlutterTaskPropertiesTest.targetPlatformValuesList
every { baseFlutterTask.minSdkVersion } returns BaseFlutterTaskPropertiesTest.MIN_SDK_VERSION_TEST
// Mock the method calls. We collapse all the args mock calls into four calls.
every { mockExecSpec.executable(any<String>()) } returns mockExecSpec
every { mockExecSpec.workingDir(any()) } returns mockProcessForkOptions
every { mockExecSpec.args(any<String>(), any()) } returns mockExecSpec
every { mockExecSpec.args(any<String>(), any()) } returns mockExecSpec
every { mockExecSpec.args(any<String>()) } returns mockExecSpec
every { mockExecSpec.args(any<List<String>>()) } returns mockExecSpec
// Generate rule names for verification and can only be generated after buildMode is mocked.
val ruleNamesList: List<String> = helper.generateRuleNames(baseFlutterTask)
// The exec function will be deprecated in gradle 8.11 and will be removed in gradle 9.0
// https://docs.gradle.org/current/kotlin-dsl/gradle/org.gradle.kotlin.dsl/-kotlin-script/exec.html?query=abstract%20fun%20exec(configuration:%20Action%3CExecSpec%3E):%20ExecResult
// The actions are executed.
execSpecActionFromTask.execute(mockExecSpec)
// After execution, we verify the functions are actually being
// called with the expected argument passed in.
verify { mockExecSpec.executable(BaseFlutterTaskPropertiesTest.FLUTTER_EXECUTABLE_ABSOLUTE_PATH_TEST) }
verify { mockExecSpec.workingDir(BaseFlutterTaskPropertiesTest.sourceDirTest) }
verify { mockExecSpec.args("--local-engine", BaseFlutterTaskPropertiesTest.LOCAL_ENGINE_TEST) }
verify { mockExecSpec.args("--local-engine-src-path", BaseFlutterTaskPropertiesTest.LOCAL_ENGINE_SRC_PATH_TEST) }
verify { mockExecSpec.args("--local-engine-host", BaseFlutterTaskPropertiesTest.LOCAL_ENGINE_HOST_TEST) }
verify { mockExecSpec.args("--quiet") }
verify { mockExecSpec.args("assemble") }
verify { mockExecSpec.args("--no-version-check") }
verify { mockExecSpec.args("--depfile", "${BaseFlutterTaskPropertiesTest.intermediateDirFileTest}/flutter_build.d") }
verify { mockExecSpec.args("--output", "${BaseFlutterTaskPropertiesTest.intermediateDirFileTest}") }
verify { mockExecSpec.args("--performance-measurement-file=${BaseFlutterTaskPropertiesTest.PERFORMANCE_MEASUREMENT_FILE_TEST}") }
verify { mockExecSpec.args("-dTargetFile=${BaseFlutterTaskPropertiesTest.FLUTTER_TARGET_PATH}") }
verify { mockExecSpec.args("-dTargetPlatform=android") }
verify { mockExecSpec.args("-dBuildMode=$buildModeString") }
verify { mockExecSpec.args("-dTrackWidgetCreation=${true}") }
verify { mockExecSpec.args("-dSplitDebugInfo=${BaseFlutterTaskPropertiesTest.SPLIT_DEBUG_INFO_TEST}") }
verify { mockExecSpec.args("-dTreeShakeIcons=true") }
verify { mockExecSpec.args("-dDartObfuscation=true") }
verify { mockExecSpec.args("--DartDefines=${BaseFlutterTaskPropertiesTest.DART_DEFINES_TEST}") }
verify { mockExecSpec.args("-dBundleSkSLPath=${BaseFlutterTaskPropertiesTest.BUNDLE_SK_SL_PATH_TEST}") }
verify { mockExecSpec.args("-dCodeSizeDirectory=${BaseFlutterTaskPropertiesTest.CODE_SIZE_DIRECTORY_TEST}") }
verify { mockExecSpec.args("-dFlavor=${BaseFlutterTaskPropertiesTest.FLAVOR_TEST}") }
verify { mockExecSpec.args("--ExtraGenSnapshotOptions=${BaseFlutterTaskPropertiesTest.EXTRA_GEN_SNAPSHOT_OPTIONS_TEST}") }
verify { mockExecSpec.args("-dFrontendServerStarterPath=${BaseFlutterTaskPropertiesTest.FRONTEND_SERVER_STARTER_PATH_TEST}") }
verify { mockExecSpec.args("--ExtraFrontEndOptions=${BaseFlutterTaskPropertiesTest.EXTRA_FRONTEND_OPTIONS_TEST}") }
verify { mockExecSpec.args("-dAndroidArchs=${BaseFlutterTaskPropertiesTest.TARGET_PLATFORM_VALUES_JOINED_LIST}") }
verify { mockExecSpec.args("-dMinSdkVersion=${BaseFlutterTaskPropertiesTest.MIN_SDK_VERSION_TEST}") }
verify { mockExecSpec.args(ruleNamesList) }
}
@Test
fun `createSpecActionFromTask creates the correct build configurations when fastStart is true and buildMode is not debug`() {
val buildModeString = "release"
// Create necessary mocks.
val baseFlutterTask = mockk<BaseFlutterTask>()
val mockExecSpec = mockk<ExecSpec>()
val mockProcessForkOptions = mockk<ProcessForkOptions>()
// When baseFlutterTask.sourceDir is null, an exception is thrown. We mock its return value
// before creating a BaseFlutterTaskHelper object.
every { baseFlutterTask.sourceDir } returns BaseFlutterTaskPropertiesTest.sourceDirTest
val helper = BaseFlutterTaskHelper(baseFlutterTask)
val execSpecActionFromTask = helper.createExecSpecActionFromTask()
// Mock return values of properties.
every { baseFlutterTask.flutterExecutable } returns BaseFlutterTaskPropertiesTest.flutterExecutableTest
every {
baseFlutterTask.flutterExecutable!!.absolutePath
} returns BaseFlutterTaskPropertiesTest.FLUTTER_EXECUTABLE_ABSOLUTE_PATH_TEST
every { baseFlutterTask.localEngine } returns BaseFlutterTaskPropertiesTest.LOCAL_ENGINE_TEST
every { baseFlutterTask.localEngineSrcPath } returns BaseFlutterTaskPropertiesTest.LOCAL_ENGINE_SRC_PATH_TEST
every { baseFlutterTask.localEngineHost } returns BaseFlutterTaskPropertiesTest.LOCAL_ENGINE_HOST_TEST
every { baseFlutterTask.verbose } returns true
every { baseFlutterTask.intermediateDir } returns BaseFlutterTaskPropertiesTest.intermediateDirFileTest
every { baseFlutterTask.performanceMeasurementFile } returns BaseFlutterTaskPropertiesTest.PERFORMANCE_MEASUREMENT_FILE_TEST
every { baseFlutterTask.fastStart } returns true
every { baseFlutterTask.buildMode } returns buildModeString
every { baseFlutterTask.targetPath } returns BaseFlutterTaskPropertiesTest.FLUTTER_TARGET_PATH
every { baseFlutterTask.trackWidgetCreation } returns true
every { baseFlutterTask.splitDebugInfo } returns BaseFlutterTaskPropertiesTest.SPLIT_DEBUG_INFO_TEST
every { baseFlutterTask.treeShakeIcons } returns true
every { baseFlutterTask.dartObfuscation } returns true
every { baseFlutterTask.dartDefines } returns BaseFlutterTaskPropertiesTest.DART_DEFINES_TEST
every { baseFlutterTask.bundleSkSLPath } returns BaseFlutterTaskPropertiesTest.BUNDLE_SK_SL_PATH_TEST
every { baseFlutterTask.codeSizeDirectory } returns BaseFlutterTaskPropertiesTest.CODE_SIZE_DIRECTORY_TEST
every { baseFlutterTask.flavor } returns BaseFlutterTaskPropertiesTest.FLAVOR_TEST
every { baseFlutterTask.extraGenSnapshotOptions } returns BaseFlutterTaskPropertiesTest.EXTRA_GEN_SNAPSHOT_OPTIONS_TEST
every { baseFlutterTask.frontendServerStarterPath } returns BaseFlutterTaskPropertiesTest.FRONTEND_SERVER_STARTER_PATH_TEST
every { baseFlutterTask.extraFrontEndOptions } returns BaseFlutterTaskPropertiesTest.EXTRA_FRONTEND_OPTIONS_TEST
every { baseFlutterTask.deferredComponents } returns true
every { baseFlutterTask.targetPlatformValues } returns BaseFlutterTaskPropertiesTest.targetPlatformValuesList
every { baseFlutterTask.minSdkVersion } returns BaseFlutterTaskPropertiesTest.MIN_SDK_VERSION_TEST
// Mock the method calls. We collapse all the args mock calls into four calls.
every { mockExecSpec.executable(any<String>()) } returns mockExecSpec
every { mockExecSpec.workingDir(any()) } returns mockProcessForkOptions
every { mockExecSpec.args(any<String>(), any()) } returns mockExecSpec
every { mockExecSpec.args(any<String>(), any()) } returns mockExecSpec
every { mockExecSpec.args(any<String>()) } returns mockExecSpec
every { mockExecSpec.args(any<List<String>>()) } returns mockExecSpec
// Generate rule names for verification and can only be generated after buildMode is mocked.
val ruleNamesList: List<String> = helper.generateRuleNames(baseFlutterTask)
// The exec function will be deprecated in gradle 8.11 and will be removed in gradle 9.0
// https://docs.gradle.org/current/kotlin-dsl/gradle/org.gradle.kotlin.dsl/-kotlin-script/exec.html?query=abstract%20fun%20exec(configuration:%20Action%3CExecSpec%3E):%20ExecResult
// The actions are executed.
execSpecActionFromTask.execute(mockExecSpec)
// After execution, we verify the functions are actually being
// called with the expected argument passed in.
verify { mockExecSpec.executable(BaseFlutterTaskPropertiesTest.FLUTTER_EXECUTABLE_ABSOLUTE_PATH_TEST) }
verify { mockExecSpec.workingDir(BaseFlutterTaskPropertiesTest.sourceDirTest) }
verify { mockExecSpec.args("--local-engine", BaseFlutterTaskPropertiesTest.LOCAL_ENGINE_TEST) }
verify { mockExecSpec.args("--local-engine-src-path", BaseFlutterTaskPropertiesTest.LOCAL_ENGINE_SRC_PATH_TEST) }
verify { mockExecSpec.args("--local-engine-host", BaseFlutterTaskPropertiesTest.LOCAL_ENGINE_HOST_TEST) }
verify { mockExecSpec.args("--verbose") }
verify { mockExecSpec.args("assemble") }
verify { mockExecSpec.args("--no-version-check") }
verify { mockExecSpec.args("--depfile", "${BaseFlutterTaskPropertiesTest.intermediateDirFileTest}/flutter_build.d") }
verify { mockExecSpec.args("--output", "${BaseFlutterTaskPropertiesTest.intermediateDirFileTest}") }
verify { mockExecSpec.args("--performance-measurement-file=${BaseFlutterTaskPropertiesTest.PERFORMANCE_MEASUREMENT_FILE_TEST}") }
verify { mockExecSpec.args("-dTargetFile=${BaseFlutterTaskPropertiesTest.FLUTTER_TARGET_PATH}") }
verify { mockExecSpec.args("-dTargetPlatform=android") }
verify { mockExecSpec.args("-dBuildMode=$buildModeString") }
verify { mockExecSpec.args("-dTrackWidgetCreation=${true}") }
verify { mockExecSpec.args("-dSplitDebugInfo=${BaseFlutterTaskPropertiesTest.SPLIT_DEBUG_INFO_TEST}") }
verify { mockExecSpec.args("-dTreeShakeIcons=true") }
verify { mockExecSpec.args("-dDartObfuscation=true") }
verify { mockExecSpec.args("--DartDefines=${BaseFlutterTaskPropertiesTest.DART_DEFINES_TEST}") }
verify { mockExecSpec.args("-dBundleSkSLPath=${BaseFlutterTaskPropertiesTest.BUNDLE_SK_SL_PATH_TEST}") }
verify { mockExecSpec.args("-dCodeSizeDirectory=${BaseFlutterTaskPropertiesTest.CODE_SIZE_DIRECTORY_TEST}") }
verify { mockExecSpec.args("-dFlavor=${BaseFlutterTaskPropertiesTest.FLAVOR_TEST}") }
verify { mockExecSpec.args("--ExtraGenSnapshotOptions=${BaseFlutterTaskPropertiesTest.EXTRA_GEN_SNAPSHOT_OPTIONS_TEST}") }
verify { mockExecSpec.args("-dFrontendServerStarterPath=${BaseFlutterTaskPropertiesTest.FRONTEND_SERVER_STARTER_PATH_TEST}") }
verify { mockExecSpec.args("--ExtraFrontEndOptions=${BaseFlutterTaskPropertiesTest.EXTRA_FRONTEND_OPTIONS_TEST}") }
verify { mockExecSpec.args("-dAndroidArchs=${BaseFlutterTaskPropertiesTest.TARGET_PLATFORM_VALUES_JOINED_LIST}") }
verify { mockExecSpec.args("-dMinSdkVersion=${BaseFlutterTaskPropertiesTest.MIN_SDK_VERSION_TEST}") }
verify { mockExecSpec.args(ruleNamesList) }
}
}

View File

@ -0,0 +1,147 @@
package com.flutter.gradle
import com.flutter.gradle.BaseFlutterTaskHelperTest.BaseFlutterTaskPropertiesTest
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import org.gradle.api.Project
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.process.ExecSpec
import org.gradle.process.ProcessForkOptions
import org.junit.jupiter.api.assertDoesNotThrow
import kotlin.test.Test
class BaseFlutterTaskTest {
@Test
fun `getDependencyFiles returns a FileCollection of dependency file(s)`() {
val baseFlutterTask = mockk<BaseFlutterTask>()
val project = mockk<Project>()
val configFileCollection = mockk<ConfigurableFileCollection>()
every { baseFlutterTask.sourceDir } returns BaseFlutterTaskPropertiesTest.sourceDirTest
val helper = BaseFlutterTaskHelper(baseFlutterTask)
every { baseFlutterTask.project } returns project
every { baseFlutterTask.intermediateDir } returns BaseFlutterTaskPropertiesTest.intermediateDirFileTest
val projectIntermediary = baseFlutterTask.project
val interDirFile = baseFlutterTask.intermediateDir
every { projectIntermediary.files() } returns configFileCollection
every { projectIntermediary.files("$interDirFile/flutter_build.d") } returns configFileCollection
every { configFileCollection.plus(configFileCollection) } returns configFileCollection
helper.getDependenciesFiles()
verify { projectIntermediary.files() }
verify { projectIntermediary.files("${BaseFlutterTaskPropertiesTest.intermediateDirFileTest}/flutter_build.d") }
}
@Test
fun `buildBundle builds a Flutter application bundle for Android`() {
val buildModeString = "debug"
// Create necessary mocks.
val baseFlutterTask = mockk<BaseFlutterTask>()
val mockExecSpec = mockk<ExecSpec>()
val mockProcessForkOptions = mockk<ProcessForkOptions>()
// Check preconditions
every { baseFlutterTask.sourceDir } returns BaseFlutterTaskPropertiesTest.sourceDirTest
every { baseFlutterTask.sourceDir!!.isDirectory } returns true
every { baseFlutterTask.intermediateDir } returns BaseFlutterTaskPropertiesTest.intermediateDirFileTest
every { baseFlutterTask.intermediateDir!!.mkdirs() } returns false
val helper = BaseFlutterTaskHelper(baseFlutterTask)
assertDoesNotThrow { helper.checkPreConditions() }
// Create action to be executed.
val execSpecActionFromTask = helper.createExecSpecActionFromTask()
// Mock return values of properties.
every { baseFlutterTask.flutterExecutable } returns BaseFlutterTaskPropertiesTest.flutterExecutableTest
every {
baseFlutterTask.flutterExecutable!!.absolutePath
} returns BaseFlutterTaskPropertiesTest.FLUTTER_EXECUTABLE_ABSOLUTE_PATH_TEST
every { baseFlutterTask.sourceDir } returns BaseFlutterTaskPropertiesTest.sourceDirTest
every { baseFlutterTask.localEngine } returns BaseFlutterTaskPropertiesTest.LOCAL_ENGINE_TEST
every { baseFlutterTask.localEngineSrcPath } returns BaseFlutterTaskPropertiesTest.LOCAL_ENGINE_SRC_PATH_TEST
every { baseFlutterTask.localEngineHost } returns BaseFlutterTaskPropertiesTest.LOCAL_ENGINE_HOST_TEST
every { baseFlutterTask.verbose } returns true
every { baseFlutterTask.intermediateDir } returns BaseFlutterTaskPropertiesTest.intermediateDirFileTest
every { baseFlutterTask.performanceMeasurementFile } returns BaseFlutterTaskPropertiesTest.PERFORMANCE_MEASUREMENT_FILE_TEST
every { baseFlutterTask.fastStart } returns true
every { baseFlutterTask.buildMode } returns buildModeString
every { baseFlutterTask.flutterRoot } returns BaseFlutterTaskPropertiesTest.flutterRootTest
every { baseFlutterTask.flutterRoot!!.absolutePath } returns BaseFlutterTaskPropertiesTest.FLUTTER_ROOT_ABSOLUTE_PATH_TEST
every { baseFlutterTask.trackWidgetCreation } returns true
every { baseFlutterTask.splitDebugInfo } returns BaseFlutterTaskPropertiesTest.SPLIT_DEBUG_INFO_TEST
every { baseFlutterTask.treeShakeIcons } returns true
every { baseFlutterTask.dartObfuscation } returns true
every { baseFlutterTask.dartDefines } returns BaseFlutterTaskPropertiesTest.DART_DEFINES_TEST
every { baseFlutterTask.bundleSkSLPath } returns BaseFlutterTaskPropertiesTest.BUNDLE_SK_SL_PATH_TEST
every { baseFlutterTask.codeSizeDirectory } returns BaseFlutterTaskPropertiesTest.CODE_SIZE_DIRECTORY_TEST
every { baseFlutterTask.flavor } returns BaseFlutterTaskPropertiesTest.FLAVOR_TEST
every { baseFlutterTask.extraGenSnapshotOptions } returns BaseFlutterTaskPropertiesTest.EXTRA_GEN_SNAPSHOT_OPTIONS_TEST
every { baseFlutterTask.frontendServerStarterPath } returns BaseFlutterTaskPropertiesTest.FRONTEND_SERVER_STARTER_PATH_TEST
every { baseFlutterTask.extraFrontEndOptions } returns BaseFlutterTaskPropertiesTest.EXTRA_FRONTEND_OPTIONS_TEST
every { baseFlutterTask.targetPlatformValues } returns BaseFlutterTaskPropertiesTest.targetPlatformValuesList
every { baseFlutterTask.minSdkVersion } returns BaseFlutterTaskPropertiesTest.MIN_SDK_VERSION_TEST
// Mock the method calls. We collapse all the args mock calls into four calls.
every { mockExecSpec.executable(any<String>()) } returns mockExecSpec
every { mockExecSpec.workingDir(any()) } returns mockProcessForkOptions
every { mockExecSpec.args(any<String>(), any()) } returns mockExecSpec
every { mockExecSpec.args(any<String>(), any()) } returns mockExecSpec
every { mockExecSpec.args(any<String>()) } returns mockExecSpec
every { mockExecSpec.args(any<List<String>>()) } returns mockExecSpec
// Generate rule names for verification and can only be generated after buildMode is mocked.
val ruleNamesList: List<String> = helper.generateRuleNames(baseFlutterTask)
// The exec function will be deprecated in gradle 8.11 and will be removed in gradle 9.0
// https://docs.gradle.org/current/kotlin-dsl/gradle/org.gradle.kotlin.dsl/-kotlin-script/exec.html?query=abstract%20fun%20exec(configuration:%20Action%3CExecSpec%3E):%20ExecResult
// The actions are executed.
execSpecActionFromTask.execute(mockExecSpec)
// After execution, we verify the functions are actually being
// called with the expected argument passed in.
verify { mockExecSpec.executable(BaseFlutterTaskPropertiesTest.FLUTTER_EXECUTABLE_ABSOLUTE_PATH_TEST) }
verify { mockExecSpec.workingDir(BaseFlutterTaskPropertiesTest.sourceDirTest) }
verify { mockExecSpec.args("--local-engine", BaseFlutterTaskPropertiesTest.LOCAL_ENGINE_TEST) }
verify { mockExecSpec.args("--local-engine-src-path", BaseFlutterTaskPropertiesTest.LOCAL_ENGINE_SRC_PATH_TEST) }
verify { mockExecSpec.args("--local-engine-host", BaseFlutterTaskPropertiesTest.LOCAL_ENGINE_HOST_TEST) }
verify { mockExecSpec.args("--verbose") }
verify { mockExecSpec.args("assemble") }
verify { mockExecSpec.args("--no-version-check") }
verify { mockExecSpec.args("--depfile", "${BaseFlutterTaskPropertiesTest.intermediateDirFileTest}/flutter_build.d") }
verify { mockExecSpec.args("--output", "${BaseFlutterTaskPropertiesTest.intermediateDirFileTest}") }
verify { mockExecSpec.args("--performance-measurement-file=${BaseFlutterTaskPropertiesTest.PERFORMANCE_MEASUREMENT_FILE_TEST}") }
verify { mockExecSpec.args("-dTargetFile=${BaseFlutterTaskPropertiesTest.FLUTTER_TARGET_FILE_PATH}") }
verify { mockExecSpec.args("-dTargetPlatform=android") }
verify { mockExecSpec.args("-dBuildMode=$buildModeString") }
verify { mockExecSpec.args("-dTrackWidgetCreation=${true}") }
verify { mockExecSpec.args("-dSplitDebugInfo=${BaseFlutterTaskPropertiesTest.SPLIT_DEBUG_INFO_TEST}") }
verify { mockExecSpec.args("-dTreeShakeIcons=true") }
verify { mockExecSpec.args("-dDartObfuscation=true") }
verify { mockExecSpec.args("--DartDefines=${BaseFlutterTaskPropertiesTest.DART_DEFINES_TEST}") }
verify { mockExecSpec.args("-dBundleSkSLPath=${BaseFlutterTaskPropertiesTest.BUNDLE_SK_SL_PATH_TEST}") }
verify { mockExecSpec.args("-dCodeSizeDirectory=${BaseFlutterTaskPropertiesTest.CODE_SIZE_DIRECTORY_TEST}") }
verify { mockExecSpec.args("-dFlavor=${BaseFlutterTaskPropertiesTest.FLAVOR_TEST}") }
verify { mockExecSpec.args("--ExtraGenSnapshotOptions=${BaseFlutterTaskPropertiesTest.EXTRA_GEN_SNAPSHOT_OPTIONS_TEST}") }
verify { mockExecSpec.args("-dFrontendServerStarterPath=${BaseFlutterTaskPropertiesTest.FRONTEND_SERVER_STARTER_PATH_TEST}") }
verify { mockExecSpec.args("--ExtraFrontEndOptions=${BaseFlutterTaskPropertiesTest.EXTRA_FRONTEND_OPTIONS_TEST}") }
verify { mockExecSpec.args("-dAndroidArchs=${BaseFlutterTaskPropertiesTest.TARGET_PLATFORM_VALUES_JOINED_LIST}") }
verify { mockExecSpec.args("-dMinSdkVersion=${BaseFlutterTaskPropertiesTest.MIN_SDK_VERSION_TEST}") }
verify { mockExecSpec.args(ruleNamesList) }
}
}