| /* |
| * Copyright 2020 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 androidx.room.compiler.processing |
| |
| import androidx.room.compiler.codegen.XClassName |
| import androidx.room.compiler.codegen.XTypeName |
| import androidx.room.compiler.codegen.asClassName |
| import androidx.room.compiler.processing.javac.JavacType |
| import androidx.room.compiler.processing.util.Source |
| import androidx.room.compiler.processing.util.XTestInvocation |
| import androidx.room.compiler.processing.util.asKClassName |
| import androidx.room.compiler.processing.util.asMutableKClassName |
| import androidx.room.compiler.processing.util.compileFiles |
| import androidx.room.compiler.processing.util.createXTypeVariableName |
| import androidx.room.compiler.processing.util.getAllFieldNames |
| import androidx.room.compiler.processing.util.getDeclaredField |
| import androidx.room.compiler.processing.util.getField |
| import androidx.room.compiler.processing.util.getMethodByJvmName |
| import androidx.room.compiler.processing.util.runProcessorTest |
| import com.google.common.truth.Truth.assertThat |
| import com.google.common.truth.Truth.assertWithMessage |
| import com.squareup.kotlinpoet.INT |
| import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy |
| import com.squareup.kotlinpoet.javapoet.JClassName |
| import com.squareup.kotlinpoet.javapoet.JParameterizedTypeName |
| import com.squareup.kotlinpoet.javapoet.JTypeName |
| import com.squareup.kotlinpoet.javapoet.JTypeVariableName |
| import com.squareup.kotlinpoet.javapoet.KClassName |
| import com.squareup.kotlinpoet.javapoet.KTypeVariableName |
| import org.junit.Test |
| import org.junit.runner.RunWith |
| import org.junit.runners.Parameterized |
| |
| @RunWith(Parameterized::class) |
| class XTypeElementTest( |
| private val isPreCompiled: Boolean, |
| ) { |
| private fun runTest( |
| sources: List<Source>, |
| handler: (XTestInvocation) -> Unit |
| ) { |
| if (isPreCompiled) { |
| val compiled = compileFiles(sources) |
| val hasKotlinSources = sources.any { |
| it is Source.KotlinSource |
| } |
| val kotlinSources = if (hasKotlinSources) { |
| listOf( |
| Source.kotlin("placeholder.kt", "class PlaceholderKotlin") |
| ) |
| } else { |
| emptyList() |
| } |
| val newSources = kotlinSources + Source.java( |
| "PlaceholderJava", |
| "public class " + |
| "PlaceholderJava {}" |
| ) |
| runProcessorTest( |
| sources = newSources, |
| handler = handler, |
| classpath = compiled |
| ) |
| } else { |
| runProcessorTest( |
| sources = sources, |
| handler = handler |
| ) |
| } |
| } |
| |
| @Test |
| fun qualifiedNames() { |
| val src1 = Source.kotlin( |
| "Foo.kt", |
| """ |
| class TopLevel |
| """.trimIndent() |
| ) |
| val src2 = Source.kotlin( |
| "Bar.kt", |
| """ |
| package foo.bar |
| class InFooBar |
| """.trimIndent() |
| ) |
| val src3 = Source.java( |
| "foo.bar.Outer", |
| """ |
| package foo.bar; |
| public class Outer { |
| public static class Nested { |
| } |
| } |
| """ |
| ) |
| runTest( |
| sources = listOf(src1, src2, src3) |
| ) { invocation -> |
| invocation.processingEnv.requireTypeElement("TopLevel").let { |
| assertThat(it.packageName).isEqualTo("") |
| assertThat(it.name).isEqualTo("TopLevel") |
| assertThat(it.qualifiedName).isEqualTo("TopLevel") |
| assertThat(it.asClassName().java) |
| .isEqualTo(JClassName.get("", "TopLevel")) |
| if (invocation.isKsp) { |
| assertThat(it.asClassName().kotlin) |
| .isEqualTo(KClassName("", "TopLevel")) |
| } else { |
| // In javac / kapt we don't have KotlinPoet names |
| assertThat(it.asClassName().kotlin) |
| .isEqualTo(XTypeName.UNAVAILABLE_KTYPE_NAME) |
| } |
| } |
| invocation.processingEnv.requireTypeElement("foo.bar.InFooBar").let { |
| assertThat(it.packageName).isEqualTo("foo.bar") |
| assertThat(it.name).isEqualTo("InFooBar") |
| assertThat(it.qualifiedName).isEqualTo("foo.bar.InFooBar") |
| assertThat(it.asClassName()).isEqualTo(XClassName.get("foo.bar", "InFooBar")) |
| } |
| invocation.processingEnv.requireTypeElement("foo.bar.Outer").let { |
| assertThat(it.packageName).isEqualTo("foo.bar") |
| assertThat(it.name).isEqualTo("Outer") |
| assertThat(it.qualifiedName).isEqualTo("foo.bar.Outer") |
| assertThat(it.asClassName()).isEqualTo(XClassName.get("foo.bar", "Outer")) |
| } |
| invocation.processingEnv.requireTypeElement("foo.bar.Outer.Nested").let { |
| assertThat(it.packageName).isEqualTo("foo.bar") |
| assertThat(it.name).isEqualTo("Nested") |
| assertThat(it.qualifiedName).isEqualTo("foo.bar.Outer.Nested") |
| assertThat(it.asClassName()) |
| .isEqualTo(XClassName.get("foo.bar", "Outer", "Nested")) |
| } |
| if (invocation.isKsp) { |
| // these are KSP specific tests, typenames are tested elsewhere |
| invocation.processingEnv.requireTypeElement("java.lang.Integer").let { |
| // always return kotlin types, this is what compiler does |
| assertThat(it.packageName).isEqualTo("kotlin") |
| assertThat(it.name).isEqualTo("Int") |
| assertThat(it.qualifiedName).isEqualTo("kotlin.Int") |
| assertThat(it.asClassName().java).isEqualTo(JTypeName.INT.box()) |
| assertThat(it.asClassName().kotlin).isEqualTo(INT) |
| } |
| invocation.processingEnv.requireTypeElement("kotlin.Int").let { |
| assertThat(it.packageName).isEqualTo("kotlin") |
| assertThat(it.name).isEqualTo("Int") |
| assertThat(it.qualifiedName).isEqualTo("kotlin.Int") |
| assertThat(it.asClassName().java).isEqualTo(JTypeName.INT.box()) |
| assertThat(it.asClassName().kotlin).isEqualTo(INT) |
| } |
| } |
| } |
| } |
| |
| @Test |
| fun typeAndSuperType() { |
| val src = Source.kotlin( |
| "foo.kt", |
| """ |
| package foo.bar |
| class Baz : MyInterface, AbstractClass() |
| abstract class AbstractClass |
| interface MyInterface |
| interface AnotherInterface : MyInterface |
| """.trimIndent() |
| ) |
| runTest(sources = listOf(src)) { invocation -> |
| invocation.processingEnv.requireTypeElement("foo.bar.Baz").let { |
| assertThat(it.superClass!!.asTypeName()).isEqualTo( |
| invocation.processingEnv.requireType("foo.bar.AbstractClass").asTypeName() |
| ) |
| assertThat(it.type.superTypes.map(XType::asTypeName)).containsExactly( |
| invocation.processingEnv.requireType("foo.bar.AbstractClass").asTypeName(), |
| invocation.processingEnv.requireType("foo.bar.MyInterface").asTypeName() |
| ) |
| assertThat(it.type.asTypeName()).isEqualTo( |
| invocation.processingEnv.requireType("foo.bar.Baz").asTypeName() |
| ) |
| assertThat(it.isInterface()).isFalse() |
| assertThat(it.isKotlinObject()).isFalse() |
| assertThat(it.isAbstract()).isFalse() |
| } |
| invocation.processingEnv.requireTypeElement("foo.bar.AbstractClass").let { |
| assertThat(it.superClass!!.asTypeName()).isEqualTo( |
| invocation.processingEnv.requireType(JTypeName.OBJECT).asTypeName() |
| ) |
| assertThat(it.type.superTypes.map(XType::asTypeName)).containsExactly( |
| invocation.processingEnv.requireType(JTypeName.OBJECT).asTypeName() |
| ) |
| assertThat(it.isAbstract()).isTrue() |
| assertThat(it.isInterface()).isFalse() |
| assertThat(it.type.asTypeName()).isEqualTo( |
| invocation.processingEnv.requireType("foo.bar.AbstractClass").asTypeName() |
| ) |
| } |
| invocation.processingEnv.requireTypeElement("foo.bar.MyInterface").let { |
| assertThat(it.superClass).isNull() |
| assertThat(it.type.superTypes.map(XType::asTypeName)).containsExactly( |
| invocation.processingEnv.requireType(JTypeName.OBJECT).asTypeName() |
| ) |
| assertThat(it.isInterface()).isTrue() |
| assertThat(it.type.asTypeName()).isEqualTo( |
| invocation.processingEnv.requireType("foo.bar.MyInterface").asTypeName() |
| ) |
| } |
| invocation.processingEnv.requireTypeElement("foo.bar.AnotherInterface").let { |
| assertThat(it.superClass).isNull() |
| assertThat(it.type.superTypes.map(XType::asTypeName)).containsExactly( |
| invocation.processingEnv.requireType("java.lang.Object").asTypeName(), |
| invocation.processingEnv.requireType("foo.bar.MyInterface").asTypeName() |
| ) |
| assertThat(it.isInterface()).isTrue() |
| assertThat(it.type.asTypeName()).isEqualTo( |
| invocation.processingEnv.requireType("foo.bar.AnotherInterface").asTypeName() |
| ) |
| } |
| } |
| } |
| |
| @Test |
| fun superInterfaces() { |
| val src = Source.kotlin( |
| "foo.kt", |
| """ |
| package foo.bar |
| class Baz : MyInterface<String>, AbstractClass() |
| abstract class AbstractClass |
| interface MyInterface<E> |
| """.trimIndent() |
| ) |
| runTest(sources = listOf(src)) { invocation -> |
| invocation.processingEnv.requireTypeElement("foo.bar.Baz").let { |
| assertThat(it.superInterfaces).hasSize(1) |
| val superInterface = it.superInterfaces.first { type -> |
| type.rawType.toString() == "foo.bar.MyInterface" |
| } |
| assertThat(superInterface.typeArguments).hasSize(1) |
| assertThat(superInterface.typeArguments[0].asTypeName().java) |
| .isEqualTo(JClassName.get("java.lang", "String")) |
| if (invocation.isKsp) { |
| assertThat(superInterface.typeArguments[0].asTypeName().kotlin) |
| .isEqualTo(KClassName("kotlin", "String")) |
| } |
| if (! invocation.isKsp) { |
| assertThat((superInterface as JavacType).kotlinType).isNotNull() |
| } |
| } |
| } |
| } |
| |
| @Test |
| fun nestedClassName() { |
| val src = Source.kotlin( |
| "Foo.kt", |
| """ |
| package foo.bar |
| class Outer { |
| class Inner |
| } |
| """.trimIndent() |
| ) |
| runTest(sources = listOf(src)) { invocation -> |
| invocation.processingEnv.requireTypeElement("foo.bar.Outer").let { |
| assertThat(it.asClassName()) |
| .isEqualTo(XClassName.get("foo.bar", "Outer")) |
| assertThat(it.isNested()).isFalse() |
| assertThat(it.enclosingTypeElement).isNull() |
| } |
| invocation.processingEnv.requireTypeElement("foo.bar.Outer.Inner").let { |
| assertThat(it.asClassName()) |
| .isEqualTo(XClassName.get("foo.bar", "Outer", "Inner")) |
| assertThat(it.packageName).isEqualTo("foo.bar") |
| assertThat(it.name).isEqualTo("Inner") |
| assertThat(it.isNested()).isTrue() |
| assertThat(it.enclosingTypeElement).isEqualTo( |
| invocation.processingEnv.requireTypeElement("foo.bar.Outer") |
| ) |
| } |
| } |
| } |
| |
| @Test |
| fun modifiers() { |
| val kotlinSrc = Source.kotlin( |
| "Foo.kt", |
| """ |
| open class OpenClass |
| abstract class AbstractClass |
| object MyObject |
| interface MyInterface |
| class Final |
| private class PrivateClass |
| class OuterKotlinClass { |
| inner class InnerKotlinClass |
| class NestedKotlinClass |
| } |
| annotation class KotlinAnnotation |
| data class DataClass(val foo: Int) |
| inline class InlineClass(val foo: Int) |
| fun interface FunInterface { |
| fun foo() |
| } |
| """.trimIndent() |
| ) |
| val javaSrc = Source.java( |
| "OuterJavaClass", |
| """ |
| public class OuterJavaClass { |
| public class InnerJavaClass {} |
| public static class NestedJavaClass {} |
| } |
| """.trimIndent() |
| ) |
| val javaAnnotationSrc = Source.java( |
| "JavaAnnotation", |
| """ |
| public @interface JavaAnnotation { |
| } |
| """.trimIndent() |
| ) |
| runTest( |
| sources = listOf(kotlinSrc, javaSrc, javaAnnotationSrc) |
| ) { invocation -> |
| fun getModifiers(element: XTypeElement): Set<String> { |
| val result = mutableSetOf<String>() |
| if (element.isAbstract()) result.add("abstract") |
| if (element.isFinal()) result.add("final") |
| if (element.isPrivate()) result.add("private") |
| if (element.isProtected()) result.add("protected") |
| if (element.isPublic()) result.add("public") |
| if (element.isKotlinObject()) result.add("object") |
| if (element.isCompanionObject()) result.add("companion") |
| if (element.isFunctionalInterface()) result.add("fun") |
| if (element.isClass()) result.add("class") |
| if (element.isDataClass()) result.add("data") |
| if (element.isValueClass()) result.add("value") |
| if (element.isExpect()) result.add("expect") |
| if (element.isInterface()) result.add("interface") |
| if (element.isStatic()) result.add("static") |
| if (element.isAnnotationClass()) result.add("annotation") |
| return result |
| } |
| |
| fun getModifiers(qName: String): Set<String> = getModifiers( |
| invocation.processingEnv |
| .requireTypeElement(qName) |
| ) |
| |
| assertThat(getModifiers("OpenClass")) |
| .containsExactly("public", "class") |
| assertThat(getModifiers("AbstractClass")) |
| .containsExactly("abstract", "public", "class") |
| assertThat(getModifiers("MyObject")) |
| .containsExactly("final", "public", "object") |
| assertThat(getModifiers("MyInterface")) |
| .containsExactly("abstract", "interface", "public") |
| assertThat(getModifiers("Final")) |
| .containsExactly("final", "public", "class") |
| assertThat(getModifiers("PrivateClass")) |
| .containsExactlyElementsIn( |
| if (invocation.isKsp) { |
| listOf("private", "final", "class") |
| } else { |
| // java does not support top level private classes. |
| listOf("final", "class") |
| } |
| ) |
| assertThat(getModifiers("OuterKotlinClass.InnerKotlinClass")) |
| .containsExactly("final", "public", "class") |
| assertThat(getModifiers("OuterKotlinClass.NestedKotlinClass")) |
| .containsExactly("final", "public", "static", "class") |
| assertThat(getModifiers("OuterJavaClass.InnerJavaClass")) |
| .containsExactly("public", "class") |
| assertThat(getModifiers("OuterJavaClass.NestedJavaClass")) |
| .containsExactly("public", "static", "class") |
| assertThat(getModifiers("JavaAnnotation")).apply { |
| // KSP vs KAPT metadata have a difference in final vs abstract modifiers |
| // for annotation types. |
| if (isPreCompiled && invocation.isKsp) { |
| containsExactly("final", "public", "annotation") |
| } else { |
| containsExactly("abstract", "public", "annotation") |
| } |
| } |
| assertThat(getModifiers("KotlinAnnotation")).apply { |
| // KSP vs KAPT metadata have a difference in final vs abstract modifiers |
| // for annotation types. |
| if (invocation.isKsp) { |
| containsExactly("final", "public", "annotation") |
| } else { |
| containsExactly("abstract", "public", "annotation") |
| } |
| } |
| assertThat(getModifiers("DataClass")) |
| .containsExactly("public", "final", "class", "data") |
| assertThat(getModifiers("InlineClass")).apply { |
| containsExactly("public", "final", "class", "value") |
| } |
| assertThat(getModifiers("FunInterface")) |
| .containsExactly("public", "abstract", "interface", "fun") |
| } |
| } |
| |
| @Test |
| fun kindName() { |
| val kotlinSrc = Source.kotlin( |
| "Foo.kt", |
| """ |
| class KotlinClass |
| interface KotlinInterface |
| annotation class KotlinAnnotation |
| """.trimIndent() |
| ) |
| val javaSrc = Source.java( |
| "Bar.java", |
| """ |
| class JavaClass {} |
| interface JavaInterface {} |
| @interface JavaAnnotation {} |
| """.trimIndent() |
| ) |
| runTest(sources = listOf(kotlinSrc, javaSrc)) { invocation -> |
| invocation.processingEnv.requireTypeElement("KotlinClass").let { |
| assertThat(it.kindName()).isEqualTo("class") |
| } |
| invocation.processingEnv.requireTypeElement("KotlinInterface").let { |
| assertThat(it.kindName()).isEqualTo("interface") |
| } |
| invocation.processingEnv.requireTypeElement("KotlinAnnotation").let { |
| // TODO(b/270557392): make the result consistent between KSP and JavaAP |
| if (invocation.isKsp) { |
| assertThat(it.kindName()).isEqualTo("annotation_class") |
| } else { |
| assertThat(it.kindName()).isEqualTo("annotation_type") |
| } |
| } |
| |
| invocation.processingEnv.requireTypeElement("JavaClass").let { |
| assertThat(it.kindName()).isEqualTo("class") |
| } |
| invocation.processingEnv.requireTypeElement("JavaInterface").let { |
| assertThat(it.kindName()).isEqualTo("interface") |
| } |
| invocation.processingEnv.requireTypeElement("JavaAnnotation").let { |
| // TODO(b/270557392): make the result consistent between KSP and JavaAP |
| if (invocation.isKsp) { |
| assertThat(it.kindName()).isEqualTo("annotation_class") |
| } else { |
| assertThat(it.kindName()).isEqualTo("annotation_type") |
| } |
| } |
| } |
| } |
| |
| @Test |
| fun fieldBasic() { |
| val src = Source.kotlin( |
| "Foo.kt", |
| """ |
| open class BaseClass<T>(val genericProp : T) { |
| fun baseMethod(input: T) {} |
| } |
| class SubClass(x : Int) : BaseClass<Int>(x) { |
| val subClassProp : String = "abc" |
| } |
| """.trimIndent() |
| ) |
| runTest(sources = listOf(src)) { invocation -> |
| val baseClass = invocation.processingEnv.requireTypeElement("BaseClass") |
| assertThat(baseClass.getAllFieldNames()).containsExactly("genericProp") |
| assertThat(baseClass.getDeclaredFields().map { it.name }) |
| .containsExactly("genericProp") |
| val subClass = invocation.processingEnv.requireTypeElement("SubClass") |
| assertThat(subClass.getAllFieldNames()).containsExactly("genericProp", "subClassProp") |
| assertThat(subClass.getDeclaredFields().map { it.name }) |
| .containsExactly("subClassProp") |
| |
| val baseMethod = baseClass.getMethodByJvmName("baseMethod") |
| baseMethod.asMemberOf(subClass.type).let { methodType -> |
| val genericArg = methodType.parameterTypes.first() |
| assertThat(genericArg.asTypeName()).isEqualTo(Int::class.asClassName()) |
| } |
| |
| baseClass.getField("genericProp").let { field -> |
| assertThat(field.type.asTypeName()).isEqualTo(createXTypeVariableName("T")) |
| } |
| |
| subClass.getField("genericProp").let { field -> |
| // this is tricky because even though it is non-null it, it should still be boxed |
| assertThat(field.asMemberOf(subClass.type).asTypeName()) |
| .isEqualTo(Int::class.asClassName()) |
| } |
| } |
| } |
| |
| @Test |
| fun fieldsOverride() { |
| val src = Source.kotlin( |
| "Foo.kt", |
| """ |
| open class BaseClass( |
| open val value : List<Int> |
| ) |
| class SubClass( |
| override val value : MutableList<Int> |
| ) : BaseClass(value) |
| """.trimIndent() |
| ) |
| runTest(sources = listOf(src)) { invocation -> |
| val baseClass = invocation.processingEnv.requireTypeElement("BaseClass") |
| assertThat(baseClass.getAllFieldNames()).containsExactly("value") |
| val subClass = invocation.processingEnv.requireTypeElement("SubClass") |
| assertThat(subClass.getAllFieldNames()).containsExactly("value") |
| val baseFieldType = baseClass.getField("value").type |
| val subFieldType = subClass.getField("value").type |
| assertThat(baseFieldType.asTypeName().java).isEqualTo( |
| JParameterizedTypeName.get(List::class.java, Integer::class.java) |
| ) |
| assertThat(subFieldType.asTypeName().java).isEqualTo( |
| JParameterizedTypeName.get(List::class.java, Integer::class.java) |
| ) |
| if (invocation.isKsp) { |
| assertThat(baseFieldType.asTypeName().kotlin).isEqualTo( |
| List::class.asKClassName().parameterizedBy(INT) |
| ) |
| assertThat(subFieldType.asTypeName().kotlin).isEqualTo( |
| List::class.asMutableKClassName().parameterizedBy(INT) |
| ) |
| } |
| } |
| } |
| |
| @Test |
| fun fieldsMethodsWithoutBacking() { |
| runTest( |
| sources = listOf(Source.kotlin( |
| "Foo.kt", |
| """ |
| package test |
| class Subject { |
| val realField: String = "" |
| get() = field |
| val noBackingVal: String |
| get() = "" |
| var noBackingVar: String |
| get() = "" |
| set(value) {} |
| |
| companion object { |
| @JvmStatic |
| val staticRealField: String = "" |
| get() = field |
| @JvmStatic |
| val staticNoBackingVal: String |
| get() = "" |
| @JvmStatic |
| var staticNoBackingVar: String |
| get() = "" |
| set(value) {} |
| } |
| } |
| """.trimIndent() |
| )), |
| ) { invocation -> |
| val subject = invocation.processingEnv.requireTypeElement("test.Subject") |
| val declaredFields = subject.getDeclaredFields().map { it.name } - |
| listOf("Companion") // skip Companion, KAPT generates it |
| val expectedFields = listOf("realField", "staticRealField") |
| assertWithMessage(subject.qualifiedName) |
| .that(declaredFields) |
| .containsExactlyElementsIn(expectedFields) |
| val allFields = subject.getAllFieldsIncludingPrivateSupers().map { it.name } - |
| listOf("Companion") // skip Companion, KAPT generates it |
| assertWithMessage(subject.qualifiedName) |
| .that(allFields.toList()) |
| .containsExactlyElementsIn(expectedFields) |
| val methodNames = subject.getDeclaredMethods().map { it.jvmName } |
| assertWithMessage(subject.qualifiedName) |
| .that(methodNames) |
| .containsAtLeast("getNoBackingVal", "getNoBackingVar", "setNoBackingVar") |
| assertWithMessage(subject.qualifiedName) |
| .that(methodNames) |
| .doesNotContain("setNoBackingVal") |
| } |
| } |
| |
| @Test |
| fun abstractFields() { |
| runTest( |
| sources = listOf(Source.kotlin( |
| "Foo.kt", |
| """ |
| package test |
| abstract class Subject { |
| val value: String = "" |
| abstract val abstractValue: String |
| companion object { |
| var realCompanion: String = "" |
| @JvmStatic |
| var jvmStatic: String = "" |
| } |
| } |
| """.trimIndent() |
| )), |
| ) { invocation -> |
| val subject = invocation.processingEnv.requireTypeElement("test.Subject") |
| val declaredFields = subject.getDeclaredFields().map { it.name } - |
| listOf("Companion") |
| val expectedFields = listOf("value", "realCompanion", "jvmStatic") |
| assertWithMessage(subject.qualifiedName) |
| .that(declaredFields) |
| .containsExactlyElementsIn(expectedFields) |
| } |
| } |
| |
| @Test |
| fun lateinitFields() { |
| runTest( |
| sources = listOf(Source.kotlin( |
| "Foo.kt", |
| """ |
| package test |
| class Subject { |
| lateinit var x:String |
| var y:String = "abc" |
| } |
| """.trimIndent() |
| )), |
| ) { invocation -> |
| val subject = invocation.processingEnv.requireTypeElement("test.Subject") |
| assertWithMessage(subject.fallbackLocationText) |
| .that(subject.getDeclaredFields().map { it.name }) |
| .containsExactly( |
| "x", "y" |
| ) |
| assertWithMessage(subject.fallbackLocationText) |
| .that(subject.getDeclaredMethods().map { it.jvmName }) |
| .containsExactly( |
| "getX", "setX", "getY", "setY" |
| ) |
| subject.getField("x").let { field -> |
| assertThat(field.isFinal()).isFalse() |
| assertThat(field.isPrivate()).isFalse() |
| } |
| } |
| } |
| |
| @Test |
| fun fieldsInInterfaces() { |
| val src = Source.kotlin( |
| "Foo.kt", |
| """ |
| interface MyInterface { |
| var x:Int |
| } |
| """.trimIndent() |
| ) |
| runTest(sources = listOf(src)) { invocation -> |
| val element = invocation.processingEnv.requireTypeElement("MyInterface") |
| assertThat(element.getAllFieldsIncludingPrivateSupers().toList()).isEmpty() |
| element.getMethodByJvmName("getX").let { |
| assertThat(it.isAbstract()).isTrue() |
| } |
| element.getMethodByJvmName("setX").let { |
| assertThat(it.isAbstract()).isTrue() |
| } |
| } |
| } |
| |
| @Test |
| fun fieldsInAbstractClass() { |
| val src = Source.kotlin( |
| "Foo.kt", |
| """ |
| abstract class MyAbstractClass { |
| @JvmField |
| var jvmVar: Int = 0 |
| abstract var abstractVar: Int |
| var nonAbstractVar: Int = 0 |
| } |
| """.trimIndent() |
| ) |
| runTest(sources = listOf(src)) { invocation -> |
| val element = invocation.processingEnv.requireTypeElement("MyAbstractClass") |
| assertThat( |
| element.getAllFieldNames() |
| ).containsExactly( |
| "nonAbstractVar", "jvmVar" |
| ) |
| assertThat( |
| element.getDeclaredMethods().map { it.jvmName } |
| ).containsExactly( |
| "getAbstractVar", "setAbstractVar", |
| "getNonAbstractVar", "setNonAbstractVar" |
| ) |
| element.getMethodByJvmName("getAbstractVar").let { |
| assertThat(it.isAbstract()).isTrue() |
| } |
| element.getMethodByJvmName("setAbstractVar").let { |
| assertThat(it.isAbstract()).isTrue() |
| } |
| |
| element.getMethodByJvmName("getNonAbstractVar").let { |
| assertThat(it.isAbstract()).isFalse() |
| } |
| element.getMethodByJvmName("setNonAbstractVar").let { |
| assertThat(it.isAbstract()).isFalse() |
| } |
| } |
| } |
| |
| @Test |
| fun propertyGettersSetters() { |
| runTest( |
| listOf( |
| Source.java( |
| "JavaSubject.java", |
| """ |
| class JavaSubject { |
| int myField; |
| private int mutable; |
| int immutable; |
| int getMutable() {return 3;} |
| void setMutable(int x) {} |
| int getImmutable() {return 3;} |
| } |
| """.trimIndent() |
| ), |
| Source.kotlin( |
| "KotlinSubject.kt", |
| """ |
| class KotlinSubject { |
| private val myField = 0 |
| var mutable: Int = 0 |
| val immutable:Int = 0 |
| } |
| """.trimIndent() |
| ) |
| ), |
| ) { invocation -> |
| listOf("JavaSubject", "KotlinSubject",).map { |
| invocation.processingEnv.requireTypeElement(it) |
| }.forEach { subject -> |
| val methods = subject.getDeclaredMethods() |
| assertWithMessage(subject.qualifiedName) |
| .that( |
| methods.map { |
| it.jvmName |
| } |
| ).containsExactly( |
| "getMutable", "setMutable", "getImmutable" |
| ) |
| methods.forEach { |
| assertWithMessage("${subject.qualifiedName}.${it.jvmName}()") |
| .that(it.isKotlinPropertyMethod()) |
| .apply { |
| if (subject.name.contains("Kotlin")) { |
| isTrue() |
| } else { |
| isFalse() |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| @Test |
| fun declaredAndInstanceMethods() { |
| val src = Source.kotlin( |
| "Foo.kt", |
| """ |
| open class Base(x:Int) { |
| open fun baseFun(): Int = TODO() |
| suspend fun suspendFun(): Int = TODO() |
| private fun privateBaseFun(): Int = TODO() |
| companion object { |
| @JvmStatic |
| fun staticBaseFun(): Int = TODO() |
| fun companionMethod(): Int = TODO() |
| } |
| } |
| open class SubClass : Base { |
| constructor(y:Int): super(y) { |
| } |
| constructor(x:Int, y:Int): super(y) { |
| } |
| override fun baseFun(): Int = TODO() |
| fun subFun(): Int = TODO() |
| private fun privateSubFun(): Int = TODO() |
| companion object { |
| @JvmStatic |
| fun staticFun(): Int = TODO() |
| } |
| } |
| """.trimIndent() |
| ) |
| runTest(sources = listOf(src)) { invocation -> |
| val base = invocation.processingEnv.requireTypeElement("Base") |
| val objectMethodNames = invocation.objectMethodNames() |
| assertThat(base.getDeclaredMethods().jvmNames()).containsExactly( |
| "baseFun", "suspendFun", "privateBaseFun", "staticBaseFun" |
| ) |
| |
| val sub = invocation.processingEnv.requireTypeElement("SubClass") |
| assertThat(sub.getDeclaredMethods().jvmNames()).containsExactly( |
| "baseFun", "subFun", "privateSubFun", "staticFun" |
| ) |
| assertThat( |
| sub.getAllNonPrivateInstanceMethods().jvmNames() - objectMethodNames |
| ).containsExactly( |
| "baseFun", "suspendFun", "subFun" |
| ) |
| } |
| } |
| |
| @Test |
| fun diamondOverride() { |
| runTest( |
| sources = listOf(Source.kotlin( |
| "Foo.kt", |
| """ |
| package test |
| interface Parent<T> { |
| fun parent(t: T) |
| } |
| |
| interface Child1<T> : Parent<T> { |
| fun child1(t: T) |
| } |
| |
| interface Child2<T> : Parent<T> { |
| fun child2(t: T) |
| } |
| |
| abstract class Subject1 : Child1<String>, Child2<String>, Parent<String> |
| abstract class Subject2 : Child1<String>, Parent<String> |
| abstract class Subject3 : Child1<String>, Parent<String> { |
| abstract override fun parent(t: String) |
| } |
| """.trimIndent() |
| )), |
| ) { invocation -> |
| invocation.processingEnv.requireTypeElement("test.Subject1").let { subject -> |
| assertWithMessage(subject.qualifiedName).that( |
| invocation.nonObjectMethodSignatures(subject) |
| ).containsExactly( |
| "child1(java.lang.String):void", |
| "child2(java.lang.String):void", |
| "parent(java.lang.String):void", |
| ) |
| } |
| invocation.processingEnv.requireTypeElement("test.Subject2").let { subject -> |
| assertWithMessage(subject.qualifiedName).that( |
| invocation.nonObjectMethodSignatures(subject) |
| ).containsExactly( |
| "child1(java.lang.String):void", |
| "parent(java.lang.String):void", |
| ) |
| } |
| invocation.processingEnv.requireTypeElement("test.Subject3").let { subject -> |
| assertWithMessage(subject.qualifiedName).that( |
| invocation.nonObjectMethodSignatures(subject) |
| ).containsExactly( |
| "child1(java.lang.String):void", |
| "parent(java.lang.String):void", |
| ) |
| } |
| } |
| } |
| |
| @Test |
| fun suspendOverride() { |
| val src = Source.kotlin( |
| "Foo.kt", |
| """ |
| interface Base<T> { |
| suspend fun get(): T |
| suspend fun getAll(): List<T> |
| suspend fun putAll(input: List<T>) |
| suspend fun getAllWithDefault(): List<T> |
| } |
| |
| interface DerivedInterface : Base<String> { |
| override suspend fun get(): String |
| override suspend fun getAll(): List<String> |
| override suspend fun putAll(input: List<String>) |
| override suspend fun getAllWithDefault(): List<String> { |
| return emptyList() |
| } |
| } |
| """.trimIndent() |
| ) |
| runTest(sources = listOf(src)) { invocation -> |
| val base = invocation.processingEnv.requireTypeElement("DerivedInterface") |
| val methodNames = base.getAllMethods().toList().jvmNames() |
| assertThat(methodNames).containsExactly( |
| "get", "getAll", "putAll", "getAllWithDefault" |
| ) |
| } |
| } |
| |
| @Test |
| fun suspendOverride_abstractClass() { |
| val src = Source.kotlin( |
| "Foo.kt", |
| """ |
| abstract class Base<T> { |
| abstract suspend fun get(): T |
| abstract suspend fun getAll(): List<T> |
| abstract suspend fun putAll(input: List<T>) |
| } |
| |
| abstract class DerivedClass : Base<Int>() { |
| abstract override suspend fun get(): Int |
| abstract override suspend fun getAll(): List<Int> |
| override suspend fun putAll(input: List<Int>) { |
| } |
| } |
| """.trimIndent() |
| ) |
| runTest(sources = listOf(src)) { invocation -> |
| val base = invocation.processingEnv.requireTypeElement("DerivedClass") |
| val methodNamesCount = |
| base.getAllMethods().toList().jvmNames().groupingBy { it }.eachCount() |
| assertThat(methodNamesCount["get"]).isEqualTo(1) |
| assertThat(methodNamesCount["getAll"]).isEqualTo(1) |
| assertThat(methodNamesCount["putAll"]).isEqualTo(1) |
| } |
| } |
| |
| @Test |
| fun overrideMethodWithCovariantReturnType() { |
| val src = Source.kotlin( |
| "ParentWithExplicitOverride.kt", |
| """ |
| interface ParentWithExplicitOverride: ChildInterface, Child { |
| override fun child(): Child |
| } |
| |
| interface ParentWithoutExplicitOverride: ChildInterface, Child |
| |
| interface Child: ChildInterface { |
| override fun child(): Child |
| } |
| |
| interface ChildInterface { |
| fun child(): ChildInterface |
| } |
| """.trimIndent() |
| ) |
| |
| runTest(sources = listOf(src)) { invocation -> |
| invocation.processingEnv.requireTypeElement( |
| "ParentWithExplicitOverride" |
| ).let { parent -> |
| assertWithMessage(parent.qualifiedName).that( |
| invocation.nonObjectMethodSignatures(parent) |
| ).containsExactly( |
| "child():Child" |
| ) |
| } |
| |
| invocation.processingEnv.requireTypeElement( |
| "ParentWithoutExplicitOverride" |
| ).let { parent -> |
| assertWithMessage(parent.qualifiedName).that( |
| invocation.nonObjectMethodSignatures(parent) |
| ).containsExactly( |
| "child():Child" |
| ) |
| } |
| } |
| } |
| |
| @Test |
| fun allMethodsFiltersInAccessibleMethods() { |
| val srcs = listOf( |
| Source.java( |
| "foo.Foo", |
| """ |
| package foo; |
| public interface Foo { |
| void foo_Public(); |
| } |
| """.trimIndent() |
| ), |
| Source.java( |
| "foo.parent.FooParent", |
| """ |
| package foo.parent; |
| public abstract class FooParent implements foo.Foo { |
| public void fooParent_Public() {} |
| protected void fooParent_Protected() {} |
| private void fooParent_Private() {} |
| void fooParent_PackagePrivate() {} |
| } |
| """.trimIndent() |
| ), |
| Source.java( |
| "foo.child.FooChild", |
| """ |
| package foo.child; |
| public abstract class FooChild extends foo.parent.FooParent { |
| public void fooChild_Public() {} |
| protected void fooChild_Protected() {} |
| private void fooChild_Private() {} |
| void fooChild_PackagePrivate() {} |
| } |
| """.trimIndent() |
| ), |
| ) |
| runTest(sources = srcs) { invocation -> |
| invocation.processingEnv.requireTypeElement("foo.child.FooChild") |
| .let { fooChild -> |
| assertWithMessage(fooChild.qualifiedName).that( |
| invocation.nonObjectMethodSignatures(fooChild) |
| ).containsExactly( |
| "foo_Public():void", |
| "fooParent_Public():void", |
| "fooParent_Protected():void", |
| "fooChild_Public():void", |
| "fooChild_Protected():void", |
| "fooChild_Private():void", |
| "fooChild_PackagePrivate():void" |
| ) |
| } |
| } |
| } |
| |
| @Test |
| fun allMethods() { |
| val src = Source.kotlin( |
| "Foo.kt", |
| """ |
| open class Base(x:Int) { |
| constructor(x:Int, y:Int): this(x) { |
| } |
| fun baseMethod(): Int = TODO() |
| open fun overriddenMethod(): Int = TODO() |
| private fun privateBaseMethod(): Int = TODO() |
| companion object { |
| @JvmStatic |
| private fun privateBaseCompanionMethod(): Int = TODO() |
| @JvmStatic |
| fun baseCompanionMethod(): Int = TODO() |
| } |
| } |
| interface MyInterface { |
| fun interfaceMethod(): Int = TODO() |
| } |
| class SubClass : Base, MyInterface { |
| constructor(x:Int): super(x) { |
| } |
| constructor(x:Int, y:Int): super(y) { |
| } |
| fun subMethod(): Int = TODO() |
| fun privateSubMethod(): Int = TODO() |
| override fun overriddenMethod(): Int = TODO() |
| override fun interfaceMethod(): Int = TODO() |
| companion object { |
| fun dontSeeThisOne(): Int = TODO() |
| @JvmStatic |
| fun subCompanionMethod(): Int = TODO() |
| } |
| } |
| """.trimIndent() |
| ) |
| runTest(sources = listOf(src)) { invocation -> |
| val objectMethodNames = invocation.objectMethodNames() |
| val klass = invocation.processingEnv.requireTypeElement("SubClass") |
| assertThat( |
| klass.getAllMethods().jvmNames() - objectMethodNames |
| ).containsExactly( |
| "baseMethod", "overriddenMethod", "baseCompanionMethod", |
| "interfaceMethod", "subMethod", "privateSubMethod", "subCompanionMethod" |
| ) |
| } |
| } |
| |
| /** |
| * When JvmNames is used along with a suppression over the error, the behavior becomes |
| * complicated. Normally, JvmName annotation is not allowed in overrides and open methods, yet |
| * developers can still use it by putting a suppression over it. The compiler will generate a |
| * delegating method in these cases in the .class file, yet in KSP, we don't really see that |
| * method (also shouldn't ideally). |
| * |
| * This test is here to acknowledge that the behavior is inconsistent yet working as intended |
| * from XProcessing's perspective. |
| * |
| * Also see: https://youtrack.jetbrains.com/issue/KT-50782 as a sign why this suppression is |
| * not worth supporting :). |
| */ |
| @Test |
| fun allMethods_withJvmNames() { |
| runTest( |
| sources = listOf( |
| Source.kotlin( |
| "Foo.kt", |
| """ |
| package test |
| interface Interface { |
| fun f1() |
| @JvmName("notF2") |
| @Suppress("INAPPLICABLE_JVM_NAME") |
| fun f2() |
| } |
| abstract class Subject : Interface { |
| @JvmName("notF1") |
| @Suppress("INAPPLICABLE_JVM_NAME") |
| override fun f1() { |
| } |
| } |
| """.trimIndent() |
| ) |
| ), |
| ) { invocation -> |
| val appSubject = invocation.processingEnv.requireTypeElement("test.Subject") |
| val methodNames = appSubject.getAllMethods().map { it.name }.toList() |
| val methodJvmNames = appSubject.getAllMethods().map { it.jvmName }.toList() |
| val objectMethodNames = invocation.objectMethodNames() |
| if (invocation.isKsp) { |
| assertThat(methodNames - objectMethodNames).containsExactly( |
| "f1", "f2" |
| ) |
| assertThat(methodJvmNames - objectMethodNames).containsExactly( |
| "notF1", "notF2" |
| ) |
| } else { |
| assertThat(methodNames - objectMethodNames).containsExactly( |
| "f1", "f1", "f2" |
| ) |
| assertThat(methodJvmNames - objectMethodNames).containsExactly( |
| "f1", "notF1", "notF2" |
| ) |
| } |
| } |
| } |
| |
| @Test |
| fun gettersSetters() { |
| val src = Source.kotlin( |
| "Foo.kt", |
| """ |
| open class JustGetter(val x:Int) { |
| private val invisible:Int = TODO() |
| private var invisibleMutable:Int = TODO() |
| } |
| class GetterSetter(var y:Int) : JustGetter(y) { |
| private val subInvisible:Int = TODO() |
| private var subInvisibleMutable:Int = TODO() |
| } |
| """.trimIndent() |
| ) |
| runTest(sources = listOf(src)) { invocation -> |
| val objectMethodNames = invocation.objectMethodNames() |
| invocation.processingEnv.requireTypeElement("JustGetter").let { base -> |
| assertThat(base.getDeclaredMethods().jvmNames()).containsExactly( |
| "getX" |
| ) |
| assertThat(base.getAllMethods().jvmNames() - objectMethodNames).containsExactly( |
| "getX" |
| ) |
| assertThat( |
| base.getAllNonPrivateInstanceMethods().jvmNames() - objectMethodNames |
| ).containsExactly( |
| "getX" |
| ) |
| } |
| invocation.processingEnv.requireTypeElement("GetterSetter").let { sub -> |
| assertThat(sub.getDeclaredMethods().jvmNames()).containsExactly( |
| "getY", "setY" |
| ) |
| assertThat(sub.getAllMethods().jvmNames() - objectMethodNames).containsExactly( |
| "getX", "getY", "setY" |
| ) |
| assertThat( |
| sub.getAllNonPrivateInstanceMethods().jvmNames() - objectMethodNames |
| ).containsExactly( |
| "getX", "getY", "setY" |
| ) |
| } |
| } |
| } |
| |
| @Test |
| fun companion() { |
| val src = Source.kotlin( |
| "Foo.kt", |
| """ |
| open class CompanionSubject { |
| companion object { |
| @JvmStatic |
| var mutableStatic: String = "a" |
| @JvmStatic |
| val immutableStatic: String = "bar" |
| val companionProp: Int = 3 |
| @get:JvmStatic |
| var companionProp_getterJvmStatic:Int =3 |
| @set:JvmStatic |
| var companionProp_setterJvmStatic:Int =3 |
| |
| fun companionMethod() { |
| } |
| |
| @JvmStatic |
| fun companionMethodWithJvmStatic() {} |
| } |
| } |
| class SubClass : CompanionSubject() |
| """.trimIndent() |
| ) |
| runTest(sources = listOf(src)) { invocation -> |
| val objectMethodNames = invocation.processingEnv.requireTypeElement( |
| Any::class |
| ).getAllMethods().jvmNames() |
| val subject = invocation.processingEnv.requireTypeElement("CompanionSubject") |
| assertThat(subject.getAllFieldNames() - "Companion").containsExactly( |
| "mutableStatic", "immutableStatic", "companionProp", |
| "companionProp_getterJvmStatic", "companionProp_setterJvmStatic" |
| ) |
| val expectedMethodNames = listOf( |
| "getMutableStatic", "setMutableStatic", "getImmutableStatic", |
| "getCompanionProp_getterJvmStatic", "setCompanionProp_setterJvmStatic", |
| "companionMethodWithJvmStatic" |
| ) |
| assertThat( |
| subject.getDeclaredMethods().jvmNames() |
| ).containsExactlyElementsIn( |
| expectedMethodNames |
| ) |
| assertThat( |
| subject.getAllMethods().jvmNames() - objectMethodNames |
| ).containsExactlyElementsIn( |
| expectedMethodNames |
| ) |
| assertThat( |
| subject.getAllNonPrivateInstanceMethods().jvmNames() - objectMethodNames |
| ).isEmpty() |
| val subClass = invocation.processingEnv.requireTypeElement("SubClass") |
| assertThat(subClass.getDeclaredMethods()).isEmpty() |
| assertThat( |
| subClass.getAllMethods().jvmNames() - objectMethodNames |
| ).containsExactlyElementsIn( |
| expectedMethodNames |
| ) |
| |
| // make sure everything coming from companion is marked as static |
| subject.getDeclaredFields().forEach { |
| assertWithMessage(it.name).that(it.isStatic()).isTrue() |
| } |
| subject.getDeclaredMethods().forEach { |
| assertWithMessage(it.jvmName).that(it.isStatic()).isTrue() |
| } |
| |
| // make sure asMemberOf works fine for statics |
| val subClassType = subClass.type |
| subject.getDeclaredFields().forEach { |
| try { |
| it.asMemberOf(subClassType) |
| } catch (th: Throwable) { |
| throw AssertionError("Couldn't run asMemberOf for ${it.name}") |
| } |
| } |
| subject.getDeclaredMethods().forEach { |
| try { |
| it.asMemberOf(subClassType) |
| } catch (th: Throwable) { |
| throw AssertionError("Couldn't run asMemberOf for ${it.jvmName}") |
| } |
| } |
| } |
| } |
| |
| @Test |
| fun gettersSetters_interface() { |
| val src = Source.kotlin( |
| "Foo.kt", |
| """ |
| interface JustGetter { |
| val x:Int |
| } |
| interface GetterSetter : JustGetter { |
| var y:Int |
| } |
| """.trimIndent() |
| ) |
| runTest(sources = listOf(src)) { invocation -> |
| invocation.processingEnv.requireTypeElement("JustGetter").let { base -> |
| assertThat(base.getDeclaredMethods().jvmNames()).containsExactly( |
| "getX" |
| ) |
| assertThat(base.getAllMethods().jvmNames()).containsExactly( |
| "getX" |
| ) |
| assertThat(base.getAllNonPrivateInstanceMethods().jvmNames()).containsExactly( |
| "getX" |
| ) |
| } |
| invocation.processingEnv.requireTypeElement("GetterSetter").let { sub -> |
| assertThat(sub.getDeclaredMethods().jvmNames()).containsExactly( |
| "getY", "setY" |
| ) |
| assertThat(sub.getAllMethods().jvmNames()).containsExactly( |
| "getX", "getY", "setY" |
| ) |
| assertThat(sub.getAllNonPrivateInstanceMethods().jvmNames()).containsExactly( |
| "getX", "getY", "setY" |
| ) |
| } |
| } |
| } |
| |
| @Test |
| fun constructors() { |
| val src = Source.kotlin( |
| "Foo.kt", |
| """ |
| interface MyInterface |
| class NoExplicitConstructor |
| open class Base(x:Int) |
| open class ExplicitConstructor { |
| constructor(x:Int) |
| } |
| open class BaseWithSecondary(x:Int) { |
| constructor(y:String):this(3) |
| } |
| class Sub(x:Int) : Base(x) |
| class SubWith3Constructors() : BaseWithSecondary("abc") { |
| constructor(list:List<String>): this() |
| constructor(list:List<String>, x:Int): this() |
| } |
| abstract class AbstractNoExplicit |
| abstract class AbstractExplicit(x:Int) |
| """.trimIndent() |
| ) |
| runTest(sources = listOf(src)) { invocation -> |
| val subjects = listOf( |
| "MyInterface", "NoExplicitConstructor", "Base", "ExplicitConstructor", |
| "BaseWithSecondary", "Sub", "SubWith3Constructors", |
| "AbstractNoExplicit", "AbstractExplicit" |
| ) |
| val constructorCounts = subjects.map { |
| it to invocation.processingEnv.requireTypeElement(it).getConstructors().size |
| } |
| assertThat(constructorCounts) |
| .containsExactly( |
| "MyInterface" to 0, |
| "NoExplicitConstructor" to 1, |
| "Base" to 1, |
| "ExplicitConstructor" to 1, |
| "BaseWithSecondary" to 2, |
| "Sub" to 1, |
| "SubWith3Constructors" to 3, |
| "AbstractNoExplicit" to 1, |
| "AbstractExplicit" to 1 |
| ) |
| |
| val primaryConstructorParameterNames = subjects.map { |
| it to invocation.processingEnv.requireTypeElement(it) |
| .findPrimaryConstructor() |
| ?.parameters?.map { |
| it.name |
| } |
| } |
| assertThat(primaryConstructorParameterNames) |
| .containsExactly( |
| "MyInterface" to null, |
| "NoExplicitConstructor" to emptyList<String>(), |
| "Base" to listOf("x"), |
| "ExplicitConstructor" to null, |
| "BaseWithSecondary" to listOf("x"), |
| "Sub" to listOf("x"), |
| "SubWith3Constructors" to emptyList<String>(), |
| "AbstractNoExplicit" to emptyList<String>(), |
| "AbstractExplicit" to listOf("x") |
| ) |
| } |
| } |
| |
| @Test |
| fun jvmDefault() { |
| val src = Source.kotlin( |
| "Foo.kt", |
| """ |
| interface MyInterface { |
| fun notJvmDefault() |
| @JvmDefault |
| fun jvmDefault() {} |
| } |
| """.trimIndent() |
| ) |
| runTest(sources = listOf(src)) { invocation -> |
| val subject = invocation.processingEnv.requireTypeElement("MyInterface") |
| assertThat(subject.getMethodByJvmName("notJvmDefault").isJavaDefault()).isFalse() |
| assertThat(subject.getMethodByJvmName("jvmDefault").isJavaDefault()).isTrue() |
| } |
| } |
| |
| @Test |
| fun constructors_java() { |
| val src = Source.java( |
| "Source", |
| """ |
| import java.util.List; |
| interface MyInterface {} |
| class NoExplicitConstructor{} |
| class Base { |
| Base(int x){} |
| } |
| class ExplicitConstructor { |
| ExplicitConstructor(int x){} |
| } |
| class BaseWithSecondary { |
| BaseWithSecondary(int x){} |
| BaseWithSecondary(String y){} |
| } |
| class Sub extends Base { |
| Sub(int x) { |
| super(x); |
| } |
| } |
| class SubWith3Constructors extends BaseWithSecondary { |
| SubWith3Constructors() { |
| super(3); |
| } |
| SubWith3Constructors(List<String> list) { |
| super(3); |
| } |
| SubWith3Constructors(List<String> list, int x) { |
| super(3); |
| } |
| } |
| abstract class AbstractNoExplicit {} |
| abstract class AbstractExplicit { |
| AbstractExplicit(int x) {} |
| } |
| """.trimIndent() |
| ) |
| runTest(sources = listOf(src)) { invocation -> |
| val subjects = listOf( |
| "MyInterface", "NoExplicitConstructor", "Base", "ExplicitConstructor", |
| "BaseWithSecondary", "Sub", "SubWith3Constructors", |
| "AbstractNoExplicit", "AbstractExplicit" |
| ) |
| val constructorCounts = subjects.map { |
| it to invocation.processingEnv.requireTypeElement(it).getConstructors().size |
| } |
| assertThat(constructorCounts) |
| .containsExactly( |
| "MyInterface" to 0, |
| "NoExplicitConstructor" to 1, |
| "Base" to 1, |
| "ExplicitConstructor" to 1, |
| "BaseWithSecondary" to 2, |
| "Sub" to 1, |
| "SubWith3Constructors" to 3, |
| "AbstractNoExplicit" to 1, |
| "AbstractExplicit" to 1 |
| ) |
| |
| subjects.forEach { |
| assertWithMessage(it) |
| .that(invocation.processingEnv.requireTypeElement(it).findPrimaryConstructor()) |
| .isNull() |
| } |
| } |
| } |
| |
| @Test |
| fun enumTypeElement() { |
| runTest( |
| sources = listOf( |
| Source.kotlin( |
| "test/KotlinEnum.kt", |
| """ |
| package test |
| enum class KotlinEnum(private val x:Int) { |
| VAL1(1), |
| VAL2(2); |
| |
| fun enumMethod(): Unit {} |
| } |
| """.trimIndent() |
| ), |
| Source.java( |
| "test.JavaEnum", |
| """ |
| package test; |
| public enum JavaEnum { |
| VAL1(1), |
| VAL2(2); |
| |
| private int x; |
| |
| JavaEnum(int x) { |
| this.x = x; |
| } |
| void enumMethod() {} |
| } |
| """.trimIndent() |
| ) |
| ), |
| ) { invocation -> |
| listOf( |
| "test.KotlinEnum", "test.JavaEnum", |
| ).forEach { qName -> |
| val typeElement = invocation.processingEnv.requireTypeElement(qName) |
| assertWithMessage("$qName is enum") |
| .that(typeElement.isEnum()) |
| .isTrue() |
| assertWithMessage("$qName does not report enum constants in methods") |
| .that(typeElement.getDeclaredMethods().map { it.jvmName }) |
| .run { |
| contains("enumMethod") |
| containsNoneOf("VAL1", "VAL2") |
| } |
| assertWithMessage("$qName can return enum constants") |
| .that((typeElement as XEnumTypeElement).entries.map { it.name }) |
| .containsExactly("VAL1", "VAL2") |
| assertWithMessage("$qName does not report enum constants in fields") |
| .that(typeElement.getAllFieldNames()) |
| .run { |
| contains("x") |
| containsNoneOf("VAL1", "VAL2") |
| } |
| assertWithMessage("$qName does not report enum constants in declared fields") |
| .that(typeElement.getDeclaredFields().map { it.name }) |
| .containsExactly("x") |
| assertWithMessage("$qName enum entries are XEnumEntry") |
| .that(typeElement.getEnclosedElements().filter { it.isEnumEntry() }) |
| .hasSize(2) |
| assertWithMessage("$qName enum entries are not type elements") |
| .that(typeElement.getEnclosedElements().filter { it.isTypeElement() }) |
| .isEmpty() |
| } |
| } |
| } |
| |
| @Test |
| fun enclosedTypes() { |
| val src = Source.kotlin( |
| "Foo.kt", |
| """ |
| class TopLevelClass { |
| class NestedClass |
| object NestedObject |
| interface NestedInterface |
| enum class NestedEnum { |
| A, B |
| } |
| companion object { |
| val foo = 1 |
| } |
| } |
| """.trimIndent() |
| ) |
| runTest(sources = listOf(src)) { invocation -> |
| val topLevelClass = invocation.processingEnv.requireTypeElement("TopLevelClass") |
| val enclosedTypeElements = topLevelClass.getEnclosedTypeElements() |
| |
| assertThat(enclosedTypeElements) |
| .containsExactly( |
| invocation.processingEnv.requireTypeElement("TopLevelClass.NestedClass"), |
| invocation.processingEnv.requireTypeElement("TopLevelClass.NestedObject"), |
| invocation.processingEnv.requireTypeElement("TopLevelClass.NestedInterface"), |
| invocation.processingEnv.requireTypeElement("TopLevelClass.NestedEnum"), |
| invocation.processingEnv.requireTypeElement("TopLevelClass.Companion"), |
| ) |
| } |
| } |
| |
| @Test |
| fun enclosedTypes_java() { |
| val src = Source.java( |
| "Source", |
| """ |
| class TopLevelClass { |
| class InnerClass { } |
| static class NestedClass { } |
| interface NestedInterface { } |
| enum NestedEnum { |
| A, B |
| } |
| } |
| """.trimIndent() |
| ) |
| runTest(sources = listOf(src)) { invocation -> |
| val topLevelClass = invocation.processingEnv.requireTypeElement("TopLevelClass") |
| val enclosedTypeElements = topLevelClass.getEnclosedTypeElements() |
| |
| assertThat(enclosedTypeElements) |
| .containsExactly( |
| invocation.processingEnv.requireTypeElement("TopLevelClass.InnerClass"), |
| invocation.processingEnv.requireTypeElement("TopLevelClass.NestedClass"), |
| invocation.processingEnv.requireTypeElement("TopLevelClass.NestedInterface"), |
| invocation.processingEnv.requireTypeElement("TopLevelClass.NestedEnum"), |
| ) |
| } |
| } |
| |
| @Test |
| fun kotlinObjects() { |
| val kotlinSrc = Source.kotlin( |
| "Test.kt", |
| """ |
| package foo.bar |
| class KotlinClass { |
| companion object |
| object NestedObject |
| } |
| """.trimIndent() |
| ) |
| runTest(listOf(kotlinSrc)) { invocation -> |
| val kotlinClass = invocation.processingEnv.requireTypeElement( |
| "foo.bar.KotlinClass") |
| val companionObjects = kotlinClass.getEnclosedTypeElements().filter { |
| it.isCompanionObject() |
| } |
| assertThat(companionObjects.size).isEqualTo(1) |
| val companionObj = companionObjects.first() |
| assertThat(companionObj.isKotlinObject()).isTrue() |
| } |
| } |
| |
| @Test |
| fun inheritedGenericMethod() { |
| runTest( |
| sources = listOf( |
| Source.kotlin( |
| "test.ConcreteClass.kt", |
| """ |
| package test |
| class ConcreteClass: AbstractClass<Foo, Bar>() {} |
| abstract class AbstractClass<T1, T2> { |
| fun method(t1: T1, t2: T2): T2 { |
| return t2 |
| } |
| } |
| class Foo |
| class Bar |
| """.trimIndent() |
| ) |
| ) |
| ) { invocation -> |
| val concreteClass = invocation.processingEnv.requireTypeElement("test.ConcreteClass") |
| val abstractClass = invocation.processingEnv.requireTypeElement("test.AbstractClass") |
| val concreteClassMethod = concreteClass.getMethodByJvmName("method") |
| val abstractClassMethod = abstractClass.getMethodByJvmName("method") |
| val fooType = invocation.processingEnv.requireType("test.Foo") |
| val barType = invocation.processingEnv.requireType("test.Bar") |
| |
| fun checkMethodElement(method: XMethodElement) { |
| checkMethodElement( |
| method = method, |
| name = "method", |
| enclosingElement = abstractClass, |
| returnType = createXTypeVariableName("T2"), |
| parameterTypes = arrayOf( |
| createXTypeVariableName("T1"), |
| createXTypeVariableName("T2") |
| ) |
| ) |
| checkMethodType( |
| methodType = method.executableType, |
| returnType = createXTypeVariableName("T2"), |
| parameterTypes = arrayOf( |
| createXTypeVariableName("T1"), |
| createXTypeVariableName("T2") |
| ) |
| ) |
| checkMethodType( |
| methodType = method.asMemberOf(abstractClass.type), |
| returnType = createXTypeVariableName("T2"), |
| parameterTypes = arrayOf( |
| createXTypeVariableName("T1"), |
| createXTypeVariableName("T2") |
| ) |
| ) |
| checkMethodType( |
| methodType = method.asMemberOf(concreteClass.type), |
| returnType = barType.asTypeName(), |
| parameterTypes = arrayOf(fooType.asTypeName(), barType.asTypeName()) |
| ) |
| } |
| |
| assertThat(concreteClassMethod).isEqualTo(abstractClassMethod) |
| checkMethodElement(concreteClassMethod) |
| checkMethodElement(abstractClassMethod) |
| } |
| } |
| |
| @Test |
| fun overriddenGenericMethod() { |
| runTest( |
| sources = listOf( |
| Source.kotlin( |
| "test.ConcreteClass.kt", |
| """ |
| package test |
| class ConcreteClass: AbstractClass<Foo, Bar>() { |
| override fun method(t1: Foo, t2: Bar): Bar { |
| return t2 |
| } |
| } |
| abstract class AbstractClass<T1, T2> { |
| abstract fun method(t1: T1, t2: T2): T2 |
| } |
| class Foo |
| class Bar |
| """.trimIndent() |
| ) |
| ) |
| ) { invocation -> |
| val concreteClass = invocation.processingEnv.requireTypeElement("test.ConcreteClass") |
| val abstractClass = invocation.processingEnv.requireTypeElement("test.AbstractClass") |
| val concreteClassMethod = concreteClass.getMethodByJvmName("method") |
| val abstractClassMethod = abstractClass.getMethodByJvmName("method") |
| val fooType = invocation.processingEnv.requireType("test.Foo") |
| val barType = invocation.processingEnv.requireType("test.Bar") |
| |
| assertThat(concreteClassMethod).isNotEqualTo(abstractClassMethod) |
| assertThat(concreteClassMethod.overrides(abstractClassMethod, concreteClass)).isTrue() |
| |
| // Check the abstract method and method type |
| checkMethodElement( |
| method = abstractClassMethod, |
| name = "method", |
| enclosingElement = abstractClass, |
| returnType = createXTypeVariableName("T2"), |
| parameterTypes = arrayOf( |
| createXTypeVariableName("T1"), |
| createXTypeVariableName("T2") |
| ) |
| ) |
| checkMethodType( |
| methodType = abstractClassMethod.executableType, |
| returnType = createXTypeVariableName("T2"), |
| parameterTypes = arrayOf( |
| createXTypeVariableName("T1"), |
| createXTypeVariableName("T2") |
| ) |
| ) |
| checkMethodType( |
| methodType = abstractClassMethod.asMemberOf(abstractClass.type), |
| returnType = createXTypeVariableName("T2"), |
| parameterTypes = arrayOf( |
| createXTypeVariableName("T1"), |
| createXTypeVariableName("T2"), |
| ) |
| ) |
| checkMethodType( |
| methodType = abstractClassMethod.asMemberOf(concreteClass.type), |
| returnType = barType.asTypeName(), |
| parameterTypes = arrayOf(fooType.asTypeName(), barType.asTypeName()) |
| ) |
| |
| // Check the concrete method and method type |
| checkMethodElement( |
| method = concreteClassMethod, |
| name = "method", |
| enclosingElement = concreteClass, |
| returnType = barType.asTypeName(), |
| parameterTypes = arrayOf(fooType.asTypeName(), barType.asTypeName()) |
| ) |
| checkMethodType( |
| methodType = concreteClassMethod.executableType, |
| returnType = barType.asTypeName(), |
| parameterTypes = arrayOf(fooType.asTypeName(), barType.asTypeName()) |
| ) |
| checkMethodType( |
| methodType = concreteClassMethod.asMemberOf(concreteClass.type), |
| returnType = barType.asTypeName(), |
| parameterTypes = arrayOf(fooType.asTypeName(), barType.asTypeName()) |
| ) |
| } |
| } |
| |
| private fun checkMethodElement( |
| method: XMethodElement, |
| name: String, |
| enclosingElement: XTypeElement, |
| returnType: XTypeName, |
| parameterTypes: Array<XTypeName> |
| ) { |
| assertThat(method.name).isEqualTo(name) |
| assertThat(method.enclosingElement).isEqualTo(enclosingElement) |
| assertThat(method.returnType.asTypeName()).isEqualTo(returnType) |
| assertThat(method.parameters.map { it.type.asTypeName() }) |
| .containsExactly(*parameterTypes) |
| .inOrder() |
| } |
| private fun checkMethodType( |
| methodType: XMethodType, |
| returnType: XTypeName, |
| parameterTypes: Array<XTypeName> |
| ) { |
| assertThat(methodType.returnType.asTypeName()).isEqualTo(returnType) |
| assertThat(methodType.parameterTypes.map { it.asTypeName() }) |
| .containsExactly(*parameterTypes) |
| .inOrder() |
| } |
| |
| @Test |
| fun overriddenGenericConstructor() { |
| runTest( |
| sources = listOf( |
| Source.kotlin( |
| "test.ConcreteClass.kt", |
| """ |
| package test |
| class ConcreteClass(foo: Foo): AbstractClass<Foo>(foo) {} |
| abstract class AbstractClass<T>(t: T) |
| class Foo |
| """.trimIndent() |
| ) |
| ) |
| ) { invocation -> |
| val concreteClass = invocation.processingEnv.requireTypeElement("test.ConcreteClass") |
| val abstractClass = invocation.processingEnv.requireTypeElement("test.AbstractClass") |
| val fooType = invocation.processingEnv.requireType("test.Foo") |
| |
| fun checkConstructorParameters( |
| typeElement: XTypeElement, |
| expectedParameters: Array<XTypeName> |
| ) { |
| assertThat(typeElement.getConstructors()).hasSize(1) |
| val constructor = typeElement.getConstructors()[0] |
| assertThat(constructor.parameters.map { it.type.asTypeName() }) |
| .containsExactly(*expectedParameters) |
| .inOrder() |
| } |
| |
| checkConstructorParameters( |
| typeElement = abstractClass, |
| expectedParameters = arrayOf( |
| createXTypeVariableName("T") |
| ) |
| ) |
| checkConstructorParameters( |
| typeElement = concreteClass, |
| expectedParameters = arrayOf(fooType.asTypeName()) |
| ) |
| } |
| } |
| |
| @Test |
| fun inheritedGenericField() { |
| runTest( |
| sources = listOf( |
| Source.kotlin( |
| "test.ConcreteClass.kt", |
| """ |
| package test |
| class ConcreteClass: AbstractClass<Foo>() |
| abstract class AbstractClass<T> { |
| val field: T = TODO() |
| } |
| class Foo |
| """.trimIndent() |
| ) |
| ) |
| ) { invocation -> |
| val concreteClass = invocation.processingEnv.requireTypeElement("test.ConcreteClass") |
| val abstractClass = invocation.processingEnv.requireTypeElement("test.AbstractClass") |
| val concreteClassField = concreteClass.getField("field") |
| val abstractClassField = abstractClass.getField("field") |
| val fooType = invocation.processingEnv.requireType("test.Foo") |
| |
| fun checkFieldElement(field: XFieldElement) { |
| assertThat(field.name).isEqualTo("field") |
| assertThat(field.type.asTypeName().java).isEqualTo(JTypeVariableName.get("T")) |
| assertThat(field.asMemberOf(abstractClass.type).asTypeName().java) |
| .isEqualTo(JTypeVariableName.get("T")) |
| assertThat(field.asMemberOf(concreteClass.type).asTypeName().java) |
| .isEqualTo(fooType.asTypeName().java) |
| if (invocation.isKsp) { |
| assertThat(field.type.asTypeName().kotlin).isEqualTo(KTypeVariableName("T")) |
| assertThat(field.asMemberOf(abstractClass.type).asTypeName().kotlin) |
| .isEqualTo(KTypeVariableName("T")) |
| assertThat(field.asMemberOf(concreteClass.type).asTypeName().kotlin) |
| .isEqualTo(fooType.asTypeName().kotlin) |
| } |
| } |
| |
| assertThat(concreteClassField).isEqualTo(abstractClassField) |
| checkFieldElement(abstractClassField) |
| checkFieldElement(concreteClassField) |
| } |
| } |
| |
| @Test |
| fun internalModifier() { |
| runTest( |
| sources = listOf( |
| Source.kotlin( |
| "test.Foo.kt", |
| """ |
| package test |
| internal class InternalClass internal constructor() { |
| internal val valField: String = TODO() |
| internal var varField: String = TODO() |
| internal fun method(): String = TODO() |
| internal lateinit var lateinitVarField: String |
| } |
| class PublicClass constructor() { |
| val valField: String = TODO() |
| var varField: String = TODO() |
| fun method(): String = TODO() |
| lateinit var lateinitVarField: String |
| } |
| """.trimIndent() |
| ) |
| ) |
| ) { invocation -> |
| // Matches by name rather than jvmName to avoid dealing with name mangling. |
| fun XTypeElement.getDeclaredMethod(name: String): XMethodElement { |
| return getDeclaredMethods().filter { it.name == name }.single() |
| } |
| |
| val internalClass = invocation.processingEnv.requireTypeElement("test.InternalClass") |
| assertThat(internalClass.isInternal()).isTrue() |
| assertThat(internalClass.getConstructors().single().isInternal()).isTrue() |
| assertThat(internalClass.getDeclaredField("valField").isInternal()).isTrue() |
| assertThat(internalClass.getDeclaredField("varField").isInternal()).isTrue() |
| assertThat(internalClass.getDeclaredField("lateinitVarField").isInternal()).isTrue() |
| assertThat(internalClass.getDeclaredMethod("method").isInternal()).isTrue() |
| assertThat(internalClass.getDeclaredMethod("getValField").isInternal()).isTrue() |
| assertThat(internalClass.getDeclaredMethod("getVarField").isInternal()).isTrue() |
| assertThat(internalClass.getDeclaredMethod("getLateinitVarField").isInternal()).isTrue() |
| assertThat(internalClass.getDeclaredMethod("setVarField").isInternal()).isTrue() |
| assertThat(internalClass.getDeclaredMethod("setLateinitVarField").isInternal()).isTrue() |
| |
| val publicClass = invocation.processingEnv.requireTypeElement("test.PublicClass") |
| assertThat(publicClass.isInternal()).isFalse() |
| assertThat(publicClass.getConstructors().single().isInternal()).isFalse() |
| assertThat(publicClass.getDeclaredField("valField").isInternal()).isFalse() |
| assertThat(publicClass.getDeclaredField("varField").isInternal()).isFalse() |
| assertThat(publicClass.getDeclaredField("lateinitVarField").isInternal()).isFalse() |
| assertThat(publicClass.getDeclaredMethod("method").isInternal()).isFalse() |
| assertThat(publicClass.getDeclaredMethod("getValField").isInternal()).isFalse() |
| assertThat(publicClass.getDeclaredMethod("getVarField").isInternal()).isFalse() |
| assertThat(publicClass.getDeclaredMethod("getLateinitVarField").isInternal()).isFalse() |
| assertThat(publicClass.getDeclaredMethod("setVarField").isInternal()).isFalse() |
| assertThat(publicClass.getDeclaredMethod("setLateinitVarField").isInternal()).isFalse() |
| } |
| } |
| |
| @Test |
| fun sameMethodNameOrder() { |
| runTest( |
| sources = listOf( |
| Source.kotlin( |
| "test.Foo.kt", |
| """ |
| package test |
| class Foo<T1: Bar, T2: Baz> { |
| fun method(): String = TODO() |
| fun method(param: String): String = TODO() |
| fun method(param: Any): String = TODO() |
| fun method(param: T1): T2 = TODO() |
| fun method(param: T2): T1 = TODO() |
| fun <U1: Baz, U2> method(param1: U1, param2: U2) {} |
| fun <U1: Baz, U2: U1> method(param1: U1, param2: U2): T1 = TODO() |
| } |
| interface Bar |
| interface Baz |
| """.trimIndent() |
| ) |
| ) |
| ) { invocation -> |
| val foo = invocation.processingEnv.requireTypeElement("test.Foo") |
| assertThat(foo.getDeclaredMethods().map { it.jvmDescriptor }.toList()) |
| .containsExactly( |
| "method()Ljava/lang/String;", |
| "method(Ljava/lang/String;)Ljava/lang/String;", |
| "method(Ljava/lang/Object;)Ljava/lang/String;", |
| "method(Ltest/Bar;)Ltest/Baz;", |
| "method(Ltest/Baz;)Ltest/Bar;", |
| "method(Ltest/Baz;Ljava/lang/Object;)V", |
| "method(Ltest/Baz;Ltest/Baz;)Ltest/Bar;" |
| ).inOrder() |
| } |
| } |
| |
| @Test |
| fun jvmDescriptors() { |
| runTest( |
| sources = listOf( |
| Source.kotlin( |
| "test.Foo.kt", |
| """ |
| package test |
| class Foo<T1: Bar, T2: Baz> { |
| val field1: String = TODO() |
| var field2: String? = TODO() |
| val field3: T1 = TODO() |
| fun method(): String = TODO() |
| fun method(param: String): String = TODO() |
| fun method(param: Any): String = TODO() |
| fun method(param: T1): T2 = TODO() |
| } |
| interface Bar |
| interface Baz |
| """.trimIndent() |
| ) |
| ) |
| ) { invocation -> |
| val foo = invocation.processingEnv.requireTypeElement("test.Foo") |
| assertThat(foo.getEnclosedElements().map { |
| when { |
| it.isField() -> it.jvmDescriptor |
| it.isMethod() -> it.jvmDescriptor |
| it.isConstructor() -> it.jvmDescriptor |
| else -> error("Unsupported element to describe.") |
| } |
| }.toList()) |
| .containsExactly( |
| "<init>()V", |
| "field1:Ljava/lang/String;", |
| "field2:Ljava/lang/String;", |
| "field3:Ltest/Bar;", |
| "getField1()Ljava/lang/String;", |
| "getField2()Ljava/lang/String;", |
| "setField2(Ljava/lang/String;)V", |
| "getField3()Ltest/Bar;", |
| "method()Ljava/lang/String;", |
| "method(Ljava/lang/String;)Ljava/lang/String;", |
| "method(Ljava/lang/Object;)Ljava/lang/String;", |
| "method(Ltest/Bar;)Ltest/Baz;", |
| ) |
| } |
| } |
| |
| /** |
| * it is good to exclude methods coming from Object when testing as they differ between KSP |
| * and KAPT but irrelevant for Room. |
| */ |
| private fun XTestInvocation.objectMethodNames() = processingEnv |
| .requireTypeElement("java.lang.Object") |
| .getAllMethods() |
| .jvmNames() |
| |
| private fun Sequence<XMethodElement>.jvmNames() = map { |
| it.jvmName |
| }.toList() |
| |
| private fun List<XMethodElement>.jvmNames() = map { |
| it.jvmName |
| }.toList() |
| |
| private fun XMethodElement.signature(owner: XType): String { |
| val methodType = this.asMemberOf(owner) |
| val params = methodType.parameterTypes.joinToString(",") { |
| it.asTypeName().java.toString() |
| } |
| return "$jvmName($params):${returnType.asTypeName().java}" |
| } |
| |
| private fun XTestInvocation.nonObjectMethodSignatures(typeElement: XTypeElement): List<String> = |
| typeElement.getAllMethods() |
| .filterNot { it.jvmName in objectMethodNames() } |
| .map { it.signature(typeElement.type) }.toList() |
| |
| companion object { |
| @JvmStatic |
| @Parameterized.Parameters(name = "isPreCompiled_{0}") |
| fun params(): List<Array<Any>> { |
| return listOf(arrayOf(false), arrayOf(true)) |
| } |
| } |
| } |