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")
18 
19 package androidx.build.lint
20 
21 import com.android.tools.lint.checks.infrastructure.LintDetectorTest
22 import com.android.tools.lint.checks.infrastructure.TestFile
23 import com.android.tools.lint.checks.infrastructure.TestFiles
24 
25 class Stubs {
26 
27     companion object {
28 
29         /** [TestFile] containing Keep.java from the annotation library. */
30         val Keep =
31             TestFiles.java(
32                 """
33 package androidx.annotation;
34 
35 public @interface Keep {
36 }
37             """
38             )
39 
40         val RunWith =
41             TestFiles.kotlin(
42                 """
43 package org.junit.runner
44 
45 annotation class RunWith(val value: KClass<*>)
46             """
47             )
48 
49         val JUnit4Runner =
50             TestFiles.kotlin(
51                 """
52 package org.junit.runners
53 
54 class JUnit4
55             """
56             )
57 
58         val ParameterizedRunner =
59             TestFiles.kotlin(
60                 """
61 package org.junit.runners
62 
63 class Parameterized
64             """
65             )
66 
67         val TestParameterInjector =
68             TestFiles.kotlin(
69                     """
70                     package com.google.testing.junit.testparameterinjector
71 
72                     class TestParameterInjector
73                     """
74                 )
75                 .indented()
76 
77         val AndroidJUnit4Runner =
78             TestFiles.kotlin(
79                 """
80 package androidx.test.ext.junit.runners
81 
82 class AndroidJUnit4
83             """
84             )
85 
86         val TestSizeAnnotations =
87             TestFiles.kotlin(
88                 """
89 package androidx.test.filters
90 
91 annotation class SmallTest
92 annotation class MediumTest
93 annotation class LargeTest
94             """
95             )
96 
97         val TestAnnotation =
98             TestFiles.kotlin(
99                 """
100 package org.junit
101 
102 annotation class Test
103             """
104             )
105 
106         /**
107          * [TestFile] containing OptIn.kt from the Kotlin standard library.
108          *
109          * This is a workaround for the Kotlin standard library used by the Lint test harness not
110          * including the Experimental annotation by default.
111          */
112         val OptIn =
113             TestFiles.kotlin(
114                 """
115 package kotlin
116 
117 import kotlin.annotation.AnnotationRetention.BINARY
118 import kotlin.annotation.AnnotationRetention.SOURCE
119 import kotlin.annotation.AnnotationTarget.*
120 import kotlin.internal.RequireKotlin
121 import kotlin.internal.RequireKotlinVersionKind
122 import kotlin.reflect.KClass
123 
124 @Target(ANNOTATION_CLASS)
125 @Retention(BINARY)
126 @SinceKotlin("1.3")
127 @RequireKotlin("1.3.70", versionKind = RequireKotlinVersionKind.COMPILER_VERSION)
128 public annotation class RequiresOptIn(
129     val message: String = "",
130     val level: Level = Level.ERROR
131 ) {
132     public enum class Level {
133         WARNING,
134         ERROR,
135     }
136 }
137 
138 @Target(
139     CLASS, PROPERTY, LOCAL_VARIABLE, VALUE_PARAMETER, CONSTRUCTOR, FUNCTION, PROPERTY_GETTER, PROPERTY_SETTER, EXPRESSION, FILE, TYPEALIAS
140 )
141 @Retention(SOURCE)
142 @SinceKotlin("1.3")
143 @RequireKotlin("1.3.70", versionKind = RequireKotlinVersionKind.COMPILER_VERSION)
144 public annotation class OptIn(
145     vararg val markerClass: KClass<out Annotation>
146 )
147             """
148             )
149 
150         /** [TestFile] containing ChecksSdkIntAtLeast.java from the annotation library. */
151         val ChecksSdkIntAtLeast =
152             TestFiles.java(
153                 """
154 package androidx.annotation;
155 
156 import static java.lang.annotation.ElementType.FIELD;
157 import static java.lang.annotation.ElementType.METHOD;
158 import static java.lang.annotation.RetentionPolicy.CLASS;
159 
160 import java.lang.annotation.Documented;
161 import java.lang.annotation.Retention;
162 import java.lang.annotation.Target;
163 
164 @Documented
165 @Retention(CLASS)
166 @Target({METHOD, FIELD})
167 public @interface ChecksSdkIntAtLeast {
168     int api() default -1;
169     String codename() default "";
170     int parameter() default -1;
171     int lambda() default -1;
172 }
173             """
174             )
175 
176         val RequiresApi =
177             TestFiles.java(
178                 """
179 package androidx.annotation;
180 
181 import static java.lang.annotation.ElementType.CONSTRUCTOR;
182 import static java.lang.annotation.ElementType.FIELD;
183 import static java.lang.annotation.ElementType.METHOD;
184 import static java.lang.annotation.ElementType.PACKAGE;
185 import static java.lang.annotation.ElementType.TYPE;
186 import static java.lang.annotation.RetentionPolicy.CLASS;
187 
188 import java.lang.annotation.Documented;
189 import java.lang.annotation.Retention;
190 import java.lang.annotation.Target;
191 
192 @Documented
193 @Retention(CLASS)
194 @Target({TYPE, METHOD, CONSTRUCTOR, FIELD, PACKAGE})
195 public @interface RequiresApi {
196     @IntRange(from = 1)
197     int value() default 1;
198     @IntRange(from = 1)
199     int api() default 1;
200 }
201             """
202             )
203 
204         val IntRange =
205             TestFiles.java(
206                 """
207 package androidx.annotation;
208 
209 import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
210 import static java.lang.annotation.ElementType.FIELD;
211 import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
212 import static java.lang.annotation.ElementType.METHOD;
213 import static java.lang.annotation.ElementType.PARAMETER;
214 import static java.lang.annotation.RetentionPolicy.CLASS;
215 
216 import java.lang.annotation.Documented;
217 import java.lang.annotation.Retention;
218 import java.lang.annotation.Target;
219 
220 @Documented
221 @Retention(CLASS)
222 @Target({METHOD, PARAMETER, FIELD, LOCAL_VARIABLE, ANNOTATION_TYPE})
223 public @interface IntRange {
224     long from() default Long.MIN_VALUE;
225     long to() default Long.MAX_VALUE;
226 }
227             """
228             )
229 
230         val RestrictTo =
231             TestFiles.java(
232                 """
233 package androidx.annotation;
234 
235 import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
236 import static java.lang.annotation.ElementType.CONSTRUCTOR;
237 import static java.lang.annotation.ElementType.FIELD;
238 import static java.lang.annotation.ElementType.METHOD;
239 import static java.lang.annotation.ElementType.PACKAGE;
240 import static java.lang.annotation.ElementType.TYPE;
241 import static java.lang.annotation.RetentionPolicy.CLASS;
242 
243 import java.lang.annotation.Documented;
244 import java.lang.annotation.Retention;
245 import java.lang.annotation.Target;
246 
247 @Documented
248 @Retention(CLASS)
249 @Target({ANNOTATION_TYPE, TYPE, METHOD, CONSTRUCTOR, FIELD, PACKAGE})
250 public @interface RestrictTo {
251     Scope[] value();
252     enum Scope {
253         LIBRARY,
254         LIBRARY_GROUP,
255         LIBRARY_GROUP_PREFIX,
256         /** @deprecated Use {@link #LIBRARY_GROUP_PREFIX} instead */
257         @Deprecated
258         GROUP_ID,
259         TESTS,
260         SUBCLASSES,
261     }
262 }
263             """
264             )
265 
266         val JetBrainsAnnotations =
267             TestFiles.kotlin(
268                 """
269 package org.jetbrains.annotations
270 
271 annotation class NotNull
272 annotation class Nullable
273             """
274             )
275 
276         val IgnoreAnnotation =
277             TestFiles.kotlin(
278                 """
279 package org.junit
280 
281 annotation class Ignore
282             """
283             )
284 
285         val DoNotInline =
286             TestFiles.java(
287                 """
288 package androidx.annotation;
289 
290 import static java.lang.annotation.ElementType.METHOD;
291 import static java.lang.annotation.RetentionPolicy.CLASS;
292 
293 import java.lang.annotation.Retention;
294 import java.lang.annotation.Target;
295 
296 @Retention(CLASS)
297 @Target({METHOD})
298 public @interface DoNotInline {
299 }
300             """
301             )
302 
303         val DeprecatedSinceApi =
304             TestFiles.kotlin(
305                 """
306 package androidx.annotation
307 
308 import kotlin.annotation.AnnotationTarget.ANNOTATION_CLASS
309 import kotlin.annotation.AnnotationTarget.CLASS
310 import kotlin.annotation.AnnotationTarget.CONSTRUCTOR
311 import kotlin.annotation.AnnotationTarget.FUNCTION
312 import kotlin.annotation.AnnotationTarget.PROPERTY_GETTER
313 import kotlin.annotation.AnnotationTarget.PROPERTY_SETTER
314 
315 @MustBeDocumented
316 @Retention(AnnotationRetention.BINARY)
317 @Target(FUNCTION, PROPERTY_GETTER, PROPERTY_SETTER, ANNOTATION_CLASS, CLASS, CONSTRUCTOR)
318 public annotation class DeprecatedSinceApi(
319     val api: Int,
320     val message: String = ""
321 )
322             """
323             )
324 
325         val JvmDefaultWithCompatibility =
326             TestFiles.kotlin(
327                 """
328 package kotlin.jvm
329 
330 @Retention(AnnotationRetention.SOURCE)
331 @Target(AnnotationTarget.CLASS)
332 annotation class JvmDefaultWithCompatibility
333             """
334                     .trimIndent()
335             )
336 
337         /** [TestFile] containing OptIn.kt from the AndroidX experimental annotation library. */
338         val JetpackOptIn: TestFile =
339             LintDetectorTest.kotlin(
340                 """
341 package androidx.annotation
342 
343 import kotlin.annotation.Retention
344 import kotlin.annotation.Target
345 import kotlin.reflect.KClass
346 
347 @Retention(AnnotationRetention.BINARY)
348 @Target(
349     AnnotationTarget.CLASS,
350     AnnotationTarget.PROPERTY,
351     AnnotationTarget.LOCAL_VARIABLE,
352     AnnotationTarget.VALUE_PARAMETER,
353     AnnotationTarget.CONSTRUCTOR,
354     AnnotationTarget.FUNCTION,
355     AnnotationTarget.PROPERTY_GETTER,
356     AnnotationTarget.PROPERTY_SETTER,
357     AnnotationTarget.FILE,
358     AnnotationTarget.TYPEALIAS
359 )
360 annotation class OptIn(
361     @get:Suppress("ArrayReturn")
362     vararg val markerClass: KClass<out Annotation>
363 )
364     """
365                     .trimIndent()
366             )
367 
368         /**
369          * [TestFile] containing RequiresOptIn.kt from the AndroidX experimental annotation library.
370          */
371         val JetpackRequiresOptIn: TestFile =
372             LintDetectorTest.kotlin(
373                 """
374 package androidx.annotation
375 
376 import kotlin.annotation.Retention
377 import kotlin.annotation.Target
378 
379 @Retention(AnnotationRetention.BINARY)
380 @Target(AnnotationTarget.ANNOTATION_CLASS)
381 annotation class RequiresOptIn(
382     val level: Level = Level.ERROR
383 ) {
384     enum class Level {
385         WARNING,
386         ERROR
387     }
388 }
389     """
390                     .trimIndent()
391             )
392 
393         /** [TestFile] containing VisibleForTesting.kt from the AndroidX annotation library. */
394         val VisibleForTesting: TestFile =
395             LintDetectorTest.kotlin(
396                 """
397 package androidx.annotation
398 
399 @MustBeDocumented
400 @Retention(AnnotationRetention.BINARY)
401 public annotation class VisibleForTesting(
402     @ProductionVisibility val otherwise: Int = PRIVATE
403 ) {
404     public companion object {
405         public const val PRIVATE: Int = 2
406         public const val PACKAGE_PRIVATE: Int = 3
407         public const val PROTECTED: Int = 4
408         public const val NONE: Int = 5
409     }
410 }
411             """
412                     .trimIndent()
413             )
414 
415         /** Contains only a few of the isAtLeastX implementations from BuildCompat for testing */
416         val BuildCompat: TestFile =
417             LintDetectorTest.java(
418                 """
419 package androidx.core.os;
420 
421 import android.os.Build;
422 import android.os.Build.VERSION;
423 
424 import androidx.annotation.ChecksSdkIntAtLeast;
425 import androidx.annotation.NonNull;
426 import androidx.annotation.RequiresOptIn;
427 import androidx.annotation.RestrictTo;
428 
429 import java.util.Locale;
430 
431 public class BuildCompat {
432     private BuildCompat() {}
433 
434     @RestrictTo(RestrictTo.Scope.TESTS)
435     protected static boolean isAtLeastPreReleaseCodename(@NonNull String codename, @NonNull String buildCodename) {
436         if ("REL".equals(buildCodename)) {
437             return false;
438         }
439         final String buildCodenameUpper = buildCodename.toUpperCase(Locale.ROOT);
440         final String codenameUpper = codename.toUpperCase(Locale.ROOT);
441         return buildCodenameUpper.compareTo(codenameUpper) >= 0;
442     }
443 
444     @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.N)
445     @Deprecated
446     public static boolean isAtLeastN() {
447         return VERSION.SDK_INT >= 24;
448     }
449 
450     @PrereleaseSdkCheck
451     @ChecksSdkIntAtLeast(api = 32, codename = "Sv2")
452     @Deprecated
453     public static boolean isAtLeastSv2() {
454         return VERSION.SDK_INT >= 32 || (VERSION.SDK_INT >= 31 && isAtLeastPreReleaseCodename("Sv2", VERSION.CODENAME));
455     }
456 
457     @PrereleaseSdkCheck
458     @ChecksSdkIntAtLeast(codename = "UpsideDownCake")
459     public static boolean isAtLeastU() {
460         return VERSION.SDK_INT >= 33 && isAtLeastPreReleaseCodename("UpsideDownCake", VERSION.CODENAME);
461     }
462 
463     @RequiresOptIn
464     public @interface PrereleaseSdkCheck { }
465 }
466         """
467                     .trimIndent()
468             )
469 
470         val FlaggedApi: TestFile =
471             TestFiles.java(
472                     """
473 package android.annotation; // HIDE-FROM-DOCUMENTATION
474 
475 import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
476 import static java.lang.annotation.ElementType.CONSTRUCTOR;
477 import static java.lang.annotation.ElementType.FIELD;
478 import static java.lang.annotation.ElementType.METHOD;
479 import static java.lang.annotation.ElementType.TYPE;
480 
481 import java.lang.annotation.Retention;
482 import java.lang.annotation.RetentionPolicy;
483 import java.lang.annotation.Target;
484 
485 @Target({TYPE, METHOD, CONSTRUCTOR, FIELD, ANNOTATION_TYPE})
486 @Retention(RetentionPolicy.CLASS)
487 public @interface FlaggedApi {
488     String value();
489 }
490       """
491                 )
492                 .indented()
493 
494         val ChecksAconfigFlag: TestFile =
495             TestFiles.kotlin(
496                     """
497 package androidx.annotation
498 
499 @MustBeDocumented
500 @Retention(AnnotationRetention.BINARY)
501 @Target(
502     AnnotationTarget.FUNCTION,
503     AnnotationTarget.PROPERTY_GETTER,
504     AnnotationTarget.PROPERTY_SETTER,
505     AnnotationTarget.FIELD
506 )
507 public annotation class ChecksAconfigFlag (
508     val flag: String
509 )
510         """
511                 )
512                 .indented()
513     }
514 }
515