1/*
2 * Copyright (C) 2016 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.writer
18
19import androidx.annotation.NonNull
20import androidx.room.processor.DatabaseProcessor
21import androidx.room.testing.TestInvocation
22import androidx.room.testing.TestProcessor
23import androidx.room.vo.Database
24import com.google.auto.common.MoreElements
25import com.google.common.truth.Truth
26import com.google.testing.compile.CompileTester
27import com.google.testing.compile.JavaFileObjects
28import com.google.testing.compile.JavaSourcesSubjectFactory
29import org.hamcrest.CoreMatchers.`is`
30import org.hamcrest.MatcherAssert.assertThat
31import org.junit.Test
32import org.junit.runner.RunWith
33import org.junit.runners.JUnit4
34
35@RunWith(JUnit4::class)
36class SQLiteOpenHelperWriterTest {
37    companion object {
38        const val ENTITY_PREFIX = """
39            package foo.bar;
40            import androidx.annotation.NonNull;
41            import androidx.room.*;
42            @Entity%s
43            public class MyEntity {
44            """
45        const val ENTITY_SUFFIX = "}"
46        const val DATABASE_CODE = """
47            package foo.bar;
48            import androidx.room.*;
49            @Database(entities = {MyEntity.class}, version = 3)
50            abstract public class MyDatabase extends RoomDatabase {
51            }
52            """
53    }
54
55    @Test
56    fun createSimpleEntity() {
57        singleEntity(
58                """
59                @PrimaryKey
60                @NonNull
61                String uuid;
62                String name;
63                int age;
64                """.trimIndent()
65        ) { database, _ ->
66            val query = SQLiteOpenHelperWriter(database)
67                    .createQuery(database.entities.first())
68            assertThat(query, `is`("CREATE TABLE IF NOT EXISTS" +
69                    " `MyEntity` (`uuid` TEXT NOT NULL, `name` TEXT, `age` INTEGER NOT NULL," +
70                    " PRIMARY KEY(`uuid`))"))
71        }.compilesWithoutError()
72    }
73
74    @Test
75    fun multiplePrimaryKeys() {
76        singleEntity(
77                """
78                @NonNull
79                String uuid;
80                @NonNull
81                String name;
82                int age;
83                """.trimIndent(), attributes = mapOf("primaryKeys" to "{\"uuid\", \"name\"}")
84        ) { database, _ ->
85            val query = SQLiteOpenHelperWriter(database)
86                    .createQuery(database.entities.first())
87            assertThat(query, `is`("CREATE TABLE IF NOT EXISTS" +
88                    " `MyEntity` (`uuid` TEXT NOT NULL, `name` TEXT NOT NULL, " +
89                    "`age` INTEGER NOT NULL, PRIMARY KEY(`uuid`, `name`))"))
90        }.compilesWithoutError()
91    }
92
93    @Test
94    fun autoIncrementObject() {
95        listOf("Long", "Integer").forEach { type ->
96            singleEntity(
97                    """
98                @PrimaryKey(autoGenerate = true)
99                $type uuid;
100                String name;
101                int age;
102                """.trimIndent()
103            ) { database, _ ->
104                val query = SQLiteOpenHelperWriter(database)
105                        .createQuery(database.entities.first())
106                assertThat(query, `is`("CREATE TABLE IF NOT EXISTS" +
107                        " `MyEntity` (`uuid` INTEGER PRIMARY KEY AUTOINCREMENT," +
108                        " `name` TEXT, `age` INTEGER NOT NULL)"))
109            }.compilesWithoutError()
110        }
111    }
112
113    @Test
114    fun autoIncrementPrimitives() {
115        listOf("long", "int").forEach { type ->
116            singleEntity(
117                    """
118                @PrimaryKey(autoGenerate = true)
119                $type uuid;
120                String name;
121                int age;
122                """.trimIndent()
123            ) { database, _ ->
124                val query = SQLiteOpenHelperWriter(database)
125                        .createQuery(database.entities.first())
126                assertThat(query, `is`("CREATE TABLE IF NOT EXISTS" +
127                        " `MyEntity` (`uuid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL," +
128                        " `name` TEXT, `age` INTEGER NOT NULL)"))
129            }.compilesWithoutError()
130        }
131    }
132
133    fun singleEntity(input: String, attributes: Map<String, String> = mapOf(),
134                     handler: (Database, TestInvocation) -> Unit): CompileTester {
135        val attributesReplacement: String
136        if (attributes.isEmpty()) {
137            attributesReplacement = ""
138        } else {
139            attributesReplacement = "(" +
140                    attributes.entries.joinToString(",") { "${it.key} = ${it.value}" } +
141                    ")".trimIndent()
142        }
143        return Truth.assertAbout(JavaSourcesSubjectFactory.javaSources())
144                .that(listOf(JavaFileObjects.forSourceString("foo.bar.MyEntity",
145                        ENTITY_PREFIX.format(attributesReplacement) + input + ENTITY_SUFFIX
146                ), JavaFileObjects.forSourceString("foo.bar.MyDatabase",
147                        DATABASE_CODE)))
148                .processedWith(TestProcessor.builder()
149                        .forAnnotations(androidx.room.Database::class,
150                                NonNull::class)
151                        .nextRunHandler { invocation ->
152                            val db = MoreElements.asType(invocation.roundEnv
153                                    .getElementsAnnotatedWith(
154                                            androidx.room.Database::class.java)
155                                    .first())
156                            handler(DatabaseProcessor(invocation.context, db).process(), invocation)
157                            true
158                        }
159                        .build())
160    }
161}
162