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