1 /* 2 * 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.tools.metalava.model.defaultConfiguration 20 import org.junit.Assert.assertEquals 21 import org.junit.Assert.assertFalse 22 import org.junit.Assert.assertTrue 23 import org.junit.Test 24 import java.io.File 25 import java.io.PrintWriter 26 import java.io.StringWriter 27 28 @Suppress("PrivatePropertyName") 29 class OptionsTest : DriverTest() { 30 private val DESCRIPTION = """ 31 $PROGRAM_NAME extracts metadata from source code to generate artifacts such as the signature files, the SDK stub files, 32 external annotations etc. 33 """.trimIndent() 34 35 private val FLAGS = """ 36 Usage: metalava <flags> 37 38 39 General: 40 --help 41 This message. 42 --version 43 Show the version of metalava. 44 --quiet 45 Only include vital output 46 --verbose 47 Include extra diagnostic output 48 --color 49 Attempt to colorize the output (defaults to true if ${"$"}TERM is xterm) 50 --no-color 51 Do not attempt to colorize the output 52 --only-update-api 53 Cancel any other "action" flags other than generating signature files. This 54 is here to make it easier customize build system tasks, particularly for 55 the "make update-api" task. 56 --only-check-api 57 Cancel any other "action" flags other than checking signature files. This 58 is here to make it easier customize build system tasks, particularly for 59 the "make checkapi" task. 60 --repeat-errors-max <N> 61 When specified, repeat at most N errors before finishing. 62 63 64 API sources: 65 --source-files <files> 66 A comma separated list of source files to be parsed. Can also be @ followed 67 by a path to a text file containing paths to the full set of files to 68 parse. 69 --source-path <paths> 70 One or more directories (separated by `:`) containing source files (within 71 a package hierarchy). If --strict-input-files, --strict-input-files:warn, 72 or --strict-input-files:stack are used, files accessed under --source-path 73 that are not explicitly specified in --source-files are reported as 74 violations. 75 --classpath <paths> 76 One or more directories or jars (separated by `:`) containing classes that 77 should be on the classpath when parsing the source files 78 --merge-qualifier-annotations <file> 79 An external annotations file to merge and overlay the sources, or a 80 directory of such files. Should be used for annotations intended for 81 inclusion in the API to be written out, e.g. nullability. Formats supported 82 are: IntelliJ's external annotations database format, .jar or .zip files 83 containing those, Android signature files, and Java stub files. 84 --merge-inclusion-annotations <file> 85 An external annotations file to merge and overlay the sources, or a 86 directory of such files. Should be used for annotations which determine 87 inclusion in the API to be written out, i.e. show and hide. The only format 88 supported is Java stub files. 89 --validate-nullability-from-merged-stubs 90 Triggers validation of nullability annotations for any class where 91 --merge-qualifier-annotations includes a Java stub file. 92 --validate-nullability-from-list 93 Triggers validation of nullability annotations for any class listed in the 94 named file (one top-level class per line, # prefix for comment line). 95 --nullability-warnings-txt <file> 96 Specifies where to write warnings encountered during validation of 97 nullability annotations. (Does not trigger validation by itself.) 98 --nullability-errors-non-fatal 99 Specifies that errors encountered during validation of nullability 100 annotations should not be treated as errors. They will be written out to 101 the file specified in --nullability-warnings-txt instead. 102 --input-api-jar <file> 103 A .jar file to read APIs from directly 104 --manifest <file> 105 A manifest file, used to for check permissions to cross check APIs 106 --hide-package <package> 107 Remove the given packages from the API even if they have not been marked 108 with @hide 109 --show-annotation <annotation class> 110 Unhide any hidden elements that are also annotated with the given 111 annotation 112 --show-single-annotation <annotation> 113 Like --show-annotation, but does not apply to members; these must also be 114 explicitly annotated 115 --show-for-stub-purposes-annotation <annotation class> 116 Like --show-annotation, but elements annotated with it are assumed to be 117 "implicitly" included in the API surface, and they'll be included in 118 certain kinds of output such as stubs, but not in others, such as the 119 signature file and API lint 120 --hide-annotation <annotation class> 121 Treat any elements annotated with the given annotation as hidden 122 --hide-meta-annotation <meta-annotation class> 123 Treat as hidden any elements annotated with an annotation which is itself 124 annotated with the given meta-annotation 125 --show-unannotated 126 Include un-annotated public APIs in the signature file as well 127 --java-source <level> 128 Sets the source level for Java source files; default is 1.8. 129 --kotlin-source <level> 130 Sets the source level for Kotlin source files; default is 1.6. 131 --sdk-home <dir> 132 If set, locate the `android.jar` file from the given Android SDK 133 --compile-sdk-version <api> 134 Use the given API level 135 --jdk-home <dir> 136 If set, add the Java APIs from the given JDK to the classpath 137 --stub-packages <package-list> 138 List of packages (separated by :) which will be used to filter out 139 irrelevant code. If specified, only code in these packages will be included 140 in signature files, stubs, etc. (This is not limited to just the stubs; the 141 name is historical.) You can also use ".*" at the end to match subpackages, 142 so `foo.*` will match both `foo` and `foo.bar`. 143 --subtract-api <api file> 144 Subtracts the API in the given signature or jar file from the current API 145 being emitted via --api, --stubs, --doc-stubs, etc. Note that the 146 subtraction only applies to classes; it does not subtract members. 147 --typedefs-in-signatures <ref|inline> 148 Whether to include typedef annotations in signature files. 149 `--typedefs-in-signatures ref` will include just a reference to the typedef 150 class, which is not itself part of the API and is not included as a class, 151 and `--typedefs-in-signatures inline` will include the constants themselves 152 into each usage site. You can also supply `--typedefs-in-signatures none` 153 to explicitly turn it off, if the default ever changes. 154 --ignore-classes-on-classpath 155 Prevents references to classes on the classpath from being added to the 156 generated stub files. 157 158 159 Documentation: 160 --public 161 Only include elements that are public 162 --protected 163 Only include elements that are public or protected 164 --package 165 Only include elements that are public, protected or package protected 166 --private 167 Include all elements except those that are marked hidden 168 --hidden 169 Include all elements, including hidden 170 171 172 Extracting Signature Files: 173 --api <file> 174 Generate a signature descriptor file 175 --dex-api <file> 176 Generate a DEX signature descriptor file listing the APIs 177 --removed-api <file> 178 Generate a signature descriptor file for APIs that have been removed 179 --format=<v1,v2,v3,...> 180 Sets the output signature file format to be the given version. 181 --output-kotlin-nulls[=yes|no] 182 Controls whether nullness annotations should be formatted as in Kotlin 183 (with "?" for nullable types, "" for non nullable types, and "!" for 184 unknown. The default is yes. 185 --output-default-values[=yes|no] 186 Controls whether default values should be included in signature files. The 187 default is yes. 188 --include-signature-version[=yes|no] 189 Whether the signature files should include a comment listing the format 190 version of the signature file. 191 --proguard <file> 192 Write a ProGuard keep file for the API 193 --sdk-values <dir> 194 Write SDK values files to the given directory 195 196 197 Generating Stubs: 198 --stubs <dir> 199 Generate stub source files for the API 200 --doc-stubs <dir> 201 Generate documentation stub source files for the API. Documentation stub 202 files are similar to regular stub files, but there are some differences. 203 For example, in the stub files, we'll use special annotations like 204 @RecentlyNonNull instead of @NonNull to indicate that an element is 205 recently marked as non null, whereas in the documentation stubs we'll just 206 list this as @NonNull. Another difference is that @doconly elements are 207 included in documentation stubs, but not regular stubs, etc. 208 --kotlin-stubs 209 [CURRENTLY EXPERIMENTAL] If specified, stubs generated from Kotlin source 210 code will be written in Kotlin rather than the Java programming language. 211 --include-annotations 212 Include annotations such as @Nullable in the stub files. 213 --exclude-all-annotations 214 Exclude annotations such as @Nullable from the stub files; the default. 215 --pass-through-annotation <annotation classes> 216 A comma separated list of fully qualified names of annotation classes that 217 must be passed through unchanged. 218 --exclude-annotation <annotation classes> 219 A comma separated list of fully qualified names of annotation classes that 220 must be stripped from metalava's outputs. 221 --enhance-documentation 222 Enhance documentation in various ways, for example auto-generating 223 documentation based on source annotations present in the code. This is 224 implied by --doc-stubs. 225 --exclude-documentation-from-stubs 226 Exclude element documentation (javadoc and kdoc) from the generated stubs. 227 (Copyright notices are not affected by this, they are always included. 228 Documentation stubs (--doc-stubs) are not affected.) 229 --write-stubs-source-list <file> 230 Write the list of generated stub files into the given source list file. If 231 generating documentation stubs and you haven't also specified 232 --write-doc-stubs-source-list, this list will refer to the documentation 233 stubs; otherwise it's the non-documentation stubs. 234 --write-doc-stubs-source-list <file> 235 Write the list of generated doc stub files into the given source list file 236 237 238 Diffs and Checks: 239 --input-kotlin-nulls[=yes|no] 240 Whether the signature file being read should be interpreted as having 241 encoded its types using Kotlin style types: a suffix of "?" for nullable 242 types, no suffix for non nullable types, and "!" for unknown. The default 243 is no. 244 --check-compatibility:type:state <file> 245 Check compatibility. Type is one of 'api' and 'removed', which checks 246 either the public api or the removed api. State is one of 'current' and 247 'released', to check either the currently in development API or the last 248 publicly released API, respectively. Different compatibility checks apply 249 in the two scenarios. For example, to check the code base against the 250 current public API, use --check-compatibility:api:current. 251 --check-compatibility:base <file> 252 When performing a compat check, use the provided signature file as a base 253 api, which is treated as part of the API being checked. This allows us to 254 compute the full API surface from a partial API surface (e.g. the current 255 @SystemApi txt file), which allows us to recognize when an API is moved 256 from the partial API to the base API and avoid incorrectly flagging this as 257 an API removal. 258 --api-lint [api file] 259 Check API for Android API best practices. If a signature file is provided, 260 only the APIs that are new since the API will be checked. 261 --api-lint-ignore-prefix [prefix] 262 A list of package prefixes to ignore API issues in when running with 263 --api-lint. 264 --migrate-nullness <api file> 265 Compare nullness information with the previous stable API and mark newly 266 annotated APIs as under migration. 267 --warnings-as-errors 268 Promote all warnings to errors 269 --lints-as-errors 270 Promote all API lint warnings to errors 271 --error <id> 272 Report issues of the given id as errors 273 --warning <id> 274 Report issues of the given id as warnings 275 --lint <id> 276 Report issues of the given id as having lint-severity 277 --hide <id> 278 Hide/skip issues of the given id 279 --report-even-if-suppressed <file> 280 Write all issues into the given file, even if suppressed (via annotation or 281 baseline) but not if hidden (by '--hide') 282 --baseline <file> 283 Filter out any errors already reported in the given baseline file, or 284 create if it does not already exist 285 --update-baseline [file] 286 Rewrite the existing baseline file with the current set of warnings. If 287 some warnings have been fixed, this will delete them from the baseline 288 files. If a file is provided, the updated baseline is written to the given 289 file; otherwise the original source baseline file is updated. 290 --baseline:api-lint <file> --update-baseline:api-lint [file] 291 Same as --baseline and --update-baseline respectively, but used 292 specifically for API lint issues performed by --api-lint. 293 --baseline:compatibility:released <file> --update-baseline:compatibility:released [file] 294 Same as --baseline and --update-baseline respectively, but used 295 specifically for API compatibility issues performed by 296 --check-compatibility:api:released and 297 --check-compatibility:removed:released. 298 --merge-baseline [file] 299 Like --update-baseline, but instead of always replacing entries in the 300 baseline, it will merge the existing baseline with the new baseline. This 301 is useful if metalava runs multiple times on the same source tree with 302 different flags at different times, such as occasionally with --api-lint. 303 --pass-baseline-updates 304 Normally, encountering error will fail the build, even when updating 305 baselines. This flag allows you to tell metalava to continue without 306 errors, such that all the baselines in the source tree can be updated in 307 one go. 308 --delete-empty-baselines 309 Whether to delete baseline files if they are updated and there is nothing 310 to include. 311 --error-message:api-lint <message> 312 If set, metalava shows it when errors are detected in --api-lint. 313 --error-message:compatibility:released <message> 314 If set, metalava shows it when errors are detected in 315 --check-compatibility:api:released and 316 --check-compatibility:removed:released. 317 318 319 JDiff: 320 --api-xml <file> 321 Like --api, but emits the API in the JDiff XML format instead 322 --convert-to-jdiff <sig> <xml> 323 Reads in the given signature file, and writes it out in the JDiff XML 324 format. Can be specified multiple times. 325 --convert-new-to-jdiff <old> <new> <xml> 326 Reads in the given old and new api files, computes the difference, and 327 writes out only the new parts of the API in the JDiff XML format. 328 329 330 Extracting Annotations: 331 --extract-annotations <zipfile> 332 Extracts source annotations from the source files and writes them into the 333 given zip file 334 --include-annotation-classes <dir> 335 Copies the given stub annotation source files into the generated stub 336 sources; <dir> is typically metalava/stub-annotations/src/main/java/. 337 --rewrite-annotations <dir/jar> 338 For a bytecode folder or output jar, rewrites the androidx annotations to 339 be package private 340 --force-convert-to-warning-nullability-annotations <package1:-package2:...> 341 On every API declared in a class referenced by the given filter, makes 342 nullability issues appear to callers as warnings rather than errors by 343 replacing @Nullable/@NonNull in these APIs with 344 @RecentlyNullable/@RecentlyNonNull 345 --copy-annotations <source> <dest> 346 For a source folder full of annotation sources, generates corresponding 347 package private versions of the same annotations. 348 --include-source-retention 349 If true, include source-retention annotations in the stub files. Does not 350 apply to signature files. Source retention annotations are extracted into 351 the external annotations files instead. 352 353 354 Injecting API Levels: 355 --apply-api-levels <api-versions.xml> 356 Reads an XML file containing API level descriptions and merges the 357 information into the documentation 358 359 360 Extracting API Levels: 361 --generate-api-levels <xmlfile> 362 Reads android.jar SDK files and generates an XML file recording the API 363 level for each class, method and field 364 --android-jar-pattern <pattern> 365 Patterns to use to locate Android JAR files. The default is 366 ${"$"}ANDROID_HOME/platforms/android-%/android.jar. 367 --first-version 368 Sets the first API level to generate an API database from; usually 1 369 --current-version 370 Sets the current API level of the current source code 371 --current-codename 372 Sets the code name for the current source code 373 --current-jar 374 Points to the current API jar, if any 375 376 377 Sandboxing: 378 --no-implicit-root 379 Disable implicit root directory detection. Otherwise, metalava adds in 380 source roots implied by the source files 381 --strict-input-files <file> 382 Do not read files that are not explicitly specified in the command line. 383 All violations are written to the given file. Reads on directories are 384 always allowed, but metalava still tracks reads on directories that are not 385 specified in the command line, and write them to the file. 386 --strict-input-files:warn <file> 387 Warn when files not explicitly specified on the command line are read. All 388 violations are written to the given file. Reads on directories not 389 specified in the command line are allowed but also logged. 390 --strict-input-files:stack <file> 391 Same as --strict-input-files but also print stacktraces. 392 --strict-input-files-exempt <files or dirs> 393 Used with --strict-input-files. Explicitly allow access to files and/or 394 directories (separated by `:). Can also be @ followed by a path to a text 395 file containing paths to the full set of files and/or directories. 396 397 398 Environment Variables: 399 METALAVA_DUMP_ARGV 400 Set to true to have metalava emit all the arguments it was invoked with. 401 Helpful when debugging or reproducing under a debugger what the build 402 system is doing. 403 METALAVA_PREPEND_ARGS 404 One or more arguments (concatenated by space) to insert into the command 405 line, before the documentation flags. 406 METALAVA_APPEND_ARGS 407 One or more arguments (concatenated by space) to append to the end of the 408 command line, after the generate documentation flags. 409 410 """.trimIndent() 411 412 @Test Test invalid argumentsnull413 fun `Test invalid arguments`() { 414 val args = listOf(ARG_NO_COLOR, "--blah-blah-blah") 415 416 val stdout = StringWriter() 417 val stderr = StringWriter() 418 run( 419 originalArgs = args.toTypedArray(), 420 stdout = PrintWriter(stdout), 421 stderr = PrintWriter(stderr) 422 ) 423 assertEquals(BANNER + "\n\n", stdout.toString()) 424 assertEquals( 425 """ 426 427 Aborting: Invalid argument --blah-blah-blah 428 429 $FLAGS 430 431 """.trimIndent(), 432 stderr.toString() 433 ) 434 } 435 436 @Test Test helpnull437 fun `Test help`() { 438 val args = listOf(ARG_NO_COLOR, "--help") 439 440 val stdout = StringWriter() 441 val stderr = StringWriter() 442 run( 443 originalArgs = args.toTypedArray(), 444 stdout = PrintWriter(stdout), 445 stderr = PrintWriter(stderr) 446 ) 447 assertEquals("", stderr.toString()) 448 assertEquals( 449 """ 450 $BANNER 451 452 453 $DESCRIPTION 454 455 $FLAGS 456 457 """.trimIndent(), 458 stdout.toString() 459 ) 460 } 461 462 @Test Test issue severity optionsnull463 fun `Test issue severity options`() { 464 check( 465 extraArguments = arrayOf( 466 "--hide", 467 "StartWithLower", 468 "--lint", 469 "EndsWithImpl", 470 "--warning", 471 "StartWithUpper", 472 "--error", 473 "ArrayReturn" 474 ) 475 ) 476 assertEquals(Severity.HIDDEN, defaultConfiguration.getSeverity(Issues.START_WITH_LOWER)) 477 assertEquals(Severity.LINT, defaultConfiguration.getSeverity(Issues.ENDS_WITH_IMPL)) 478 assertEquals(Severity.WARNING, defaultConfiguration.getSeverity(Issues.START_WITH_UPPER)) 479 assertEquals(Severity.ERROR, defaultConfiguration.getSeverity(Issues.ARRAY_RETURN)) 480 } 481 482 @Test Test multiple issue severity optionsnull483 fun `Test multiple issue severity options`() { 484 check( 485 extraArguments = arrayOf("--hide", "StartWithLower,StartWithUpper,ArrayReturn") 486 ) 487 assertEquals(Severity.HIDDEN, defaultConfiguration.getSeverity(Issues.START_WITH_LOWER)) 488 assertEquals(Severity.HIDDEN, defaultConfiguration.getSeverity(Issues.START_WITH_UPPER)) 489 assertEquals(Severity.HIDDEN, defaultConfiguration.getSeverity(Issues.ARRAY_RETURN)) 490 } 491 492 @Test Test issue severity options with inheriting issuesnull493 fun `Test issue severity options with inheriting issues`() { 494 check( 495 extraArguments = arrayOf("--error", "RemovedClass") 496 ) 497 assertEquals(Severity.ERROR, defaultConfiguration.getSeverity(Issues.REMOVED_CLASS)) 498 assertEquals(Severity.ERROR, defaultConfiguration.getSeverity(Issues.REMOVED_DEPRECATED_CLASS)) 499 } 500 501 @Test Test issue severity options with case insensitive namesnull502 fun `Test issue severity options with case insensitive names`() { 503 check( 504 extraArguments = arrayOf("--hide", "arrayreturn"), 505 expectedIssues = "warning: Case-insensitive issue matching is deprecated, use --hide ArrayReturn instead of --hide arrayreturn [DeprecatedOption]" 506 ) 507 assertEquals(Severity.HIDDEN, defaultConfiguration.getSeverity(Issues.ARRAY_RETURN)) 508 } 509 510 @Test Test issue severity options with non-existing issuenull511 fun `Test issue severity options with non-existing issue`() { 512 check( 513 extraArguments = arrayOf("--hide", "ThisIssueDoesNotExist"), 514 expectedFail = "Aborting: Unknown issue id: --hide ThisIssueDoesNotExist" 515 ) 516 } 517 518 @Test Test for --strict-input-files-exemptnull519 fun `Test for --strict-input-files-exempt`() { 520 val top = temporaryFolder.newFolder() 521 522 val dir = File(top, "childdir").apply { mkdirs() } 523 val grandchild1 = File(dir, "grandchiild1").apply { createNewFile() } 524 val grandchild2 = File(dir, "grandchiild2").apply { createNewFile() } 525 val file1 = File(top, "file1").apply { createNewFile() } 526 val file2 = File(top, "file2").apply { createNewFile() } 527 528 try { 529 check( 530 extraArguments = arrayOf( 531 "--strict-input-files-exempt", 532 file1.path + File.pathSeparatorChar + dir.path 533 ) 534 ) 535 536 assertTrue(FileReadSandbox.isAccessAllowed(file1)) 537 assertTrue(FileReadSandbox.isAccessAllowed(grandchild1)) 538 assertTrue(FileReadSandbox.isAccessAllowed(grandchild2)) 539 540 assertFalse(FileReadSandbox.isAccessAllowed(file2)) // Access *not* allowed 541 } finally { 542 FileReadSandbox.reset() 543 } 544 } 545 } 546