| package android.dawn |
| |
| import kotlin.reflect.KCallable |
| import kotlin.reflect.KClass |
| import kotlin.reflect.KProperty1 |
| import kotlin.reflect.full.companionObjectInstance |
| import kotlin.reflect.full.primaryConstructor |
| import kotlin.test.assertContains |
| import kotlin.test.assertEquals |
| import kotlin.test.fail |
| import org.junit.Test |
| import org.reflections.Reflections |
| import org.reflections.scanners.Scanners |
| |
| class MappedNamedConstantsTest { |
| |
| val TYPES_WITH_MAPPED_NAMED_CONSTANTS = arrayOf( |
| AdapterType::class, |
| AddressMode::class, |
| BackendType::class, |
| BlendFactor::class, |
| BlendOperation::class, |
| BufferBindingType::class, |
| BufferMapAsyncStatus::class, |
| BufferMapState::class, |
| BufferUsage::class, |
| CallbackMode::class, |
| ColorWriteMask::class, |
| CompareFunction::class, |
| CompilationInfoRequestStatus::class, |
| CompilationMessageType::class, |
| CompositeAlphaMode::class, |
| CreatePipelineAsyncStatus::class, |
| CullMode::class, |
| DeviceLostReason::class, |
| ErrorFilter::class, |
| ErrorType::class, |
| FeatureName::class, |
| FilterMode::class, |
| FrontFace::class, |
| IndexFormat::class, |
| LoadOp::class, |
| MapAsyncStatus::class, |
| MapMode::class, |
| MipmapFilterMode::class, |
| PopErrorScopeStatus::class, |
| PowerPreference::class, |
| PresentMode::class, |
| PrimitiveTopology::class, |
| QueryType::class, |
| QueueWorkDoneStatus::class, |
| RequestAdapterStatus::class, |
| RequestDeviceStatus::class, |
| SamplerBindingType::class, |
| ShaderStage::class, |
| Status::class, |
| StencilOperation::class, |
| StorageTextureAccess::class, |
| StoreOp::class, |
| SType::class, |
| SurfaceGetCurrentTextureStatus::class, |
| TextureAspect::class, |
| TextureDimension::class, |
| TextureFormat::class, |
| TextureSampleType::class, |
| TextureUsage::class, |
| TextureViewDimension::class, |
| VertexFormat::class, |
| VertexStepMode::class, |
| WaitStatus::class, |
| WGSLFeatureName::class |
| ) |
| |
| /** |
| * Test that the actual classes in our generated Kotlin are all tested in this unit test, |
| * and that there are no unexpected new classes. |
| * |
| * It's worth hardcoding the list above (rather than just listing the target classes to |
| * inspect dynamically) because this way, if the number of classes found dynamically were |
| * to suddenly drop to zero (as in, the code changed and the test filter dropped all the |
| * classes) then we'd know the test was no longer testing the correct thing. Similarly, |
| * if the number of classes found dynamically were to suddenly increase, then we'd know |
| * that the test was no longer testing the correct thing. |
| */ |
| @Test |
| fun testPackageClassesMatchTestTargets() { |
| val dawnClasses = Reflections("android.dawn").getAll(Scanners.TypesAnnotated) |
| val actual = dawnClasses.filter { clazz -> |
| isAndroidDawn(clazz) && hasCompanionObjectWithNames(clazz) |
| }.map { it.removePrefix("android.dawn.") } |
| |
| val expected = TYPES_WITH_MAPPED_NAMED_CONSTANTS.mapNotNull { it.simpleName } |
| |
| // Test that the two lists match, throw a useful failure if they don't |
| actual.forEach { className -> assertContains(expected, className) } |
| expected.forEach { className -> assertContains(actual, className) } |
| } |
| |
| /** |
| * Test that every class listed above (a) has a names field, and (b) each name is |
| * mapped to the correct string and instance value. |
| */ |
| @Test |
| fun testMappedConstantsNamesAreCorrect() { |
| for (clazz in TYPES_WITH_MAPPED_NAMED_CONSTANTS) { |
| val companionObject = getCompanionObjectOrFail(clazz) |
| val namesMap = getNamesMapOrFail(companionObject, clazz) |
| val companionConstants = getCompanionConstants(companionObject) |
| |
| for ((key, constantName) in namesMap) { |
| val constantProperty = companionConstants[constantName] |
| val actual = (constantProperty as KProperty1<*, *>).getter.call(companionObject) |
| val expected = clazz.primaryConstructor?.call(key) |
| assertEquals(expected, actual) |
| } |
| } |
| } |
| |
| private fun isAndroidDawn(clazz: String): Boolean { |
| return clazz.startsWith("android.dawn") && clazz.count { it == '.' } == 2 |
| } |
| |
| private fun hasCompanionObjectWithNames(clazz: String): Boolean { |
| val kClass = Class.forName(clazz).kotlin |
| val companionObject = kClass.companionObjectInstance |
| return companionObject != null && companionObject::class.members.any { it.name == "names" } |
| } |
| |
| private fun getCompanionConstants(companionObject: Any): Map<String, KCallable<*>> { |
| return companionObject::class.members.filter { it.isFinal && it is KProperty1<*, *> } |
| .associateBy { it.name } |
| } |
| |
| private fun getCompanionObjectOrFail(clazz: KClass<out Any>): Any { |
| return clazz.companionObjectInstance |
| ?: fail("No companion object found in ${clazz.simpleName}") |
| } |
| |
| private fun getNamesMapOrFail(companionObject: Any, clazz: KClass<out Any>): Map<Int, String> { |
| val namesProperty = companionObject::class.members.find { it.name == "names" } |
| ?: fail("Property 'names' not found in companion object of ${clazz.simpleName}") |
| |
| @Suppress("UNCHECKED_CAST") |
| return namesProperty.call(companionObject) as? Map<Int, String> |
| ?: fail("Property 'names' is not of type Map<Int, String>") |
| } |
| } |