blob: a452bb8a85727ffcce6b31fedecbd760deca0185 [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.compatibility
import com.android.tools.metalava.DriverTest
import com.android.tools.metalava.cli.common.ARG_HIDE
import com.android.tools.metalava.testing.getAndroidJar
import java.io.File
import org.junit.Assert.assertTrue
import org.junit.AssumptionViolatedException
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
abstract class CompatibilityCheckAndroidApisTest(
private val apiLevelCheck: ApiLevelCheck,
) : DriverTest() {
data class ApiLevelCheck(
val apiLevel: Int,
val expectedIssues: String,
val issueArgs: List<String>,
val disabled: Boolean = false,
) {
val extraArgs = listOf("--omit-locations") + issueArgs
override fun toString(): String = "${apiLevel - 1} to $apiLevel"
}
companion object {
private val DEFAULT_HIDDEN_ISSUES =
listOf(
"AddedClass",
"AddedField",
"AddedInterface",
"AddedMethod",
"AddedPackage",
"ChangedDeprecated",
"RemovedClass",
"RemovedDeprecatedClass",
"RemovedField",
)
private val DEFAULT_HIDDEN_ISSUES_STRING = DEFAULT_HIDDEN_ISSUES.joinToString(",")
private fun joinIssues(issues: Array<out String>): String = issues.joinToString(",")
fun hide(vararg issues: String): List<String> {
return listOf(ARG_HIDE, joinIssues(issues))
}
/** Data for each api version to check. */
private val data =
listOf(
ApiLevelCheck(
5,
"""
warning: Method android.view.Surface.lockCanvas added thrown exception java.lang.IllegalArgumentException [ChangedThrows]
""",
hide(DEFAULT_HIDDEN_ISSUES_STRING),
disabled = true,
),
ApiLevelCheck(
6,
"""
warning: Method android.accounts.AbstractAccountAuthenticator.confirmCredentials added thrown exception android.accounts.NetworkErrorException [ChangedThrows]
warning: Method android.accounts.AbstractAccountAuthenticator.updateCredentials added thrown exception android.accounts.NetworkErrorException [ChangedThrows]
warning: Field android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL has changed value from 2008 to 2014 [ChangedValue]
""",
hide(DEFAULT_HIDDEN_ISSUES_STRING),
disabled = true,
),
ApiLevelCheck(
7,
"""
error: Removed field android.view.ViewGroup.FLAG_USE_CHILD_DRAWING_ORDER [RemovedField]
""",
hide(
"AddedClass",
"AddedField",
"AddedInterface",
"AddedMethod",
"AddedPackage",
"ChangedDeprecated",
),
disabled = true,
),
ApiLevelCheck(
8,
// setOption getting removed here is wrong! Seems to be a PSI loading bug.
"""
warning: Constructor android.net.SSLCertificateSocketFactory no longer throws exception java.security.KeyManagementException [ChangedThrows]
warning: Constructor android.net.SSLCertificateSocketFactory no longer throws exception java.security.NoSuchAlgorithmException [ChangedThrows]
error: Removed method java.net.DatagramSocketImpl.getOption(int) [RemovedMethod]
error: Removed method java.net.DatagramSocketImpl.setOption(int,Object) [RemovedMethod]
warning: Constructor java.nio.charset.Charset no longer throws exception java.nio.charset.IllegalCharsetNameException [ChangedThrows]
warning: Method java.nio.charset.Charset.forName no longer throws exception java.nio.charset.IllegalCharsetNameException [ChangedThrows]
warning: Method java.nio.charset.Charset.forName no longer throws exception java.nio.charset.UnsupportedCharsetException [ChangedThrows]
warning: Method java.nio.charset.Charset.isSupported no longer throws exception java.nio.charset.IllegalCharsetNameException [ChangedThrows]
warning: Method java.util.regex.Matcher.appendReplacement no longer throws exception java.lang.IllegalStateException [ChangedThrows]
warning: Method java.util.regex.Matcher.start no longer throws exception java.lang.IllegalStateException [ChangedThrows]
warning: Method java.util.regex.Pattern.compile no longer throws exception java.util.regex.PatternSyntaxException [ChangedThrows]
warning: Class javax.xml.XMLConstants added final qualifier [AddedFinal]
error: Removed constructor javax.xml.XMLConstants() [RemovedMethod]
warning: Method javax.xml.parsers.DocumentBuilder.isXIncludeAware no longer throws exception java.lang.UnsupportedOperationException [ChangedThrows]
warning: Method javax.xml.parsers.DocumentBuilderFactory.newInstance no longer throws exception javax.xml.parsers.FactoryConfigurationError [ChangedThrows]
warning: Method javax.xml.parsers.SAXParser.isXIncludeAware no longer throws exception java.lang.UnsupportedOperationException [ChangedThrows]
warning: Method javax.xml.parsers.SAXParserFactory.newInstance no longer throws exception javax.xml.parsers.FactoryConfigurationError [ChangedThrows]
warning: Method org.w3c.dom.Element.getAttributeNS added thrown exception org.w3c.dom.DOMException [ChangedThrows]
warning: Method org.w3c.dom.Element.getAttributeNodeNS added thrown exception org.w3c.dom.DOMException [ChangedThrows]
warning: Method org.w3c.dom.Element.getElementsByTagNameNS added thrown exception org.w3c.dom.DOMException [ChangedThrows]
warning: Method org.w3c.dom.Element.hasAttributeNS added thrown exception org.w3c.dom.DOMException [ChangedThrows]
warning: Method org.w3c.dom.NamedNodeMap.getNamedItemNS added thrown exception org.w3c.dom.DOMException [ChangedThrows]
""",
hide(DEFAULT_HIDDEN_ISSUES_STRING),
disabled = true,
),
ApiLevelCheck(
18,
"""
warning: Class android.os.Looper added final qualifier but was previously uninstantiable and therefore could not be subclassed [AddedFinalUninstantiable]
warning: Class android.os.MessageQueue added final qualifier but was previously uninstantiable and therefore could not be subclassed [AddedFinalUninstantiable]
error: Removed field android.os.Process.BLUETOOTH_GID [RemovedField]
error: Removed class android.renderscript.Program [RemovedClass]
error: Removed class android.renderscript.ProgramStore [RemovedClass]
""",
hide(
"AddedClass",
"AddedField",
"AddedFinal",
"AddedInterface",
"AddedMethod",
"AddedPackage",
"ChangedDeprecated",
"ChangedThrows",
"ChangedType",
"RemovedDeprecatedClass",
"RemovedMethod",
),
disabled = true,
),
ApiLevelCheck(
19,
"""
warning: Method android.app.Notification.Style.build has changed 'abstract' qualifier [ChangedAbstract]
error: Removed method android.os.Debug.MemoryInfo.getOtherLabel(int) [RemovedMethod]
error: Removed method android.os.Debug.MemoryInfo.getOtherPrivateDirty(int) [RemovedMethod]
error: Removed method android.os.Debug.MemoryInfo.getOtherPss(int) [RemovedMethod]
error: Removed method android.os.Debug.MemoryInfo.getOtherSharedDirty(int) [RemovedMethod]
warning: Field android.view.animation.Transformation.TYPE_ALPHA has changed value from nothing/not constant to 1 [ChangedValue]
warning: Field android.view.animation.Transformation.TYPE_ALPHA has added 'final' qualifier [AddedFinal]
warning: Field android.view.animation.Transformation.TYPE_BOTH has changed value from nothing/not constant to 3 [ChangedValue]
warning: Field android.view.animation.Transformation.TYPE_BOTH has added 'final' qualifier [AddedFinal]
warning: Field android.view.animation.Transformation.TYPE_IDENTITY has changed value from nothing/not constant to 0 [ChangedValue]
warning: Field android.view.animation.Transformation.TYPE_IDENTITY has added 'final' qualifier [AddedFinal]
warning: Field android.view.animation.Transformation.TYPE_MATRIX has changed value from nothing/not constant to 2 [ChangedValue]
warning: Field android.view.animation.Transformation.TYPE_MATRIX has added 'final' qualifier [AddedFinal]
warning: Method java.nio.CharBuffer.subSequence has changed return type from CharSequence to java.nio.CharBuffer [ChangedType]
""",
// The last warning above is not right; seems to be a PSI jar loading bug. It
// returns the wrong return type!
hide(DEFAULT_HIDDEN_ISSUES_STRING),
disabled = true,
),
ApiLevelCheck(
20,
"""
error: Removed method android.util.TypedValue.complexToDimensionNoisy(int,android.util.DisplayMetrics) [RemovedMethod]
warning: Method org.json.JSONObject.keys has changed return type from java.util.Iterator to java.util.Iterator<java.lang.String> [ChangedType]
warning: Field org.xmlpull.v1.XmlPullParserFactory.features has changed type from java.util.HashMap to java.util.HashMap<java.lang.String, java.lang.Boolean> [ChangedType]
""",
hide(DEFAULT_HIDDEN_ISSUES_STRING),
disabled = true,
),
ApiLevelCheck(
26,
"""
warning: Field android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE has changed value from 130 to 230 [ChangedValue]
warning: Field android.content.pm.PermissionInfo.PROTECTION_MASK_FLAGS has changed value from 4080 to 65520 [ChangedValue]
""",
hide(
"AddedClass",
"AddedField",
"AddedFinal",
"AddedInterface",
"AddedMethod",
"AddedPackage",
"ChangedDeprecated",
"ChangedThrows",
"RemovedClass",
"RemovedDeprecatedClass",
"RemovedMethod",
),
disabled = true,
),
ApiLevelCheck(
27,
"",
hide(
"AddedClass",
"AddedField",
"AddedFinal",
"AddedInterface",
"AddedMethod",
"AddedPackage",
"ChangedAbstract",
"ChangedDeprecated",
"ChangedThrows",
"RemovedMethod",
),
),
)
@JvmStatic
protected fun shardTestParameters(apiLevelRange: IntRange) =
data.filter { it.apiLevel in apiLevelRange }
}
@Test
fun `Test All Android API levels`() {
// Checks API across Android SDK versions and makes sure the results are
// intentional (to help shake out bugs in the API compatibility checker)
// Temporary let block to keep the same indent as before when this was looping over the data
// itself.
let {
if (apiLevelCheck.disabled) {
throw AssumptionViolatedException("Test disabled")
}
val apiLevel = apiLevelCheck.apiLevel
val expectedIssues = apiLevelCheck.expectedIssues
val expectedFail =
if (expectedIssues.contains("error: ")) "Aborting: Found compatibility problems"
else ""
val extraArgs = apiLevelCheck.extraArgs.toTypedArray()
val current = getAndroidJar(apiLevel)
val previous = getAndroidJar(apiLevel - 1)
val previousApi = previous.path
// PSI based check
check(
extraArguments = extraArgs,
expectedIssues = expectedIssues,
expectedFail = expectedFail,
checkCompatibilityApiReleased = previousApi,
apiJar = current,
skipEmitPackages = emptyList(),
)
// Signature based check
if (apiLevel >= 21) {
// Check signature file checks. We have .txt files for API level 14 and up, but
// there are a
// BUNCH of problems in older signature files that make the comparisons not work --
// missing type variables in class declarations, missing generics in method
// signatures, etc.
val signatureFile =
File("../../../prebuilts/sdk/${apiLevel - 1}/public/api/android.txt")
assertTrue(
"Couldn't find $signatureFile: Check that pwd (${File("").absolutePath}) for test is correct",
signatureFile.isFile
)
check(
extraArguments = extraArgs,
expectedIssues = expectedIssues,
expectedFail = expectedFail,
checkCompatibilityApiReleased = signatureFile.path,
apiJar = current,
skipEmitPackages = emptyList(),
)
}
}
}
}