| /* |
| * Copyright (C) 2017 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.tools.metalava |
| |
| import com.android.tools.metalava.model.defaultConfiguration |
| import org.junit.Assert.assertEquals |
| import org.junit.Assert.assertFalse |
| import org.junit.Assert.assertTrue |
| import org.junit.Test |
| import java.io.File |
| import java.io.PrintWriter |
| import java.io.StringWriter |
| |
| @Suppress("PrivatePropertyName") |
| class OptionsTest : DriverTest() { |
| private val DESCRIPTION = """ |
| $PROGRAM_NAME extracts metadata from source code to generate artifacts such as the signature files, the SDK stub files, |
| external annotations etc. |
| """.trimIndent() |
| |
| private val FLAGS = """ |
| Usage: metalava <flags> |
| |
| |
| General: |
| --help |
| This message. |
| --version |
| Show the version of metalava. |
| --quiet |
| Only include vital output |
| --verbose |
| Include extra diagnostic output |
| --color |
| Attempt to colorize the output (defaults to true if ${"$"}TERM is xterm) |
| --no-color |
| Do not attempt to colorize the output |
| --only-update-api |
| Cancel any other "action" flags other than generating signature files. This |
| is here to make it easier customize build system tasks, particularly for |
| the "make update-api" task. |
| --only-check-api |
| Cancel any other "action" flags other than checking signature files. This |
| is here to make it easier customize build system tasks, particularly for |
| the "make checkapi" task. |
| --repeat-errors-max <N> |
| When specified, repeat at most N errors before finishing. |
| |
| |
| API sources: |
| --source-files <files> |
| A comma separated list of source files to be parsed. Can also be @ followed |
| by a path to a text file containing paths to the full set of files to |
| parse. |
| --source-path <paths> |
| One or more directories (separated by `:`) containing source files (within |
| a package hierarchy). If --strict-input-files, --strict-input-files:warn, |
| or --strict-input-files:stack are used, files accessed under --source-path |
| that are not explicitly specified in --source-files are reported as |
| violations. |
| --classpath <paths> |
| One or more directories or jars (separated by `:`) containing classes that |
| should be on the classpath when parsing the source files |
| --merge-qualifier-annotations <file> |
| An external annotations file to merge and overlay the sources, or a |
| directory of such files. Should be used for annotations intended for |
| inclusion in the API to be written out, e.g. nullability. Formats supported |
| are: IntelliJ's external annotations database format, .jar or .zip files |
| containing those, Android signature files, and Java stub files. |
| --merge-inclusion-annotations <file> |
| An external annotations file to merge and overlay the sources, or a |
| directory of such files. Should be used for annotations which determine |
| inclusion in the API to be written out, i.e. show and hide. The only format |
| supported is Java stub files. |
| --validate-nullability-from-merged-stubs |
| Triggers validation of nullability annotations for any class where |
| --merge-qualifier-annotations includes a Java stub file. |
| --validate-nullability-from-list |
| Triggers validation of nullability annotations for any class listed in the |
| named file (one top-level class per line, # prefix for comment line). |
| --nullability-warnings-txt <file> |
| Specifies where to write warnings encountered during validation of |
| nullability annotations. (Does not trigger validation by itself.) |
| --nullability-errors-non-fatal |
| Specifies that errors encountered during validation of nullability |
| annotations should not be treated as errors. They will be written out to |
| the file specified in --nullability-warnings-txt instead. |
| --input-api-jar <file> |
| A .jar file to read APIs from directly |
| --manifest <file> |
| A manifest file, used to for check permissions to cross check APIs |
| --replace-documentation <p> <r> <t> |
| Amongst nonempty documentation of items from Java packages <p> and their |
| subpackages, replaces any matches of regular expression <r> with |
| replacement text <t>. <p> is given as a nonempty list of Java package names |
| separated by ':' (e.g. "java:android.util"); <t> may contain backreferences |
| ($1, $2 etc.) to matching groups from <r>. |
| --hide-package <package> |
| Remove the given packages from the API even if they have not been marked |
| with @hide |
| --show-annotation <annotation class> |
| Unhide any hidden elements that are also annotated with the given |
| annotation |
| --show-single-annotation <annotation> |
| Like --show-annotation, but does not apply to members; these must also be |
| explicitly annotated |
| --show-for-stub-purposes-annotation <annotation class> |
| Like --show-annotation, but elements annotated with it are assumed to be |
| "implicitly" included in the API surface, and they'll be included in |
| certain kinds of output such as stubs, but not in others, such as the |
| signature file and API lint |
| --hide-annotation <annotation class> |
| Treat any elements annotated with the given annotation as hidden |
| --hide-meta-annotation <meta-annotation class> |
| Treat as hidden any elements annotated with an annotation which is itself |
| annotated with the given meta-annotation |
| --show-unannotated |
| Include un-annotated public APIs in the signature file as well |
| --java-source <level> |
| Sets the source level for Java source files; default is 1.8. |
| --kotlin-source <level> |
| Sets the source level for Kotlin source files; default is 1.5. |
| --sdk-home <dir> |
| If set, locate the `android.jar` file from the given Android SDK |
| --compile-sdk-version <api> |
| Use the given API level |
| --jdk-home <dir> |
| If set, add the Java APIs from the given JDK to the classpath |
| --stub-packages <package-list> |
| List of packages (separated by :) which will be used to filter out |
| irrelevant code. If specified, only code in these packages will be included |
| in signature files, stubs, etc. (This is not limited to just the stubs; the |
| name is historical.) You can also use ".*" at the end to match subpackages, |
| so `foo.*` will match both `foo` and `foo.bar`. |
| --subtract-api <api file> |
| Subtracts the API in the given signature or jar file from the current API |
| being emitted via --api, --stubs, --doc-stubs, etc. Note that the |
| subtraction only applies to classes; it does not subtract members. |
| --typedefs-in-signatures <ref|inline> |
| Whether to include typedef annotations in signature files. |
| `--typedefs-in-signatures ref` will include just a reference to the typedef |
| class, which is not itself part of the API and is not included as a class, |
| and `--typedefs-in-signatures inline` will include the constants themselves |
| into each usage site. You can also supply `--typedefs-in-signatures none` |
| to explicitly turn it off, if the default ever changes. |
| --ignore-classes-on-classpath |
| Prevents references to classes on the classpath from being added to the |
| generated stub files. |
| |
| |
| Documentation: |
| --public |
| Only include elements that are public |
| --protected |
| Only include elements that are public or protected |
| --package |
| Only include elements that are public, protected or package protected |
| --private |
| Include all elements except those that are marked hidden |
| --hidden |
| Include all elements, including hidden |
| |
| |
| Extracting Signature Files: |
| --api <file> |
| Generate a signature descriptor file |
| --dex-api <file> |
| Generate a DEX signature descriptor file listing the APIs |
| --removed-api <file> |
| Generate a signature descriptor file for APIs that have been removed |
| --format=<v1,v2,v3,...> |
| Sets the output signature file format to be the given version. |
| --output-kotlin-nulls[=yes|no] |
| Controls whether nullness annotations should be formatted as in Kotlin |
| (with "?" for nullable types, "" for non nullable types, and "!" for |
| unknown. The default is yes. |
| --output-default-values[=yes|no] |
| Controls whether default values should be included in signature files. The |
| default is yes. |
| --include-signature-version[=yes|no] |
| Whether the signature files should include a comment listing the format |
| version of the signature file. |
| --proguard <file> |
| Write a ProGuard keep file for the API |
| --sdk-values <dir> |
| Write SDK values files to the given directory |
| --enable-kotlin-psi |
| [EXPERIMENTAL] If set, use Kotlin PSI for Kotlin instead of UAST |
| |
| |
| Generating Stubs: |
| --stubs <dir> |
| Generate stub source files for the API |
| --doc-stubs <dir> |
| Generate documentation stub source files for the API. Documentation stub |
| files are similar to regular stub files, but there are some differences. |
| For example, in the stub files, we'll use special annotations like |
| @RecentlyNonNull instead of @NonNull to indicate that an element is |
| recently marked as non null, whereas in the documentation stubs we'll just |
| list this as @NonNull. Another difference is that @doconly elements are |
| included in documentation stubs, but not regular stubs, etc. |
| --kotlin-stubs |
| [CURRENTLY EXPERIMENTAL] If specified, stubs generated from Kotlin source |
| code will be written in Kotlin rather than the Java programming language. |
| --include-annotations |
| Include annotations such as @Nullable in the stub files. |
| --exclude-all-annotations |
| Exclude annotations such as @Nullable from the stub files; the default. |
| --pass-through-annotation <annotation classes> |
| A comma separated list of fully qualified names of annotation classes that |
| must be passed through unchanged. |
| --exclude-annotation <annotation classes> |
| A comma separated list of fully qualified names of annotation classes that |
| must be stripped from metalava's outputs. |
| --enhance-documentation |
| Enhance documentation in various ways, for example auto-generating |
| documentation based on source annotations present in the code. This is |
| implied by --doc-stubs. |
| --exclude-documentation-from-stubs |
| Exclude element documentation (javadoc and kdoc) from the generated stubs. |
| (Copyright notices are not affected by this, they are always included. |
| Documentation stubs (--doc-stubs) are not affected.) |
| --write-stubs-source-list <file> |
| Write the list of generated stub files into the given source list file. If |
| generating documentation stubs and you haven't also specified |
| --write-doc-stubs-source-list, this list will refer to the documentation |
| stubs; otherwise it's the non-documentation stubs. |
| --write-doc-stubs-source-list <file> |
| Write the list of generated doc stub files into the given source list file |
| --register-artifact <api-file> <id> |
| Registers the given id for the packages found in the given signature file. |
| metalava will inject an @artifactId <id> tag into every top level stub |
| class in that API. |
| |
| |
| Diffs and Checks: |
| --input-kotlin-nulls[=yes|no] |
| Whether the signature file being read should be interpreted as having |
| encoded its types using Kotlin style types: a suffix of "?" for nullable |
| types, no suffix for non nullable types, and "!" for unknown. The default |
| is no. |
| --check-compatibility:type:state <file> |
| Check compatibility. Type is one of 'api' and 'removed', which checks |
| either the public api or the removed api. State is one of 'current' and |
| 'released', to check either the currently in development API or the last |
| publicly released API, respectively. Different compatibility checks apply |
| in the two scenarios. For example, to check the code base against the |
| current public API, use --check-compatibility:api:current. |
| --check-compatibility:base <file> |
| When performing a compat check, use the provided signature file as a base |
| api, which is treated as part of the API being checked. This allows us to |
| compute the full API surface from a partial API surface (e.g. the current |
| @SystemApi txt file), which allows us to recognize when an API is moved |
| from the partial API to the base API and avoid incorrectly flagging this as |
| an API removal. |
| --api-lint [api file] |
| Check API for Android API best practices. If a signature file is provided, |
| only the APIs that are new since the API will be checked. |
| --api-lint-ignore-prefix [prefix] |
| A list of package prefixes to ignore API issues in when running with |
| --api-lint. |
| --migrate-nullness <api file> |
| Compare nullness information with the previous stable API and mark newly |
| annotated APIs as under migration. |
| --warnings-as-errors |
| Promote all warnings to errors |
| --lints-as-errors |
| Promote all API lint warnings to errors |
| --error <id> |
| Report issues of the given id as errors |
| --warning <id> |
| Report issues of the given id as warnings |
| --lint <id> |
| Report issues of the given id as having lint-severity |
| --hide <id> |
| Hide/skip issues of the given id |
| --report-even-if-suppressed <file> |
| Write all issues into the given file, even if suppressed (via annotation or |
| baseline) but not if hidden (by '--hide') |
| --baseline <file> |
| Filter out any errors already reported in the given baseline file, or |
| create if it does not already exist |
| --update-baseline [file] |
| Rewrite the existing baseline file with the current set of warnings. If |
| some warnings have been fixed, this will delete them from the baseline |
| files. If a file is provided, the updated baseline is written to the given |
| file; otherwise the original source baseline file is updated. |
| --baseline:api-lint <file> --update-baseline:api-lint [file] |
| Same as --baseline and --update-baseline respectively, but used |
| specifically for API lint issues performed by --api-lint. |
| --baseline:compatibility:released <file> --update-baseline:compatibility:released [file] |
| Same as --baseline and --update-baseline respectively, but used |
| specifically for API compatibility issues performed by |
| --check-compatibility:api:released and |
| --check-compatibility:removed:released. |
| --merge-baseline [file] |
| Like --update-baseline, but instead of always replacing entries in the |
| baseline, it will merge the existing baseline with the new baseline. This |
| is useful if metalava runs multiple times on the same source tree with |
| different flags at different times, such as occasionally with --api-lint. |
| --pass-baseline-updates |
| Normally, encountering error will fail the build, even when updating |
| baselines. This flag allows you to tell metalava to continue without |
| errors, such that all the baselines in the source tree can be updated in |
| one go. |
| --delete-empty-baselines |
| Whether to delete baseline files if they are updated and there is nothing |
| to include. |
| --error-message:api-lint <message> |
| If set, metalava shows it when errors are detected in --api-lint. |
| --error-message:compatibility:released <message> |
| If set, metalava shows it when errors are detected in |
| --check-compatibility:api:released and |
| --check-compatibility:removed:released. |
| --error-message:compatibility:current <message> |
| If set, metalava shows it when errors are detected in |
| --check-compatibility:api:current and |
| --check-compatibility:removed:current. |
| |
| |
| JDiff: |
| --api-xml <file> |
| Like --api, but emits the API in the JDiff XML format instead |
| --convert-to-jdiff <sig> <xml> |
| Reads in the given signature file, and writes it out in the JDiff XML |
| format. Can be specified multiple times. |
| --convert-new-to-jdiff <old> <new> <xml> |
| Reads in the given old and new api files, computes the difference, and |
| writes out only the new parts of the API in the JDiff XML format. |
| --convert-to-v1 <sig> <sig> |
| Reads in the given signature file and writes it out as a signature file in |
| the original v1/doclava format. |
| --convert-to-v2 <sig> <sig> |
| Reads in the given signature file and writes it out as a signature file in |
| the new signature format, v2. |
| --convert-new-to-v2 <old> <new> <sig> |
| Reads in the given old and new api files, computes the difference, and |
| writes out only the new parts of the API in the v2 format. |
| |
| |
| Extracting Annotations: |
| --extract-annotations <zipfile> |
| Extracts source annotations from the source files and writes them into the |
| given zip file |
| --include-annotation-classes <dir> |
| Copies the given stub annotation source files into the generated stub |
| sources; <dir> is typically metalava/stub-annotations/src/main/java/. |
| --rewrite-annotations <dir/jar> |
| For a bytecode folder or output jar, rewrites the androidx annotations to |
| be package private |
| --force-convert-to-warning-nullability-annotations <package1:-package2:...> |
| On every API declared in a class referenced by the given filter, makes |
| nullability issues appear to callers as warnings rather than errors by |
| replacing @Nullable/@NonNull in these APIs with |
| @RecentlyNullable/@RecentlyNonNull |
| --copy-annotations <source> <dest> |
| For a source folder full of annotation sources, generates corresponding |
| package private versions of the same annotations. |
| --include-source-retention |
| If true, include source-retention annotations in the stub files. Does not |
| apply to signature files. Source retention annotations are extracted into |
| the external annotations files instead. |
| |
| |
| Injecting API Levels: |
| --apply-api-levels <api-versions.xml> |
| Reads an XML file containing API level descriptions and merges the |
| information into the documentation |
| |
| |
| Extracting API Levels: |
| --generate-api-levels <xmlfile> |
| Reads android.jar SDK files and generates an XML file recording the API |
| level for each class, method and field |
| --android-jar-pattern <pattern> |
| Patterns to use to locate Android JAR files. The default is |
| ${"$"}ANDROID_HOME/platforms/android-%/android.jar. |
| --first-version |
| Sets the first API level to generate an API database from; usually 1 |
| --current-version |
| Sets the current API level of the current source code |
| --current-codename |
| Sets the code name for the current source code |
| --current-jar |
| Points to the current API jar, if any |
| |
| |
| Sandboxing: |
| --no-implicit-root |
| Disable implicit root directory detection. Otherwise, metalava adds in |
| source roots implied by the source files |
| --strict-input-files <file> |
| Do not read files that are not explicitly specified in the command line. |
| All violations are written to the given file. Reads on directories are |
| always allowed, but metalava still tracks reads on directories that are not |
| specified in the command line, and write them to the file. |
| --strict-input-files:warn <file> |
| Warn when files not explicitly specified on the command line are read. All |
| violations are written to the given file. Reads on directories not |
| specified in the command line are allowed but also logged. |
| --strict-input-files:stack <file> |
| Same as --strict-input-files but also print stacktraces. |
| --strict-input-files-exempt <files or dirs> |
| Used with --strict-input-files. Explicitly allow access to files and/or |
| directories (separated by `:). Can also be @ followed by a path to a text |
| file containing paths to the full set of files and/or directories. |
| |
| |
| Environment Variables: |
| METALAVA_DUMP_ARGV |
| Set to true to have metalava emit all the arguments it was invoked with. |
| Helpful when debugging or reproducing under a debugger what the build |
| system is doing. |
| METALAVA_PREPEND_ARGS |
| One or more arguments (concatenated by space) to insert into the command |
| line, before the documentation flags. |
| METALAVA_APPEND_ARGS |
| One or more arguments (concatenated by space) to append to the end of the |
| command line, after the generate documentation flags. |
| |
| """.trimIndent() |
| |
| @Test |
| fun `Test invalid arguments`() { |
| val args = listOf(ARG_NO_COLOR, "--blah-blah-blah") |
| |
| val stdout = StringWriter() |
| val stderr = StringWriter() |
| run( |
| originalArgs = args.toTypedArray(), |
| stdout = PrintWriter(stdout), |
| stderr = PrintWriter(stderr) |
| ) |
| assertEquals(BANNER + "\n\n", stdout.toString()) |
| assertEquals( |
| """ |
| |
| Aborting: Invalid argument --blah-blah-blah |
| |
| $FLAGS |
| |
| """.trimIndent(), |
| stderr.toString() |
| ) |
| } |
| |
| @Test |
| fun `Test help`() { |
| val args = listOf(ARG_NO_COLOR, "--help") |
| |
| val stdout = StringWriter() |
| val stderr = StringWriter() |
| run( |
| originalArgs = args.toTypedArray(), |
| stdout = PrintWriter(stdout), |
| stderr = PrintWriter(stderr) |
| ) |
| assertEquals("", stderr.toString()) |
| assertEquals( |
| """ |
| $BANNER |
| |
| |
| $DESCRIPTION |
| |
| $FLAGS |
| |
| """.trimIndent(), |
| stdout.toString() |
| ) |
| } |
| |
| @Test |
| fun `Test issue severity options`() { |
| check( |
| extraArguments = arrayOf( |
| "--hide", |
| "StartWithLower", |
| "--lint", |
| "EndsWithImpl", |
| "--warning", |
| "StartWithUpper", |
| "--error", |
| "ArrayReturn" |
| ) |
| ) |
| assertEquals(Severity.HIDDEN, defaultConfiguration.getSeverity(Issues.START_WITH_LOWER)) |
| assertEquals(Severity.LINT, defaultConfiguration.getSeverity(Issues.ENDS_WITH_IMPL)) |
| assertEquals(Severity.WARNING, defaultConfiguration.getSeverity(Issues.START_WITH_UPPER)) |
| assertEquals(Severity.ERROR, defaultConfiguration.getSeverity(Issues.ARRAY_RETURN)) |
| } |
| |
| @Test |
| fun `Test multiple issue severity options`() { |
| check( |
| extraArguments = arrayOf("--hide", "StartWithLower,StartWithUpper,ArrayReturn") |
| ) |
| assertEquals(Severity.HIDDEN, defaultConfiguration.getSeverity(Issues.START_WITH_LOWER)) |
| assertEquals(Severity.HIDDEN, defaultConfiguration.getSeverity(Issues.START_WITH_UPPER)) |
| assertEquals(Severity.HIDDEN, defaultConfiguration.getSeverity(Issues.ARRAY_RETURN)) |
| } |
| |
| @Test |
| fun `Test issue severity options with inheriting issues`() { |
| check( |
| extraArguments = arrayOf("--error", "RemovedClass") |
| ) |
| assertEquals(Severity.ERROR, defaultConfiguration.getSeverity(Issues.REMOVED_CLASS)) |
| assertEquals(Severity.ERROR, defaultConfiguration.getSeverity(Issues.REMOVED_DEPRECATED_CLASS)) |
| } |
| |
| @Test |
| fun `Test issue severity options with case insensitive names`() { |
| check( |
| extraArguments = arrayOf("--hide", "arrayreturn"), |
| expectedIssues = "warning: Case-insensitive issue matching is deprecated, use --hide ArrayReturn instead of --hide arrayreturn [DeprecatedOption]" |
| ) |
| assertEquals(Severity.HIDDEN, defaultConfiguration.getSeverity(Issues.ARRAY_RETURN)) |
| } |
| |
| @Test |
| fun `Test issue severity options with non-existing issue`() { |
| check( |
| extraArguments = arrayOf("--hide", "ThisIssueDoesNotExist"), |
| expectedFail = "Aborting: Unknown issue id: --hide ThisIssueDoesNotExist" |
| ) |
| } |
| |
| @Test |
| fun `Test for --strict-input-files-exempt`() { |
| val top = temporaryFolder.newFolder() |
| |
| val dir = File(top, "childdir").apply { mkdirs() } |
| val grandchild1 = File(dir, "grandchiild1").apply { createNewFile() } |
| val grandchild2 = File(dir, "grandchiild2").apply { createNewFile() } |
| val file1 = File(top, "file1").apply { createNewFile() } |
| val file2 = File(top, "file2").apply { createNewFile() } |
| |
| try { |
| check( |
| extraArguments = arrayOf( |
| "--strict-input-files-exempt", |
| file1.path + File.pathSeparatorChar + dir.path |
| ) |
| ) |
| |
| assertTrue(FileReadSandbox.isAccessAllowed(file1)) |
| assertTrue(FileReadSandbox.isAccessAllowed(grandchild1)) |
| assertTrue(FileReadSandbox.isAccessAllowed(grandchild2)) |
| |
| assertFalse(FileReadSandbox.isAccessAllowed(file2)) // Access *not* allowed |
| } finally { |
| FileReadSandbox.reset() |
| } |
| } |
| } |