1/*
2 * Copyright 2017 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
17package androidx.room.solver
18
19import androidx.room.ext.getAllFieldsIncludingPrivateSupers
20import androidx.room.ext.isAssignableWithoutVariance
21import androidx.room.testing.TestInvocation
22import com.google.testing.compile.JavaFileObjects
23import org.hamcrest.CoreMatchers.`is`
24import org.hamcrest.MatcherAssert.assertThat
25import org.junit.Test
26import simpleRun
27import javax.annotation.processing.ProcessingEnvironment
28import javax.lang.model.element.TypeElement
29import javax.lang.model.element.VariableElement
30
31class TypeAssignmentTest {
32    companion object {
33        private val TEST_OBJECT = JavaFileObjects.forSourceString("foo.bar.MyObject",
34                """
35            package foo.bar;
36            import java.util.Set;
37            import java.util.HashSet;
38            import java.util.Map;
39            class MyObject {
40                String mString;
41                Integer mInteger;
42                Set<MyObject> mSet;
43                Set<? extends MyObject> mVarianceSet;
44                HashSet<MyObject> mHashSet;
45                Map<String, ?> mUnboundedMap;
46                Map<String, String> mStringMap;
47            }
48            """.trimIndent())
49    }
50
51    @Test
52    fun basic() {
53        runTest {
54            val testObject = typeElement("foo.bar.MyObject")
55            val string = testObject.getField(processingEnv, "mString")
56            val integer = testObject.getField(processingEnv, "mInteger")
57            assertThat(typeUtils.isAssignableWithoutVariance(string.asType(),
58                    integer.asType()),
59                    `is`(false))
60        }
61    }
62
63    @Test
64    fun generics() {
65        runTest {
66            val testObject = typeElement("foo.bar.MyObject")
67            val set = testObject.getField(processingEnv, "mSet").asType()
68            val hashSet = testObject.getField(processingEnv, "mHashSet").asType()
69            assertThat(typeUtils.isAssignableWithoutVariance(
70                    from = set,
71                    to = hashSet
72            ), `is`(false))
73            assertThat(typeUtils.isAssignableWithoutVariance(
74                    from = hashSet,
75                    to = set
76            ), `is`(true))
77        }
78    }
79
80    @Test
81    fun variance() {
82        /**
83         *  Set<User> userSet = null;
84         *  Set<? extends User> userSet2 = null;
85         *  userSet = userSet2;  // NOT OK for java but kotlin data classes hit this so we want
86         *                       // to accept it
87         */
88        runTest {
89            val testObject = typeElement("foo.bar.MyObject")
90            val set = testObject.getField(processingEnv, "mSet").asType()
91            val varianceSet = testObject.getField(processingEnv, "mVarianceSet").asType()
92            assertThat(typeUtils.isAssignableWithoutVariance(
93                    from = set,
94                    to = varianceSet
95            ), `is`(true))
96            assertThat(typeUtils.isAssignableWithoutVariance(
97                    from = varianceSet,
98                    to = set
99            ), `is`(true))
100        }
101    }
102
103    @Test
104    fun unboundedVariance() {
105        runTest {
106            val testObject = typeElement("foo.bar.MyObject")
107            val unbounded = testObject.getField(processingEnv, "mUnboundedMap").asType()
108            val objectMap = testObject.getField(processingEnv, "mStringMap").asType()
109            assertThat(typeUtils.isAssignableWithoutVariance(
110                    from = unbounded,
111                    to = objectMap
112            ), `is`(false))
113            assertThat(typeUtils.isAssignableWithoutVariance(
114                    from = objectMap,
115                    to = unbounded
116            ), `is`(true))
117        }
118    }
119
120    private fun TypeElement.getField(
121            env: ProcessingEnvironment,
122            name: String): VariableElement {
123        return getAllFieldsIncludingPrivateSupers(env).first {
124            it.simpleName.toString() == name
125        }
126    }
127
128    private fun runTest(handler: TestInvocation.() -> Unit) {
129        simpleRun(TEST_OBJECT) {
130            it.apply { handler() }
131        }.compilesWithoutError()
132    }
133}