1 /* 2 * Copyright 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 @file:Suppress("UnstableApiUsage", "GroovyUnusedAssignment") 18 19 package androidx.build.lint 20 21 import androidx.build.lint.BanInappropriateExperimentalUsage.Companion.getMavenCoordinatesFromPath 22 import androidx.build.lint.BanInappropriateExperimentalUsage.Companion.isAnnotationAlwaysAllowed 23 import androidx.build.lint.Stubs.Companion.JetpackOptIn 24 import androidx.build.lint.Stubs.Companion.JetpackRequiresOptIn 25 import com.android.tools.lint.checks.infrastructure.ProjectDescription 26 import org.junit.Test 27 import org.junit.runner.RunWith 28 import org.junit.runners.JUnit4 29 30 /** 31 * Important: 32 * 33 * [BanInappropriateExperimentalUsage] uses the atomic library groups list generated from production 34 * data. For tests, we want to overwrite this to provide a different list of atomic library groups; 35 * the file location is lint-checks/src/test/resources/atomic-library-groups.txt. 36 * 37 * Note that the filename must match 38 * [BanInappropriateExperimentalUsage.ATOMIC_LIBRARY_GROUPS_FILENAME]. 39 */ 40 @RunWith(JUnit4::class) 41 class BanInappropriateExperimentalUsageTest : 42 AbstractLintDetectorTest( 43 useDetector = BanInappropriateExperimentalUsage(), 44 useIssues = listOf(BanInappropriateExperimentalUsage.ISSUE), 45 stubs = arrayOf(Stubs.OptIn), 46 ) { 47 48 @Test Check if annotation is always allowednull49 fun `Check if annotation is always allowed`() { 50 51 // These annotations are used in AndroidX 52 assertTrue(isAnnotationAlwaysAllowed("com.google.devtools.ksp.KspExperimental")) 53 assertTrue(isAnnotationAlwaysAllowed("kotlin.contracts.ExperimentalContracts")) 54 assertTrue(isAnnotationAlwaysAllowed("kotlin.ExperimentalStdlibApi")) 55 assertTrue(isAnnotationAlwaysAllowed("kotlin.experimental.ExperimentalTypeInference")) 56 assertTrue(isAnnotationAlwaysAllowed("kotlinx.coroutines.DelicateCoroutinesApi")) 57 assertTrue(isAnnotationAlwaysAllowed("kotlinx.coroutines.ExperimentalCoroutinesApi")) 58 assertTrue( 59 isAnnotationAlwaysAllowed( 60 "org.jetbrains.kotlin.extensions.internal.InternalNonStableExtensionPoints" 61 ) 62 ) 63 assertTrue(isAnnotationAlwaysAllowed("org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI")) 64 65 assertFalse(isAnnotationAlwaysAllowed("androidx.foo.bar")) 66 assertFalse(isAnnotationAlwaysAllowed("com.google.foo.bar")) 67 } 68 69 @Test getLibraryFromPath should return correct Maven coordinatesnull70 fun `getLibraryFromPath should return correct Maven coordinates`() { 71 72 val paging = 73 getMavenCoordinatesFromPath( 74 "/path/to/checkout/out/androidx/paging/paging-common/build/libs/paging-common-3.2.0-alpha01.jar" 75 ) 76 val room = 77 getMavenCoordinatesFromPath( 78 "/path/to/checkout/out/androidx/room/room-compiler-processing/build/libs/room-compiler-processing-2.5.0-alpha02.jar" 79 ) 80 val uiTest = 81 getMavenCoordinatesFromPath( 82 "/path/to/checkout/out/androidx/compose/ui/ui-test/build/libs/ui-test-jvmstubs-1.8.0-beta01.jar" 83 ) 84 85 assertNotNull(paging!!) 86 assertEquals("androidx.paging", paging.groupId) 87 assertEquals("paging-common", paging.artifactId) 88 assertEquals("3.2.0-alpha01", paging.version) 89 90 assertNotNull(room!!) 91 assertEquals("androidx.room", room.groupId) 92 assertEquals("room-compiler-processing", room.artifactId) 93 assertEquals("2.5.0-alpha02", room.version) 94 95 assertNotNull(uiTest!!) 96 assertEquals("androidx.compose.ui", uiTest.groupId) 97 assertEquals("ui-test", uiTest.artifactId) 98 assertEquals("jvmstubs-1.8.0-beta01", uiTest.version) 99 100 val invalid = getMavenCoordinatesFromPath("/foo/bar/baz") 101 assertNull(invalid) 102 } 103 104 @Test Test same atomic module Experimental usage via Gradle modelnull105 fun `Test same atomic module Experimental usage via Gradle model`() { 106 val provider = 107 project() 108 .name("provider") 109 .files( 110 ktSample("sample.annotation.provider.WithinGroupExperimentalAnnotatedClass"), 111 ktSample("sample.annotation.provider.ExperimentalSampleAnnotation"), 112 gradle( 113 """ 114 apply plugin: 'com.android.library' 115 group=sample.annotation.provider 116 """ 117 ) 118 .indented(), 119 ) 120 121 val expected = 122 """ 123 No warnings. 124 """ 125 .trimIndent() 126 127 check(provider).expect(expected) 128 } 129 130 @Test Test same non-atomic module Experimental usage via Gradle modelnull131 fun `Test same non-atomic module Experimental usage via Gradle model`() { 132 val provider = 133 project() 134 .name("provider") 135 .files( 136 ktSample("sample.annotation.provider.WithinGroupExperimentalAnnotatedClass"), 137 ktSample("sample.annotation.provider.ExperimentalSampleAnnotation"), 138 gradle( 139 """ 140 apply plugin: 'com.android.library' 141 group=sample.annotation.provider 142 """ 143 ) 144 .indented(), 145 ) 146 147 val expected = 148 """ 149 No warnings. 150 """ 151 .trimIndent() 152 153 check(provider).expect(expected) 154 } 155 156 @Test Test cross-module Experimental usage via Gradle modelnull157 fun `Test cross-module Experimental usage via Gradle model`() { 158 159 val provider = 160 project() 161 .name("provider") 162 .type(ProjectDescription.Type.LIBRARY) 163 .report(false) 164 .files( 165 JetpackRequiresOptIn, 166 ktSample("sample.annotation.provider.ExperimentalSampleAnnotation"), 167 javaSample("sample.annotation.provider.ExperimentalSampleAnnotationJava"), 168 javaSample("sample.annotation.provider.RequiresOptInSampleAnnotationJava"), 169 javaSample( 170 "sample.annotation.provider.RequiresOptInSampleAnnotationJavaDuplicate" 171 ), 172 javaSample( 173 "sample.annotation.provider.RequiresAndroidXOptInSampleAnnotationJava" 174 ), 175 javaSample( 176 "sample.annotation.provider.RequiresAndroidXOptInSampleAnnotationJavaDuplicate" 177 ), 178 gradle( 179 """ 180 apply plugin: 'com.android.library' 181 group=sample.annotation.provider 182 """ 183 ) 184 .indented(), 185 ) 186 187 val consumer = 188 project() 189 .name("consumer") 190 .type(ProjectDescription.Type.LIBRARY) 191 .dependsOn(provider) 192 .files( 193 JetpackOptIn, 194 ktSample("androidx.sample.consumer.OutsideGroupExperimentalAnnotatedClass"), 195 gradle( 196 """ 197 apply plugin: 'com.android.library' 198 group=androidx.sample.consumer 199 """ 200 ) 201 .indented(), 202 ) 203 204 val expected = 205 """ 206 ../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] 207 @ExperimentalSampleAnnotationJava 208 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 209 ../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] 210 @RequiresOptInSampleAnnotationJava 211 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 212 ../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] 213 @kotlin.OptIn(RequiresOptInSampleAnnotationJava::class) 214 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 215 ../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] 216 @kotlin.OptIn( 217 ^ 218 ../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] 219 @kotlin.OptIn( 220 ^ 221 ../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] 222 @androidx.annotation.OptIn(RequiresAndroidXOptInSampleAnnotationJava::class) 223 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 224 ../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] 225 @androidx.annotation.OptIn( 226 ^ 227 ../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] 228 @androidx.annotation.OptIn( 229 ^ 230 8 errors, 0 warnings 231 """ 232 .trimIndent() 233 234 check(provider, consumer).expect(expected) 235 } 236 237 @Test Test cross-module Experimental usage in alpha via Gradle modelnull238 fun `Test cross-module Experimental usage in alpha via Gradle model`() { 239 240 val provider = 241 project() 242 .name("provider") 243 .type(ProjectDescription.Type.LIBRARY) 244 .report(false) 245 .files( 246 JetpackRequiresOptIn, 247 ktSample("sample.annotation.provider.ExperimentalSampleAnnotation"), 248 javaSample("sample.annotation.provider.ExperimentalSampleAnnotationJava"), 249 javaSample("sample.annotation.provider.RequiresOptInSampleAnnotationJava"), 250 javaSample( 251 "sample.annotation.provider.RequiresOptInSampleAnnotationJavaDuplicate" 252 ), 253 javaSample( 254 "sample.annotation.provider.RequiresAndroidXOptInSampleAnnotationJava" 255 ), 256 javaSample( 257 "sample.annotation.provider.RequiresAndroidXOptInSampleAnnotationJavaDuplicate" 258 ), 259 gradle( 260 """ 261 apply plugin: 'com.android.library' 262 group=sample.annotation.provider 263 version=1.0.0-beta02 264 """ 265 ) 266 .indented(), 267 ) 268 269 val consumer = 270 project() 271 .name("consumer") 272 .type(ProjectDescription.Type.LIBRARY) 273 .dependsOn(provider) 274 .files( 275 JetpackOptIn, 276 ktSample("androidx.sample.consumer.OutsideGroupExperimentalAnnotatedClass"), 277 gradle( 278 """ 279 apply plugin: 'com.android.library' 280 group=androidx.sample.consumer 281 version=1.0.0-alpha01 282 """ 283 ) 284 .indented(), 285 ) 286 287 val expected = 288 """ 289 No warnings. 290 """ 291 .trimIndent() 292 293 check(provider, consumer).expect(expected) 294 } 295 } 296