• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2017 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 package com.android.tools.metalava
18 
19 import com.android.SdkConstants
20 import com.android.SdkConstants.DOT_KT
21 import com.android.SdkConstants.DOT_TXT
22 import com.android.SdkConstants.DOT_XML
23 import com.android.ide.common.process.DefaultProcessExecutor
24 import com.android.ide.common.process.LoggedProcessOutputHandler
25 import com.android.ide.common.process.ProcessException
26 import com.android.ide.common.process.ProcessInfoBuilder
27 import com.android.tools.lint.LintCliClient
28 import com.android.tools.lint.UastEnvironment
29 import com.android.tools.lint.checks.ApiLookup
30 import com.android.tools.lint.checks.infrastructure.ClassName
31 import com.android.tools.lint.checks.infrastructure.TestFile
32 import com.android.tools.lint.checks.infrastructure.TestFiles.java
33 import com.android.tools.lint.checks.infrastructure.TestFiles.kotlin
34 import com.android.tools.lint.checks.infrastructure.stripComments
35 import com.android.tools.lint.client.api.LintClient
36 import com.android.tools.metalava.model.SUPPORT_TYPE_USE_ANNOTATIONS
37 import com.android.tools.metalava.model.defaultConfiguration
38 import com.android.tools.metalava.model.parseDocument
39 import com.android.tools.metalava.model.text.ApiFile
40 import com.android.utils.SdkUtils
41 import com.android.utils.StdLogger
42 import com.google.common.io.ByteStreams
43 import com.google.common.io.Closeables
44 import com.google.common.io.Files
45 import com.intellij.openapi.util.Disposer
46 import org.intellij.lang.annotations.Language
47 import org.junit.Assert.assertEquals
48 import org.junit.Assert.assertNotNull
49 import org.junit.Assert.assertTrue
50 import org.junit.Assert.fail
51 import org.junit.Before
52 import org.junit.Rule
53 import org.junit.rules.ErrorCollector
54 import org.junit.rules.TemporaryFolder
55 import java.io.ByteArrayOutputStream
56 import java.io.File
57 import java.io.FileNotFoundException
58 import java.io.PrintStream
59 import java.io.PrintWriter
60 import java.io.StringWriter
61 import java.net.URL
62 import kotlin.text.Charsets.UTF_8
63 
64 const val CHECK_JDIFF = false
65 
66 abstract class DriverTest {
67     @get:Rule
68     val temporaryFolder = TemporaryFolder()
69 
70     @get:Rule
71     val errorCollector = ErrorCollector()
72 
73     @Before
74     fun setup() {
75         System.setProperty(ENV_VAR_METALAVA_TESTS_RUNNING, SdkConstants.VALUE_TRUE)
76         Disposer.setDebugMode(true)
77     }
78 
79     protected fun createProject(vararg files: TestFile): File {
80         val dir = temporaryFolder.newFolder("project")
81 
82         files
83             .map { it.createFile(dir) }
84             .forEach { assertNotNull(it) }
85 
86         return dir
87     }
88 
89     // Makes a note to fail the test, but still allows the test to complete before failing
90     protected fun addError(error: String) {
91         errorCollector.addError(Throwable(error))
92     }
93 
94     protected fun runDriver(vararg args: String, expectedFail: String = ""): String {
95 
96         resetTicker()
97 
98         // Capture the actual input and output from System.out/err and compare it
99         // to the output printed through the official writer; they should be the same,
100         // otherwise we have stray println's littered in the code!
101         val previousOut = System.out
102         val previousErr = System.err
103         try {
104             val output = TeeWriter(previousOut)
105             System.setOut(PrintStream(output))
106             val error = TeeWriter(previousErr)
107             System.setErr(PrintStream(error))
108 
109             val sw = StringWriter()
110             val writer = PrintWriter(sw)
111 
112             Disposer.setDebugMode(true)
113 
114             if (run(arrayOf(*args), writer, writer)) {
115                 assertTrue("Test expected to fail but didn't. Expected failure: $expectedFail", expectedFail.isEmpty())
116             } else {
117                 val actualFail = cleanupString(sw.toString(), null)
118                 if (cleanupString(expectedFail, null).replace(".", "").trim() !=
119                     actualFail.replace(".", "").trim()
120                 ) {
121                     val reportedCompatError = actualFail.startsWith("Aborting: Found compatibility problems checking the ")
122                     if (expectedFail == "Aborting: Found compatibility problems with --check-compatibility" &&
123                         reportedCompatError
124                     ) {
125                         // Special case for compat checks; we don't want to force each one of them
126                         // to pass in the right string (which may vary based on whether writing out
127                         // the signature was passed at the same time
128                         // ignore
129                     } else {
130                         if (reportedCompatError) {
131                             // if a compatibility error was unexpectedly reported, then mark that as
132                             // an error but keep going so we can see the actual compatibility error
133                             if (expectedFail.trimIndent() != actualFail) {
134                                 addError("ComparisonFailure: expected failure $expectedFail, actual $actualFail")
135                             }
136                         } else {
137                             // no compatibility error; check for other errors now, and
138                             // if one is found, fail right away
139                             assertEquals(expectedFail.trimIndent(), actualFail)
140                         }
141                     }
142                 }
143             }
144 
145             val stdout = output.toString(UTF_8.name())
146             if (stdout.isNotEmpty()) {
147                 addError("Unexpected write to stdout:\n $stdout")
148             }
149             val stderr = error.toString(UTF_8.name())
150             if (stderr.isNotEmpty()) {
151                 addError("Unexpected write to stderr:\n $stderr")
152             }
153 
154             val printedOutput = sw.toString()
155             if (printedOutput.isNotEmpty() && printedOutput.trim().isEmpty()) {
156                 fail("Printed newlines with nothing else")
157             }
158 
159             UastEnvironment.checkApplicationEnvironmentDisposed()
160             Disposer.assertIsEmpty(true)
161 
162             return printedOutput
163         } finally {
164             System.setOut(previousOut)
165             System.setErr(previousErr)
166         }
167     }
168 
169     // This is here so we can keep a record of what was printed, to make sure we
170     // don't have any unexpected printlns in the source that are left behind after
171     // debugging and pollute the production output
172     class TeeWriter(private val otherStream: PrintStream) : ByteArrayOutputStream() {
173         override fun write(b: ByteArray?, off: Int, len: Int) {
174             otherStream.write(b, off, len)
175             super.write(b, off, len)
176         }
177 
178         override fun write(b: ByteArray?) {
179             otherStream.write(b)
180             super.write(b)
181         }
182 
183         override fun write(b: Int) {
184             otherStream.write(b)
185             super.write(b)
186         }
187     }
188 
189     protected fun getJdkPath(): String? {
190         val javaHome = System.getProperty("java.home")
191         if (javaHome != null) {
192             var javaHomeFile = File(javaHome)
193             if (File(javaHomeFile, "bin${File.separator}javac").exists()) {
194                 return javaHome
195             } else if (javaHomeFile.name == "jre") {
196                 javaHomeFile = javaHomeFile.parentFile
197                 if (javaHomeFile != null && File(javaHomeFile, "bin${File.separator}javac").exists()) {
198                     return javaHomeFile.path
199                 }
200             }
201         }
202         return System.getenv("JAVA_HOME")
203     }
204 
205     private fun <T> buildOptionalArgs(option: T?, converter: (T) -> Array<String>): Array<String> {
206         return if (option != null) {
207             converter(option)
208         } else {
209             emptyArray()
210         }
211     }
212 
213     /** File conversion tasks */
214     data class ConvertData(
215         val fromApi: String,
216         val outputFile: String,
217         val baseApi: String? = null,
218         val strip: Boolean = true,
219     )
220 
221     protected fun check(
222         /** Any jars to add to the class path */
223         classpath: Array<TestFile>? = null,
224         /** The API signature content (corresponds to --api) */
225         @Language("TEXT")
226         api: String? = null,
227         /** The API signature content (corresponds to --api-xml) */
228         @Language("XML")
229         apiXml: String? = null,
230         /** The DEX API (corresponds to --dex-api) */
231         dexApi: String? = null,
232         /** The removed API (corresponds to --removed-api) */
233         removedApi: String? = null,
234         /** The subtract api signature content (corresponds to --subtract-api) */
235         @Language("TEXT")
236         subtractApi: String? = null,
237         /** Expected stubs (corresponds to --stubs) */
238         stubFiles: Array<TestFile> = emptyArray(),
239         /** Stub source file list generated */
240         stubsSourceList: String? = null,
241         /** Doc Stub source file list generated */
242         docStubsSourceList: String? = null,
243         /** Whether the stubs should be written as documentation stubs instead of plain stubs. Decides
244          * whether the stubs include @doconly elements, uses rewritten/migration annotations, etc */
245         docStubs: Boolean = false,
246         /** Signature file format */
247         format: FileFormat = FileFormat.latest,
248         /** Whether to trim the output (leading/trailing whitespace removal) */
249         trim: Boolean = true,
250         /** Whether to remove blank lines in the output (the signature file usually contains a lot of these) */
251         stripBlankLines: Boolean = true,
252         /** All expected issues to be generated when analyzing these sources */
253         expectedIssues: String? = "",
254         /** Expected [Severity.ERROR] issues to be generated when analyzing these sources */
255         errorSeverityExpectedIssues: String? = null,
256         checkCompilation: Boolean = false,
257         /** Annotations to merge in (in .xml format) */
258         @Language("XML")
259         mergeXmlAnnotations: String? = null,
260         /** Annotations to merge in (in .txt/.signature format) */
261         @Language("TEXT")
262         mergeSignatureAnnotations: String? = null,
263         /** Qualifier annotations to merge in (in Java stub format) */
264         @Language("JAVA")
265         mergeJavaStubAnnotations: String? = null,
266         /** Inclusion annotations to merge in (in Java stub format) */
267         @Language("JAVA")
268         mergeInclusionAnnotations: String? = null,
269         /** Optional API signature files content to load **instead** of Java/Kotlin source files */
270         @Language("TEXT")
271         signatureSources: Array<String> = emptyArray(),
272         /**
273          * An optional API signature file content to load **instead** of Java/Kotlin source files.
274          * This is added to [signatureSources]. This argument exists for backward compatibility.
275          */
276         @Language("TEXT")
277         signatureSource: String? = null,
278         /** An optional API jar file content to load **instead** of Java/Kotlin source files */
279         apiJar: File? = null,
280         /** An optional API signature to check the last released API's compatibility with */
281         @Language("TEXT")
282         checkCompatibilityApiReleased: String? = null,
283         /** An optional API signature to check the last released removed API's compatibility with */
284         @Language("TEXT")
285         checkCompatibilityRemovedApiReleased: String? = null,
286         /** An optional API signature to use as the base API codebase during compat checks */
287         @Language("TEXT")
288         checkCompatibilityBaseApi: String? = null,
289         @Language("TEXT")
290         migrateNullsApi: String? = null,
291         /** An optional Proguard keep file to generate */
292         @Language("Proguard")
293         proguard: String? = null,
294         /** Show annotations (--show-annotation arguments) */
295         showAnnotations: Array<String> = emptyArray(),
296         /** "Show for stub purposes" API annotation ([ARG_SHOW_FOR_STUB_PURPOSES_ANNOTATION]) */
297         showForStubPurposesAnnotations: Array<String> = emptyArray(),
298         /** Hide annotations (--hide-annotation arguments) */
299         hideAnnotations: Array<String> = emptyArray(),
300         /** Hide meta-annotations (--hide-meta-annotation arguments) */
301         hideMetaAnnotations: Array<String> = emptyArray(),
302         /** If using [showAnnotations], whether to include unannotated */
303         showUnannotated: Boolean = false,
304         /** Additional arguments to supply */
305         extraArguments: Array<String> = emptyArray(),
306         /** Whether we should emit Kotlin-style null signatures */
307         outputKotlinStyleNulls: Boolean = format.useKotlinStyleNulls(),
308         /** Whether we should interpret API files being read as having Kotlin-style nullness types */
309         inputKotlinStyleNulls: Boolean = false,
310         /** Expected output (stdout and stderr combined). If null, don't check. */
311         expectedOutput: String? = null,
312         /** Expected fail message and state, if any */
313         expectedFail: String? = null,
314         /** Optional manifest to load and associate with the codebase */
315         @Language("XML")
316         manifest: String? = null,
317         /** Packages to pre-import (these will therefore NOT be included in emitted stubs, signature files etc */
318         importedPackages: List<String> = emptyList(),
319         /** Packages to skip emitting signatures/stubs for even if public (typically used for unit tests
320          * referencing to classpath classes that aren't part of the definitions and shouldn't be part of the
321          * test output; e.g. a test may reference java.lang.Enum but we don't want to start reporting all the
322          * public APIs in the java.lang package just because it's indirectly referenced via the "enum" superclass
323          */
324         skipEmitPackages: List<String> = listOf("java.lang", "java.util", "java.io"),
325         /** Whether we should include --showAnnotations=android.annotation.SystemApi */
326         includeSystemApiAnnotations: Boolean = false,
327         /** Whether we should warn about super classes that are stripped because they are hidden */
328         includeStrippedSuperclassWarnings: Boolean = false,
329         /** Apply level to XML */
330         applyApiLevelsXml: String? = null,
331         /** Corresponds to SDK constants file broadcast_actions.txt */
332         sdk_broadcast_actions: String? = null,
333         /** Corresponds to SDK constants file activity_actions.txt */
334         sdk_activity_actions: String? = null,
335         /** Corresponds to SDK constants file service_actions.txt */
336         sdk_service_actions: String? = null,
337         /** Corresponds to SDK constants file categories.txt */
338         sdk_categories: String? = null,
339         /** Corresponds to SDK constants file features.txt */
340         sdk_features: String? = null,
341         /** Corresponds to SDK constants file widgets.txt */
342         sdk_widgets: String? = null,
343         /** Extract annotations and check that the given packages contain the given extracted XML files */
344         extractAnnotations: Map<String, String>? = null,
345         /** Creates the nullability annotations validator, and check that the report has the given lines (does not define files to be validated) */
346         validateNullability: Set<String>? = null,
347         /** Enable nullability validation for the listed classes */
348         validateNullabilityFromList: String? = null,
349         /**
350          * Whether to include source retention annotations in the stubs (in that case they do not
351          * go into the extracted annotations zip file)
352          */
353         includeSourceRetentionAnnotations: Boolean = true,
354         /**
355          * Whether to include the signature version in signatures
356          */
357         includeSignatureVersion: Boolean = false,
358         /**
359          * List of signature files to convert to JDiff XML and the
360          * expected XML output.
361          */
362         convertToJDiff: List<ConvertData> = emptyList(),
363         /**
364          * Hook for performing additional initialization of the project
365          * directory
366          */
367         projectSetup: ((File) -> Unit)? = null,
368         /** Content of the baseline file to use, if any */
369         baseline: String? = null,
370         /** If non-null, we expect the baseline file to be updated to this. [baseline] must also be set. */
371         updateBaseline: String? = null,
372         /** Merge instead of replacing the baseline */
373         mergeBaseline: String? = null,
374 
375         /** [ARG_BASELINE_API_LINT] */
376         baselineApiLint: String? = null,
377         /** [ARG_UPDATE_BASELINE_API_LINT] */
378         updateBaselineApiLint: String? = null,
379 
380         /** [ARG_BASELINE_CHECK_COMPATIBILITY_RELEASED] */
381         baselineCheckCompatibilityReleased: String? = null,
382         /** [ARG_UPDATE_BASELINE_CHECK_COMPATIBILITY_RELEASED] */
383         updateBaselineCheckCompatibilityReleased: String? = null,
384 
385         /** [ARG_ERROR_MESSAGE_API_LINT] */
386         errorMessageApiLint: String? = null,
387         /** [ARG_ERROR_MESSAGE_CHECK_COMPATIBILITY_RELEASED] */
388         errorMessageCheckCompatibilityReleased: String? = null,
389 
390         /**
391          * If non null, enable API lint. If non-blank, a codebase where only new APIs not in the codebase
392          * are linted.
393          */
394         @Language("TEXT")
395         apiLint: String? = null,
396         /** The source files to pass to the analyzer */
397         sourceFiles: Array<TestFile> = emptyArray(),
398         /** [ARG_REPEAT_ERRORS_MAX] */
399         repeatErrorsMax: Int = 0
400     ) {
401         // Ensure different API clients don't interfere with each other
402         try {
403             val method = ApiLookup::class.java.getDeclaredMethod("dispose")
404             method.isAccessible = true
405             method.invoke(null)
406         } catch (ignore: Throwable) {
407             ignore.printStackTrace()
408         }
409 
410         // Ensure that lint infrastructure (for UAST) knows it's dealing with a test
411         LintCliClient(LintClient.CLIENT_UNIT_TESTS)
412 
413         defaultConfiguration.reset()
414 
415         val actualExpectedFail = when {
416             expectedFail != null -> expectedFail
417             (checkCompatibilityApiReleased != null || checkCompatibilityRemovedApiReleased != null) &&
418                 expectedIssues != null && expectedIssues.trim().isNotEmpty() -> {
419                 "Aborting: Found compatibility problems with --check-compatibility"
420             }
421             else -> ""
422         }
423 
424         // Unit test which checks that a signature file is as expected
425         val androidJar = getAndroidJar()
426 
427         val project = createProject(*sourceFiles)
428 
429         val sourcePathDir = File(project, "src")
430         if (!sourcePathDir.isDirectory) {
431             sourcePathDir.mkdirs()
432         }
433 
434         var sourcePath = sourcePathDir.path
435 
436         // Make it easy to configure a source path with more than one source root: src and src2
437         if (sourceFiles.any { it.targetPath.startsWith("src2") }) {
438             sourcePath = sourcePath + File.pathSeparator + sourcePath + "2"
439         }
440 
441         val sourceList =
442             if (signatureSources.isNotEmpty() || signatureSource != null) {
443                 sourcePathDir.mkdirs()
444 
445                 // if signatureSource is set, add it to signatureSources.
446                 val sources = signatureSources.toMutableList()
447                 signatureSource?. let { sources.add(it) }
448 
449                 var num = 0
450                 val args = mutableListOf<String>()
451                 sources.forEach { file ->
452                     val signatureFile = File(
453                         project,
454                         "load-api${ if (++num == 1) "" else num.toString() }.txt"
455                     )
456                     signatureFile.writeText(file.trimIndent())
457                     args.add(signatureFile.path)
458                 }
459                 if (!includeStrippedSuperclassWarnings) {
460                     args.add(ARG_HIDE)
461                     args.add("HiddenSuperclass") // Suppress warning #111
462                 }
463                 args.toTypedArray()
464             } else if (apiJar != null) {
465                 sourcePathDir.mkdirs()
466                 assert(sourceFiles.isEmpty()) { "Shouldn't combine sources with API jar file loads" }
467                 arrayOf(apiJar.path)
468             } else {
469                 sourceFiles.asSequence().map { File(project, it.targetPath).path }.toList().toTypedArray()
470             }
471 
472         val classpathArgs: Array<String> = if (classpath != null) {
473             val classpathString = classpath
474                 .map { it.createFile(project) }
475                 .map { it.path }
476                 .joinToString(separator = File.pathSeparator) { it }
477 
478             arrayOf(ARG_CLASS_PATH, classpathString)
479         } else {
480             emptyArray()
481         }
482 
483         val allReportedIssues = StringBuilder()
484         val errorSeverityReportedIssues = StringBuilder()
485         Reporter.rootFolder = project
486         Reporter.reportPrinter = { message, severity ->
487             val cleanedUpMessage = cleanupString(message, project).trim()
488             if (severity == Severity.ERROR) {
489                 errorSeverityReportedIssues.append(cleanedUpMessage).append('\n')
490             }
491             allReportedIssues.append(cleanedUpMessage).append('\n')
492         }
493 
494         val mergeAnnotationsArgs = if (mergeXmlAnnotations != null) {
495             val merged = File(project, "merged-annotations.xml")
496             merged.writeText(mergeXmlAnnotations.trimIndent())
497             arrayOf(ARG_MERGE_QUALIFIER_ANNOTATIONS, merged.path)
498         } else {
499             emptyArray()
500         }
501 
502         val signatureAnnotationsArgs = if (mergeSignatureAnnotations != null) {
503             val merged = File(project, "merged-annotations.txt")
504             merged.writeText(mergeSignatureAnnotations.trimIndent())
505             arrayOf(ARG_MERGE_QUALIFIER_ANNOTATIONS, merged.path)
506         } else {
507             emptyArray()
508         }
509 
510         val javaStubAnnotationsArgs = if (mergeJavaStubAnnotations != null) {
511             // We need to place the qualifier class into its proper package location
512             // to make the parsing machinery happy
513             val cls = ClassName(mergeJavaStubAnnotations)
514             val pkg = cls.packageName
515             val relative = pkg?.replace('.', File.separatorChar) ?: "."
516             val merged = File(project, "qualifier/$relative/${cls.className}.java")
517             merged.parentFile.mkdirs()
518             merged.writeText(mergeJavaStubAnnotations.trimIndent())
519             arrayOf(ARG_MERGE_QUALIFIER_ANNOTATIONS, merged.path)
520         } else {
521             emptyArray()
522         }
523 
524         val inclusionAnnotationsArgs = if (mergeInclusionAnnotations != null) {
525             val cls = ClassName(mergeInclusionAnnotations)
526             val pkg = cls.packageName
527             val relative = pkg?.replace('.', File.separatorChar) ?: "."
528             val merged = File(project, "inclusion/$relative/${cls.className}.java")
529             merged.parentFile?.mkdirs()
530             merged.writeText(mergeInclusionAnnotations.trimIndent())
531             arrayOf(ARG_MERGE_INCLUSION_ANNOTATIONS, merged.path)
532         } else {
533             emptyArray()
534         }
535 
536         val apiLintArgs = if (apiLint != null) {
537             if (apiLint.isBlank()) {
538                 arrayOf(ARG_API_LINT)
539             } else {
540                 val file = File(project, "prev-api-lint.txt")
541                 file.writeText(apiLint.trimIndent())
542                 arrayOf(ARG_API_LINT, file.path)
543             }
544         } else {
545             emptyArray()
546         }
547 
548         val checkCompatibilityApiReleasedFile = if (checkCompatibilityApiReleased != null) {
549             val jar = File(checkCompatibilityApiReleased)
550             if (jar.isFile) {
551                 jar
552             } else {
553                 val file = File(project, "released-api.txt")
554                 file.writeText(checkCompatibilityApiReleased.trimIndent())
555                 file
556             }
557         } else {
558             null
559         }
560 
561         val checkCompatibilityRemovedApiReleasedFile = if (checkCompatibilityRemovedApiReleased != null) {
562             val jar = File(checkCompatibilityRemovedApiReleased)
563             if (jar.isFile) {
564                 jar
565             } else {
566                 val file = File(project, "removed-released-api.txt")
567                 file.writeText(checkCompatibilityRemovedApiReleased.trimIndent())
568                 file
569             }
570         } else {
571             null
572         }
573 
574         val checkCompatibilityBaseApiFile = if (checkCompatibilityBaseApi != null) {
575             val maybeFile = File(checkCompatibilityBaseApi)
576             if (maybeFile.isFile) {
577                 maybeFile
578             } else {
579                 val file = File(project, "compatibility-base-api.txt")
580                 file.writeText(checkCompatibilityBaseApi.trimIndent())
581                 file
582             }
583         } else {
584             null
585         }
586 
587         val migrateNullsApiFile = if (migrateNullsApi != null) {
588             val jar = File(migrateNullsApi)
589             if (jar.isFile) {
590                 jar
591             } else {
592                 val file = File(project, "stable-api.txt")
593                 file.writeText(migrateNullsApi.trimIndent())
594                 file
595             }
596         } else {
597             null
598         }
599 
600         val manifestFileArgs = if (manifest != null) {
601             val file = File(project, "manifest.xml")
602             file.writeText(manifest.trimIndent())
603             arrayOf(ARG_MANIFEST, file.path)
604         } else {
605             emptyArray()
606         }
607 
608         val migrateNullsArguments = if (migrateNullsApiFile != null) {
609             arrayOf(ARG_MIGRATE_NULLNESS, migrateNullsApiFile.path)
610         } else {
611             emptyArray()
612         }
613 
614         val checkCompatibilityApiReleasedArguments = if (checkCompatibilityApiReleasedFile != null) {
615             arrayOf(ARG_CHECK_COMPATIBILITY_API_RELEASED, checkCompatibilityApiReleasedFile.path)
616         } else {
617             emptyArray()
618         }
619 
620         val checkCompatibilityBaseApiArguments = if (checkCompatibilityBaseApiFile != null) {
621             arrayOf(ARG_CHECK_COMPATIBILITY_BASE_API, checkCompatibilityBaseApiFile.path)
622         } else {
623             emptyArray()
624         }
625 
626         val checkCompatibilityRemovedReleasedArguments = if (checkCompatibilityRemovedApiReleasedFile != null) {
627             arrayOf(ARG_CHECK_COMPATIBILITY_REMOVED_RELEASED, checkCompatibilityRemovedApiReleasedFile.path)
628         } else {
629             emptyArray()
630         }
631 
632         val quiet = if (expectedOutput != null && !extraArguments.contains(ARG_VERBOSE)) {
633             // If comparing output, avoid noisy output such as the banner etc
634             arrayOf(ARG_QUIET)
635         } else {
636             emptyArray()
637         }
638 
639         var proguardFile: File? = null
640         val proguardKeepArguments = if (proguard != null) {
641             proguardFile = File(project, "proguard.cfg")
642             arrayOf(ARG_PROGUARD, proguardFile.path)
643         } else {
644             emptyArray()
645         }
646 
647         val showAnnotationArguments = if (showAnnotations.isNotEmpty() || includeSystemApiAnnotations) {
648             val args = mutableListOf<String>()
649             for (annotation in showAnnotations) {
650                 args.add(ARG_SHOW_ANNOTATION)
651                 args.add(annotation)
652             }
653             if (includeSystemApiAnnotations && !args.contains("android.annotation.SystemApi")) {
654                 args.add(ARG_SHOW_ANNOTATION)
655                 args.add("android.annotation.SystemApi")
656             }
657             if (includeSystemApiAnnotations && !args.contains("android.annotation.TestApi")) {
658                 args.add(ARG_SHOW_ANNOTATION)
659                 args.add("android.annotation.TestApi")
660             }
661             args.toTypedArray()
662         } else {
663             emptyArray()
664         }
665 
666         val hideAnnotationArguments = if (hideAnnotations.isNotEmpty()) {
667             val args = mutableListOf<String>()
668             for (annotation in hideAnnotations) {
669                 args.add(ARG_HIDE_ANNOTATION)
670                 args.add(annotation)
671             }
672             args.toTypedArray()
673         } else {
674             emptyArray()
675         }
676 
677         val showForStubPurposesAnnotationArguments = if (showForStubPurposesAnnotations.isNotEmpty()) {
678             val args = mutableListOf<String>()
679             for (annotation in showForStubPurposesAnnotations) {
680                 args.add(ARG_SHOW_FOR_STUB_PURPOSES_ANNOTATION)
681                 args.add(annotation)
682             }
683             args.toTypedArray()
684         } else {
685             emptyArray()
686         }
687 
688         val hideMetaAnnotationArguments = if (hideMetaAnnotations.isNotEmpty()) {
689             val args = mutableListOf<String>()
690             for (annotation in hideMetaAnnotations) {
691                 args.add(ARG_HIDE_META_ANNOTATION)
692                 args.add(annotation)
693             }
694             args.toTypedArray()
695         } else {
696             emptyArray()
697         }
698 
699         val showUnannotatedArgs =
700             if (showUnannotated) {
701                 arrayOf(ARG_SHOW_UNANNOTATED)
702             } else {
703                 emptyArray()
704             }
705 
706         val includeSourceRetentionAnnotationArgs =
707             if (includeSourceRetentionAnnotations) {
708                 arrayOf(ARG_INCLUDE_SOURCE_RETENTION)
709             } else {
710                 emptyArray()
711             }
712 
713         var removedApiFile: File? = null
714         val removedArgs = if (removedApi != null) {
715             removedApiFile = temporaryFolder.newFile("removed.txt")
716             arrayOf(ARG_REMOVED_API, removedApiFile.path)
717         } else {
718             emptyArray()
719         }
720 
721         var apiFile: File? = null
722         val apiArgs = if (api != null) {
723             apiFile = temporaryFolder.newFile("public-api.txt")
724             arrayOf(ARG_API, apiFile.path)
725         } else {
726             emptyArray()
727         }
728 
729         var apiXmlFile: File? = null
730         val apiXmlArgs = if (apiXml != null) {
731             apiXmlFile = temporaryFolder.newFile("public-api-xml.txt")
732             arrayOf(ARG_XML_API, apiXmlFile.path)
733         } else {
734             emptyArray()
735         }
736 
737         var dexApiFile: File? = null
738         val dexApiArgs = if (dexApi != null) {
739             dexApiFile = temporaryFolder.newFile("public-dex.txt")
740             arrayOf(ARG_DEX_API, dexApiFile.path)
741         } else {
742             emptyArray()
743         }
744 
745         val subtractApiFile: File?
746         val subtractApiArgs = if (subtractApi != null) {
747             subtractApiFile = temporaryFolder.newFile("subtract-api.txt")
748             subtractApiFile.writeText(subtractApi.trimIndent())
749             arrayOf(ARG_SUBTRACT_API, subtractApiFile.path)
750         } else {
751             emptyArray()
752         }
753 
754         val convertFiles = mutableListOf<Options.ConvertFile>()
755         val convertArgs = if (convertToJDiff.isNotEmpty()) {
756             val args = mutableListOf<String>()
757             var index = 1
758             for (convert in convertToJDiff) {
759                 val signature = convert.fromApi
760                 val base = convert.baseApi
761                 val convertSig = temporaryFolder.newFile("convert-signatures$index.txt")
762                 convertSig.writeText(signature.trimIndent(), UTF_8)
763                 val extension = FileFormat.JDIFF.preferredExtension()
764                 val output = temporaryFolder.newFile("convert-output$index$extension")
765                 val baseFile = if (base != null) {
766                     val baseFile = temporaryFolder.newFile("convert-signatures$index-base.txt")
767                     baseFile.writeText(base.trimIndent(), UTF_8)
768                     baseFile
769                 } else {
770                     null
771                 }
772                 convertFiles += Options.ConvertFile(convertSig, output, baseFile, strip = true)
773                 index++
774 
775                 if (baseFile != null) {
776                     args +=
777                         when {
778                             convert.strip -> "-new_api"
779                             else -> ARG_CONVERT_NEW_TO_JDIFF
780                         }
781                     args += baseFile.path
782                 } else {
783                     args +=
784                         when {
785                             convert.strip -> "-convert2xml"
786                             else -> ARG_CONVERT_TO_JDIFF
787                         }
788                 }
789                 args += convertSig.path
790                 args += output.path
791             }
792             args.toTypedArray()
793         } else {
794             emptyArray()
795         }
796 
797         var stubsDir: File? = null
798         val stubsArgs = if (stubFiles.isNotEmpty()) {
799             stubsDir = temporaryFolder.newFolder("stubs")
800             if (docStubs) {
801                 arrayOf(ARG_DOC_STUBS, stubsDir.path)
802             } else {
803                 arrayOf(ARG_STUBS, stubsDir.path)
804             }
805         } else {
806             emptyArray()
807         }
808 
809         var stubsSourceListFile: File? = null
810         val stubsSourceListArgs = if (stubsSourceList != null) {
811             stubsSourceListFile = temporaryFolder.newFile("droiddoc-src-list")
812             arrayOf(ARG_STUBS_SOURCE_LIST, stubsSourceListFile.path)
813         } else {
814             emptyArray()
815         }
816 
817         var docStubsSourceListFile: File? = null
818         val docStubsSourceListArgs = if (docStubsSourceList != null) {
819             docStubsSourceListFile = temporaryFolder.newFile("droiddoc-doc-src-list")
820             arrayOf(ARG_DOC_STUBS_SOURCE_LIST, docStubsSourceListFile.path)
821         } else {
822             emptyArray()
823         }
824 
825         val applyApiLevelsXmlFile: File?
826         val applyApiLevelsXmlArgs = if (applyApiLevelsXml != null) {
827             ApiLookup::class.java.getDeclaredMethod("dispose").apply { isAccessible = true }.invoke(null)
828             applyApiLevelsXmlFile = temporaryFolder.newFile("api-versions.xml")
829             applyApiLevelsXmlFile?.writeText(applyApiLevelsXml.trimIndent())
830             arrayOf(ARG_APPLY_API_LEVELS, applyApiLevelsXmlFile.path)
831         } else {
832             emptyArray()
833         }
834 
835         fun buildBaselineArgs(
836             argBaseline: String,
837             argUpdateBaseline: String,
838             argMergeBaseline: String,
839             filename: String,
840             baselineContent: String?,
841             updateContent: String?,
842             merge: Boolean
843         ): Pair<Array<String>, File?> {
844             if (baselineContent != null) {
845                 val baselineFile = temporaryFolder.newFile(filename)
846                 baselineFile?.writeText(baselineContent.trimIndent())
847                 if (!(updateContent != null || merge)) {
848                     return Pair(arrayOf(argBaseline, baselineFile.path), baselineFile)
849                 } else {
850                     return Pair(
851                         arrayOf(
852                             argBaseline,
853                             baselineFile.path,
854                             if (mergeBaseline != null) argMergeBaseline else argUpdateBaseline,
855                             baselineFile.path
856                         ),
857                         baselineFile
858                     )
859                 }
860             } else {
861                 return Pair(emptyArray(), null)
862             }
863         }
864 
865         val (baselineArgs, baselineFile) = buildBaselineArgs(
866             ARG_BASELINE, ARG_UPDATE_BASELINE, ARG_MERGE_BASELINE, "baseline.txt",
867             baseline, updateBaseline, mergeBaseline != null
868         )
869         val (baselineApiLintArgs, baselineApiLintFile) = buildBaselineArgs(
870             ARG_BASELINE_API_LINT, ARG_UPDATE_BASELINE_API_LINT, "",
871             "baseline-api-lint.txt",
872             baselineApiLint, updateBaselineApiLint, false
873         )
874         val (baselineCheckCompatibilityReleasedArgs, baselineCheckCompatibilityReleasedFile) = buildBaselineArgs(
875             ARG_BASELINE_CHECK_COMPATIBILITY_RELEASED, ARG_UPDATE_BASELINE_CHECK_COMPATIBILITY_RELEASED, "",
876             "baseline-check-released.txt",
877             baselineCheckCompatibilityReleased, updateBaselineCheckCompatibilityReleased, false
878         )
879 
880         val importedPackageArgs = mutableListOf<String>()
881         importedPackages.forEach {
882             importedPackageArgs.add("--stub-import-packages")
883             importedPackageArgs.add(it)
884         }
885 
886         val skipEmitPackagesArgs = mutableListOf<String>()
887         skipEmitPackages.forEach {
888             skipEmitPackagesArgs.add("--skip-emit-packages")
889             skipEmitPackagesArgs.add(it)
890         }
891 
892         val kotlinPathArgs = findKotlinStdlibPathArgs(sourceList)
893 
894         val sdkFilesDir: File?
895         val sdkFilesArgs: Array<String>
896         if (sdk_broadcast_actions != null ||
897             sdk_activity_actions != null ||
898             sdk_service_actions != null ||
899             sdk_categories != null ||
900             sdk_features != null ||
901             sdk_widgets != null
902         ) {
903             val dir = File(project, "sdk-files")
904             sdkFilesArgs = arrayOf(ARG_SDK_VALUES, dir.path)
905             sdkFilesDir = dir
906         } else {
907             sdkFilesArgs = emptyArray()
908             sdkFilesDir = null
909         }
910 
911         val extractedAnnotationsZip: File?
912         val extractAnnotationsArgs = if (extractAnnotations != null) {
913             extractedAnnotationsZip = temporaryFolder.newFile("extracted-annotations.zip")
914             arrayOf(ARG_EXTRACT_ANNOTATIONS, extractedAnnotationsZip.path)
915         } else {
916             extractedAnnotationsZip = null
917             emptyArray()
918         }
919 
920         val validateNullabilityTxt: File?
921         val validateNullabilityArgs = if (validateNullability != null) {
922             validateNullabilityTxt = temporaryFolder.newFile("validate-nullability.txt")
923             arrayOf(
924                 ARG_NULLABILITY_WARNINGS_TXT, validateNullabilityTxt.path,
925                 ARG_NULLABILITY_ERRORS_NON_FATAL // for testing, report on errors instead of throwing
926             )
927         } else {
928             validateNullabilityTxt = null
929             emptyArray()
930         }
931         val validateNullablityFromListFile: File?
932         val validateNullabilityFromListArgs = if (validateNullabilityFromList != null) {
933             validateNullablityFromListFile = temporaryFolder.newFile("validate-nullability-classes.txt")
934             validateNullablityFromListFile.writeText(validateNullabilityFromList)
935             arrayOf(
936                 ARG_VALIDATE_NULLABILITY_FROM_LIST, validateNullablityFromListFile.path
937             )
938         } else {
939             emptyArray()
940         }
941 
942         val errorMessageApiLintArgs = buildOptionalArgs(errorMessageApiLint) {
943             arrayOf(ARG_ERROR_MESSAGE_API_LINT, it)
944         }
945         val errorMessageCheckCompatibilityReleasedArgs = buildOptionalArgs(errorMessageCheckCompatibilityReleased) {
946             arrayOf(ARG_ERROR_MESSAGE_CHECK_COMPATIBILITY_RELEASED, it)
947         }
948 
949         val repeatErrorsMaxArgs = if (repeatErrorsMax > 0) {
950             arrayOf(ARG_REPEAT_ERRORS_MAX, repeatErrorsMax.toString())
951         } else {
952             emptyArray()
953         }
954 
955         // Run optional additional setup steps on the project directory
956         projectSetup?.invoke(project)
957 
958         val actualOutput = runDriver(
959             ARG_NO_COLOR,
960             ARG_NO_BANNER,
961 
962             // Tell metalava where to store temp folder: place them under the
963             // test root folder such that we clean up the output strings referencing
964             // paths to the temp folder
965             "--temp-folder",
966             temporaryFolder.newFolder("temp").path,
967 
968             // Annotation generation temporarily turned off by default while integrating with
969             // SDK builds; tests need these
970             ARG_INCLUDE_ANNOTATIONS,
971 
972             ARG_SOURCE_PATH,
973             sourcePath,
974             ARG_CLASS_PATH,
975             androidJar.path,
976             *classpathArgs,
977             *kotlinPathArgs,
978             *removedArgs,
979             *apiArgs,
980             *apiXmlArgs,
981             *dexApiArgs,
982             *subtractApiArgs,
983             *stubsArgs,
984             *stubsSourceListArgs,
985             *docStubsSourceListArgs,
986             "$ARG_OUTPUT_KOTLIN_NULLS=${if (outputKotlinStyleNulls) "yes" else "no"}",
987             "$ARG_INPUT_KOTLIN_NULLS=${if (inputKotlinStyleNulls) "yes" else "no"}",
988             "$ARG_INCLUDE_SIG_VERSION=${if (includeSignatureVersion) "yes" else "no"}",
989             *quiet,
990             *mergeAnnotationsArgs,
991             *signatureAnnotationsArgs,
992             *javaStubAnnotationsArgs,
993             *inclusionAnnotationsArgs,
994             *migrateNullsArguments,
995             *checkCompatibilityApiReleasedArguments,
996             *checkCompatibilityBaseApiArguments,
997             *checkCompatibilityRemovedReleasedArguments,
998             *proguardKeepArguments,
999             *manifestFileArgs,
1000             *convertArgs,
1001             *applyApiLevelsXmlArgs,
1002             *baselineArgs,
1003             *baselineApiLintArgs,
1004             *baselineCheckCompatibilityReleasedArgs,
1005             *showAnnotationArguments,
1006             *hideAnnotationArguments,
1007             *hideMetaAnnotationArguments,
1008             *showForStubPurposesAnnotationArguments,
1009             *showUnannotatedArgs,
1010             *includeSourceRetentionAnnotationArgs,
1011             *apiLintArgs,
1012             *sdkFilesArgs,
1013             *importedPackageArgs.toTypedArray(),
1014             *skipEmitPackagesArgs.toTypedArray(),
1015             *extractAnnotationsArgs,
1016             *validateNullabilityArgs,
1017             *validateNullabilityFromListArgs,
1018             format.outputFlag(),
1019             *sourceList,
1020             *extraArguments,
1021             *errorMessageApiLintArgs,
1022             *errorMessageCheckCompatibilityReleasedArgs,
1023             *repeatErrorsMaxArgs,
1024             expectedFail = actualExpectedFail
1025         )
1026 
1027         if (expectedIssues != null || allReportedIssues.toString() != "") {
1028             assertEquals(
1029                 expectedIssues?.trimIndent()?.trim() ?: "",
1030                 cleanupString(allReportedIssues.toString(), project)
1031             )
1032         }
1033         if (errorSeverityExpectedIssues != null) {
1034             assertEquals(
1035                 errorSeverityExpectedIssues.trimIndent().trim(),
1036                 cleanupString(errorSeverityReportedIssues.toString(), project)
1037             )
1038         }
1039 
1040         if (expectedOutput != null) {
1041             assertEquals(expectedOutput.trimIndent().trim(), actualOutput.trim())
1042         }
1043 
1044         if (api != null && apiFile != null) {
1045             assertTrue("${apiFile.path} does not exist even though --api was used", apiFile.exists())
1046             val actualText = readFile(apiFile, stripBlankLines, trim)
1047             assertEquals(prepareExpectedApi(api, format), actualText)
1048             // Make sure we can read back the files we write
1049             ApiFile.parseApi(apiFile, options.outputKotlinStyleNulls)
1050         }
1051 
1052         if (apiXml != null && apiXmlFile != null) {
1053             assertTrue(
1054                 "${apiXmlFile.path} does not exist even though $ARG_XML_API was used",
1055                 apiXmlFile.exists()
1056             )
1057             val actualText = readFile(apiXmlFile, stripBlankLines, trim)
1058             assertEquals(stripComments(apiXml, DOT_XML, stripLineComments = false).trimIndent(), actualText)
1059             // Make sure we can read back the files we write
1060             parseDocument(apiXmlFile.readText(UTF_8), false)
1061         }
1062 
1063         fun checkBaseline(arg: String, baselineContent: String?, updateBaselineContent: String?, mergeBaselineContent: String?, file: File?) {
1064             if (file == null) {
1065                 return
1066             }
1067             assertTrue(
1068                 "${file.path} does not exist even though $arg was used",
1069                 file.exists()
1070             )
1071             val actualText = readFile(file, stripBlankLines, trim)
1072 
1073             // Compare against:
1074             // If "merged baseline" is set, use it.
1075             // If "update baseline" is set, use it.
1076             // Otherwise, the original baseline.
1077             val sourceFile = mergeBaselineContent ?: updateBaselineContent ?: baselineContent ?: ""
1078             assertEquals(stripComments(sourceFile, DOT_XML, stripLineComments = false).trimIndent(), actualText)
1079         }
1080         checkBaseline(ARG_BASELINE, baseline, updateBaseline, mergeBaseline, baselineFile)
1081         checkBaseline(ARG_BASELINE_API_LINT, baselineApiLint, updateBaselineApiLint, null, baselineApiLintFile)
1082         checkBaseline(
1083             ARG_BASELINE_CHECK_COMPATIBILITY_RELEASED, baselineCheckCompatibilityReleased,
1084             updateBaselineCheckCompatibilityReleased, null, baselineCheckCompatibilityReleasedFile
1085         )
1086 
1087         if (convertFiles.isNotEmpty()) {
1088             for (i in convertToJDiff.indices) {
1089                 val expected = convertToJDiff[i].outputFile
1090                 val converted = convertFiles[i].outputFile
1091                 assertTrue(
1092                     "${converted.path} does not exist even though $ARG_CONVERT_TO_JDIFF was used",
1093                     converted.exists()
1094                 )
1095                 val actualText = readFile(converted, stripBlankLines, trim)
1096                 if (actualText.contains("<api")) {
1097                     parseDocument(actualText, false)
1098                 }
1099                 assertEquals(
1100                     stripComments(expected, DOT_XML, stripLineComments = false).trimIndent(),
1101                     actualText
1102                 )
1103                 // Make sure we can read back the files we write
1104             }
1105         }
1106 
1107         if (dexApi != null && dexApiFile != null) {
1108             assertTrue(
1109                 "${dexApiFile.path} does not exist even though --dex-api was used",
1110                 dexApiFile.exists()
1111             )
1112             val actualText = readFile(dexApiFile, stripBlankLines, trim)
1113             assertEquals(
1114                 stripComments(dexApi, DOT_TXT, stripLineComments = false).trimIndent(),
1115                 actualText
1116             )
1117         }
1118 
1119         if (removedApi != null && removedApiFile != null) {
1120             assertTrue(
1121                 "${removedApiFile.path} does not exist even though --removed-api was used",
1122                 removedApiFile.exists()
1123             )
1124             val actualText = readFile(removedApiFile, stripBlankLines, trim)
1125             assertEquals(prepareExpectedApi(removedApi, format), actualText)
1126             // Make sure we can read back the files we write
1127             ApiFile.parseApi(removedApiFile, options.outputKotlinStyleNulls)
1128         }
1129 
1130         if (proguard != null && proguardFile != null) {
1131             val expectedProguard = readFile(proguardFile)
1132             assertTrue(
1133                 "${proguardFile.path} does not exist even though --proguard was used",
1134                 proguardFile.exists()
1135             )
1136             assertEquals(
1137                 stripComments(proguard, DOT_TXT, stripLineComments = false).trimIndent(),
1138                 expectedProguard.trim()
1139             )
1140         }
1141 
1142         if (sdk_broadcast_actions != null) {
1143             val actual = readFile(File(sdkFilesDir, "broadcast_actions.txt"), stripBlankLines, trim)
1144             assertEquals(sdk_broadcast_actions.trimIndent().trim(), actual.trim())
1145         }
1146 
1147         if (sdk_activity_actions != null) {
1148             val actual = readFile(File(sdkFilesDir, "activity_actions.txt"), stripBlankLines, trim)
1149             assertEquals(sdk_activity_actions.trimIndent().trim(), actual.trim())
1150         }
1151 
1152         if (sdk_service_actions != null) {
1153             val actual = readFile(File(sdkFilesDir, "service_actions.txt"), stripBlankLines, trim)
1154             assertEquals(sdk_service_actions.trimIndent().trim(), actual.trim())
1155         }
1156 
1157         if (sdk_categories != null) {
1158             val actual = readFile(File(sdkFilesDir, "categories.txt"), stripBlankLines, trim)
1159             assertEquals(sdk_categories.trimIndent().trim(), actual.trim())
1160         }
1161 
1162         if (sdk_features != null) {
1163             val actual = readFile(File(sdkFilesDir, "features.txt"), stripBlankLines, trim)
1164             assertEquals(sdk_features.trimIndent().trim(), actual.trim())
1165         }
1166 
1167         if (sdk_widgets != null) {
1168             val actual = readFile(File(sdkFilesDir, "widgets.txt"), stripBlankLines, trim)
1169             assertEquals(sdk_widgets.trimIndent().trim(), actual.trim())
1170         }
1171 
1172         if (extractAnnotations != null && extractedAnnotationsZip != null) {
1173             assertTrue(
1174                 "Using --extract-annotations but $extractedAnnotationsZip was not created",
1175                 extractedAnnotationsZip.isFile
1176             )
1177             for ((pkg, xml) in extractAnnotations) {
1178                 assertPackageXml(pkg, extractedAnnotationsZip, xml)
1179             }
1180         }
1181 
1182         if (validateNullabilityTxt != null) {
1183             assertTrue(
1184                 "Using $ARG_NULLABILITY_WARNINGS_TXT but $validateNullabilityTxt was not created",
1185                 validateNullabilityTxt.isFile
1186             )
1187             val actualReport =
1188                 Files.asCharSource(validateNullabilityTxt, UTF_8).readLines().map(String::trim).toSet()
1189             assertEquals(validateNullability, actualReport)
1190         }
1191 
1192         if (stubFiles.isNotEmpty()) {
1193             for (expected in stubFiles) {
1194                 val actual = File(stubsDir!!, expected.targetRelativePath)
1195                 if (actual.exists()) {
1196                     val actualContents = readFile(actual, stripBlankLines, trim)
1197                     assertEquals(expected.contents, actualContents)
1198                 } else {
1199                     val existing = stubsDir.walkTopDown().filter { it.isFile }.map { it.path }.joinToString("\n  ")
1200                     throw FileNotFoundException(
1201                         "Could not find a generated stub for ${expected.targetRelativePath}. Found these files: \n  $existing"
1202                     )
1203                 }
1204             }
1205         }
1206 
1207         if (stubsSourceList != null && stubsSourceListFile != null) {
1208             assertTrue(
1209                 "${stubsSourceListFile.path} does not exist even though --write-stubs-source-list was used",
1210                 stubsSourceListFile.exists()
1211             )
1212             val actualText = cleanupString(readFile(stubsSourceListFile, stripBlankLines, trim), project)
1213                 // To make golden files look better put one entry per line instead of a single
1214                 // space separated line
1215                 .replace(' ', '\n')
1216             assertEquals(
1217                 stripComments(stubsSourceList, DOT_TXT, stripLineComments = false).trimIndent(),
1218                 actualText
1219             )
1220         }
1221 
1222         if (docStubsSourceList != null && docStubsSourceListFile != null) {
1223             assertTrue(
1224                 "${docStubsSourceListFile.path} does not exist even though --write-stubs-source-list was used",
1225                 docStubsSourceListFile.exists()
1226             )
1227             val actualText = cleanupString(readFile(docStubsSourceListFile, stripBlankLines, trim), project)
1228                 // To make golden files look better put one entry per line instead of a single
1229                 // space separated line
1230                 .replace(' ', '\n')
1231             assertEquals(
1232                 stripComments(docStubsSourceList, DOT_TXT, stripLineComments = false).trimIndent(),
1233                 actualText
1234             )
1235         }
1236 
1237         if (checkCompilation && stubsDir != null) {
1238             val generated = gatherSources(listOf(stubsDir)).asSequence().map { it.path }.toList().toTypedArray()
1239 
1240             // Also need to include on the compile path annotation classes referenced in the stubs
1241             val extraAnnotationsDir = File("stub-annotations/src/main/java")
1242             if (!extraAnnotationsDir.isDirectory) {
1243                 fail("Couldn't find $extraAnnotationsDir: Is the pwd set to the root of the metalava source code?")
1244                 fail("Couldn't find $extraAnnotationsDir: Is the pwd set to the root of an Android source tree?")
1245             }
1246             val extraAnnotations =
1247                 gatherSources(listOf(extraAnnotationsDir)).asSequence().map { it.path }.toList().toTypedArray()
1248 
1249             if (!runCommand(
1250                     "${getJdkPath()}/bin/javac",
1251                     arrayOf(
1252                             "-d", project.path, *generated, *extraAnnotations
1253                         )
1254                 )
1255             ) {
1256                 fail("Couldn't compile stub file -- compilation problems")
1257                 return
1258             }
1259         }
1260 
1261         if (CHECK_JDIFF && apiXmlFile != null && convertToJDiff.isNotEmpty()) {
1262             // TODO: Parse the XML file with jdiff too
1263         }
1264     }
1265 
1266     /** Checks that the given zip annotations file contains the given XML package contents */
1267     private fun assertPackageXml(pkg: String, output: File, @Language("XML") expected: String) {
1268         assertNotNull(output)
1269         assertTrue(output.exists())
1270         val url = URL(
1271             "jar:" + SdkUtils.fileToUrlString(output) + "!/" + pkg.replace('.', '/') +
1272                 "/annotations.xml"
1273         )
1274         val stream = url.openStream()
1275         try {
1276             val bytes = ByteStreams.toByteArray(stream)
1277             assertNotNull(bytes)
1278             val xml = String(bytes, UTF_8).replace("\r\n", "\n")
1279             assertEquals(expected.trimIndent().trim(), xml.trimIndent().trim())
1280         } finally {
1281             Closeables.closeQuietly(stream)
1282         }
1283     }
1284 
1285     /** Hides path prefixes from /tmp folders used by the testing infrastructure */
1286     private fun cleanupString(string: String, project: File?, dropTestRoot: Boolean = false): String {
1287         var s = string
1288 
1289         if (project != null) {
1290             s = s.replace(project.path, "TESTROOT")
1291             s = s.replace(project.canonicalPath, "TESTROOT")
1292         }
1293 
1294         s = s.replace(temporaryFolder.root.path, "TESTROOT")
1295 
1296         val tmp = System.getProperty("java.io.tmpdir")
1297         if (tmp != null) {
1298             s = s.replace(tmp, "TEST")
1299         }
1300 
1301         s = s.trim()
1302 
1303         if (dropTestRoot) {
1304             s = s.replace("TESTROOT/", "")
1305         }
1306 
1307         return s
1308     }
1309 
1310     private fun runCommand(executable: String, args: Array<String>): Boolean {
1311         try {
1312             val logger = StdLogger(StdLogger.Level.ERROR)
1313             val processExecutor = DefaultProcessExecutor(logger)
1314             val processInfo = ProcessInfoBuilder()
1315                 .setExecutable(executable)
1316                 .addArgs(args)
1317                 .createProcess()
1318 
1319             val processOutputHandler = LoggedProcessOutputHandler(logger)
1320             val result = processExecutor.execute(processInfo, processOutputHandler)
1321 
1322             result.rethrowFailure().assertNormalExitValue()
1323         } catch (e: ProcessException) {
1324             fail("Failed to run $executable (${e.message}): not verifying this API on the old doclava engine")
1325             return false
1326         }
1327         return true
1328     }
1329 
1330     /** Strip comments, trim indent, and add a signature format version header if one is missing */
1331     private fun prepareExpectedApi(expectedApi: String, format: FileFormat): String {
1332         val header = format.header()
1333 
1334         return stripComments(expectedApi, DOT_TXT, stripLineComments = false)
1335             .trimIndent()
1336             .let {
1337                 if (header != null && !it.startsWith("// Signature format:")) header + it else it
1338             }
1339             .trim()
1340     }
1341 
1342     companion object {
1343         private const val API_LEVEL = 31
1344 
1345         private fun getAndroidJarFromEnv(apiLevel: Int): File {
1346             val sdkRoot = System.getenv("ANDROID_SDK_ROOT")
1347                 ?: System.getenv("ANDROID_HOME")
1348                 ?: error("Expected ANDROID_SDK_ROOT to be set")
1349             val jar = File(sdkRoot, "platforms/android-$apiLevel/android.jar")
1350             if (!jar.exists()) {
1351                 error("Missing ${jar.absolutePath} file in the SDK")
1352             }
1353             return jar
1354         }
1355 
1356         fun getAndroidJar(apiLevel: Int = API_LEVEL): File {
1357             val localFile = File("../../prebuilts/sdk/$apiLevel/public/android.jar")
1358             if (localFile.exists()) {
1359                 return localFile
1360             } else {
1361                 val androidJar = File("../../prebuilts/sdk/$apiLevel/android.jar")
1362                 if (androidJar.exists()) return androidJar
1363                 return getAndroidJarFromEnv(apiLevel)
1364             }
1365         }
1366 
1367         private fun readFile(file: File, stripBlankLines: Boolean = false, trim: Boolean = false): String {
1368             var apiLines: List<String> = Files.asCharSource(file, UTF_8).readLines()
1369             if (stripBlankLines) {
1370                 apiLines = apiLines.asSequence().filter { it.isNotBlank() }.toList()
1371             }
1372             var apiText = apiLines.joinToString(separator = "\n") { it }
1373             if (trim) {
1374                 apiText = apiText.trim()
1375             }
1376             return apiText
1377         }
1378     }
1379 }
1380 
1381 /**
1382  * A slight modification of com.android.tools.lint.checks.infrastructure.findKotlinStdLibPath
1383  * that prints program name on error. Returns the paths as metalava args expected by Options.
1384  */
findKotlinStdlibPathArgsnull1385 fun findKotlinStdlibPathArgs(sources: Array<String>): Array<String> {
1386     val classPath: String = System.getProperty("java.class.path")
1387     val paths = mutableListOf<String>()
1388     for (path in classPath.split(':')) {
1389         val file = File(path)
1390         val name = file.name
1391         if (name.startsWith("kotlin-stdlib") ||
1392             name.startsWith("kotlin-reflect") ||
1393             name.startsWith("kotlin-script-runtime")
1394         ) {
1395             paths.add(file.path)
1396         }
1397     }
1398     if (paths.isEmpty()) {
1399         error("Did not find kotlin-stdlib-jre8 in $PROGRAM_NAME classpath: $classPath")
1400     }
1401     val kotlinPathArgs =
1402         if (paths.isNotEmpty() &&
1403             sources.asSequence().any { it.endsWith(DOT_KT) }
1404         ) {
1405             arrayOf(ARG_CLASS_PATH, paths.joinToString(separator = File.pathSeparator) { it })
1406         } else {
1407             emptyArray()
1408         }
1409     return kotlinPathArgs
1410 }
1411 
1412 val intRangeAnnotationSource: TestFile = java(
1413     """
1414         package android.annotation;
1415         import java.lang.annotation.*;
1416         import static java.lang.annotation.ElementType.*;
1417         import static java.lang.annotation.RetentionPolicy.SOURCE;
1418         @Retention(SOURCE)
1419         @Target({METHOD,PARAMETER,FIELD,LOCAL_VARIABLE,ANNOTATION_TYPE})
1420         public @interface IntRange {
1421             long from() default Long.MIN_VALUE;
1422             long to() default Long.MAX_VALUE;
1423         }
1424         """
1425 ).indented()
1426 
1427 val intDefAnnotationSource: TestFile = java(
1428     """
1429     package android.annotation;
1430     import java.lang.annotation.Retention;
1431     import java.lang.annotation.RetentionPolicy;
1432     import java.lang.annotation.Target;
1433     import static java.lang.annotation.ElementType.*;
1434     import static java.lang.annotation.RetentionPolicy.SOURCE;
1435     @Retention(SOURCE)
1436     @Target({ANNOTATION_TYPE})
1437     public @interface IntDef {
1438         int[] value() default {};
1439         boolean flag() default false;
1440     }
1441     """
1442 ).indented()
1443 
1444 val longDefAnnotationSource: TestFile = java(
1445     """
1446     package android.annotation;
1447     import java.lang.annotation.Retention;
1448     import java.lang.annotation.RetentionPolicy;
1449     import java.lang.annotation.Target;
1450     import static java.lang.annotation.ElementType.*;
1451     import static java.lang.annotation.RetentionPolicy.SOURCE;
1452     @Retention(SOURCE)
1453     @Target({ANNOTATION_TYPE})
1454     public @interface LongDef {
1455         long[] value() default {};
1456         boolean flag() default false;
1457     }
1458     """
1459 ).indented()
1460 
1461 @Suppress("ConstantConditionIf")
1462 val nonNullSource: TestFile = java(
1463     """
1464     package android.annotation;
1465     import java.lang.annotation.Retention;
1466     import java.lang.annotation.Target;
1467 
1468     import static java.lang.annotation.ElementType.FIELD;
1469     import static java.lang.annotation.ElementType.METHOD;
1470     import static java.lang.annotation.ElementType.PARAMETER;
1471     import static java.lang.annotation.RetentionPolicy.SOURCE;
1472     /**
1473      * Denotes that a parameter, field or method return value can never be null.
1474      * @paramDoc This value must never be {@code null}.
1475      * @returnDoc This value will never be {@code null}.
1476      * @hide
1477      */
1478     @SuppressWarnings({"WeakerAccess", "JavaDoc"})
1479     @Retention(SOURCE)
1480     @Target({METHOD, PARAMETER, FIELD${if (SUPPORT_TYPE_USE_ANNOTATIONS) ", TYPE_USE" else ""}})
1481     public @interface NonNull {
1482     }
1483     """
1484 ).indented()
1485 
1486 val libcoreNonNullSource: TestFile = java(
1487     """
1488     package libcore.util;
1489     import static java.lang.annotation.ElementType.*;
1490     import static java.lang.annotation.RetentionPolicy.SOURCE;
1491     import java.lang.annotation.*;
1492     @Documented
1493     @Retention(SOURCE)
1494     @Target({TYPE_USE})
1495     public @interface NonNull {
1496     }
1497     """
1498 ).indented()
1499 
1500 val libcoreNullFromTypeParamSource: TestFile = java(
1501     """
1502     package libcore.util;
1503     import static java.lang.annotation.ElementType.*;
1504     import static java.lang.annotation.RetentionPolicy.SOURCE;
1505     import java.lang.annotation.*;
1506     @Documented
1507     @Retention(SOURCE)
1508     @Target({TYPE_USE})
1509     public @interface NullFromTypeParam {
1510     }
1511     """
1512 ).indented()
1513 
1514 val libcoreNullableSource: TestFile = java(
1515     """
1516     package libcore.util;
1517     import static java.lang.annotation.ElementType.*;
1518     import static java.lang.annotation.RetentionPolicy.SOURCE;
1519     import java.lang.annotation.*;
1520     @Documented
1521     @Retention(SOURCE)
1522     @Target({TYPE_USE})
1523     public @interface Nullable {
1524     }
1525     """
1526 ).indented()
1527 
1528 val requiresPermissionSource: TestFile = java(
1529     """
1530     package android.annotation;
1531     import java.lang.annotation.*;
1532     import static java.lang.annotation.ElementType.*;
1533     import static java.lang.annotation.RetentionPolicy.SOURCE;
1534     @Retention(SOURCE)
1535     @Target({ANNOTATION_TYPE,METHOD,CONSTRUCTOR,FIELD,PARAMETER})
1536     public @interface RequiresPermission {
1537         String value() default "";
1538         String[] allOf() default {};
1539         String[] anyOf() default {};
1540         boolean conditional() default false;
1541         @Target({FIELD, METHOD, PARAMETER})
1542         @interface Read {
1543             RequiresPermission value() default @RequiresPermission;
1544         }
1545         @Target({FIELD, METHOD, PARAMETER})
1546         @interface Write {
1547             RequiresPermission value() default @RequiresPermission;
1548         }
1549     }
1550     """
1551 ).indented()
1552 
1553 val requiresFeatureSource: TestFile = java(
1554     """
1555     package android.annotation;
1556     import java.lang.annotation.*;
1557     import static java.lang.annotation.ElementType.*;
1558     import static java.lang.annotation.RetentionPolicy.SOURCE;
1559     @Retention(SOURCE)
1560     @Target({TYPE,FIELD,METHOD,CONSTRUCTOR})
1561     public @interface RequiresFeature {
1562         String value();
1563     }
1564     """
1565 ).indented()
1566 
1567 val requiresApiSource: TestFile = java(
1568     """
1569     package androidx.annotation;
1570     import java.lang.annotation.*;
1571     import static java.lang.annotation.ElementType.*;
1572     import static java.lang.annotation.RetentionPolicy.SOURCE;
1573     @Retention(SOURCE)
1574     @Target({TYPE,FIELD,METHOD,CONSTRUCTOR})
1575     public @interface RequiresApi {
1576         int value() default 1;
1577         int api() default 1;
1578     }
1579     """
1580 ).indented()
1581 
1582 val sdkConstantSource: TestFile = java(
1583     """
1584     package android.annotation;
1585     import java.lang.annotation.*;
1586     @Target({ ElementType.FIELD })
1587     @Retention(RetentionPolicy.SOURCE)
1588     public @interface SdkConstant {
1589         enum SdkConstantType {
1590             ACTIVITY_INTENT_ACTION, BROADCAST_INTENT_ACTION, SERVICE_ACTION, INTENT_CATEGORY, FEATURE
1591         }
1592         SdkConstantType value();
1593     }
1594     """
1595 ).indented()
1596 
1597 val broadcastBehaviorSource: TestFile = java(
1598     """
1599     package android.annotation;
1600     import java.lang.annotation.*;
1601     /** @hide */
1602     @Target({ ElementType.FIELD })
1603     @Retention(RetentionPolicy.SOURCE)
1604     public @interface BroadcastBehavior {
1605         boolean explicitOnly() default false;
1606         boolean registeredOnly() default false;
1607         boolean includeBackground() default false;
1608         boolean protectedBroadcast() default false;
1609     }
1610     """
1611 ).indented()
1612 
1613 @Suppress("ConstantConditionIf")
1614 val nullableSource: TestFile = java(
1615     """
1616     package android.annotation;
1617     import java.lang.annotation.*;
1618     import static java.lang.annotation.ElementType.*;
1619     import static java.lang.annotation.RetentionPolicy.SOURCE;
1620     /**
1621      * Denotes that a parameter, field or method return value can be null.
1622      * @paramDoc This value may be {@code null}.
1623      * @returnDoc This value may be {@code null}.
1624      * @hide
1625      */
1626     @SuppressWarnings({"WeakerAccess", "JavaDoc"})
1627     @Retention(SOURCE)
1628     @Target({METHOD, PARAMETER, FIELD${if (SUPPORT_TYPE_USE_ANNOTATIONS) ", TYPE_USE" else ""}})
1629     public @interface Nullable {
1630     }
1631     """
1632 ).indented()
1633 
1634 val androidxNonNullSource: TestFile = java(
1635     """
1636     package androidx.annotation;
1637     import java.lang.annotation.*;
1638     import static java.lang.annotation.ElementType.*;
1639     import static java.lang.annotation.RetentionPolicy.SOURCE;
1640     @SuppressWarnings("WeakerAccess")
1641     @Retention(SOURCE)
1642     @Target({METHOD, PARAMETER, FIELD, LOCAL_VARIABLE, TYPE_USE, ANNOTATION_TYPE, PACKAGE})
1643     public @interface NonNull {
1644     }
1645     """
1646 ).indented()
1647 
1648 val androidxNullableSource: TestFile = java(
1649     """
1650     package androidx.annotation;
1651     import java.lang.annotation.*;
1652     import static java.lang.annotation.ElementType.*;
1653     import static java.lang.annotation.RetentionPolicy.SOURCE;
1654     @SuppressWarnings("WeakerAccess")
1655     @Retention(SOURCE)
1656     @Target({METHOD, PARAMETER, FIELD, LOCAL_VARIABLE, TYPE_USE, ANNOTATION_TYPE, PACKAGE})
1657     public @interface Nullable {
1658     }
1659     """
1660 ).indented()
1661 
1662 val recentlyNonNullSource: TestFile = java(
1663     """
1664     package androidx.annotation;
1665     import java.lang.annotation.*;
1666     import static java.lang.annotation.ElementType.*;
1667     import static java.lang.annotation.RetentionPolicy.SOURCE;
1668     @SuppressWarnings("WeakerAccess")
1669     @Retention(SOURCE)
1670     @Target({METHOD, PARAMETER, FIELD, TYPE_USE})
1671     public @interface RecentlyNonNull {
1672     }
1673     """
1674 ).indented()
1675 
1676 val recentlyNullableSource: TestFile = java(
1677     """
1678     package androidx.annotation;
1679     import java.lang.annotation.*;
1680     import static java.lang.annotation.ElementType.*;
1681     import static java.lang.annotation.RetentionPolicy.SOURCE;
1682     @SuppressWarnings("WeakerAccess")
1683     @Retention(SOURCE)
1684     @Target({METHOD, PARAMETER, FIELD, TYPE_USE})
1685     public @interface RecentlyNullable {
1686     }
1687     """
1688 ).indented()
1689 
1690 val androidxIntRangeSource: TestFile = java(
1691     """
1692     package androidx.annotation;
1693     import java.lang.annotation.*;
1694     import static java.lang.annotation.ElementType.*;
1695     import static java.lang.annotation.RetentionPolicy.SOURCE;
1696     @Retention(CLASS)
1697     @Target({METHOD, PARAMETER, FIELD, LOCAL_VARIABLE, ANNOTATION_TYPE})
1698     public @interface IntRange {
1699         long from() default Long.MIN_VALUE;
1700         long to() default Long.MAX_VALUE;
1701     }
1702     """
1703 ).indented()
1704 
1705 val supportParameterName: TestFile = java(
1706     """
1707     package androidx.annotation;
1708     import java.lang.annotation.*;
1709     import static java.lang.annotation.ElementType.*;
1710     import static java.lang.annotation.RetentionPolicy.SOURCE;
1711     @SuppressWarnings("WeakerAccess")
1712     @Retention(SOURCE)
1713     @Target({METHOD, PARAMETER, FIELD})
1714     public @interface ParameterName {
1715         String value();
1716     }
1717     """
1718 ).indented()
1719 
1720 val supportDefaultValue: TestFile = java(
1721     """
1722     package androidx.annotation;
1723     import java.lang.annotation.*;
1724     import static java.lang.annotation.ElementType.*;
1725     import static java.lang.annotation.RetentionPolicy.SOURCE;
1726     @SuppressWarnings("WeakerAccess")
1727     @Retention(SOURCE)
1728     @Target({METHOD, PARAMETER, FIELD})
1729     public @interface DefaultValue {
1730         String value();
1731     }
1732     """
1733 ).indented()
1734 
1735 val uiThreadSource: TestFile = java(
1736     """
1737     package androidx.annotation;
1738     import java.lang.annotation.*;
1739     import static java.lang.annotation.ElementType.*;
1740     import static java.lang.annotation.RetentionPolicy.SOURCE;
1741     /**
1742      * Denotes that the annotated method or constructor should only be called on the
1743      * UI thread. If the annotated element is a class, then all methods in the class
1744      * should be called on the UI thread.
1745      * @memberDoc This method must be called on the thread that originally created
1746      *            this UI element. This is typically the main thread of your app.
1747      * @classDoc Methods in this class must be called on the thread that originally created
1748      *            this UI element, unless otherwise noted. This is typically the
1749      *            main thread of your app. * @hide
1750      */
1751     @SuppressWarnings({"WeakerAccess", "JavaDoc"})
1752     @Retention(SOURCE)
1753     @Target({METHOD,CONSTRUCTOR,TYPE,PARAMETER})
1754     public @interface UiThread {
1755     }
1756     """
1757 ).indented()
1758 
1759 val workerThreadSource: TestFile = java(
1760     """
1761     package androidx.annotation;
1762     import java.lang.annotation.*;
1763     import static java.lang.annotation.ElementType.*;
1764     import static java.lang.annotation.RetentionPolicy.SOURCE;
1765     /**
1766      * @memberDoc This method may take several seconds to complete, so it should
1767      *            only be called from a worker thread.
1768      * @classDoc Methods in this class may take several seconds to complete, so it should
1769      *            only be called from a worker thread unless otherwise noted.
1770      * @hide
1771      */
1772     @SuppressWarnings({"WeakerAccess", "JavaDoc"})
1773     @Retention(SOURCE)
1774     @Target({METHOD,CONSTRUCTOR,TYPE,PARAMETER})
1775     public @interface WorkerThread {
1776     }
1777     """
1778 ).indented()
1779 
1780 val suppressLintSource: TestFile = java(
1781     """
1782     package android.annotation;
1783 
1784     import static java.lang.annotation.ElementType.*;
1785     import java.lang.annotation.*;
1786     @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
1787     @Retention(RetentionPolicy.CLASS)
1788     public @interface SuppressLint {
1789         String[] value();
1790     }
1791     """
1792 ).indented()
1793 
1794 val systemServiceSource: TestFile = java(
1795     """
1796     package android.annotation;
1797     import static java.lang.annotation.ElementType.TYPE;
1798     import static java.lang.annotation.RetentionPolicy.SOURCE;
1799     import java.lang.annotation.*;
1800     @Retention(SOURCE)
1801     @Target(TYPE)
1802     public @interface SystemService {
1803         String value();
1804     }
1805     """
1806 ).indented()
1807 
1808 val systemApiSource: TestFile = java(
1809     """
1810     package android.annotation;
1811     import static java.lang.annotation.ElementType.*;
1812     import java.lang.annotation.*;
1813     @Target({TYPE, FIELD, METHOD, CONSTRUCTOR, ANNOTATION_TYPE, PACKAGE})
1814     @Retention(RetentionPolicy.SOURCE)
1815     public @interface SystemApi {
1816     }
1817     """
1818 ).indented()
1819 
1820 val testApiSource: TestFile = java(
1821     """
1822     package android.annotation;
1823     import static java.lang.annotation.ElementType.*;
1824     import java.lang.annotation.*;
1825     @Target({TYPE, FIELD, METHOD, CONSTRUCTOR, ANNOTATION_TYPE, PACKAGE})
1826     @Retention(RetentionPolicy.SOURCE)
1827     public @interface TestApi {
1828     }
1829     """
1830 ).indented()
1831 
1832 val widgetSource: TestFile = java(
1833     """
1834     package android.annotation;
1835     import java.lang.annotation.*;
1836     @Target({ ElementType.TYPE })
1837     @Retention(RetentionPolicy.SOURCE)
1838     public @interface Widget {
1839     }
1840     """
1841 ).indented()
1842 
1843 val restrictToSource: TestFile = kotlin(
1844     """
1845     package androidx.annotation
1846 
1847     import androidx.annotation.RestrictTo.Scope
1848     import java.lang.annotation.ElementType.*
1849 
1850     @MustBeDocumented
1851     @kotlin.annotation.Retention(AnnotationRetention.BINARY)
1852     @Target(
1853         AnnotationTarget.ANNOTATION_CLASS,
1854         AnnotationTarget.CLASS,
1855         AnnotationTarget.FUNCTION,
1856         AnnotationTarget.PROPERTY_GETTER,
1857         AnnotationTarget.PROPERTY_SETTER,
1858         AnnotationTarget.CONSTRUCTOR,
1859         AnnotationTarget.FIELD,
1860         AnnotationTarget.FILE
1861     )
1862     // Needed due to Kotlin's lack of PACKAGE annotation target
1863     // https://youtrack.jetbrains.com/issue/KT-45921
1864     @Suppress("DEPRECATED_JAVA_ANNOTATION")
1865     @java.lang.annotation.Target(ANNOTATION_TYPE, TYPE, METHOD, CONSTRUCTOR, FIELD, PACKAGE)
1866     public annotation class RestrictTo(vararg val value: Scope) {
1867         public enum class Scope {
1868             LIBRARY,
1869             LIBRARY_GROUP,
1870             LIBRARY_GROUP_PREFIX,
1871             @Deprecated("Use LIBRARY_GROUP_PREFIX instead.")
1872             GROUP_ID,
1873             TESTS,
1874             SUBCLASSES,
1875         }
1876     }
1877     """
1878 ).indented()
1879 
1880 val visibleForTestingSource: TestFile = java(
1881     """
1882     package androidx.annotation;
1883     import static java.lang.annotation.RetentionPolicy.CLASS;
1884     import java.lang.annotation.Retention;
1885     @Retention(CLASS)
1886     @SuppressWarnings("WeakerAccess")
1887     public @interface VisibleForTesting {
1888         int otherwise() default PRIVATE;
1889         int PRIVATE = 2;
1890         int PACKAGE_PRIVATE = 3;
1891         int PROTECTED = 4;
1892         int NONE = 5;
1893     }
1894     """
1895 ).indented()
1896 
1897 val columnSource: TestFile = java(
1898     """
1899     package android.provider;
1900 
1901     import static java.lang.annotation.ElementType.FIELD;
1902     import static java.lang.annotation.RetentionPolicy.RUNTIME;
1903 
1904     import android.content.ContentProvider;
1905     import android.content.ContentValues;
1906     import android.database.Cursor;
1907 
1908     import java.lang.annotation.Documented;
1909     import java.lang.annotation.Retention;
1910     import java.lang.annotation.Target;
1911 
1912     @Documented
1913     @Retention(RUNTIME)
1914     @Target({FIELD})
1915     public @interface Column {
1916         int value();
1917         boolean readOnly() default false;
1918     }
1919     """
1920 ).indented()
1921 
1922 val publishedApiSource: TestFile = kotlin(
1923     """
1924     /**
1925      * When applied to a class or a member with internal visibility allows to use it from public inline functions and
1926      * makes it effectively public.
1927      *
1928      * Public inline functions cannot use non-public API, since if they are inlined, those non-public API references
1929      * would violate access restrictions at a call site (https://kotlinlang.org/docs/reference/inline-functions.html#public-inline-restrictions).
1930      *
1931      * To overcome this restriction an `internal` declaration can be annotated with the `@PublishedApi` annotation:
1932      * - this allows to call that declaration from public inline functions;
1933      * - the declaration becomes effectively public, and this should be considered with respect to binary compatibility maintaining.
1934      */
1935     @Target(AnnotationTarget.CLASS, AnnotationTarget.CONSTRUCTOR, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY)
1936     @Retention(AnnotationRetention.BINARY)
1937     @MustBeDocumented
1938     @SinceKotlin("1.1")
1939     public annotation class PublishedApi
1940     """
1941 ).indented()
1942 
1943 val deprecatedForSdkSource: TestFile = java(
1944     """
1945     package android.annotation;
1946     import static java.lang.annotation.RetentionPolicy.SOURCE;
1947     import java.lang.annotation.Retention;
1948     /** @hide */
1949     @Retention(SOURCE)
1950     @SuppressWarnings("WeakerAccess")
1951     public @interface DeprecatedForSdk {
1952         String value();
1953         Class<?>[] allowIn() default {};
1954     }
1955     """
1956 ).indented()
1957