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