blob: 1eab2b8d26e0f3f0050051b51d0d5e1e39001fc9 [file] [log] [blame]
/*
* Copyright 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@file:Suppress("UnstableApiUsage", "GroovyUnusedAssignment")
package androidx.build.lint
import androidx.build.lint.BanInappropriateExperimentalUsage.Companion.getMavenCoordinatesFromPath
import androidx.build.lint.BanInappropriateExperimentalUsage.Companion.isAnnotationAlwaysAllowed
import androidx.build.lint.Stubs.Companion.JetpackOptIn
import androidx.build.lint.Stubs.Companion.JetpackRequiresOptIn
import com.android.tools.lint.checks.infrastructure.ProjectDescription
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
/**
* Important:
*
* [BanInappropriateExperimentalUsage] uses the atomic library groups list generated from production
* data. For tests, we want to overwrite this to provide a different list of atomic library groups;
* the file location is lint-checks/src/test/resources/atomic-library-groups.txt.
*
* Note that the filename must match
* [BanInappropriateExperimentalUsage.ATOMIC_LIBRARY_GROUPS_FILENAME].
*/
@RunWith(JUnit4::class)
class BanInappropriateExperimentalUsageTest :
AbstractLintDetectorTest(
useDetector = BanInappropriateExperimentalUsage(),
useIssues = listOf(BanInappropriateExperimentalUsage.ISSUE),
stubs = arrayOf(Stubs.OptIn),
) {
@Test
fun `Check if annotation is always allowed`() {
// These annotations are used in AndroidX
assertTrue(isAnnotationAlwaysAllowed("com.google.devtools.ksp.KspExperimental"))
assertTrue(isAnnotationAlwaysAllowed("kotlin.contracts.ExperimentalContracts"))
assertTrue(isAnnotationAlwaysAllowed("kotlin.ExperimentalStdlibApi"))
assertTrue(isAnnotationAlwaysAllowed("kotlin.experimental.ExperimentalTypeInference"))
assertTrue(isAnnotationAlwaysAllowed("kotlinx.coroutines.DelicateCoroutinesApi"))
assertTrue(isAnnotationAlwaysAllowed("kotlinx.coroutines.ExperimentalCoroutinesApi"))
assertTrue(
isAnnotationAlwaysAllowed(
"org.jetbrains.kotlin.extensions.internal.InternalNonStableExtensionPoints"
)
)
assertTrue(isAnnotationAlwaysAllowed("org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI"))
assertFalse(isAnnotationAlwaysAllowed("androidx.foo.bar"))
assertFalse(isAnnotationAlwaysAllowed("com.google.foo.bar"))
}
@Test
fun `getLibraryFromPath should return correct Maven coordinates`() {
val paging =
getMavenCoordinatesFromPath(
"/path/to/checkout/out/androidx/paging/paging-common/build/libs/paging-common-3.2.0-alpha01.jar"
)
val room =
getMavenCoordinatesFromPath(
"/path/to/checkout/out/androidx/room/room-compiler-processing/build/libs/room-compiler-processing-2.5.0-alpha02.jar"
)
assertNotNull(paging!!)
assertEquals("androidx.paging", paging.groupId)
assertEquals("paging-common", paging.artifactId)
assertEquals("3.2.0-alpha01", paging.version)
assertNotNull(room!!)
assertEquals("androidx.room", room.groupId)
assertEquals("room-compiler-processing", room.artifactId)
assertEquals("2.5.0-alpha02", room.version)
val invalid = getMavenCoordinatesFromPath("/foo/bar/baz")
assertNull(invalid)
}
@Test
fun `Test same atomic module Experimental usage via Gradle model`() {
val provider =
project()
.name("provider")
.files(
ktSample("sample.annotation.provider.WithinGroupExperimentalAnnotatedClass"),
ktSample("sample.annotation.provider.ExperimentalSampleAnnotation"),
gradle(
"""
apply plugin: 'com.android.library'
group=sample.annotation.provider
"""
)
.indented(),
)
val expected =
"""
No warnings.
"""
.trimIndent()
check(provider).expect(expected)
}
@Test
fun `Test same non-atomic module Experimental usage via Gradle model`() {
val provider =
project()
.name("provider")
.files(
ktSample("sample.annotation.provider.WithinGroupExperimentalAnnotatedClass"),
ktSample("sample.annotation.provider.ExperimentalSampleAnnotation"),
gradle(
"""
apply plugin: 'com.android.library'
group=sample.annotation.provider
"""
)
.indented(),
)
val expected =
"""
No warnings.
"""
.trimIndent()
check(provider).expect(expected)
}
@Test
fun `Test cross-module Experimental usage via Gradle model`() {
val provider =
project()
.name("provider")
.type(ProjectDescription.Type.LIBRARY)
.report(false)
.files(
JetpackRequiresOptIn,
ktSample("sample.annotation.provider.ExperimentalSampleAnnotation"),
javaSample("sample.annotation.provider.ExperimentalSampleAnnotationJava"),
javaSample("sample.annotation.provider.RequiresOptInSampleAnnotationJava"),
javaSample(
"sample.annotation.provider.RequiresOptInSampleAnnotationJavaDuplicate"
),
javaSample(
"sample.annotation.provider.RequiresAndroidXOptInSampleAnnotationJava"
),
javaSample(
"sample.annotation.provider.RequiresAndroidXOptInSampleAnnotationJavaDuplicate"
),
gradle(
"""
apply plugin: 'com.android.library'
group=sample.annotation.provider
"""
)
.indented(),
)
val consumer =
project()
.name("consumer")
.type(ProjectDescription.Type.LIBRARY)
.dependsOn(provider)
.files(
JetpackOptIn,
ktSample("androidx.sample.consumer.OutsideGroupExperimentalAnnotatedClass"),
gradle(
"""
apply plugin: 'com.android.library'
group=androidx.sample.consumer
"""
)
.indented(),
)
val expected =
"""
../consumer/src/main/kotlin/androidx/sample/consumer/OutsideGroupExperimentalAnnotatedClass.kt:35: Error: Experimental and RequiresOptIn APIs may only be used within the same-version group where they were defined. [IllegalExperimentalApiUsage]
@ExperimentalSampleAnnotationJava
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
../consumer/src/main/kotlin/androidx/sample/consumer/OutsideGroupExperimentalAnnotatedClass.kt:40: Error: Experimental and RequiresOptIn APIs may only be used within the same-version group where they were defined. [IllegalExperimentalApiUsage]
@RequiresOptInSampleAnnotationJava
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
../consumer/src/main/kotlin/androidx/sample/consumer/OutsideGroupExperimentalAnnotatedClass.kt:45: Error: Experimental and RequiresOptIn APIs may only be used within the same-version group where they were defined. [IllegalExperimentalApiUsage]
@kotlin.OptIn(RequiresOptInSampleAnnotationJava::class)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
../consumer/src/main/kotlin/androidx/sample/consumer/OutsideGroupExperimentalAnnotatedClass.kt:50: Error: Experimental and RequiresOptIn APIs may only be used within the same-version group where they were defined. [IllegalExperimentalApiUsage]
@kotlin.OptIn(
^
../consumer/src/main/kotlin/androidx/sample/consumer/OutsideGroupExperimentalAnnotatedClass.kt:58: Error: Experimental and RequiresOptIn APIs may only be used within the same-version group where they were defined. [IllegalExperimentalApiUsage]
@kotlin.OptIn(
^
../consumer/src/main/kotlin/androidx/sample/consumer/OutsideGroupExperimentalAnnotatedClass.kt:66: Error: Experimental and RequiresOptIn APIs may only be used within the same-version group where they were defined. [IllegalExperimentalApiUsage]
@androidx.annotation.OptIn(RequiresAndroidXOptInSampleAnnotationJava::class)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
../consumer/src/main/kotlin/androidx/sample/consumer/OutsideGroupExperimentalAnnotatedClass.kt:71: Error: Experimental and RequiresOptIn APIs may only be used within the same-version group where they were defined. [IllegalExperimentalApiUsage]
@androidx.annotation.OptIn(
^
../consumer/src/main/kotlin/androidx/sample/consumer/OutsideGroupExperimentalAnnotatedClass.kt:79: Error: Experimental and RequiresOptIn APIs may only be used within the same-version group where they were defined. [IllegalExperimentalApiUsage]
@androidx.annotation.OptIn(
^
8 errors, 0 warnings
"""
.trimIndent()
check(provider, consumer).expect(expected)
}
@Test
fun `Test cross-module Experimental usage in alpha via Gradle model`() {
val provider =
project()
.name("provider")
.type(ProjectDescription.Type.LIBRARY)
.report(false)
.files(
JetpackRequiresOptIn,
ktSample("sample.annotation.provider.ExperimentalSampleAnnotation"),
javaSample("sample.annotation.provider.ExperimentalSampleAnnotationJava"),
javaSample("sample.annotation.provider.RequiresOptInSampleAnnotationJava"),
javaSample(
"sample.annotation.provider.RequiresOptInSampleAnnotationJavaDuplicate"
),
javaSample(
"sample.annotation.provider.RequiresAndroidXOptInSampleAnnotationJava"
),
javaSample(
"sample.annotation.provider.RequiresAndroidXOptInSampleAnnotationJavaDuplicate"
),
gradle(
"""
apply plugin: 'com.android.library'
group=sample.annotation.provider
version=1.0.0-beta02
"""
)
.indented(),
)
val consumer =
project()
.name("consumer")
.type(ProjectDescription.Type.LIBRARY)
.dependsOn(provider)
.files(
JetpackOptIn,
ktSample("androidx.sample.consumer.OutsideGroupExperimentalAnnotatedClass"),
gradle(
"""
apply plugin: 'com.android.library'
group=androidx.sample.consumer
version=1.0.0-alpha01
"""
)
.indented(),
)
val expected =
"""
No warnings.
"""
.trimIndent()
check(provider, consumer).expect(expected)
}
}