Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add possibility to check flavoured android modules #143

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 10 additions & 0 deletions README.md
Expand Up @@ -88,6 +88,11 @@ apiValidation {
* Flag to programmatically disable compatibility validator
*/
validationDisabled = true

/**
* flavour to be tested in flavoured Android modules
*/
testedFlavourName = "blue"
}
```

Expand Down Expand Up @@ -122,6 +127,11 @@ apiValidation {
* Flag to programmatically disable compatibility validator
*/
validationDisabled = false

/**
* flavour to be tested in flavoured Android modules
*/
testedFlavourName = "blue"
}
```

Expand Down
Expand Up @@ -11,7 +11,6 @@ import org.junit.Ignore
import org.junit.Test
import java.io.File

@Ignore // Leftovers after revert of #94
internal class AndroidLibraryTest : BaseKotlinGradleTest() {

// region Kotlin Android Library
Expand Down Expand Up @@ -47,8 +46,42 @@ internal class AndroidLibraryTest : BaseKotlinGradleTest() {

//endregion

// region Kotlin Flavoured Android Library

@Test
fun `Given a Kotlin Flavoured Android Library, when api is dumped, then task should be successful`() {
assumeHasAndroid()
val runner = test {
createProjectWithSubModulesAndFlavour()
runner {
arguments.add(":kotlin-library:apiDump")
arguments.add("--full-stacktrace")
}
}

runner.build().apply {
assertTaskSuccess(":kotlin-library:apiDump")
}
}

@Test
fun `Given a Kotlin Flavoured Android Library, when api is checked, then it should match the expected`() {
assumeHasAndroid()
test {
createProjectWithSubModulesAndFlavour()
runner {
arguments.add(":kotlin-library:apiCheck")
}
}.build().apply {
assertTaskSuccess(":kotlin-library:apiCheck")
}
}

//endregion

//region Java Android Library

@Ignore // Leftovers after revert of #94
@Test
fun `Given a Java Android Library, when api is dumped, then task should be successful`() {
assumeHasAndroid()
Expand All @@ -65,6 +98,7 @@ internal class AndroidLibraryTest : BaseKotlinGradleTest() {
}
}

@Ignore // Leftovers after revert of #94
@Test
fun `Given a Java Android Library, when api is checked, then it should match the expected`() {
assumeHasAndroid()
Expand Down Expand Up @@ -117,6 +151,43 @@ internal class AndroidLibraryTest : BaseKotlinGradleTest() {
}
}

private fun BaseKotlinScope.createProjectWithSubModulesAndFlavour() {
settingsGradleKts {
resolve("examples/gradle/settings/settings-android-project.gradle.kts")
}
buildGradleKts {
resolve("examples/gradle/base/androidProjectRoot.gradle.kts")
}
initLocalProperties()

dir("kotlin-library") {
buildGradleKts {
resolve("examples/gradle/base/androidFlavouredKotlinLibrary.gradle.kts")
}
kotlin("KotlinLib.kt") {
resolve("examples/classes/KotlinLib.kt")
}
// different files in different folder only selected flavour should be picked
// unfortunately files in flavour folder are not picked up
kotlin("FlavourGreen.kt", sourceSet = "green") {
resolve("examples/classes/FlavourGreen.kt")
}
kotlin("FlavourRed.kt", sourceSet = "red") {
resolve("examples/classes/FlavourRed.kt")
}
// same file in different folder should not create problems
kotlin("KotlinLib2.kt", sourceSet = "green") {
resolve("examples/classes/KotlinFlavour.kt")
}
kotlin("KotlinLib2.kt", sourceSet = "red") {
resolve("examples/classes/KotlinFlavour.kt")
}
apiFile(projectName = "kotlin-library") {
resolve("examples/classes/KotlinFlavouredLib.dump")
}
}
}

private fun initLocalProperties() {
val home = System.getenv("ANDROID_HOME") ?: System.getenv("HOME")
File(rootProjectDir, "local.properties").apply {
Expand Down
13 changes: 13 additions & 0 deletions src/functionalTest/resources/examples/classes/FlavourGreen.kt
@@ -0,0 +1,13 @@
/*
* Copyright 2016-2022 JetBrains s.r.o.
* Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file.
*/

package examples.classes

class FlavourGreen {

fun foo(): String = "foo"
internal fun bar(): String = "bar"

}
13 changes: 13 additions & 0 deletions src/functionalTest/resources/examples/classes/FlavourRed.kt
@@ -0,0 +1,13 @@
/*
* Copyright 2016-2022 JetBrains s.r.o.
* Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file.
*/

package examples.classes

class FlavourRed {

fun foo(): String = "foo"
internal fun bar(): String = "bar"

}
13 changes: 13 additions & 0 deletions src/functionalTest/resources/examples/classes/KotlinFlavour.kt
@@ -0,0 +1,13 @@
/*
* Copyright 2016-2022 JetBrains s.r.o.
* Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file.
*/

package examples.classes

class KotlinFlavour {

fun foo(): String = "foo"
internal fun bar(): String = "bar"

}
@@ -0,0 +1,23 @@
public final class examples/classes/FlavourGreen {
public fun <init> ()V
public final fun foo ()Ljava/lang/String;
}

public final class examples/classes/KotlinFlavour {
public fun <init> ()V
public final fun foo ()Ljava/lang/String;
}

public final class examples/classes/KotlinLib {
public fun <init> ()V
public final fun foo ()Ljava/lang/String;
}

public final class org/jetbrains/kotlinx/android/kotlin/library/BuildConfig {
public static final field BUILD_TYPE Ljava/lang/String;
public static final field DEBUG Z
public static final field FLAVOR Ljava/lang/String;
public static final field LIBRARY_PACKAGE_NAME Ljava/lang/String;
public fun <init> ()V
}

@@ -0,0 +1,53 @@
/*
* Copyright 2016-2022 JetBrains s.r.o.
* Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file.
*/
import org.jetbrains.kotlin.gradle.plugin.*

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: Should it follow the same rules as in other changed files for explicit imports? Or vice versa?

plugins {
id("com.android.library")
id("kotlin-android")
id("org.jetbrains.kotlinx.binary-compatibility-validator")
}

android {

namespace = "org.jetbrains.kotlinx.android.kotlin.library"

compileSdk = 32

defaultConfig {
minSdk = 31
targetSdk = 32

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles("consumer-rules.pro")
}

buildTypes {
release {
isMinifyEnabled = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}

flavorDimensions += "brand"
productFlavors {
create("green") {
dimension = "brand"
}
create("red") {
dimension = "brand"
}
}
}

apiValidation {
testedFlavourName = "green"
additionalSourceSets.add("green")
}
dependencies {
// no dependencies required
}
Expand Up @@ -2,7 +2,6 @@
* Copyright 2016-2022 JetBrains s.r.o.
* Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file.
*/

plugins {
id("com.android.library")
id("kotlin-android")
Expand Down
6 changes: 6 additions & 0 deletions src/main/kotlin/ApiValidationExtension.kt
Expand Up @@ -64,4 +64,10 @@ open class ApiValidationExtension {
* By default, only the `main` source set is checked.
*/
public var additionalSourceSets: MutableSet<String> = HashSet()

/**
* In Android projects with multiple flavour,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo: flavours

* it specifies the flavour you want to test (assuming projects have same API)
*/
public var testedFlavourName: String? = null
}
33 changes: 25 additions & 8 deletions src/main/kotlin/BinaryCompatibilityValidatorPlugin.kt
Expand Up @@ -5,13 +5,22 @@

package kotlinx.validation

import org.gradle.api.*
import org.gradle.api.plugins.*
import org.gradle.api.provider.*
import org.gradle.api.tasks.*
import org.jetbrains.kotlin.gradle.dsl.*
import org.jetbrains.kotlin.gradle.plugin.*
import java.io.*
import java.io.File
import org.gradle.api.Action
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.plugins.AppliedPlugin
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.SourceSet
import org.gradle.api.tasks.SourceSetContainer
import org.gradle.api.tasks.Sync
import org.gradle.api.tasks.TaskProvider
import org.jetbrains.kotlin.gradle.dsl.KotlinAndroidProjectExtension
import org.jetbrains.kotlin.gradle.dsl.KotlinCommonOptions
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType

const val API_DIR = "api"

Expand Down Expand Up @@ -120,7 +129,15 @@ class BinaryCompatibilityValidatorPlugin : Plugin<Project> {
val androidExtension = project.extensions
.getByName("kotlin") as KotlinAndroidProjectExtension
androidExtension.target.compilations.matching {
it.compilationName == "release"
val testedFlavourName = extension.testedFlavourName
if (testedFlavourName == null) {
it.compilationName == "release"
} else {
it.compilationName.contains("release", ignoreCase = true)
&& it.compilationName.contains("test", ignoreCase = true).not()
&& it.compilationName.contains(testedFlavourName, ignoreCase = true)
}

}.all {
project.configureKotlinCompilation(it, extension, useOutput = true)
}
Expand Down