Merge "Include raw output in processor tests" into androidx-main
diff --git a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/AnnotatedString.kt b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/AnnotatedString.kt
index 732c765..77f77cf 100644
--- a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/AnnotatedString.kt
+++ b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/AnnotatedString.kt
@@ -126,10 +126,11 @@
      * with the range [start, end) will be returned. When [start] is bigger than [end], an empty
      * list will be returned.
      */
+    @Suppress("UNCHECKED_CAST")
     fun getStringAnnotations(start: Int, end: Int): List<StringAnnotation> =
-        annotations.filterIsInstance<Range<String>>().filter {
-            intersect(start, end, it.start, it.end)
-        }
+        annotations.filter {
+            it.item is String && intersect(start, end, it.start, it.end)
+        } as List<StringAnnotation>
 
     /**
      * Query all of the string annotations attached on this AnnotatedString.
@@ -140,10 +141,11 @@
      * with the range [start, end) will be returned. When [start] is bigger than [end], an empty
      * list will be returned.
      */
+    @Suppress("UNCHECKED_CAST")
     fun getTtsAnnotations(start: Int, end: Int): List<Range<TtsAnnotation>> =
-        annotations.filterIsInstance<Range<TtsAnnotation>>().filter {
-            intersect(start, end, it.start, it.end)
-        }
+        annotations.filter {
+            it.item is TtsAnnotation && intersect(start, end, it.start, it.end)
+        } as List<Range<TtsAnnotation>>
 
     /**
      * The information attached on the text such as a [SpanStyle].
diff --git a/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/AnnotatedStringBuilderTest.kt b/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/AnnotatedStringBuilderTest.kt
index 641b23cd..51643d7 100644
--- a/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/AnnotatedStringBuilderTest.kt
+++ b/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/AnnotatedStringBuilderTest.kt
@@ -709,6 +709,38 @@
         )
     }
 
+    @Test
+    fun getAnnotation_separates_ttsAnnotation_and_stringAnnotation() {
+        val annotation1 = VerbatimTtsAnnotation("abc")
+        val annotation2 = "annotation"
+        val tag = "tag"
+        val buildResult = AnnotatedString.Builder().apply {
+            pushTtsAnnotation(annotation1)
+            append("Hello")
+            pushStringAnnotation(tag, annotation2)
+            append("world")
+            pop()
+            append("!")
+            pop()
+        }.toAnnotatedString()
+
+        // The final result is Helloworld!
+        //                     [         ] TtsAnnotation
+        //                          [   ]  StringAnnotation
+        assertThat(buildResult.getTtsAnnotations(0, 5)).isEqualTo(
+            listOf(AnnotatedString.Range(annotation1, 0, 11, ""))
+        )
+        assertThat(buildResult.getTtsAnnotations(5, 6)).isEqualTo(
+            listOf(AnnotatedString.Range(annotation1, 0, 11, ""))
+        )
+
+        assertThat(buildResult.getStringAnnotations(0, 5)).isEmpty()
+        assertThat(buildResult.getStringAnnotations(5, 6)).isEqualTo(
+            listOf(AnnotatedString.Range(annotation2, 5, 10, tag))
+        )
+        assertThat(buildResult.getStringAnnotations(10, 11)).isEmpty()
+    }
+
     private fun createAnnotatedString(
         text: String,
         color: Color = Color.Red,
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspAnnotationBox.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspAnnotationBox.kt
index b62e6ad..d478ac4 100644
--- a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspAnnotationBox.kt
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspAnnotationBox.kt
@@ -132,7 +132,12 @@
                     }
                 }
                 is Array<*> -> mapNotNull { it?.readAs(returnType.componentType) }
-                else -> error("unexpected type for array: $this / ${this::class.java}")
+                else -> {
+                    // If array syntax is not used in java code, KSP might return it as a single
+                    // item instead of list or array
+                    // see: https://github.com/google/ksp/issues/214
+                    listOf(this.readAs(returnType.componentType))
+                }
             }
             val resultArray = java.lang.reflect.Array.newInstance(
                 returnType.componentType,
diff --git a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationBoxTest.kt b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationBoxTest.kt
index 40cab24..b966fb6 100644
--- a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationBoxTest.kt
+++ b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationBoxTest.kt
@@ -17,6 +17,7 @@
 package androidx.room.compiler.processing
 
 import androidx.room.compiler.processing.testcode.JavaAnnotationWithDefaults
+import androidx.room.compiler.processing.testcode.JavaAnnotationWithTypeReferences
 import androidx.room.compiler.processing.testcode.JavaEnum
 import androidx.room.compiler.processing.testcode.MainAnnotation
 import androidx.room.compiler.processing.testcode.OtherAnnotation
@@ -202,6 +203,30 @@
     }
 
     @Test
+    fun typeReferenceArray_singleItemInJava() {
+        val src = Source.java(
+            "Subject",
+            """
+            import androidx.room.compiler.processing.testcode.JavaAnnotationWithTypeReferences;
+            @JavaAnnotationWithTypeReferences(String.class)
+            class Subject {
+            }
+            """.trimIndent()
+        )
+        runProcessorTest(
+            sources = listOf(src)
+        ) { invocation ->
+            val subject = invocation.processingEnv.requireTypeElement("Subject")
+            val annotationValue = subject.toAnnotationBox(
+                JavaAnnotationWithTypeReferences::class
+            )?.getAsTypeList("value")
+            assertThat(annotationValue?.map { it.typeName }).containsExactly(
+                ClassName.get(String::class.java)
+            )
+        }
+    }
+
+    @Test
     fun propertyAnnotations() {
         val src = Source.kotlin(
             "Foo.kt",
diff --git a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/testcode/JavaAnnotationWithTypeReferences.java b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/testcode/JavaAnnotationWithTypeReferences.java
new file mode 100644
index 0000000..cd263ff
--- /dev/null
+++ b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/testcode/JavaAnnotationWithTypeReferences.java
@@ -0,0 +1,21 @@
+/*
+ * 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.testcode;
+
+public @interface JavaAnnotationWithTypeReferences {
+    Class<?>[] value();
+}
diff --git a/room/compiler/src/main/kotlin/androidx/room/processor/Context.kt b/room/compiler/src/main/kotlin/androidx/room/processor/Context.kt
index 5bbeea0..484baaa 100644
--- a/room/compiler/src/main/kotlin/androidx/room/processor/Context.kt
+++ b/room/compiler/src/main/kotlin/androidx/room/processor/Context.kt
@@ -101,8 +101,12 @@
         val STRING: XType by lazy {
             processingEnv.requireType("java.lang.String")
         }
-        val COLLECTION: XType by lazy {
-            processingEnv.requireType("java.util.Collection")
+        val READONLY_COLLECTION: XType by lazy {
+            if (processingEnv.backend == XProcessingEnv.Backend.KSP) {
+                processingEnv.requireType("kotlin.collections.Collection")
+            } else {
+                processingEnv.requireType("java.util.Collection")
+            }
         }
     }
 
diff --git a/room/compiler/src/main/kotlin/androidx/room/solver/TypeAdapterStore.kt b/room/compiler/src/main/kotlin/androidx/room/solver/TypeAdapterStore.kt
index b279c68..f12c2b1 100644
--- a/room/compiler/src/main/kotlin/androidx/room/solver/TypeAdapterStore.kt
+++ b/room/compiler/src/main/kotlin/androidx/room/solver/TypeAdapterStore.kt
@@ -541,7 +541,7 @@
         typeMirror: XType,
         isMultipleParameter: Boolean
     ): QueryParameterAdapter? {
-        if (context.COMMON_TYPES.COLLECTION.rawType.isAssignableFrom(typeMirror)) {
+        if (context.COMMON_TYPES.READONLY_COLLECTION.rawType.isAssignableFrom(typeMirror)) {
             val typeArg = typeMirror.typeArguments.first().extendsBoundOrSelf()
             // An adapter for the collection type arg wrapped in the built-in collection adapter.
             val wrappedCollectionAdapter = findStatementValueBinder(typeArg, null)?.let {
diff --git a/room/compiler/src/test/kotlin/androidx/room/solver/TypeAdapterStoreTest.kt b/room/compiler/src/test/kotlin/androidx/room/solver/TypeAdapterStoreTest.kt
index 1632335..e6feb02 100644
--- a/room/compiler/src/test/kotlin/androidx/room/solver/TypeAdapterStoreTest.kt
+++ b/room/compiler/src/test/kotlin/androidx/room/solver/TypeAdapterStoreTest.kt
@@ -22,7 +22,6 @@
 import androidx.room.compiler.processing.XProcessingEnv
 import androidx.room.compiler.processing.util.Source
 import androidx.room.compiler.processing.util.XTestInvocation
-import androidx.room.compiler.processing.util.runKaptTest
 import androidx.room.compiler.processing.util.runProcessorTest
 import androidx.room.ext.GuavaUtilConcurrentTypeNames
 import androidx.room.ext.L
@@ -42,6 +41,7 @@
 import androidx.room.solver.binderprovider.LiveDataQueryResultBinderProvider
 import androidx.room.solver.binderprovider.PagingSourceQueryResultBinderProvider
 import androidx.room.solver.binderprovider.RxQueryResultBinderProvider
+import androidx.room.solver.query.parameter.CollectionQueryParameterAdapter
 import androidx.room.solver.shortcut.binderprovider.GuavaListenableFutureDeleteOrUpdateMethodBinderProvider
 import androidx.room.solver.shortcut.binderprovider.GuavaListenableFutureInsertMethodBinderProvider
 import androidx.room.solver.shortcut.binderprovider.RxCallableDeleteOrUpdateMethodBinderProvider
@@ -74,7 +74,6 @@
 
     @Test
     fun testInvalidNonStaticInnerClass() {
-        // TODO: (b/176180385)
         val converter = Source.java(
             "foo.bar.EmptyClass",
             """
@@ -115,7 +114,7 @@
             }
             """.trimIndent()
         )
-        runKaptTest(
+        runProcessorTest(
             sources = listOf(entity, converter)
         ) { invocation ->
             val typeElement =
@@ -843,6 +842,42 @@
         }
     }
 
+    @Test
+    fun findQueryParameterAdapter_collections() {
+        runProcessorTest { invocation ->
+            val store = TypeAdapterStore.create(
+                context = invocation.context
+            )
+            val javacCollectionTypes = listOf(
+                "java.util.Set",
+                "java.util.List",
+                "java.util.ArrayList"
+            )
+            val kotlinCollectionTypes = listOf(
+                "kotlin.collections.List",
+                "kotlin.collections.MutableList"
+            )
+            val collectionTypes = if (invocation.isKsp) {
+                javacCollectionTypes + kotlinCollectionTypes
+            } else {
+                javacCollectionTypes
+            }
+            collectionTypes.map { collectionType ->
+                invocation.processingEnv.getDeclaredType(
+                    invocation.processingEnv.requireTypeElement(collectionType),
+                    invocation.processingEnv.requireType(TypeName.INT).boxed()
+                )
+            }.forEach { type ->
+                val adapter = store.findQueryParameterAdapter(
+                    typeMirror = type,
+                    isMultipleParameter = true
+                )
+                assertThat(adapter).isNotNull()
+                assertThat(adapter).isInstanceOf(CollectionQueryParameterAdapter::class.java)
+            }
+        }
+    }
+
     private fun createIntListToStringBinders(invocation: XTestInvocation): List<TypeConverter> {
         val intType = invocation.processingEnv.requireType(Integer::class)
         val listElement = invocation.processingEnv.requireTypeElement(java.util.List::class)