blob: b11d0a091972a1bead067ea3d6d902fc2b6ec2a9 [file] [log] [blame]
/*
* Copyright (C) 2023 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.cli.common
import com.android.tools.metalava.DefaultReporter
import com.android.tools.metalava.IssueConfiguration
import com.android.tools.metalava.reporter.Issues
import com.android.tools.metalava.reporter.Severity
import org.junit.Assert.assertEquals
import org.junit.Assert.assertThrows
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TestRule
import org.junit.runner.Description
import org.junit.runners.model.Statement
val REPORTING_OPTIONS_HELP =
"""
Issue Reporting:
Options that control which issues are reported and the severity of the reports.
--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
--error-category <name> Report all issues in the given category as errors
--warning-category <name> Report all issues in the given category as warnings
--lint-category <name> Report all issues in the given category as having lint-severity
--hide-category <name> Hide/skip all issues in the given category
"""
.trimIndent()
/**
* JUnit [TestRule] that will intercept calls to [DefaultReporter.reportPrinter], save them into a
* couple of buffers and then allow the test to verify them. If there are any unverified errors then
* the test will fail. The other issues will only be verified when requested.
*/
class ReportCollectorRule(private val cleaner: (String) -> String) : TestRule {
private val allReportedIssues = StringBuilder()
private val errorSeverityReportedIssues = StringBuilder()
override fun apply(base: Statement, description: Description): Statement {
return object : Statement() {
override fun evaluate() {
val oldReportPrinter = DefaultReporter.reportPrinter
try {
DefaultReporter.reportPrinter = this@ReportCollectorRule::report
// Evaluate the test.
base.evaluate()
} finally {
DefaultReporter.reportPrinter = oldReportPrinter
}
assertEquals("", errorSeverityReportedIssues.toString())
}
}
}
private fun report(message: String, severity: Severity) {
val cleanedMessage = cleaner(message)
if (severity == Severity.ERROR) {
errorSeverityReportedIssues.append(cleanedMessage).append('\n')
}
allReportedIssues.append(cleanedMessage).append('\n')
}
fun verifyAll(expected: String) {
assertEquals(expected.trim(), allReportedIssues.toString().trim())
allReportedIssues.clear()
}
fun verifyErrors(expected: String) {
assertEquals(expected.trim(), errorSeverityReportedIssues.toString().trim())
errorSeverityReportedIssues.clear()
}
}
class ReporterOptionsTest :
BaseOptionGroupTest<ReporterOptions>({ ReporterOptions() }, REPORTING_OPTIONS_HELP) {
@get:Rule val reportCollector = ReportCollectorRule(this::cleanupString)
@Test
fun `Test issue severity options`() {
runTest(
"--hide",
"StartWithLower",
"--lint",
"EndsWithImpl",
"--warning",
"StartWithUpper",
"--error",
"ArrayReturn"
) {
val issueConfiguration = it.issueConfiguration
assertEquals(Severity.HIDDEN, issueConfiguration.getSeverity(Issues.START_WITH_LOWER))
assertEquals(Severity.LINT, issueConfiguration.getSeverity(Issues.ENDS_WITH_IMPL))
assertEquals(Severity.WARNING, issueConfiguration.getSeverity(Issues.START_WITH_UPPER))
assertEquals(Severity.ERROR, issueConfiguration.getSeverity(Issues.ARRAY_RETURN))
}
}
@Test
fun `Test multiple issue severity options`() {
// Purposely includes some whitespace as that is something callers of metalava do.
runTest("--hide", "StartWithLower ,StartWithUpper, ArrayReturn") {
val issueConfiguration = it.issueConfiguration
assertEquals(Severity.HIDDEN, issueConfiguration.getSeverity(Issues.START_WITH_LOWER))
assertEquals(Severity.HIDDEN, issueConfiguration.getSeverity(Issues.START_WITH_UPPER))
assertEquals(Severity.HIDDEN, issueConfiguration.getSeverity(Issues.ARRAY_RETURN))
}
}
@Test
fun `Test issue severity options with inheriting issues`() {
runTest("--error", "RemovedClass") {
val issueConfiguration = it.issueConfiguration
assertEquals(Severity.ERROR, issueConfiguration.getSeverity(Issues.REMOVED_CLASS))
assertEquals(
Severity.ERROR,
issueConfiguration.getSeverity(Issues.REMOVED_DEPRECATED_CLASS)
)
}
}
@Test
fun `Test issue severity options with case insensitive names`() {
runTest("--hide", "arrayreturn") {
reportCollector.verifyAll(
"warning: Case-insensitive issue matching is deprecated, use --hide ArrayReturn instead of --hide arrayreturn [DeprecatedOption]"
)
val issueConfiguration = it.issueConfiguration
assertEquals(Severity.HIDDEN, issueConfiguration.getSeverity(Issues.ARRAY_RETURN))
}
}
@Test
fun `Test issue severity options with non-existing issue`() {
val e =
assertThrows(MetalavaCliException::class.java) {
runTest("--hide", "ThisIssueDoesNotExist") {}
}
assertEquals("Unknown issue id: '--hide' 'ThisIssueDoesNotExist'", e.message)
}
@Test
fun `Test options process in order`() {
// Interleave and change the order so that if all the hide options are processed before all
// the error options (or vice versa) they would result in different behavior.
runTest(
"--hide",
"UnavailableSymbol",
"--error",
"HiddenSuperclass",
"--hide",
"HiddenSuperclass",
"--error",
"UnavailableSymbol",
) {
// Make sure the two issues both default to warning.
val baseConfiguration = IssueConfiguration()
assertEquals(Severity.WARNING, baseConfiguration.getSeverity(Issues.HIDDEN_SUPERCLASS))
assertEquals(Severity.WARNING, baseConfiguration.getSeverity(Issues.UNAVAILABLE_SYMBOL))
// Now make sure the issues fine.
val issueConfiguration = it.issueConfiguration
assertEquals(Severity.HIDDEN, issueConfiguration.getSeverity(Issues.HIDDEN_SUPERCLASS))
assertEquals(Severity.ERROR, issueConfiguration.getSeverity(Issues.UNAVAILABLE_SYMBOL))
}
}
@Test
fun `Test issue severity options can affect issues related to processing the options`() {
runTest("--error", "DeprecatedOption", "--hide", "arrayreturn") {
reportCollector.verifyErrors(
"error: Case-insensitive issue matching is deprecated, use --hide ArrayReturn instead of --hide arrayreturn [DeprecatedOption]\n"
)
val issueConfiguration = it.issueConfiguration
assertEquals(Severity.HIDDEN, issueConfiguration.getSeverity(Issues.ARRAY_RETURN))
assertEquals(Severity.ERROR, issueConfiguration.getSeverity(Issues.DEPRECATED_OPTION))
}
}
}