DataSourceFactoryTest.java revision 7d2309c06787d20bafe9582e1f47d106b624ab59
1/* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package androidx.room.integration.testapp.paging; 18 19import static org.hamcrest.CoreMatchers.is; 20import static org.hamcrest.CoreMatchers.notNullValue; 21import static org.hamcrest.CoreMatchers.nullValue; 22import static org.hamcrest.MatcherAssert.assertThat; 23import static org.junit.Assert.assertNotNull; 24 25import android.support.test.filters.LargeTest; 26import android.support.test.runner.AndroidJUnit4; 27 28import androidx.annotation.Nullable; 29import androidx.arch.core.executor.ArchTaskExecutor; 30import androidx.arch.core.executor.testing.CountingTaskExecutorRule; 31import androidx.lifecycle.Lifecycle; 32import androidx.lifecycle.LifecycleOwner; 33import androidx.lifecycle.LifecycleRegistry; 34import androidx.lifecycle.LiveData; 35import androidx.lifecycle.Observer; 36import androidx.paging.DataSource; 37import androidx.paging.LivePagedListBuilder; 38import androidx.paging.PagedList; 39import androidx.room.integration.testapp.test.TestDatabaseTest; 40import androidx.room.integration.testapp.test.TestUtil; 41import androidx.room.integration.testapp.vo.Pet; 42import androidx.room.integration.testapp.vo.User; 43import androidx.room.integration.testapp.vo.UserAndAllPets; 44import androidx.sqlite.db.SimpleSQLiteQuery; 45 46import org.junit.Rule; 47import org.junit.Test; 48import org.junit.runner.RunWith; 49 50import java.util.Arrays; 51import java.util.Collections; 52import java.util.concurrent.ExecutionException; 53import java.util.concurrent.FutureTask; 54import java.util.concurrent.TimeUnit; 55import java.util.concurrent.TimeoutException; 56 57@LargeTest 58@RunWith(AndroidJUnit4.class) 59public class DataSourceFactoryTest extends TestDatabaseTest { 60 @Rule 61 public CountingTaskExecutorRule mExecutorRule = new CountingTaskExecutorRule(); 62 63 private interface LivePagedListFactory { 64 LiveData<PagedList<User>> create(); 65 } 66 67 @Test 68 public void getUsersAsPagedList() 69 throws InterruptedException, ExecutionException, TimeoutException { 70 validateUsersAsPagedList(() -> new LivePagedListBuilder<>( 71 mUserDao.loadPagedByAge(3), 72 new PagedList.Config.Builder() 73 .setPageSize(10) 74 .setPrefetchDistance(1) 75 .setInitialLoadSizeHint(10).build()) 76 .build()); 77 } 78 79 @Test 80 public void getUsersAsPagedList_ViaRawQuery_WithObservable() 81 throws InterruptedException, ExecutionException, TimeoutException { 82 SimpleSQLiteQuery query = new SimpleSQLiteQuery( 83 "SELECT * FROM user where mAge > ?", 84 new Object[]{3}); 85 validateUsersAsPagedList(() -> new LivePagedListBuilder<>( 86 mUserDao.loadPagedByAgeWithObserver(query), 87 new PagedList.Config.Builder() 88 .setPageSize(10) 89 .setPrefetchDistance(1) 90 .setInitialLoadSizeHint(10).build()) 91 .build()); 92 } 93 94 private void validateUsersAsPagedList( 95 LivePagedListFactory factory) 96 throws InterruptedException, ExecutionException, TimeoutException { 97 mDatabase.beginTransaction(); 98 try { 99 for (int i = 0; i < 100; i++) { 100 final User user = TestUtil.createUser(i + 1); 101 user.setAge(i); 102 mUserDao.insert(user); 103 } 104 mDatabase.setTransactionSuccessful(); 105 } finally { 106 mDatabase.endTransaction(); 107 } 108 assertThat(mUserDao.count(), is(100)); 109 110 final LiveData<PagedList<User>> livePagedUsers = factory.create(); 111 112 final TestLifecycleOwner testOwner = new TestLifecycleOwner(); 113 testOwner.handleEvent(Lifecycle.Event.ON_CREATE); 114 drain(); 115 PagedListObserver<User> observer = new PagedListObserver<>(); 116 117 observe(livePagedUsers, testOwner, observer); 118 assertThat(observer.get(), nullValue()); 119 observer.reset(); 120 121 testOwner.handleEvent(Lifecycle.Event.ON_START); 122 drain(); 123 124 final PagedList<User> pagedList1 = observer.get(); 125 assertThat(pagedList1, is(notNullValue())); 126 127 assertThat(pagedList1.size(), is(96)); 128 assertThat(getAndLoad(pagedList1, 20), is(nullValue())); 129 drain(); 130 assertThat(getAndLoad(pagedList1, 31), nullValue()); 131 assertThat(getAndLoad(pagedList1, 20), notNullValue()); 132 assertThat(getAndLoad(pagedList1, 16), notNullValue()); 133 134 drain(); 135 assertThat(getAndLoad(pagedList1, 31), notNullValue()); 136 assertThat(getAndLoad(pagedList1, 50), nullValue()); 137 drain(); 138 assertThat(getAndLoad(pagedList1, 50), notNullValue()); 139 observer.reset(); 140 // now invalidate the database but don't get the new paged list 141 mUserDao.updateById(50, "foo"); 142 drain(); // Sync with InvalidationTracker 143 assertThat(getAndLoad(pagedList1, 70), nullValue()); 144 drain(); 145 assertThat(getAndLoad(pagedList1, 70), nullValue()); 146 final PagedList<User> pagedList = observer.get(); 147 assertThat(getAndLoad(pagedList, 50), notNullValue()); 148 assertThat(getAndLoad(pagedList, 70), nullValue()); 149 } 150 151 private <T> T getAndLoad(PagedList<T> list, int pos) { 152 T result = list.get(pos); 153 list.loadAround(pos); 154 return result; 155 } 156 157 private void drain() throws InterruptedException, TimeoutException { 158 mExecutorRule.drainTasks(60, TimeUnit.SECONDS); 159 } 160 161 private void observe(final LiveData liveData, final LifecycleOwner provider, 162 final Observer observer) throws ExecutionException, InterruptedException { 163 FutureTask<Void> futureTask = new FutureTask<>(() -> { 164 //noinspection unchecked 165 liveData.observe(provider, observer); 166 return null; 167 }); 168 ArchTaskExecutor.getInstance().executeOnMainThread(futureTask); 169 futureTask.get(); 170 } 171 172 @Test 173 public void withRelation() throws ExecutionException, InterruptedException, TimeoutException { 174 // verify DataSourceFactory can be created from a multi table join 175 DataSource.Factory<Integer, UserAndAllPets> factory = 176 mUserPetDao.dataSourceFactoryMultiTable(); 177 LiveData<PagedList<UserAndAllPets>> liveData = 178 new LivePagedListBuilder<>(mUserPetDao.dataSourceFactoryMultiTable(), 10).build(); 179 assertNotNull(factory.create()); 180 181 PagedListObserver<UserAndAllPets> observer = new PagedListObserver<>(); 182 final TestLifecycleOwner lifecycleOwner = new TestLifecycleOwner(); 183 lifecycleOwner.handleEvent(Lifecycle.Event.ON_START); 184 observe(liveData, lifecycleOwner, observer); 185 drain(); 186 assertThat(observer.get(), is(Collections.emptyList())); 187 188 observer.reset(); 189 User user = TestUtil.createUser(3); 190 mUserDao.insert(user); 191 drain(); 192 final UserAndAllPets noPets = observer.get().get(0); 193 assertThat(noPets.user, is(user)); 194 195 observer.reset(); 196 Pet[] pets = TestUtil.createPetsForUser(3, 1, 2); 197 mPetDao.insertAll(pets); 198 199 drain(); 200 final UserAndAllPets withPets = observer.get().get(0); 201 assertThat(withPets.user, is(user)); 202 assertThat(withPets.pets, is(Arrays.asList(pets))); 203 } 204 205 static class TestLifecycleOwner implements LifecycleOwner { 206 207 private LifecycleRegistry mLifecycle; 208 209 TestLifecycleOwner() { 210 mLifecycle = new LifecycleRegistry(this); 211 } 212 213 @Override 214 public Lifecycle getLifecycle() { 215 return mLifecycle; 216 } 217 218 void handleEvent(Lifecycle.Event event) { 219 mLifecycle.handleLifecycleEvent(event); 220 } 221 } 222 223 private static class PagedListObserver<T> implements Observer<PagedList<T>> { 224 private PagedList<T> mList; 225 226 void reset() { 227 mList = null; 228 } 229 230 public PagedList<T> get() { 231 return mList; 232 } 233 234 @Override 235 public void onChanged(@Nullable PagedList<T> pagedList) { 236 mList = pagedList; 237 } 238 } 239} 240