Validate merged Config before returning from ConfigParser
Previously, `ConfigParser` would just return the merged `Config` object
and leave it up to properties, e.g. `ApiSurfacesConfig.byName` to
perform any validation that was required. However, that meant it was
possible for some code to access `Config` objects in an invalid state
through other properties, e.g. `apiSurfaceList`.
This change performs validation on the merged `Config` object before it
is returned by `ConfigParser`, ensuring that it is valid before any
other code access it.
Bug: 391554590
Test: ./gradlew
Change-Id: I4ed1eb76af294367559a232ed8fca10b716f58fc
diff --git a/metalava/src/main/java/com/android/tools/metalava/config/Config.kt b/metalava/src/main/java/com/android/tools/metalava/config/Config.kt
index d12b249..995d4fb 100644
--- a/metalava/src/main/java/com/android/tools/metalava/config/Config.kt
+++ b/metalava/src/main/java/com/android/tools/metalava/config/Config.kt
@@ -28,7 +28,12 @@
data class Config(
@field:JacksonXmlProperty(localName = "api-surfaces", namespace = CONFIG_NAMESPACE)
val apiSurfaces: ApiSurfacesConfig? = null,
-)
+) {
+ /** Validate this object, i.e. check to make sure that the contained objects are consistent. */
+ internal fun validate() {
+ apiSurfaces?.validate()
+ }
+}
/** A set of [ApiSurfaceConfig]s. */
data class ApiSurfacesConfig(
@@ -49,6 +54,12 @@
error("Found duplicate surfaces called `$name`")
}
}
+
+ /** Validate this object, i.e. check to make sure that the contained objects are consistent. */
+ fun validate() {
+ // Force check for duplicates.
+ byName
+ }
}
/** An API surface that Metalava could generate. */
diff --git a/metalava/src/main/java/com/android/tools/metalava/config/ConfigParser.kt b/metalava/src/main/java/com/android/tools/metalava/config/ConfigParser.kt
index 9676543..6fc9958 100644
--- a/metalava/src/main/java/com/android/tools/metalava/config/ConfigParser.kt
+++ b/metalava/src/main/java/com/android/tools/metalava/config/ConfigParser.kt
@@ -103,6 +103,8 @@
}
// Merge the config objects together.
.reduceOrNull { configLeft, configRight -> merge(configLeft, configRight) }
+ // Validate the config.
+ ?.apply { validate() }
// If no configuration files were created then return an empty Config.
?: Config()
}
diff --git a/metalava/src/test/java/com/android/tools/metalava/config/ConfigParserTest.kt b/metalava/src/test/java/com/android/tools/metalava/config/ConfigParserTest.kt
index f1089c8..eee7490 100644
--- a/metalava/src/test/java/com/android/tools/metalava/config/ConfigParserTest.kt
+++ b/metalava/src/test/java/com/android/tools/metalava/config/ConfigParserTest.kt
@@ -21,7 +21,6 @@
import com.android.tools.metalava.testing.TemporaryFolderOwner
import com.google.common.truth.Truth.assertThat
import org.intellij.lang.annotations.Language
-import org.junit.Assert.assertThrows
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder
@@ -343,28 +342,7 @@
</config>
""",
),
- ) {
- assertThat(config)
- .isEqualTo(
- Config(
- apiSurfaces =
- ApiSurfacesConfig(
- apiSurfaceList =
- listOf(
- ApiSurfaceConfig(
- name = "public",
- ),
- ApiSurfaceConfig(
- name = "public",
- ),
- ),
- ),
- )
- )
-
- val exception =
- assertThrows(IllegalStateException::class.java) { config.apiSurfaces?.byName }
- assertThat(exception.message).isEqualTo("Found duplicate surfaces called `public`")
- }
+ expectedFail = "Found duplicate surfaces called `public`"
+ )
}
}