1/*
2 * Copyright (C) 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.processor
18
19import androidx.room.Dao
20import androidx.room.Transaction
21import androidx.room.testing.TestInvocation
22import androidx.room.testing.TestProcessor
23import androidx.room.vo.TransactionMethod
24import com.google.auto.common.MoreElements
25import com.google.auto.common.MoreTypes
26import com.google.common.truth.Truth
27import com.google.testing.compile.CompileTester
28import com.google.testing.compile.JavaFileObjects
29import com.google.testing.compile.JavaSourcesSubjectFactory
30import org.hamcrest.CoreMatchers.`is`
31import org.hamcrest.MatcherAssert.assertThat
32import org.junit.Test
33import org.junit.runner.RunWith
34import org.junit.runners.JUnit4
35
36@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
37@RunWith(JUnit4::class)
38class TransactionMethodProcessorTest {
39
40    companion object {
41        const val DAO_PREFIX = """
42                package foo.bar;
43                import androidx.room.*;
44                import java.util.*;
45                @Dao
46                abstract class MyClass {
47                """
48        const val DAO_SUFFIX = "}"
49    }
50
51    @Test
52    fun simple() {
53        singleTransactionMethod(
54                """
55                @Transaction
56                public String doInTransaction(int param) { return null; }
57                """) { transaction, _ ->
58            assertThat(transaction.name, `is`("doInTransaction"))
59        }.compilesWithoutError()
60    }
61
62    @Test
63    fun modifier_private() {
64        singleTransactionMethod(
65                """
66                @Transaction
67                private String doInTransaction(int param) { return null; }
68                """) { transaction, _ ->
69            assertThat(transaction.name, `is`("doInTransaction"))
70        }.failsToCompile().withErrorContaining(ProcessorErrors.TRANSACTION_METHOD_MODIFIERS)
71    }
72
73    @Test
74    fun modifier_final() {
75        singleTransactionMethod(
76                """
77                @Transaction
78                public final String doInTransaction(int param) { return null; }
79                """) { transaction, _ ->
80            assertThat(transaction.name, `is`("doInTransaction"))
81        }.failsToCompile().withErrorContaining(ProcessorErrors.TRANSACTION_METHOD_MODIFIERS)
82    }
83
84    private fun singleTransactionMethod(vararg input: String,
85                                handler: (TransactionMethod, TestInvocation) -> Unit):
86            CompileTester {
87        return Truth.assertAbout(JavaSourcesSubjectFactory.javaSources())
88                .that(listOf(JavaFileObjects.forSourceString("foo.bar.MyClass",
89                        TransactionMethodProcessorTest.DAO_PREFIX + input.joinToString("\n") +
90                                TransactionMethodProcessorTest.DAO_SUFFIX
91                )))
92                .processedWith(TestProcessor.builder()
93                        .forAnnotations(Transaction::class, Dao::class)
94                        .nextRunHandler { invocation ->
95                            val (owner, methods) = invocation.roundEnv
96                                    .getElementsAnnotatedWith(Dao::class.java)
97                                    .map {
98                                        Pair(it,
99                                                invocation.processingEnv.elementUtils
100                                                        .getAllMembers(MoreElements.asType(it))
101                                                        .filter {
102                                                            MoreElements.isAnnotationPresent(it,
103                                                                    Transaction::class.java)
104                                                        }
105                                        )
106                                    }.first { it.second.isNotEmpty() }
107                            val processor = TransactionMethodProcessor(
108                                    baseContext = invocation.context,
109                                    containing = MoreTypes.asDeclared(owner.asType()),
110                                    executableElement = MoreElements.asExecutable(methods.first()))
111                            val processed = processor.process()
112                            handler(processed, invocation)
113                            true
114                        }
115                        .build())
116    }
117}
118