134e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar/*
234e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar * Copyright (C) 2017 The Android Open Source Project
334e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar *
434e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar * Licensed under the Apache License, Version 2.0 (the "License");
534e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar * you may not use this file except in compliance with the License.
634e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar * You may obtain a copy of the License at
734e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar *
834e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar *      http://www.apache.org/licenses/LICENSE-2.0
934e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar *
1034e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar * Unless required by applicable law or agreed to in writing, software
1134e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar * distributed under the License is distributed on an "AS IS" BASIS,
1234e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1334e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar * See the License for the specific language governing permissions and
1434e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar * limitations under the License.
1534e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar */
1634e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar
17ba069d50913c3fb250bb60ec310439db36895337Alan Viverettepackage androidx.room;
1834e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar
1934e5031083f735db3a395b0f6aa430880b072d71Yigit Boyarimport static org.hamcrest.CoreMatchers.is;
2034e5031083f735db3a395b0f6aa430880b072d71Yigit Boyarimport static org.hamcrest.MatcherAssert.assertThat;
212ec1285ef79d4849069efe95cfbac2307d291a47Yuichi Arakiimport static org.hamcrest.core.IsCollectionContaining.hasItem;
222ec1285ef79d4849069efe95cfbac2307d291a47Yuichi Arakiimport static org.hamcrest.core.IsCollectionContaining.hasItems;
2334e5031083f735db3a395b0f6aa430880b072d71Yigit Boyarimport static org.mockito.Matchers.any;
2434e5031083f735db3a395b0f6aa430880b072d71Yigit Boyarimport static org.mockito.Matchers.anyInt;
2534e5031083f735db3a395b0f6aa430880b072d71Yigit Boyarimport static org.mockito.Matchers.anyString;
2634e5031083f735db3a395b0f6aa430880b072d71Yigit Boyarimport static org.mockito.Matchers.eq;
2734e5031083f735db3a395b0f6aa430880b072d71Yigit Boyarimport static org.mockito.Mockito.doReturn;
28489d61a70a742408cda5ca1a4d9788b9f14282a0Yigit Boyarimport static org.mockito.Mockito.doThrow;
2934e5031083f735db3a395b0f6aa430880b072d71Yigit Boyarimport static org.mockito.Mockito.mock;
3034e5031083f735db3a395b0f6aa430880b072d71Yigit Boyarimport static org.mockito.Mockito.reset;
3134e5031083f735db3a395b0f6aa430880b072d71Yigit Boyarimport static org.mockito.Mockito.verify;
3234e5031083f735db3a395b0f6aa430880b072d71Yigit Boyarimport static org.mockito.Mockito.when;
3334e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar
34e185ed6ba937bdc218104c18d2615e1ce524adb7Yigit Boyarimport android.database.Cursor;
35e185ed6ba937bdc218104c18d2615e1ce524adb7Yigit Boyarimport android.database.sqlite.SQLiteException;
36ba069d50913c3fb250bb60ec310439db36895337Alan Viverette
37ba069d50913c3fb250bb60ec310439db36895337Alan Viveretteimport androidx.annotation.NonNull;
38ddee2b5170ae257a7b2494f8aaa8459ebed806dcAurimas Liutikasimport androidx.arch.core.executor.JunitTaskExecutorRule;
39ba069d50913c3fb250bb60ec310439db36895337Alan Viveretteimport androidx.sqlite.db.SupportSQLiteDatabase;
40ba069d50913c3fb250bb60ec310439db36895337Alan Viveretteimport androidx.sqlite.db.SupportSQLiteOpenHelper;
41ba069d50913c3fb250bb60ec310439db36895337Alan Viveretteimport androidx.sqlite.db.SupportSQLiteStatement;
4234e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar
43c4a5787f74164596892999fa876aa9bf963b898dYigit Boyarimport org.junit.After;
4434e5031083f735db3a395b0f6aa430880b072d71Yigit Boyarimport org.junit.Before;
45846dfcf52e22de6d912f8ece05ff939c2c9bd154Yigit Boyarimport org.junit.Rule;
4634e5031083f735db3a395b0f6aa430880b072d71Yigit Boyarimport org.junit.Test;
4734e5031083f735db3a395b0f6aa430880b072d71Yigit Boyarimport org.junit.runner.RunWith;
4834e5031083f735db3a395b0f6aa430880b072d71Yigit Boyarimport org.junit.runners.JUnit4;
4934e5031083f735db3a395b0f6aa430880b072d71Yigit Boyarimport org.mockito.Mockito;
5034e5031083f735db3a395b0f6aa430880b072d71Yigit Boyarimport org.mockito.invocation.InvocationOnMock;
5134e5031083f735db3a395b0f6aa430880b072d71Yigit Boyarimport org.mockito.stubbing.Answer;
5234e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar
532bc5b84ed32ea5d4643a3e7fc50fb4e8086c2df1Yigit Boyarimport java.lang.ref.WeakReference;
542bc5b84ed32ea5d4643a3e7fc50fb4e8086c2df1Yigit Boyarimport java.util.ArrayList;
55c4a5787f74164596892999fa876aa9bf963b898dYigit Boyarimport java.util.Locale;
562ec1285ef79d4849069efe95cfbac2307d291a47Yuichi Arakiimport java.util.Set;
5734e5031083f735db3a395b0f6aa430880b072d71Yigit Boyarimport java.util.concurrent.CountDownLatch;
5834e5031083f735db3a395b0f6aa430880b072d71Yigit Boyarimport java.util.concurrent.TimeUnit;
5934e5031083f735db3a395b0f6aa430880b072d71Yigit Boyarimport java.util.concurrent.atomic.AtomicInteger;
605843a353d7d4740a2d6119fdd90f258f645f4f20Yuichi Arakiimport java.util.concurrent.locks.ReentrantLock;
6134e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar
6234e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar@RunWith(JUnit4.class)
6334e5031083f735db3a395b0f6aa430880b072d71Yigit Boyarpublic class InvalidationTrackerTest {
6434e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar    private InvalidationTracker mTracker;
6534e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar    private RoomDatabase mRoomDatabase;
66489d61a70a742408cda5ca1a4d9788b9f14282a0Yigit Boyar    private SupportSQLiteOpenHelper mOpenHelper;
67846dfcf52e22de6d912f8ece05ff939c2c9bd154Yigit Boyar    @Rule
68459caadc8f6875fc78a36ae716193bf991f0808cSergey Vasilinets    public JunitTaskExecutorRule mTaskExecutorRule = new JunitTaskExecutorRule(1, true);
6934e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar
7034e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar    @Before
7134e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar    public void setup() {
7234e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar        mRoomDatabase = mock(RoomDatabase.class);
7334e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar        SupportSQLiteDatabase sqliteDb = mock(SupportSQLiteDatabase.class);
74846dfcf52e22de6d912f8ece05ff939c2c9bd154Yigit Boyar        final SupportSQLiteStatement statement = mock(SupportSQLiteStatement.class);
75489d61a70a742408cda5ca1a4d9788b9f14282a0Yigit Boyar        mOpenHelper = mock(SupportSQLiteOpenHelper.class);
7634e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar
77846dfcf52e22de6d912f8ece05ff939c2c9bd154Yigit Boyar        doReturn(statement).when(sqliteDb).compileStatement(eq(InvalidationTracker.CLEANUP_SQL));
78489d61a70a742408cda5ca1a4d9788b9f14282a0Yigit Boyar        doReturn(sqliteDb).when(mOpenHelper).getWritableDatabase();
79489d61a70a742408cda5ca1a4d9788b9f14282a0Yigit Boyar        doReturn(true).when(mRoomDatabase).isOpen();
805843a353d7d4740a2d6119fdd90f258f645f4f20Yuichi Araki        ReentrantLock closeLock = new ReentrantLock();
815843a353d7d4740a2d6119fdd90f258f645f4f20Yuichi Araki        doReturn(closeLock).when(mRoomDatabase).getCloseLock();
82846dfcf52e22de6d912f8ece05ff939c2c9bd154Yigit Boyar        //noinspection ResultOfMethodCallIgnored
83489d61a70a742408cda5ca1a4d9788b9f14282a0Yigit Boyar        doReturn(mOpenHelper).when(mRoomDatabase).getOpenHelper();
8434e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar
85c4a5787f74164596892999fa876aa9bf963b898dYigit Boyar        mTracker = new InvalidationTracker(mRoomDatabase, "a", "B", "i");
86846dfcf52e22de6d912f8ece05ff939c2c9bd154Yigit Boyar        mTracker.internalInit(sqliteDb);
8734e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar    }
8834e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar
89c4a5787f74164596892999fa876aa9bf963b898dYigit Boyar    @Before
90c4a5787f74164596892999fa876aa9bf963b898dYigit Boyar    public void setLocale() {
91c4a5787f74164596892999fa876aa9bf963b898dYigit Boyar        Locale.setDefault(Locale.forLanguageTag("tr-TR"));
92c4a5787f74164596892999fa876aa9bf963b898dYigit Boyar    }
93c4a5787f74164596892999fa876aa9bf963b898dYigit Boyar
94c4a5787f74164596892999fa876aa9bf963b898dYigit Boyar    @After
95c4a5787f74164596892999fa876aa9bf963b898dYigit Boyar    public void unsetLocale() {
96c4a5787f74164596892999fa876aa9bf963b898dYigit Boyar        Locale.setDefault(Locale.US);
97c4a5787f74164596892999fa876aa9bf963b898dYigit Boyar    }
98c4a5787f74164596892999fa876aa9bf963b898dYigit Boyar
9934e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar    @Test
10034e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar    public void tableIds() {
10134e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar        assertThat(mTracker.mTableIdLookup.get("a"), is(0));
10234e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar        assertThat(mTracker.mTableIdLookup.get("b"), is(1));
10334e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar    }
10434e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar
10534e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar    @Test
1062bc5b84ed32ea5d4643a3e7fc50fb4e8086c2df1Yigit Boyar    public void testWeak() throws InterruptedException {
1072bc5b84ed32ea5d4643a3e7fc50fb4e8086c2df1Yigit Boyar        final AtomicInteger data = new AtomicInteger(0);
1082bc5b84ed32ea5d4643a3e7fc50fb4e8086c2df1Yigit Boyar        InvalidationTracker.Observer observer = new InvalidationTracker.Observer("a") {
1092bc5b84ed32ea5d4643a3e7fc50fb4e8086c2df1Yigit Boyar            @Override
1102bc5b84ed32ea5d4643a3e7fc50fb4e8086c2df1Yigit Boyar            public void onInvalidated(@NonNull Set<String> tables) {
1112bc5b84ed32ea5d4643a3e7fc50fb4e8086c2df1Yigit Boyar                data.incrementAndGet();
1122bc5b84ed32ea5d4643a3e7fc50fb4e8086c2df1Yigit Boyar            }
1132bc5b84ed32ea5d4643a3e7fc50fb4e8086c2df1Yigit Boyar        };
1142bc5b84ed32ea5d4643a3e7fc50fb4e8086c2df1Yigit Boyar        mTracker.addWeakObserver(observer);
1152bc5b84ed32ea5d4643a3e7fc50fb4e8086c2df1Yigit Boyar        setVersions(1, 0);
1162bc5b84ed32ea5d4643a3e7fc50fb4e8086c2df1Yigit Boyar        refreshSync();
1172bc5b84ed32ea5d4643a3e7fc50fb4e8086c2df1Yigit Boyar        assertThat(data.get(), is(1));
1182bc5b84ed32ea5d4643a3e7fc50fb4e8086c2df1Yigit Boyar        observer = null;
1192bc5b84ed32ea5d4643a3e7fc50fb4e8086c2df1Yigit Boyar        forceGc();
1202bc5b84ed32ea5d4643a3e7fc50fb4e8086c2df1Yigit Boyar        setVersions(2, 0);
1212bc5b84ed32ea5d4643a3e7fc50fb4e8086c2df1Yigit Boyar        refreshSync();
1222bc5b84ed32ea5d4643a3e7fc50fb4e8086c2df1Yigit Boyar        assertThat(data.get(), is(1));
1232bc5b84ed32ea5d4643a3e7fc50fb4e8086c2df1Yigit Boyar    }
1242bc5b84ed32ea5d4643a3e7fc50fb4e8086c2df1Yigit Boyar
1252bc5b84ed32ea5d4643a3e7fc50fb4e8086c2df1Yigit Boyar    @Test
12634e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar    public void addRemoveObserver() throws Exception {
12734e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar        InvalidationTracker.Observer observer = new LatchObserver(1, "a");
12834e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar        mTracker.addObserver(observer);
129fdda61893b7dda4733230f9bf2c975a463f6a00fSergey Vasilinets        assertThat(mTracker.mObserverMap.size(), is(1));
13034e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar        mTracker.removeObserver(new LatchObserver(1, "a"));
131fdda61893b7dda4733230f9bf2c975a463f6a00fSergey Vasilinets        assertThat(mTracker.mObserverMap.size(), is(1));
13234e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar        mTracker.removeObserver(observer);
133fdda61893b7dda4733230f9bf2c975a463f6a00fSergey Vasilinets        assertThat(mTracker.mObserverMap.size(), is(0));
13434e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar    }
13534e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar
136846dfcf52e22de6d912f8ece05ff939c2c9bd154Yigit Boyar    private void drainTasks() throws InterruptedException {
137846dfcf52e22de6d912f8ece05ff939c2c9bd154Yigit Boyar        mTaskExecutorRule.drainTasks(200);
138846dfcf52e22de6d912f8ece05ff939c2c9bd154Yigit Boyar    }
139846dfcf52e22de6d912f8ece05ff939c2c9bd154Yigit Boyar
14034e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar    @Test(expected = IllegalArgumentException.class)
14134e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar    public void badObserver() {
14234e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar        InvalidationTracker.Observer observer = new LatchObserver(1, "x");
14334e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar        mTracker.addObserver(observer);
14434e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar    }
14534e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar
14634e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar    @Test
14734e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar    public void refreshReadValues() throws Exception {
14834e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar        setVersions(1, 0, 2, 1);
149846dfcf52e22de6d912f8ece05ff939c2c9bd154Yigit Boyar        refreshSync();
150c4a5787f74164596892999fa876aa9bf963b898dYigit Boyar        assertThat(mTracker.mTableVersions, is(new long[]{1, 2, 0}));
15134e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar
15234e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar        setVersions(3, 1);
153846dfcf52e22de6d912f8ece05ff939c2c9bd154Yigit Boyar        refreshSync();
154c4a5787f74164596892999fa876aa9bf963b898dYigit Boyar        assertThat(mTracker.mTableVersions, is(new long[]{1, 3, 0}));
15534e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar
15634e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar        setVersions(7, 0);
157846dfcf52e22de6d912f8ece05ff939c2c9bd154Yigit Boyar        refreshSync();
158c4a5787f74164596892999fa876aa9bf963b898dYigit Boyar        assertThat(mTracker.mTableVersions, is(new long[]{7, 3, 0}));
15934e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar
160846dfcf52e22de6d912f8ece05ff939c2c9bd154Yigit Boyar        refreshSync();
161c4a5787f74164596892999fa876aa9bf963b898dYigit Boyar        assertThat(mTracker.mTableVersions, is(new long[]{7, 3, 0}));
16234e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar    }
16334e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar
164846dfcf52e22de6d912f8ece05ff939c2c9bd154Yigit Boyar    private void refreshSync() throws InterruptedException {
165846dfcf52e22de6d912f8ece05ff939c2c9bd154Yigit Boyar        mTracker.refreshVersionsAsync();
166846dfcf52e22de6d912f8ece05ff939c2c9bd154Yigit Boyar        drainTasks();
167846dfcf52e22de6d912f8ece05ff939c2c9bd154Yigit Boyar    }
168846dfcf52e22de6d912f8ece05ff939c2c9bd154Yigit Boyar
16934e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar    @Test
17034e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar    public void refreshCheckTasks() throws Exception {
171e185ed6ba937bdc218104c18d2615e1ce524adb7Yigit Boyar        when(mRoomDatabase.query(anyString(), any(Object[].class)))
17234e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar                .thenReturn(mock(Cursor.class));
17334e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar        mTracker.refreshVersionsAsync();
17434e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar        mTracker.refreshVersionsAsync();
175846dfcf52e22de6d912f8ece05ff939c2c9bd154Yigit Boyar        verify(mTaskExecutorRule.getTaskExecutor()).executeOnDiskIO(mTracker.mRefreshRunnable);
176846dfcf52e22de6d912f8ece05ff939c2c9bd154Yigit Boyar        drainTasks();
17734e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar
178846dfcf52e22de6d912f8ece05ff939c2c9bd154Yigit Boyar        reset(mTaskExecutorRule.getTaskExecutor());
17934e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar        mTracker.refreshVersionsAsync();
180846dfcf52e22de6d912f8ece05ff939c2c9bd154Yigit Boyar        verify(mTaskExecutorRule.getTaskExecutor()).executeOnDiskIO(mTracker.mRefreshRunnable);
18134e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar    }
18234e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar
18334e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar    @Test
18434e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar    public void observe1Table() throws Exception {
18534e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar        LatchObserver observer = new LatchObserver(1, "a");
18634e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar        mTracker.addObserver(observer);
18734e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar        setVersions(1, 0, 2, 1);
188846dfcf52e22de6d912f8ece05ff939c2c9bd154Yigit Boyar        refreshSync();
18934e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar        assertThat(observer.await(), is(true));
1902ec1285ef79d4849069efe95cfbac2307d291a47Yuichi Araki        assertThat(observer.getInvalidatedTables().size(), is(1));
1912ec1285ef79d4849069efe95cfbac2307d291a47Yuichi Araki        assertThat(observer.getInvalidatedTables(), hasItem("a"));
19234e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar
19334e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar        setVersions(3, 1);
19434e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar        observer.reset(1);
195846dfcf52e22de6d912f8ece05ff939c2c9bd154Yigit Boyar        refreshSync();
19634e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar        assertThat(observer.await(), is(false));
19734e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar
19834e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar        setVersions(4, 0);
199846dfcf52e22de6d912f8ece05ff939c2c9bd154Yigit Boyar        refreshSync();
20034e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar        assertThat(observer.await(), is(true));
2012ec1285ef79d4849069efe95cfbac2307d291a47Yuichi Araki        assertThat(observer.getInvalidatedTables().size(), is(1));
2022ec1285ef79d4849069efe95cfbac2307d291a47Yuichi Araki        assertThat(observer.getInvalidatedTables(), hasItem("a"));
20334e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar    }
20434e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar
20534e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar    @Test
20634e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar    public void observe2Tables() throws Exception {
20734e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar        LatchObserver observer = new LatchObserver(1, "A", "B");
20834e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar        mTracker.addObserver(observer);
20934e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar        setVersions(1, 0, 2, 1);
210846dfcf52e22de6d912f8ece05ff939c2c9bd154Yigit Boyar        refreshSync();
21134e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar        assertThat(observer.await(), is(true));
2122ec1285ef79d4849069efe95cfbac2307d291a47Yuichi Araki        assertThat(observer.getInvalidatedTables().size(), is(2));
2132ec1285ef79d4849069efe95cfbac2307d291a47Yuichi Araki        assertThat(observer.getInvalidatedTables(), hasItems("A", "B"));
21434e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar
21534e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar        setVersions(3, 1);
21634e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar        observer.reset(1);
217846dfcf52e22de6d912f8ece05ff939c2c9bd154Yigit Boyar        refreshSync();
21834e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar        assertThat(observer.await(), is(true));
2192ec1285ef79d4849069efe95cfbac2307d291a47Yuichi Araki        assertThat(observer.getInvalidatedTables().size(), is(1));
2202ec1285ef79d4849069efe95cfbac2307d291a47Yuichi Araki        assertThat(observer.getInvalidatedTables(), hasItem("B"));
22134e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar
22234e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar        setVersions(4, 0);
22334e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar        observer.reset(1);
224846dfcf52e22de6d912f8ece05ff939c2c9bd154Yigit Boyar        refreshSync();
22534e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar        assertThat(observer.await(), is(true));
2262ec1285ef79d4849069efe95cfbac2307d291a47Yuichi Araki        assertThat(observer.getInvalidatedTables().size(), is(1));
2272ec1285ef79d4849069efe95cfbac2307d291a47Yuichi Araki        assertThat(observer.getInvalidatedTables(), hasItem("A"));
22834e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar
22934e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar        observer.reset(1);
230846dfcf52e22de6d912f8ece05ff939c2c9bd154Yigit Boyar        refreshSync();
23134e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar        assertThat(observer.await(), is(false));
23234e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar    }
23334e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar
234c4a5787f74164596892999fa876aa9bf963b898dYigit Boyar    @Test
235c4a5787f74164596892999fa876aa9bf963b898dYigit Boyar    public void locale() {
236c4a5787f74164596892999fa876aa9bf963b898dYigit Boyar        LatchObserver observer = new LatchObserver(1, "I");
237c4a5787f74164596892999fa876aa9bf963b898dYigit Boyar        mTracker.addObserver(observer);
238c4a5787f74164596892999fa876aa9bf963b898dYigit Boyar    }
239c4a5787f74164596892999fa876aa9bf963b898dYigit Boyar
240489d61a70a742408cda5ca1a4d9788b9f14282a0Yigit Boyar    @Test
241489d61a70a742408cda5ca1a4d9788b9f14282a0Yigit Boyar    public void closedDb() {
242458885cbb4677ae06ce3bd0c4172c86928a7b101Yuichi Araki        doReturn(false).when(mRoomDatabase).isOpen();
243489d61a70a742408cda5ca1a4d9788b9f14282a0Yigit Boyar        doThrow(new IllegalStateException("foo")).when(mOpenHelper).getWritableDatabase();
244489d61a70a742408cda5ca1a4d9788b9f14282a0Yigit Boyar        mTracker.addObserver(new LatchObserver(1, "a", "b"));
245489d61a70a742408cda5ca1a4d9788b9f14282a0Yigit Boyar        mTracker.mRefreshRunnable.run();
246489d61a70a742408cda5ca1a4d9788b9f14282a0Yigit Boyar    }
247489d61a70a742408cda5ca1a4d9788b9f14282a0Yigit Boyar
2489ca46219aa94a53b2011b45aa4817da53dce28c0Aurimas Liutikas    // @Test - disabled due to flakiness b/65257997
249489d61a70a742408cda5ca1a4d9788b9f14282a0Yigit Boyar    public void closedDbAfterOpen() throws InterruptedException {
250489d61a70a742408cda5ca1a4d9788b9f14282a0Yigit Boyar        setVersions(3, 1);
251489d61a70a742408cda5ca1a4d9788b9f14282a0Yigit Boyar        mTracker.addObserver(new LatchObserver(1, "a", "b"));
252489d61a70a742408cda5ca1a4d9788b9f14282a0Yigit Boyar        mTracker.syncTriggers();
253489d61a70a742408cda5ca1a4d9788b9f14282a0Yigit Boyar        mTracker.mRefreshRunnable.run();
254489d61a70a742408cda5ca1a4d9788b9f14282a0Yigit Boyar        doThrow(new SQLiteException("foo")).when(mRoomDatabase).query(
255489d61a70a742408cda5ca1a4d9788b9f14282a0Yigit Boyar                Mockito.eq(InvalidationTracker.SELECT_UPDATED_TABLES_SQL),
256e185ed6ba937bdc218104c18d2615e1ce524adb7Yigit Boyar                any(Object[].class));
257489d61a70a742408cda5ca1a4d9788b9f14282a0Yigit Boyar        mTracker.mPendingRefresh.set(true);
258489d61a70a742408cda5ca1a4d9788b9f14282a0Yigit Boyar        mTracker.mRefreshRunnable.run();
259489d61a70a742408cda5ca1a4d9788b9f14282a0Yigit Boyar    }
260489d61a70a742408cda5ca1a4d9788b9f14282a0Yigit Boyar
26134e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar    /**
26234e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar     * Key value pairs of VERSION, TABLE_ID
26334e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar     */
264846dfcf52e22de6d912f8ece05ff939c2c9bd154Yigit Boyar    private void setVersions(int... keyValuePairs) throws InterruptedException {
265846dfcf52e22de6d912f8ece05ff939c2c9bd154Yigit Boyar        // mockito does not like multi-threaded access so before setting versions, make sure we
266846dfcf52e22de6d912f8ece05ff939c2c9bd154Yigit Boyar        // sync background tasks.
267846dfcf52e22de6d912f8ece05ff939c2c9bd154Yigit Boyar        drainTasks();
26834e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar        Cursor cursor = createCursorWithValues(keyValuePairs);
26934e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar        doReturn(cursor).when(mRoomDatabase).query(
27034e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar                Mockito.eq(InvalidationTracker.SELECT_UPDATED_TABLES_SQL),
271e185ed6ba937bdc218104c18d2615e1ce524adb7Yigit Boyar                any(Object[].class)
27234e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar        );
27334e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar    }
27434e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar
27534e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar    private Cursor createCursorWithValues(final int... keyValuePairs) {
27634e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar        Cursor cursor = mock(Cursor.class);
27734e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar        final AtomicInteger index = new AtomicInteger(-2);
27834e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar        when(cursor.moveToNext()).thenAnswer(new Answer<Boolean>() {
27934e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar            @Override
28034e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar            public Boolean answer(InvocationOnMock invocation) throws Throwable {
28134e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar                return index.addAndGet(2) < keyValuePairs.length;
28234e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar            }
28334e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar        });
284daa68aab1adbf8c5b5c64d2b34a60955da742f1dSergey Vasilinets        Answer<Integer> intAnswer = new Answer<Integer>() {
28534e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar            @Override
28634e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar            public Integer answer(InvocationOnMock invocation) throws Throwable {
28734e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar                return keyValuePairs[index.intValue() + (Integer) invocation.getArguments()[0]];
28834e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar            }
28934e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar        };
290daa68aab1adbf8c5b5c64d2b34a60955da742f1dSergey Vasilinets        Answer<Long> longAnswer = new Answer<Long>() {
291daa68aab1adbf8c5b5c64d2b34a60955da742f1dSergey Vasilinets            @Override
292daa68aab1adbf8c5b5c64d2b34a60955da742f1dSergey Vasilinets            public Long answer(InvocationOnMock invocation) throws Throwable {
293daa68aab1adbf8c5b5c64d2b34a60955da742f1dSergey Vasilinets                return (long) keyValuePairs[index.intValue()
294daa68aab1adbf8c5b5c64d2b34a60955da742f1dSergey Vasilinets                        + (Integer) invocation.getArguments()[0]];
295daa68aab1adbf8c5b5c64d2b34a60955da742f1dSergey Vasilinets            }
296daa68aab1adbf8c5b5c64d2b34a60955da742f1dSergey Vasilinets        };
297daa68aab1adbf8c5b5c64d2b34a60955da742f1dSergey Vasilinets        when(cursor.getInt(anyInt())).thenAnswer(intAnswer);
298daa68aab1adbf8c5b5c64d2b34a60955da742f1dSergey Vasilinets        when(cursor.getLong(anyInt())).thenAnswer(longAnswer);
29934e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar        return cursor;
30034e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar    }
30134e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar
30234e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar    static class LatchObserver extends InvalidationTracker.Observer {
30334e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar        private CountDownLatch mLatch;
3042ec1285ef79d4849069efe95cfbac2307d291a47Yuichi Araki        private Set<String> mInvalidatedTables;
30534e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar
30634e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar        LatchObserver(int count, String... tableNames) {
30734e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar            super(tableNames);
30834e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar            mLatch = new CountDownLatch(count);
30934e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar        }
31034e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar
31134e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar        boolean await() throws InterruptedException {
31234e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar            return mLatch.await(3, TimeUnit.SECONDS);
31334e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar        }
31434e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar
31534e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar        @Override
3162ec1285ef79d4849069efe95cfbac2307d291a47Yuichi Araki        public void onInvalidated(@NonNull Set<String> tables) {
3172ec1285ef79d4849069efe95cfbac2307d291a47Yuichi Araki            mInvalidatedTables = tables;
31834e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar            mLatch.countDown();
31934e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar        }
32034e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar
32134e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar        void reset(@SuppressWarnings("SameParameterValue") int count) {
3222ec1285ef79d4849069efe95cfbac2307d291a47Yuichi Araki            mInvalidatedTables = null;
32334e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar            mLatch = new CountDownLatch(count);
32434e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar        }
3252ec1285ef79d4849069efe95cfbac2307d291a47Yuichi Araki
3262ec1285ef79d4849069efe95cfbac2307d291a47Yuichi Araki        Set<String> getInvalidatedTables() {
3272ec1285ef79d4849069efe95cfbac2307d291a47Yuichi Araki            return mInvalidatedTables;
3282ec1285ef79d4849069efe95cfbac2307d291a47Yuichi Araki        }
32934e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar    }
3302bc5b84ed32ea5d4643a3e7fc50fb4e8086c2df1Yigit Boyar
3312bc5b84ed32ea5d4643a3e7fc50fb4e8086c2df1Yigit Boyar    private static void forceGc() {
3322bc5b84ed32ea5d4643a3e7fc50fb4e8086c2df1Yigit Boyar        // Use a random index in the list to detect the garbage collection each time because
3332bc5b84ed32ea5d4643a3e7fc50fb4e8086c2df1Yigit Boyar        // .get() may accidentally trigger a strong reference during collection.
3342bc5b84ed32ea5d4643a3e7fc50fb4e8086c2df1Yigit Boyar        ArrayList<WeakReference<byte[]>> leak = new ArrayList<>();
3352bc5b84ed32ea5d4643a3e7fc50fb4e8086c2df1Yigit Boyar        do {
3362bc5b84ed32ea5d4643a3e7fc50fb4e8086c2df1Yigit Boyar            WeakReference<byte[]> arr = new WeakReference<>(new byte[100]);
3372bc5b84ed32ea5d4643a3e7fc50fb4e8086c2df1Yigit Boyar            leak.add(arr);
3382bc5b84ed32ea5d4643a3e7fc50fb4e8086c2df1Yigit Boyar        } while (leak.get((int) (Math.random() * leak.size())).get() != null);
3392bc5b84ed32ea5d4643a3e7fc50fb4e8086c2df1Yigit Boyar    }
34034e5031083f735db3a395b0f6aa430880b072d71Yigit Boyar}
341