10045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar/*
20045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar * Copyright (C) 2017 The Android Open Source Project
30045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar *
40045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar * Licensed under the Apache License, Version 2.0 (the "License");
50045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar * you may not use this file except in compliance with the License.
60045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar * You may obtain a copy of the License at
70045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar *
80045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar *      http://www.apache.org/licenses/LICENSE-2.0
90045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar *
100045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar * Unless required by applicable law or agreed to in writing, software
110045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar * distributed under the License is distributed on an "AS IS" BASIS,
120045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
130045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar * See the License for the specific language governing permissions and
140045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar * limitations under the License.
150045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar */
160045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar
17bdc4c86d3dff74f6634a38e2f7b316b0e823a2c8Alan Viverettepackage androidx.room.integration.testapp.test;
180045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar
1924b0c2d5bde2848da5caa57d9b1ec2a95d12bcd9Yuichi Arakiimport static org.hamcrest.CoreMatchers.both;
200045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyarimport static org.hamcrest.CoreMatchers.containsString;
2124b0c2d5bde2848da5caa57d9b1ec2a95d12bcd9Yuichi Arakiimport static org.hamcrest.CoreMatchers.either;
220045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyarimport static org.hamcrest.CoreMatchers.equalTo;
230045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyarimport static org.hamcrest.CoreMatchers.instanceOf;
240045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyarimport static org.hamcrest.CoreMatchers.notNullValue;
250045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyarimport static org.hamcrest.CoreMatchers.nullValue;
260045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyarimport static org.hamcrest.MatcherAssert.assertThat;
2724b0c2d5bde2848da5caa57d9b1ec2a95d12bcd9Yuichi Arakiimport static org.hamcrest.core.Is.is;
280045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar
293542101509120586bbae17b1f748f302e1fb82a3Yigit Boyarimport android.database.sqlite.SQLiteException;
303542101509120586bbae17b1f748f302e1fb82a3Yigit Boyarimport android.support.test.InstrumentationRegistry;
313542101509120586bbae17b1f748f302e1fb82a3Yigit Boyarimport android.support.test.filters.SmallTest;
323542101509120586bbae17b1f748f302e1fb82a3Yigit Boyarimport android.support.test.runner.AndroidJUnit4;
330045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar
34bdc4c86d3dff74f6634a38e2f7b316b0e823a2c8Alan Viveretteimport androidx.room.Dao;
35bdc4c86d3dff74f6634a38e2f7b316b0e823a2c8Alan Viveretteimport androidx.room.Database;
36bdc4c86d3dff74f6634a38e2f7b316b0e823a2c8Alan Viveretteimport androidx.room.Delete;
37bdc4c86d3dff74f6634a38e2f7b316b0e823a2c8Alan Viveretteimport androidx.room.Entity;
38bdc4c86d3dff74f6634a38e2f7b316b0e823a2c8Alan Viveretteimport androidx.room.ForeignKey;
39bdc4c86d3dff74f6634a38e2f7b316b0e823a2c8Alan Viveretteimport androidx.room.Ignore;
40bdc4c86d3dff74f6634a38e2f7b316b0e823a2c8Alan Viveretteimport androidx.room.Index;
41bdc4c86d3dff74f6634a38e2f7b316b0e823a2c8Alan Viveretteimport androidx.room.Insert;
42bdc4c86d3dff74f6634a38e2f7b316b0e823a2c8Alan Viveretteimport androidx.room.PrimaryKey;
43bdc4c86d3dff74f6634a38e2f7b316b0e823a2c8Alan Viveretteimport androidx.room.Query;
44bdc4c86d3dff74f6634a38e2f7b316b0e823a2c8Alan Viveretteimport androidx.room.Room;
45bdc4c86d3dff74f6634a38e2f7b316b0e823a2c8Alan Viveretteimport androidx.room.RoomDatabase;
46bdc4c86d3dff74f6634a38e2f7b316b0e823a2c8Alan Viverette
4724b0c2d5bde2848da5caa57d9b1ec2a95d12bcd9Yuichi Arakiimport org.hamcrest.Matcher;
480045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyarimport org.junit.Before;
490045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyarimport org.junit.Test;
500045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyarimport org.junit.runner.RunWith;
510045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar
5236023a6ffdcc24a64c3f86efd4e754cfc3493761Yigit Boyarimport java.util.Locale;
5336023a6ffdcc24a64c3f86efd4e754cfc3493761Yigit Boyar
540045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar@RunWith(AndroidJUnit4.class)
550045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar@SmallTest
560045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyarpublic class ForeignKeyTest {
570045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    @Database(version = 1, entities = {A.class, B.class, C.class, D.class, E.class},
580045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar            exportSchema = false)
590045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    abstract static class ForeignKeyDb extends RoomDatabase {
600045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        abstract FkDao dao();
610045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    }
620045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar
630045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    @SuppressWarnings({"SqlNoDataSourceInspection", "SameParameterValue"})
640045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    @Dao
650045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    interface FkDao {
660045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        @Insert
670045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        void insert(A... a);
680045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar
690045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        @Insert
700045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        void insert(B... b);
710045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar
720045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        @Insert
730045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        void insert(C... c);
740045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar
750045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        @Insert
760045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        void insert(D... d);
770045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar
783542101509120586bbae17b1f748f302e1fb82a3Yigit Boyar        @Query("SELECT * FROM A WHERE id = :id")
790045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        A loadA(int id);
800045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar
813542101509120586bbae17b1f748f302e1fb82a3Yigit Boyar        @Query("SELECT * FROM B WHERE id = :id")
820045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        B loadB(int id);
830045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar
843542101509120586bbae17b1f748f302e1fb82a3Yigit Boyar        @Query("SELECT * FROM C WHERE id = :id")
850045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        C loadC(int id);
860045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar
873542101509120586bbae17b1f748f302e1fb82a3Yigit Boyar        @Query("SELECT * FROM D WHERE id = :id")
880045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        D loadD(int id);
890045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar
903542101509120586bbae17b1f748f302e1fb82a3Yigit Boyar        @Query("SELECT * FROM E WHERE id = :id")
910045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        E loadE(int id);
920045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar
930045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        @Delete
940045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        void delete(A... a);
950045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar
960045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        @Delete
970045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        void delete(B... b);
980045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar
990045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        @Delete
1000045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        void delete(C... c);
1010045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar
1020045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        @Query("UPDATE A SET name = :newName WHERE id = :id")
1030045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        void changeNameA(int id, String newName);
1040045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar
1050045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        @Insert
1060045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        void insert(E... e);
1070045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar
1080045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar
1090045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    }
1100045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar
1110045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    @Entity(indices = {@Index(value = "name", unique = true),
1120045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar            @Index(value = {"name", "lastName"}, unique = true)})
1130045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    static class A {
1140045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        @PrimaryKey(autoGenerate = true)
1150045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        public int id;
1160045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        public String name;
1170045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        public String lastName;
1180045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar
1190045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        A(String name) {
1200045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar            this.name = name;
1210045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        }
1220045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar
123f8c3624579d5761a2d34a7199932492d267f5f85Yigit Boyar        @Ignore
1240045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        A(String name, String lastName) {
1250045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar            this.name = name;
1260045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar            this.lastName = lastName;
1270045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        }
1280045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    }
1290045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar
1300045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    @SuppressWarnings("WeakerAccess")
1310045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    @Entity(foreignKeys = {
1320045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar            @ForeignKey(entity = A.class,
1330045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar                    parentColumns = "name",
1340045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar                    childColumns = "aName")})
1350045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar
1360045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    static class B {
1370045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        @PrimaryKey(autoGenerate = true)
1380045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        public int id;
1390045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        public String aName;
1400045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar
1410045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        B(String aName) {
1420045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar            this.aName = aName;
1430045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        }
1440045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    }
1450045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar
1460045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    @SuppressWarnings("WeakerAccess")
1470045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    @Entity(foreignKeys = {
1480045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar            @ForeignKey(entity = A.class,
1490045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar                    parentColumns = "name",
1500045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar                    childColumns = "aName",
1510045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar                    deferred = true)})
1520045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    static class C {
1530045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        @PrimaryKey(autoGenerate = true)
1540045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        public int id;
1550045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        public String aName;
1560045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar
1570045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        C(String aName) {
1580045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar            this.aName = aName;
1590045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        }
1600045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    }
1610045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar
1620045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    @SuppressWarnings("WeakerAccess")
1630045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    @Entity(foreignKeys = {
1640045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar            @ForeignKey(entity = A.class,
1650045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar                    parentColumns = "name",
1660045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar                    childColumns = "aName",
1670045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar                    onDelete = ForeignKey.CASCADE,
1680045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar                    onUpdate = ForeignKey.CASCADE)})
1690045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    static class D {
1700045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        @PrimaryKey(autoGenerate = true)
1710045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        public int id;
1720045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        public String aName;
1730045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar
1740045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        D(String aName) {
1750045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar            this.aName = aName;
1760045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        }
1770045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    }
1780045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar
1790045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    @SuppressWarnings("WeakerAccess")
1800045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    @Entity(foreignKeys = {
1810045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar            @ForeignKey(entity = A.class,
1820045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar                    parentColumns = {"name", "lastName"},
1830045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar                    childColumns = {"aName", "aLastName"},
1840045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar                    onDelete = ForeignKey.SET_NULL,
1850045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar                    onUpdate = ForeignKey.CASCADE)})
1860045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    static class E {
1870045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        @PrimaryKey(autoGenerate = true)
1880045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        public int id;
1890045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        public String aName;
1900045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        public String aLastName;
1910045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar
1920045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        E() {
1930045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        }
1940045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar
195f8c3624579d5761a2d34a7199932492d267f5f85Yigit Boyar        @Ignore
1960045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        E(String aName, String aLastName) {
1970045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar            this.aName = aName;
1980045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar            this.aLastName = aLastName;
1990045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        }
2000045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    }
2010045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar
2020045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar
2030045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    private ForeignKeyDb mDb;
2040045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    private FkDao mDao;
2050045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar
2060045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    @Before
2070045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    public void openDb() {
2086be69eb5aa66706ff5880e12c17ea6e62c35eda3Yuichi Araki        mDb = Room.inMemoryDatabaseBuilder(InstrumentationRegistry.getTargetContext(),
2090045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar                ForeignKeyDb.class).build();
2100045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        mDao = mDb.dao();
2110045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    }
2120045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar
2130045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    @Test
2140045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    public void simpleForeignKeyFailure() {
2150045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        Throwable t = catchException(new ThrowingRunnable() {
2160045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar            @Override
2170045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar            public void run() throws Exception {
2180045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar                mDao.insert(new B("foo"));
2190045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar            }
2200045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        });
2210045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        assertThat(t, instanceOf(SQLiteException.class));
22224b0c2d5bde2848da5caa57d9b1ec2a95d12bcd9Yuichi Araki        assertThat(t.getMessage().toUpperCase(Locale.US), is(foreignKeyErrorMessage()));
2230045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    }
2240045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar
2250045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    @Test
2260045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    public void simpleForeignKeyDeferredFailure() {
2270045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        Throwable t = catchException(new ThrowingRunnable() {
2280045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar            @Override
2290045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar            public void run() throws Exception {
2300045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar                mDao.insert(new C("foo"));
2310045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar            }
2320045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        });
2330045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        assertThat(t, instanceOf(SQLiteException.class));
23424b0c2d5bde2848da5caa57d9b1ec2a95d12bcd9Yuichi Araki        assertThat(t.getMessage().toUpperCase(Locale.US), is(foreignKeyErrorMessage()));
2350045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    }
2360045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar
2370045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    @Test
2380045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    public void immediateForeignKeyFailure() {
2390045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        Throwable t = catchException(new ThrowingRunnable() {
2400045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar            @Override
2410045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar            public void run() throws Exception {
2420045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar                try {
2430045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar                    mDb.beginTransaction();
2440045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar                    mDao.insert(new B("foo"));
2450045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar                    mDao.insert(new A("foo"));
2460045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar                    mDb.setTransactionSuccessful();
2470045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar                } finally {
2480045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar                    mDb.endTransaction();
2490045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar                }
2500045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar            }
2510045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        });
2520045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        assertThat(t, instanceOf(SQLiteException.class));
2530045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    }
2540045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar
2550045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    @Test
2560045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    public void deferredForeignKeySuccess() {
2570045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        try {
2580045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar            mDb.beginTransaction();
2590045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar            mDao.insert(new C("foo"));
2600045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar            mDao.insert(new A("foo"));
2610045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar            mDb.setTransactionSuccessful();
2620045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        } finally {
2630045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar            mDb.endTransaction();
2640045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        }
2650045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        assertThat(mDao.loadA(1), notNullValue());
2660045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        assertThat(mDao.loadC(1), notNullValue());
2670045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    }
2680045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar
2690045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    @Test
2700045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    public void onDelete_noAction() {
2710045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        mDao.insert(new A("a1"));
2720045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        final A a = mDao.loadA(1);
2730045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        mDao.insert(new B("a1"));
2740045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        Throwable t = catchException(new ThrowingRunnable() {
2750045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar            @Override
2760045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar            public void run() throws Exception {
2770045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar                mDao.delete(a);
2780045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar            }
2790045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        });
2800045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        assertThat(t, instanceOf(SQLiteException.class));
28124b0c2d5bde2848da5caa57d9b1ec2a95d12bcd9Yuichi Araki        assertThat(t.getMessage().toUpperCase(Locale.US), is(foreignKeyErrorMessage()));
2820045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    }
2830045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar
2840045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    @Test
2850045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    public void onDelete_noAction_withTransaction() {
2860045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        mDao.insert(new A("a1"));
2870045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        final A a = mDao.loadA(1);
2880045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        mDao.insert(new B("a1"));
2890045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        final B b = mDao.loadB(1);
2900045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        Throwable t = catchException(new ThrowingRunnable() {
2910045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar            @Override
2920045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar            public void run() throws Exception {
2930045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar                deleteInTransaction(a, b);
2940045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar            }
2950045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        });
2960045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        assertThat(t, instanceOf(SQLiteException.class));
29724b0c2d5bde2848da5caa57d9b1ec2a95d12bcd9Yuichi Araki        assertThat(t.getMessage().toUpperCase(Locale.US), is(foreignKeyErrorMessage()));
2980045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    }
2990045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar
3000045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    @Test
3010045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    public void onDelete_noAction_deferred() {
3020045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        mDao.insert(new A("a1"));
3030045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        final A a = mDao.loadA(1);
3040045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        mDao.insert(new C("a1"));
3050045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        Throwable t = catchException(new ThrowingRunnable() {
3060045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar            @Override
3070045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar            public void run() throws Exception {
3080045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar                mDao.delete(a);
3090045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar            }
3100045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        });
3110045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        assertThat(t, instanceOf(SQLiteException.class));
31224b0c2d5bde2848da5caa57d9b1ec2a95d12bcd9Yuichi Araki        assertThat(t.getMessage().toUpperCase(Locale.US), is(foreignKeyErrorMessage()));
3130045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    }
3140045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar
3150045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    @Test
3160045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    public void onDelete_noAction__deferredWithTransaction() {
3170045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        mDao.insert(new A("a1"));
3180045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        final A a = mDao.loadA(1);
3190045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        mDao.insert(new C("a1"));
3200045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        final C c = mDao.loadC(1);
3210045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        deleteInTransaction(a, c);
3220045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    }
3230045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar
3240045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    @Test
3250045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    public void onDelete_cascade() {
3260045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        mDao.insert(new A("a1"));
3270045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        final A a = mDao.loadA(1);
3280045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        mDao.insert(new D("a1"));
3290045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        final D d = mDao.loadD(1);
3300045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        assertThat("test sanity", d, notNullValue());
3310045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        mDao.delete(a);
3320045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        assertThat(mDao.loadD(1), nullValue());
3330045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    }
3340045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar
3350045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    @Test
3360045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    public void onUpdate_cascade() {
3370045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        mDao.insert(new A("a1"));
3380045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        mDao.insert(new D("a1"));
3390045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        final D d = mDao.loadD(1);
3400045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        assertThat("test sanity", d, notNullValue());
3410045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        mDao.changeNameA(1, "bla");
3420045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        assertThat(mDao.loadD(1).aName, equalTo("bla"));
3430045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        assertThat(mDao.loadA(1).name, equalTo("bla"));
3440045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    }
3450045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar
3460045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    @Test
3470045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    public void multipleReferences() {
3480045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        mDao.insert(new A("a1", "a2"));
3490045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        final A a = mDao.loadA(1);
3500045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        assertThat("test sanity", a, notNullValue());
3510045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        Throwable t = catchException(new ThrowingRunnable() {
3520045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar            @Override
3530045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar            public void run() throws Exception {
3540045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar                mDao.insert(new E("a1", "dsa"));
3550045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar            }
3560045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        });
35724b0c2d5bde2848da5caa57d9b1ec2a95d12bcd9Yuichi Araki        assertThat(t.getMessage().toUpperCase(Locale.US), is(foreignKeyErrorMessage()));
3580045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    }
3590045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar
3600045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    @Test
3610045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    public void onDelete_setNull_multipleReferences() {
3620045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        mDao.insert(new A("a1", "a2"));
3630045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        final A a = mDao.loadA(1);
3640045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        mDao.insert(new E("a1", "a2"));
3650045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        assertThat(mDao.loadE(1), notNullValue());
3660045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        mDao.delete(a);
3670045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        E e = mDao.loadE(1);
3680045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        assertThat(e, notNullValue());
3690045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        assertThat(e.aName, nullValue());
3700045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        assertThat(e.aLastName, nullValue());
3710045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    }
3720045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar
3730045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    @Test
3740045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    public void onUpdate_cascade_multipleReferences() {
3750045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        mDao.insert(new A("a1", "a2"));
3760045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        final A a = mDao.loadA(1);
3770045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        mDao.insert(new E("a1", "a2"));
3780045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        assertThat(mDao.loadE(1), notNullValue());
3790045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        mDao.changeNameA(1, "foo");
3800045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        assertThat(mDao.loadE(1), notNullValue());
3810045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        assertThat(mDao.loadE(1).aName, equalTo("foo"));
3820045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        assertThat(mDao.loadE(1).aLastName, equalTo("a2"));
3830045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    }
3840045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar
38524b0c2d5bde2848da5caa57d9b1ec2a95d12bcd9Yuichi Araki    private static Matcher<String> foreignKeyErrorMessage() {
38624b0c2d5bde2848da5caa57d9b1ec2a95d12bcd9Yuichi Araki        return either(containsString("FOREIGN KEY"))
38724b0c2d5bde2848da5caa57d9b1ec2a95d12bcd9Yuichi Araki                .or(both(containsString("CODE 19")).and(containsString("CONSTRAINT FAILED")));
38824b0c2d5bde2848da5caa57d9b1ec2a95d12bcd9Yuichi Araki    }
38924b0c2d5bde2848da5caa57d9b1ec2a95d12bcd9Yuichi Araki
3900045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    @SuppressWarnings("Duplicates")
3910045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    private void deleteInTransaction(A a, B b) {
3920045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        mDb.beginTransaction();
3930045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        try {
3940045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar            mDao.delete(a);
3950045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar            mDao.delete(b);
3960045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar            mDb.setTransactionSuccessful();
3970045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        } finally {
3980045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar            mDb.endTransaction();
3990045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        }
4000045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    }
4010045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar
4020045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    @SuppressWarnings("Duplicates")
4030045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    private void deleteInTransaction(A a, C c) {
4040045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        mDb.beginTransaction();
4050045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        try {
4060045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar            mDao.delete(a);
4070045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar            mDao.delete(c);
4080045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar            mDb.setTransactionSuccessful();
4090045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        } finally {
4100045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar            mDb.endTransaction();
4110045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        }
4120045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    }
4130045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar
4140045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    private static Throwable catchException(ThrowingRunnable throwingRunnable) {
4150045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        try {
4160045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar            throwingRunnable.run();
4170045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        } catch (Throwable t) {
4180045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar            return t;
4190045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        }
4200045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        throw new RuntimeException("didn't throw an exception");
4210045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    }
4220045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar
4230045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    private interface ThrowingRunnable {
4240045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar        void run() throws Exception;
4250045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar    }
4260045a1c980b44c882f4ece571a0a113d36bbf0fbYigit Boyar}
427