Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d2c70761 authored by Mårten Kongstad's avatar Mårten Kongstad
Browse files

check-flagged-apis: consider interfaces when looking up symbol

When searching for potential errors, if a symbol can't be found in the
api-verions.xml data, check if it is present in any of the class'
interfaces.

A follow-up CL will add similar logic to handle super classes.

Bug: 334870672
Test: atest --host check-flagged-apis-test
Change-Id: Ia6dfcfa8495b89465db60f6a4eb77d304112046b
parent 04d8b46c
Loading
Loading
Loading
Loading
+41 −0
Original line number Diff line number Diff line
@@ -171,6 +171,47 @@ class CheckFlaggedApisTest {
    assertEquals(expected, actual)
  }

  @Test
  fun testFindErrorsVerifyImplements() {
    val apiSignature =
        """
          // Signature format: 2.0
          package android {
            @FlaggedApi("android.flag.foo") public final class Clazz implements android.Interface {
              method @FlaggedApi("android.flag.foo") public boolean foo();
              method @FlaggedApi("android.flag.foo") public boolean bar();
            }
            public interface Interface {
              method public boolean bar();
            }
          }
        """
            .trim()

    val apiVersions =
        """
          <?xml version="1.0" encoding="utf-8"?>
          <api version="3">
            <class name="android/Clazz" since="1">
              <implements name="android/Interface"/>
              <method name="foo()Z"/>
            </class>
            <class name="android/Interface" since="1">
              <method name="bar()Z"/>
            </class>
          </api>
        """
            .trim()

    val expected = setOf<ApiError>()
    val actual =
        findErrors(
            parseApiSignature("in-memory", apiSignature.byteInputStream()),
            parseFlagValues(generateFlagsProto(ENABLED, ENABLED)),
            parseApiVersions(apiVersions.byteInputStream()))
    assertEquals(expected, actual)
  }

  @Test
  fun testFindErrorsDisabledFlaggedApiIsPresent() {
    val expected =
+32 −2
Original line number Diff line number Diff line
@@ -333,15 +333,45 @@ internal fun findErrors(
    flags: Map<Flag, Boolean>,
    symbolsInOutput: Set<Symbol>
): Set<ApiError> {
  fun Set<Symbol>.containsSymbol(symbol: Symbol): Boolean {
    // trivial case: the symbol is explicitly listed in api-versions.xml
    if (contains(symbol)) {
      return true
    }

    // non-trivial case: the symbol could be part of the surrounding class'
    // super class or interfaces
    val (className, memberName) =
        when (symbol) {
          is ClassSymbol -> return false
          is MemberSymbol -> {
            Pair(symbol.clazz, symbol.member)
          }
        }
    val clazz = find { it is ClassSymbol && it.clazz == className } as? ClassSymbol?
    if (clazz == null) {
      return false
    }

    for (interfaceName in clazz.interfaces) {
      // createMethod is the same as createField, except it allows parenthesis
      val interfaceSymbol = Symbol.createMethod(interfaceName, memberName)
      if (contains(interfaceSymbol)) {
        return true
      }
    }

    return false
  }
  val errors = mutableSetOf<ApiError>()
  for ((symbol, flag) in flaggedSymbolsInSource) {
    try {
      if (flags.getValue(flag)) {
        if (!symbolsInOutput.contains(symbol)) {
        if (!symbolsInOutput.containsSymbol(symbol)) {
          errors.add(EnabledFlaggedApiNotPresentError(symbol, flag))
        }
      } else {
        if (symbolsInOutput.contains(symbol)) {
        if (symbolsInOutput.containsSymbol(symbol)) {
          errors.add(DisabledFlaggedApiIsPresentError(symbol, flag))
        }
      }