1 /* <lambda>null2 * Copyright 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package androidx.room.compiler.processing 18 19 import androidx.kruth.assertThat 20 import androidx.room.compiler.processing.ksp.KspTypeElement 21 import androidx.room.compiler.processing.util.Source 22 import androidx.room.compiler.processing.util.XTestInvocation 23 import androidx.room.compiler.processing.util.compileFiles 24 import androidx.room.compiler.processing.util.getAllFieldNames 25 import androidx.room.compiler.processing.util.runKspTest 26 import androidx.room.compiler.processing.util.runProcessorTest 27 import com.google.devtools.ksp.symbol.Origin 28 import org.junit.Test 29 import org.junit.runner.RunWith 30 import org.junit.runners.Parameterized 31 32 /** see: https://github.com/google/ksp/issues/250 */ 33 @RunWith(Parameterized::class) 34 class KspClassFileUtilityTest(val preCompile: Boolean) { 35 @Test 36 fun outOfOrderKotlin_fields() { 37 val libSource = 38 Source.kotlin( 39 "lib.kt", 40 """ 41 class KotlinClass( 42 val consA: String, 43 val consB: String, 44 ) { 45 val b: String = TODO() 46 val a: String = TODO() 47 val c: String = TODO() 48 val isB:String = TODO() 49 val isA:String = TODO() 50 val isC:String = TODO() 51 } 52 """ 53 .trimIndent() 54 ) 55 runTest(sources = listOf(libSource)) { invocation -> 56 val element = invocation.processingEnv.requireTypeElement("KotlinClass") 57 assertThat(element.getAllFieldNames()) 58 .containsExactly("consA", "consB", "b", "a", "c", "isB", "isA", "isC") 59 .inOrder() 60 } 61 } 62 63 @Test 64 fun outOfOrderJava_fields() { 65 val libSource = 66 Source.java( 67 "JavaClass", 68 """ 69 class JavaClass { 70 String b; 71 String a; 72 String c; 73 String isB; 74 String isA; 75 String isC; 76 } 77 """ 78 .trimIndent() 79 ) 80 runTest( 81 sources = listOf(libSource), 82 ) { invocation -> 83 val element = invocation.processingEnv.requireTypeElement("JavaClass") 84 assertThat(element.getAllFieldNames()) 85 .containsExactly("b", "a", "c", "isB", "isA", "isC") 86 .inOrder() 87 } 88 } 89 90 @Test 91 fun outOfOrderKotlin_methods() { 92 val libSource = 93 Source.kotlin( 94 "lib.kt", 95 """ 96 class KotlinClass { 97 fun b(): String = TODO() 98 fun a(): String = TODO() 99 fun c(): String = TODO() 100 fun isB(): String = TODO() 101 fun isA(): String = TODO() 102 fun isC(): String = TODO() 103 } 104 """ 105 .trimIndent() 106 ) 107 runTest(sources = listOf(libSource)) { invocation -> 108 val element = invocation.processingEnv.requireTypeElement("KotlinClass") 109 assertThat(element.getDeclaredMethods().map { it.jvmName }) 110 .containsExactly("b", "a", "c", "isB", "isA", "isC") 111 .inOrder() 112 } 113 } 114 115 @Test 116 fun outOfOrderJava_methods() { 117 val libSource = 118 Source.java( 119 "JavaClass", 120 """ 121 class JavaClass { 122 String b() { return ""; } 123 String a() { return ""; } 124 String c() { return ""; } 125 String isB() { return ""; } 126 String isA() { return ""; } 127 String isC() { return ""; } 128 } 129 """ 130 .trimIndent() 131 ) 132 runTest( 133 sources = listOf(libSource), 134 ) { invocation -> 135 val element = invocation.processingEnv.requireTypeElement("JavaClass") 136 assertThat(element.getDeclaredMethods().map { it.jvmName }) 137 .containsExactly("b", "a", "c", "isB", "isA", "isC") 138 .inOrder() 139 } 140 } 141 142 @Test 143 fun trueOrigin() { 144 // this test is kind of testing ksp itself but it is still good to keep it in case it 145 // breaks. 146 fun createSources(pkg: String) = 147 listOf( 148 Source.java( 149 "$pkg.JavaClass", 150 """ 151 package $pkg; 152 public class JavaClass {} 153 """ 154 .trimIndent() 155 ), 156 Source.kotlin( 157 "$pkg/KotlinClass.kt", 158 """ 159 package $pkg; 160 class KotlinClass {} 161 """ 162 .trimIndent() 163 ) 164 ) 165 166 fun XTestInvocation.findOrigin(qName: String) = 167 (processingEnv.requireTypeElement(qName) as KspTypeElement).declaration.origin 168 169 val preCompiled = compileFiles(createSources("lib")) 170 runKspTest(sources = createSources("main"), classpath = preCompiled) { invocation -> 171 assertThat(invocation.findOrigin("lib.JavaClass")).isEqualTo(Origin.JAVA_LIB) 172 assertThat(invocation.findOrigin("lib.KotlinClass")).isEqualTo(Origin.KOTLIN_LIB) 173 assertThat(invocation.findOrigin("main.JavaClass")).isEqualTo(Origin.JAVA) 174 assertThat(invocation.findOrigin("main.KotlinClass")).isEqualTo(Origin.KOTLIN) 175 } 176 } 177 178 @Suppress("NAME_SHADOWING") // intentional 179 private fun runTest(sources: List<Source>, handler: (XTestInvocation) -> Unit) { 180 val (sources, classpath) = 181 if (preCompile) { 182 emptyList<Source>() to compileFiles(sources) 183 } else { 184 sources to emptyList() 185 } 186 runProcessorTest( 187 sources = sources + Source.kotlin("Placeholder.kt", ""), 188 classpath = classpath, 189 handler = handler 190 ) 191 } 192 193 companion object { 194 @JvmStatic 195 @Parameterized.Parameters(name = "preCompile_{0}") 196 fun params() = arrayOf(false, true) 197 } 198 } 199