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:released <file> 245 Check compatibility. Type is one of 'api' and 'removed', which checks 246 either the public api or the removed api. 247 --check-compatibility:base <file> 248 When performing a compat check, use the provided signature file as a base 249 api, which is treated as part of the API being checked. This allows us to 250 compute the full API surface from a partial API surface (e.g. the current 251 @SystemApi txt file), which allows us to recognize when an API is moved 252 from the partial API to the base API and avoid incorrectly flagging this as 253 an API removal. 254 --api-lint [api file] 255 Check API for Android API best practices. If a signature file is provided, 256 only the APIs that are new since the API will be checked. 257 --api-lint-ignore-prefix [prefix] 258 A list of package prefixes to ignore API issues in when running with 259 --api-lint. 260 --migrate-nullness <api file> 261 Compare nullness information with the previous stable API and mark newly 262 annotated APIs as under migration. 263 --warnings-as-errors 264 Promote all warnings to errors 265 --lints-as-errors 266 Promote all API lint warnings to errors 267 --error <id> 268 Report issues of the given id as errors 269 --warning <id> 270 Report issues of the given id as warnings 271 --lint <id> 272 Report issues of the given id as having lint-severity 273 --hide <id> 274 Hide/skip issues of the given id 275 --report-even-if-suppressed <file> 276 Write all issues into the given file, even if suppressed (via annotation or 277 baseline) but not if hidden (by '--hide') 278 --baseline <file> 279 Filter out any errors already reported in the given baseline file, or 280 create if it does not already exist 281 --update-baseline [file] 282 Rewrite the existing baseline file with the current set of warnings. If 283 some warnings have been fixed, this will delete them from the baseline 284 files. If a file is provided, the updated baseline is written to the given 285 file; otherwise the original source baseline file is updated. 286 --baseline:api-lint <file> --update-baseline:api-lint [file] 287 Same as --baseline and --update-baseline respectively, but used 288 specifically for API lint issues performed by --api-lint. 289 --baseline:compatibility:released <file> --update-baseline:compatibility:released [file] 290 Same as --baseline and --update-baseline respectively, but used 291 specifically for API compatibility issues performed by 292 --check-compatibility:api:released and 293 --check-compatibility:removed:released. 294 --merge-baseline [file] 295 Like --update-baseline, but instead of always replacing entries in the 296 baseline, it will merge the existing baseline with the new baseline. This 297 is useful if metalava runs multiple times on the same source tree with 298 different flags at different times, such as occasionally with --api-lint. 299 --pass-baseline-updates 300 Normally, encountering error will fail the build, even when updating 301 baselines. This flag allows you to tell metalava to continue without 302 errors, such that all the baselines in the source tree can be updated in 303 one go. 304 --delete-empty-baselines 305 Whether to delete baseline files if they are updated and there is nothing 306 to include. 307 --error-message:api-lint <message> 308 If set, metalava shows it when errors are detected in --api-lint. 309 --error-message:compatibility:released <message> 310 If set, metalava shows it when errors are detected in 311 --check-compatibility:api:released and 312 --check-compatibility:removed:released. 313 314 315 JDiff: 316 --api-xml <file> 317 Like --api, but emits the API in the JDiff XML format instead 318 --convert-to-jdiff <sig> <xml> 319 Reads in the given signature file, and writes it out in the JDiff XML 320 format. Can be specified multiple times. 321 --convert-new-to-jdiff <old> <new> <xml> 322 Reads in the given old and new api files, computes the difference, and 323 writes out only the new parts of the API in the JDiff XML format. 324 325 326 Extracting Annotations: 327 --extract-annotations <zipfile> 328 Extracts source annotations from the source files and writes them into the 329 given zip file 330 --force-convert-to-warning-nullability-annotations <package1:-package2:...> 331 On every API declared in a class referenced by the given filter, makes 332 nullability issues appear to callers as warnings rather than errors by 333 replacing @Nullable/@NonNull in these APIs with 334 @RecentlyNullable/@RecentlyNonNull 335 --copy-annotations <source> <dest> 336 For a source folder full of annotation sources, generates corresponding 337 package private versions of the same annotations. 338 --include-source-retention 339 If true, include source-retention annotations in the stub files. Does not 340 apply to signature files. Source retention annotations are extracted into 341 the external annotations files instead. 342 343 344 Injecting API Levels: 345 --apply-api-levels <api-versions.xml> 346 Reads an XML file containing API level descriptions and merges the 347 information into the documentation 348 349 350 Extracting API Levels: 351 --generate-api-levels <xmlfile> 352 Reads android.jar SDK files and generates an XML file recording the API 353 level for each class, method and field 354 --remove-missing-class-references-in-api-levels 355 Removes references to missing classes when generating the API levels XML 356 file. This can happen when generating the XML file for the non-updatable 357 portions of the module-lib sdk, as those non-updatable portions can 358 reference classes that are part of an updatable apex. 359 --android-jar-pattern <pattern> 360 Patterns to use to locate Android JAR files. The default is 361 ${"$"}ANDROID_HOME/platforms/android-%/android.jar. 362 --first-version 363 Sets the first API level to generate an API database from; usually 1 364 --current-version 365 Sets the current API level of the current source code 366 --current-codename 367 Sets the code name for the current source code 368 --current-jar 369 Points to the current API jar, if any 370 --sdk-extensions-root 371 Points to root of prebuilt extension SDK jars, if any. This directory is 372 expected to contain snapshots of historical extension SDK versions in the 373 form of stub jars. The paths should be on the format 374 "<int>/public/<module-name>.jar", where <int> corresponds to the extension 375 SDK version, and <module-name> to the name of the mainline module. 376 --sdk-extensions-info 377 Points to map of extension SDK APIs to include, if any. The file is a plain 378 text file and describes, per extension SDK, what APIs from that extension 379 to include in the file created via --generate-api-levels. The format of 380 each line is one of the following: "<module-name> <pattern> <ext-name> 381 [<ext-name> [...]]", where <module-name> is the name of the mainline module 382 this line refers to, <pattern> is a common Java name prefix of the APIs 383 this line refers to, and <ext-name> is a list of extension SDK names in 384 which these SDKs first appeared, or "<ext-name> <ext-id> <type>", where 385 <ext-name> is the name of an SDK, <ext-id> its numerical ID and <type> is 386 one of "platform" (the Android platform SDK), "platform-ext" (an extension 387 to the Android platform SDK), "standalone" (a separate SDK). Fields are 388 separated by whitespace. A mainline module may be listed multiple times. 389 The special pattern "*" refers to all APIs in the given mainline module. 390 Lines beginning with # are comments. 391 392 393 Sandboxing: 394 --no-implicit-root 395 Disable implicit root directory detection. Otherwise, metalava adds in 396 source roots implied by the source files 397 --strict-input-files <file> 398 Do not read files that are not explicitly specified in the command line. 399 All violations are written to the given file. Reads on directories are 400 always allowed, but metalava still tracks reads on directories that are not 401 specified in the command line, and write them to the file. 402 --strict-input-files:warn <file> 403 Warn when files not explicitly specified on the command line are read. All 404 violations are written to the given file. Reads on directories not 405 specified in the command line are allowed but also logged. 406 --strict-input-files:stack <file> 407 Same as --strict-input-files but also print stacktraces. 408 --strict-input-files-exempt <files or dirs> 409 Used with --strict-input-files. Explicitly allow access to files and/or 410 directories (separated by `:). Can also be @ followed by a path to a text 411 file containing paths to the full set of files and/or directories. 412 413 414 Environment Variables: 415 METALAVA_DUMP_ARGV 416 Set to true to have metalava emit all the arguments it was invoked with. 417 Helpful when debugging or reproducing under a debugger what the build 418 system is doing. 419 METALAVA_PREPEND_ARGS 420 One or more arguments (concatenated by space) to insert into the command 421 line, before the documentation flags. 422 METALAVA_APPEND_ARGS 423 One or more arguments (concatenated by space) to append to the end of the 424 command line, after the generate documentation flags. 425 426 """.trimIndent() 427 428 @Test Test invalid argumentsnull429 fun `Test invalid arguments`() { 430 val args = listOf(ARG_NO_COLOR, "--blah-blah-blah") 431 432 val stdout = StringWriter() 433 val stderr = StringWriter() 434 run( 435 originalArgs = args.toTypedArray(), 436 stdout = PrintWriter(stdout), 437 stderr = PrintWriter(stderr) 438 ) 439 assertEquals(BANNER + "\n\n", stdout.toString()) 440 assertEquals( 441 """ 442 443 Aborting: Invalid argument --blah-blah-blah 444 445 $FLAGS 446 447 """.trimIndent(), 448 stderr.toString() 449 ) 450 } 451 452 @Test Test helpnull453 fun `Test help`() { 454 val args = listOf(ARG_NO_COLOR, "--help") 455 456 val stdout = StringWriter() 457 val stderr = StringWriter() 458 run( 459 originalArgs = args.toTypedArray(), 460 stdout = PrintWriter(stdout), 461 stderr = PrintWriter(stderr) 462 ) 463 assertEquals("", stderr.toString()) 464 assertEquals( 465 """ 466 $BANNER 467 468 469 $DESCRIPTION 470 471 $FLAGS 472 473 """.trimIndent(), 474 stdout.toString() 475 ) 476 } 477 478 @Test Test issue severity optionsnull479 fun `Test issue severity options`() { 480 check( 481 extraArguments = arrayOf( 482 "--hide", 483 "StartWithLower", 484 "--lint", 485 "EndsWithImpl", 486 "--warning", 487 "StartWithUpper", 488 "--error", 489 "ArrayReturn" 490 ) 491 ) 492 assertEquals(Severity.HIDDEN, defaultConfiguration.getSeverity(Issues.START_WITH_LOWER)) 493 assertEquals(Severity.LINT, defaultConfiguration.getSeverity(Issues.ENDS_WITH_IMPL)) 494 assertEquals(Severity.WARNING, defaultConfiguration.getSeverity(Issues.START_WITH_UPPER)) 495 assertEquals(Severity.ERROR, defaultConfiguration.getSeverity(Issues.ARRAY_RETURN)) 496 } 497 498 @Test Test multiple issue severity optionsnull499 fun `Test multiple issue severity options`() { 500 check( 501 extraArguments = arrayOf("--hide", "StartWithLower,StartWithUpper,ArrayReturn") 502 ) 503 assertEquals(Severity.HIDDEN, defaultConfiguration.getSeverity(Issues.START_WITH_LOWER)) 504 assertEquals(Severity.HIDDEN, defaultConfiguration.getSeverity(Issues.START_WITH_UPPER)) 505 assertEquals(Severity.HIDDEN, defaultConfiguration.getSeverity(Issues.ARRAY_RETURN)) 506 } 507 508 @Test Test issue severity options with inheriting issuesnull509 fun `Test issue severity options with inheriting issues`() { 510 check( 511 extraArguments = arrayOf("--error", "RemovedClass") 512 ) 513 assertEquals(Severity.ERROR, defaultConfiguration.getSeverity(Issues.REMOVED_CLASS)) 514 assertEquals(Severity.ERROR, defaultConfiguration.getSeverity(Issues.REMOVED_DEPRECATED_CLASS)) 515 } 516 517 @Test Test issue severity options with case insensitive namesnull518 fun `Test issue severity options with case insensitive names`() { 519 check( 520 extraArguments = arrayOf("--hide", "arrayreturn"), 521 expectedIssues = "warning: Case-insensitive issue matching is deprecated, use --hide ArrayReturn instead of --hide arrayreturn [DeprecatedOption]" 522 ) 523 assertEquals(Severity.HIDDEN, defaultConfiguration.getSeverity(Issues.ARRAY_RETURN)) 524 } 525 526 @Test Test issue severity options with non-existing issuenull527 fun `Test issue severity options with non-existing issue`() { 528 check( 529 extraArguments = arrayOf("--hide", "ThisIssueDoesNotExist"), 530 expectedFail = "Aborting: Unknown issue id: --hide ThisIssueDoesNotExist" 531 ) 532 } 533 534 @Test Test for --strict-input-files-exemptnull535 fun `Test for --strict-input-files-exempt`() { 536 val top = temporaryFolder.newFolder() 537 538 val dir = File(top, "childdir").apply { mkdirs() } 539 val grandchild1 = File(dir, "grandchiild1").apply { createNewFile() } 540 val grandchild2 = File(dir, "grandchiild2").apply { createNewFile() } 541 val file1 = File(top, "file1").apply { createNewFile() } 542 val file2 = File(top, "file2").apply { createNewFile() } 543 544 try { 545 check( 546 extraArguments = arrayOf( 547 "--strict-input-files-exempt", 548 file1.path + File.pathSeparatorChar + dir.path 549 ) 550 ) 551 552 assertTrue(FileReadSandbox.isAccessAllowed(file1)) 553 assertTrue(FileReadSandbox.isAccessAllowed(grandchild1)) 554 assertTrue(FileReadSandbox.isAccessAllowed(grandchild2)) 555 556 assertFalse(FileReadSandbox.isAccessAllowed(file2)) // Access *not* allowed 557 } finally { 558 FileReadSandbox.reset() 559 } 560 } 561 } 562