1ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik/* 2ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik * Copyright (C) 2017 The Android Open Source Project 3ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik * 4ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik * Licensed under the Apache License, Version 2.0 (the "License"); 5ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik * you may not use this file except in compliance with the License. 6ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik * You may obtain a copy of the License at 7ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik * 8ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik * http://www.apache.org/licenses/LICENSE-2.0 9ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik * 10ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik * Unless required by applicable law or agreed to in writing, software 11ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik * distributed under the License is distributed on an "AS IS" BASIS, 12ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik * See the License for the specific language governing permissions and 14ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik * limitations under the License. 15ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik */ 16ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik 17bdc4c86d3dff74f6634a38e2f7b316b0e823a2c8Alan Viverettepackage androidx.paging 18ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik 19bdc4c86d3dff74f6634a38e2f7b316b0e823a2c8Alan Viveretteimport androidx.arch.core.util.Function 20ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craikimport org.junit.Assert.assertArrayEquals 21ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craikimport org.junit.Assert.assertEquals 225dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craikimport org.junit.Assert.assertFalse 23ed6541a9e1b9737dc9aed9f46ad134fdb3d47a7cChris Craikimport org.junit.Assert.assertSame 245dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craikimport org.junit.Assert.assertTrue 25ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craikimport org.junit.Test 26ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craikimport org.junit.runner.RunWith 27ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craikimport org.junit.runners.Parameterized 28ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craikimport org.mockito.Mockito.mock 29ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craikimport org.mockito.Mockito.verify 30ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craikimport org.mockito.Mockito.verifyNoMoreInteractions 31ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craikimport org.mockito.Mockito.verifyZeroInteractions 32f174a1ccda73d8a42dc752a0b1c487508af2c54dChris Craikimport java.util.concurrent.Executor 335dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik 34ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik@RunWith(Parameterized::class) 35ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craikclass ContiguousPagedListTest(private val mCounted: Boolean) { 36ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik private val mMainThread = TestExecutor() 37ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik private val mBackgroundThread = TestExecutor() 38ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik 39ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik private class Item(position: Int) { 40ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik val name: String = "Item $position" 41ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik 42ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik override fun toString(): String { 43ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik return name 44ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik } 45ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik } 46ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik 4767077406223e49eba5ecd0def10ca80dd6909f16Chris Craik private inner class TestSource(val listData: List<Item> = ITEMS) 485dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik : ContiguousDataSource<Int, Item>() { 49694588d1d059ac96142d6334ec7fce90abb7622bChris Craik override fun dispatchLoadInitial( 50f174a1ccda73d8a42dc752a0b1c487508af2c54dChris Craik key: Int?, 51f174a1ccda73d8a42dc752a0b1c487508af2c54dChris Craik initialLoadSize: Int, 52f174a1ccda73d8a42dc752a0b1c487508af2c54dChris Craik pageSize: Int, 53f174a1ccda73d8a42dc752a0b1c487508af2c54dChris Craik enablePlaceholders: Boolean, 54f174a1ccda73d8a42dc752a0b1c487508af2c54dChris Craik mainThreadExecutor: Executor, 55f174a1ccda73d8a42dc752a0b1c487508af2c54dChris Craik receiver: PageResult.Receiver<Item>) { 565dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik 575dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik val convertPosition = key ?: 0 58f174a1ccda73d8a42dc752a0b1c487508af2c54dChris Craik val position = Math.max(0, (convertPosition - initialLoadSize / 2)) 59f174a1ccda73d8a42dc752a0b1c487508af2c54dChris Craik val data = getClampedRange(position, position + initialLoadSize) 60f174a1ccda73d8a42dc752a0b1c487508af2c54dChris Craik val trailingUnloadedCount = listData.size - position - data.size 615dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik 625dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik if (enablePlaceholders && mCounted) { 63f174a1ccda73d8a42dc752a0b1c487508af2c54dChris Craik receiver.onPageResult(PageResult.INIT, 64f174a1ccda73d8a42dc752a0b1c487508af2c54dChris Craik PageResult(data, position, trailingUnloadedCount, 0)) 65ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik } else { 665dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik // still must pass offset, even if not counted 67f174a1ccda73d8a42dc752a0b1c487508af2c54dChris Craik receiver.onPageResult(PageResult.INIT, 68f174a1ccda73d8a42dc752a0b1c487508af2c54dChris Craik PageResult(data, position)) 69ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik } 70ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik } 71ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik 72694588d1d059ac96142d6334ec7fce90abb7622bChris Craik override fun dispatchLoadAfter( 73f174a1ccda73d8a42dc752a0b1c487508af2c54dChris Craik currentEndIndex: Int, 74f174a1ccda73d8a42dc752a0b1c487508af2c54dChris Craik currentEndItem: Item, 75f174a1ccda73d8a42dc752a0b1c487508af2c54dChris Craik pageSize: Int, 76f174a1ccda73d8a42dc752a0b1c487508af2c54dChris Craik mainThreadExecutor: Executor, 77f174a1ccda73d8a42dc752a0b1c487508af2c54dChris Craik receiver: PageResult.Receiver<Item>) { 785dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik val startIndex = currentEndIndex + 1 79f174a1ccda73d8a42dc752a0b1c487508af2c54dChris Craik val data = getClampedRange(startIndex, startIndex + pageSize) 80f174a1ccda73d8a42dc752a0b1c487508af2c54dChris Craik 81f174a1ccda73d8a42dc752a0b1c487508af2c54dChris Craik mainThreadExecutor.execute { 82f174a1ccda73d8a42dc752a0b1c487508af2c54dChris Craik receiver.onPageResult(PageResult.APPEND, PageResult(data, 0, 0, 0)) 83f174a1ccda73d8a42dc752a0b1c487508af2c54dChris Craik } 84ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik } 85ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik 86694588d1d059ac96142d6334ec7fce90abb7622bChris Craik override fun dispatchLoadBefore( 87f174a1ccda73d8a42dc752a0b1c487508af2c54dChris Craik currentBeginIndex: Int, 88f174a1ccda73d8a42dc752a0b1c487508af2c54dChris Craik currentBeginItem: Item, 89f174a1ccda73d8a42dc752a0b1c487508af2c54dChris Craik pageSize: Int, 90f174a1ccda73d8a42dc752a0b1c487508af2c54dChris Craik mainThreadExecutor: Executor, 91f174a1ccda73d8a42dc752a0b1c487508af2c54dChris Craik receiver: PageResult.Receiver<Item>) { 92f174a1ccda73d8a42dc752a0b1c487508af2c54dChris Craik 935dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik val startIndex = currentBeginIndex - 1 94f174a1ccda73d8a42dc752a0b1c487508af2c54dChris Craik val data = getClampedRange(startIndex - pageSize + 1, startIndex + 1) 95f174a1ccda73d8a42dc752a0b1c487508af2c54dChris Craik 96f174a1ccda73d8a42dc752a0b1c487508af2c54dChris Craik mainThreadExecutor.execute { 97f174a1ccda73d8a42dc752a0b1c487508af2c54dChris Craik receiver.onPageResult(PageResult.PREPEND, PageResult(data, 0, 0, 0)) 98f174a1ccda73d8a42dc752a0b1c487508af2c54dChris Craik } 99ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik } 100ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik 1015dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik override fun getKey(position: Int, item: Item?): Int { 1025dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik return 0 1035dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik } 1045dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik 1055dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik private fun getClampedRange(startInc: Int, endExc: Int): List<Item> { 1065dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik return listData.subList(Math.max(0, startInc), Math.min(listData.size, endExc)) 107ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik } 10849da2e7f2d6d92afae01d99c292dd477f3bf7ffcChris Craik 10949da2e7f2d6d92afae01d99c292dd477f3bf7ffcChris Craik override fun <ToValue : Any?> mapByPage(function: Function<List<Item>, List<ToValue>>): 11049da2e7f2d6d92afae01d99c292dd477f3bf7ffcChris Craik DataSource<Int, ToValue> { 11149da2e7f2d6d92afae01d99c292dd477f3bf7ffcChris Craik throw UnsupportedOperationException() 11249da2e7f2d6d92afae01d99c292dd477f3bf7ffcChris Craik } 11349da2e7f2d6d92afae01d99c292dd477f3bf7ffcChris Craik 11449da2e7f2d6d92afae01d99c292dd477f3bf7ffcChris Craik override fun <ToValue : Any?> map(function: Function<Item, ToValue>): 11549da2e7f2d6d92afae01d99c292dd477f3bf7ffcChris Craik DataSource<Int, ToValue> { 11649da2e7f2d6d92afae01d99c292dd477f3bf7ffcChris Craik throw UnsupportedOperationException() 11749da2e7f2d6d92afae01d99c292dd477f3bf7ffcChris Craik } 118ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik } 119ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik 1205dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik private fun verifyRange(start: Int, count: Int, actual: PagedStorage<Item>) { 121ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik if (mCounted) { 122771816bab34735dd8fb47a93085f6b86c132154cChris Craik // assert nulls + content 123ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik val expected = arrayOfNulls<Item>(ITEMS.size) 124ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik System.arraycopy(ITEMS.toTypedArray(), start, expected, start, count) 125ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik assertArrayEquals(expected, actual.toTypedArray()) 126ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik 127ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik val expectedTrailing = ITEMS.size - start - count 128ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik assertEquals(ITEMS.size, actual.size) 129771816bab34735dd8fb47a93085f6b86c132154cChris Craik assertEquals((ITEMS.size - start - expectedTrailing), 130ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik actual.storageCount) 131771816bab34735dd8fb47a93085f6b86c132154cChris Craik assertEquals(start, actual.leadingNullCount) 132ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik assertEquals(expectedTrailing, actual.trailingNullCount) 133ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik } else { 134771816bab34735dd8fb47a93085f6b86c132154cChris Craik assertEquals(ITEMS.subList(start, start + count), actual) 135ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik 136ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik assertEquals(count, actual.size) 137ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik assertEquals(actual.size, actual.storageCount) 138ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik assertEquals(0, actual.leadingNullCount) 139ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik assertEquals(0, actual.trailingNullCount) 140ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik } 141ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik } 142ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik 143ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik private fun verifyRange(start: Int, count: Int, actual: PagedList<Item>) { 144ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik verifyRange(start, count, actual.mStorage) 145ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik } 146ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik 147ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik private fun createCountedPagedList( 14867077406223e49eba5ecd0def10ca80dd6909f16Chris Craik initialPosition: Int, 14967077406223e49eba5ecd0def10ca80dd6909f16Chris Craik pageSize: Int = 20, 15067077406223e49eba5ecd0def10ca80dd6909f16Chris Craik initLoadSize: Int = 40, 15167077406223e49eba5ecd0def10ca80dd6909f16Chris Craik prefetchDistance: Int = 20, 15267077406223e49eba5ecd0def10ca80dd6909f16Chris Craik listData: List<Item> = ITEMS, 153e4cede1bc21d8f16cee3c0de2d77f70d7c3fd2cfChris Craik boundaryCallback: PagedList.BoundaryCallback<Item>? = null, 154e4cede1bc21d8f16cee3c0de2d77f70d7c3fd2cfChris Craik lastLoad: Int = ContiguousPagedList.LAST_LOAD_UNSPECIFIED 1556f1f5567abe765d30fda9c8fedce5617ecdeda9cAurimas Liutikas ): ContiguousPagedList<Int, Item> { 156ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik return ContiguousPagedList( 15767077406223e49eba5ecd0def10ca80dd6909f16Chris Craik TestSource(listData), mMainThread, mBackgroundThread, boundaryCallback, 158ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik PagedList.Config.Builder() 15967077406223e49eba5ecd0def10ca80dd6909f16Chris Craik .setInitialLoadSizeHint(initLoadSize) 16067077406223e49eba5ecd0def10ca80dd6909f16Chris Craik .setPageSize(pageSize) 16167077406223e49eba5ecd0def10ca80dd6909f16Chris Craik .setPrefetchDistance(prefetchDistance) 162ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik .build(), 163e4cede1bc21d8f16cee3c0de2d77f70d7c3fd2cfChris Craik initialPosition, 164e4cede1bc21d8f16cee3c0de2d77f70d7c3fd2cfChris Craik lastLoad) 165ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik } 166ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik 167ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik @Test 168ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik fun construct() { 169ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik val pagedList = createCountedPagedList(0) 170ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik verifyRange(0, 40, pagedList) 171ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik } 172ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik 173ed6541a9e1b9737dc9aed9f46ad134fdb3d47a7cChris Craik @Test 174ed6541a9e1b9737dc9aed9f46ad134fdb3d47a7cChris Craik fun getDataSource() { 175ed6541a9e1b9737dc9aed9f46ad134fdb3d47a7cChris Craik val pagedList = createCountedPagedList(0) 176ed6541a9e1b9737dc9aed9f46ad134fdb3d47a7cChris Craik assertTrue(pagedList.dataSource is TestSource) 177ed6541a9e1b9737dc9aed9f46ad134fdb3d47a7cChris Craik 178ed6541a9e1b9737dc9aed9f46ad134fdb3d47a7cChris Craik // snapshot keeps same DataSource 179ed6541a9e1b9737dc9aed9f46ad134fdb3d47a7cChris Craik assertSame(pagedList.dataSource, 180ed6541a9e1b9737dc9aed9f46ad134fdb3d47a7cChris Craik (pagedList.snapshot() as SnapshotPagedList<Item>).dataSource) 181ed6541a9e1b9737dc9aed9f46ad134fdb3d47a7cChris Craik } 182ed6541a9e1b9737dc9aed9f46ad134fdb3d47a7cChris Craik 183771816bab34735dd8fb47a93085f6b86c132154cChris Craik private fun verifyCallback(callback: PagedList.Callback, countedPosition: Int, 184771816bab34735dd8fb47a93085f6b86c132154cChris Craik uncountedPosition: Int) { 185771816bab34735dd8fb47a93085f6b86c132154cChris Craik if (mCounted) { 186771816bab34735dd8fb47a93085f6b86c132154cChris Craik verify(callback).onChanged(countedPosition, 20) 187771816bab34735dd8fb47a93085f6b86c132154cChris Craik } else { 188771816bab34735dd8fb47a93085f6b86c132154cChris Craik verify(callback).onInserted(uncountedPosition, 20) 189771816bab34735dd8fb47a93085f6b86c132154cChris Craik } 190771816bab34735dd8fb47a93085f6b86c132154cChris Craik } 191771816bab34735dd8fb47a93085f6b86c132154cChris Craik 192ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik @Test 193ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik fun append() { 194ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik val pagedList = createCountedPagedList(0) 195ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik val callback = mock(PagedList.Callback::class.java) 196ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik pagedList.addWeakCallback(null, callback) 197ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik verifyRange(0, 40, pagedList) 198ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik verifyZeroInteractions(callback) 199ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik 200ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik pagedList.loadAround(35) 201ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik drain() 202ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik 203ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik verifyRange(0, 60, pagedList) 204ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik verifyCallback(callback, 40, 40) 205ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik verifyNoMoreInteractions(callback) 206ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik } 207ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik 208ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik @Test 209ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik fun prepend() { 210ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik val pagedList = createCountedPagedList(80) 211ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik val callback = mock(PagedList.Callback::class.java) 212ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik pagedList.addWeakCallback(null, callback) 213ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik verifyRange(60, 40, pagedList) 214ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik verifyZeroInteractions(callback) 215ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik 216ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik pagedList.loadAround(if (mCounted) 65 else 5) 217ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik drain() 218ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik 219ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik verifyRange(40, 60, pagedList) 220ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik verifyCallback(callback, 40, 0) 221ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik verifyNoMoreInteractions(callback) 222ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik } 223ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik 224ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik @Test 225ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik fun outwards() { 226ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik val pagedList = createCountedPagedList(50) 227ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik val callback = mock(PagedList.Callback::class.java) 228ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik pagedList.addWeakCallback(null, callback) 229ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik verifyRange(30, 40, pagedList) 230ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik verifyZeroInteractions(callback) 231ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik 232ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik pagedList.loadAround(if (mCounted) 65 else 35) 233ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik drain() 234ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik 235ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik verifyRange(30, 60, pagedList) 236ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik verifyCallback(callback, 70, 40) 237ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik verifyNoMoreInteractions(callback) 238ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik 239ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik pagedList.loadAround(if (mCounted) 35 else 5) 240ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik drain() 241ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik 242ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik verifyRange(10, 80, pagedList) 243ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik verifyCallback(callback, 10, 0) 244ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik verifyNoMoreInteractions(callback) 245ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik } 246ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik 247ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik @Test 248ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik fun multiAppend() { 249ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik val pagedList = createCountedPagedList(0) 250ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik val callback = mock(PagedList.Callback::class.java) 251ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik pagedList.addWeakCallback(null, callback) 252ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik verifyRange(0, 40, pagedList) 253ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik verifyZeroInteractions(callback) 254ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik 255ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik pagedList.loadAround(55) 256ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik drain() 257ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik 258ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik verifyRange(0, 80, pagedList) 259ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik verifyCallback(callback, 40, 40) 260ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik verifyCallback(callback, 60, 60) 261ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik verifyNoMoreInteractions(callback) 262ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik } 263ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik 264ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik @Test 265ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik fun distantPrefetch() { 26667077406223e49eba5ecd0def10ca80dd6909f16Chris Craik val pagedList = createCountedPagedList(0, 26767077406223e49eba5ecd0def10ca80dd6909f16Chris Craik initLoadSize = 10, pageSize = 10, prefetchDistance = 30) 268ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik val callback = mock(PagedList.Callback::class.java) 269ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik pagedList.addWeakCallback(null, callback) 270ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik verifyRange(0, 10, pagedList) 271ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik verifyZeroInteractions(callback) 272ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik 273ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik pagedList.loadAround(5) 274ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik drain() 275ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik 276ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik verifyRange(0, 40, pagedList) 277ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik 278ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik pagedList.loadAround(6) 279ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik drain() 280ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik 281ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik // although our prefetch window moves forward, no new load triggered 282ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik verifyRange(0, 40, pagedList) 283ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik } 284ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik 285ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik @Test 286ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik fun appendCallbackAddedLate() { 287ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik val pagedList = createCountedPagedList(0) 288ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik verifyRange(0, 40, pagedList) 289ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik 290ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik pagedList.loadAround(35) 291ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik drain() 292ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik verifyRange(0, 60, pagedList) 293ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik 294ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik // snapshot at 60 items 295ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik val snapshot = pagedList.snapshot() as PagedList<Item> 296ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik verifyRange(0, 60, snapshot) 297ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik 298ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik // load more items... 299ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik pagedList.loadAround(55) 300ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik drain() 301ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik verifyRange(0, 80, pagedList) 302ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik verifyRange(0, 60, snapshot) 303ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik 304ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik // and verify the snapshot hasn't received them 305ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik val callback = mock(PagedList.Callback::class.java) 306ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik pagedList.addWeakCallback(snapshot, callback) 307ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik verifyCallback(callback, 60, 60) 308ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik verifyNoMoreInteractions(callback) 309ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik } 310ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik 311ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik @Test 312ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik fun prependCallbackAddedLate() { 313ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik val pagedList = createCountedPagedList(80) 314ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik verifyRange(60, 40, pagedList) 315ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik 316ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik pagedList.loadAround(if (mCounted) 65 else 5) 317ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik drain() 318ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik verifyRange(40, 60, pagedList) 319ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik 320ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik // snapshot at 60 items 321ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik val snapshot = pagedList.snapshot() as PagedList<Item> 322ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik verifyRange(40, 60, snapshot) 323ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik 324ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik pagedList.loadAround(if (mCounted) 45 else 5) 325ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik drain() 326ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik verifyRange(20, 80, pagedList) 327ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik verifyRange(40, 60, snapshot) 328ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik 329ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik val callback = mock(PagedList.Callback::class.java) 330ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik pagedList.addWeakCallback(snapshot, callback) 331ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik verifyCallback(callback, 40, 0) 332ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik verifyNoMoreInteractions(callback) 333ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik } 334ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik 33567077406223e49eba5ecd0def10ca80dd6909f16Chris Craik @Test 336e4cede1bc21d8f16cee3c0de2d77f70d7c3fd2cfChris Craik fun initialLoad_lastLoad() { 337e4cede1bc21d8f16cee3c0de2d77f70d7c3fd2cfChris Craik val pagedList = createCountedPagedList( 338e4cede1bc21d8f16cee3c0de2d77f70d7c3fd2cfChris Craik initialPosition = 0, 339e4cede1bc21d8f16cee3c0de2d77f70d7c3fd2cfChris Craik initLoadSize = 20, 340e4cede1bc21d8f16cee3c0de2d77f70d7c3fd2cfChris Craik lastLoad = 4) 341e4cede1bc21d8f16cee3c0de2d77f70d7c3fd2cfChris Craik // last load is param passed 342e4cede1bc21d8f16cee3c0de2d77f70d7c3fd2cfChris Craik assertEquals(4, pagedList.mLastLoad) 343e4cede1bc21d8f16cee3c0de2d77f70d7c3fd2cfChris Craik verifyRange(0, 20, pagedList) 344e4cede1bc21d8f16cee3c0de2d77f70d7c3fd2cfChris Craik } 345e4cede1bc21d8f16cee3c0de2d77f70d7c3fd2cfChris Craik 346e4cede1bc21d8f16cee3c0de2d77f70d7c3fd2cfChris Craik @Test 347e4cede1bc21d8f16cee3c0de2d77f70d7c3fd2cfChris Craik fun initialLoad_lastLoadComputed() { 348e4cede1bc21d8f16cee3c0de2d77f70d7c3fd2cfChris Craik val pagedList = createCountedPagedList( 349e4cede1bc21d8f16cee3c0de2d77f70d7c3fd2cfChris Craik initialPosition = 0, 350e4cede1bc21d8f16cee3c0de2d77f70d7c3fd2cfChris Craik initLoadSize = 20, 351e4cede1bc21d8f16cee3c0de2d77f70d7c3fd2cfChris Craik lastLoad = ContiguousPagedList.LAST_LOAD_UNSPECIFIED) 352e4cede1bc21d8f16cee3c0de2d77f70d7c3fd2cfChris Craik // last load is middle of initial load 353e4cede1bc21d8f16cee3c0de2d77f70d7c3fd2cfChris Craik assertEquals(10, pagedList.mLastLoad) 354e4cede1bc21d8f16cee3c0de2d77f70d7c3fd2cfChris Craik verifyRange(0, 20, pagedList) 355e4cede1bc21d8f16cee3c0de2d77f70d7c3fd2cfChris Craik } 356e4cede1bc21d8f16cee3c0de2d77f70d7c3fd2cfChris Craik 357e4cede1bc21d8f16cee3c0de2d77f70d7c3fd2cfChris Craik @Test 3585dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik fun initialLoadAsync() { 3595dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik // Note: ignores Parameterized param 3605dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik val asyncDataSource = AsyncListDataSource(ITEMS) 3615dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik val dataSource = asyncDataSource.wrapAsContiguousWithoutPlaceholders() 3625dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik val pagedList = ContiguousPagedList( 3635dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik dataSource, mMainThread, mBackgroundThread, null, 364e4cede1bc21d8f16cee3c0de2d77f70d7c3fd2cfChris Craik PagedList.Config.Builder().setPageSize(10).build(), null, 365e4cede1bc21d8f16cee3c0de2d77f70d7c3fd2cfChris Craik ContiguousPagedList.LAST_LOAD_UNSPECIFIED) 3665f9d120fb2dbcebb87c1caa171320e9246e00c10Chris Craik val callback = mock(PagedList.Callback::class.java) 3675f9d120fb2dbcebb87c1caa171320e9246e00c10Chris Craik pagedList.addWeakCallback(null, callback) 3685dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik 3695dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik assertTrue(pagedList.isEmpty()) 3705dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik drain() 3715dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik assertTrue(pagedList.isEmpty()) 3725dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik asyncDataSource.flush() 3735dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik assertTrue(pagedList.isEmpty()) 3745dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik mBackgroundThread.executeAll() 3755dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik assertTrue(pagedList.isEmpty()) 3765f9d120fb2dbcebb87c1caa171320e9246e00c10Chris Craik verifyZeroInteractions(callback) 3775dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik 3785dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik // Data source defers callbacks until flush, which posts result to main thread 3795dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik mMainThread.executeAll() 3805dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik assertFalse(pagedList.isEmpty()) 3815f9d120fb2dbcebb87c1caa171320e9246e00c10Chris Craik // callback onInsert called once with initial size 3825f9d120fb2dbcebb87c1caa171320e9246e00c10Chris Craik verify(callback).onInserted(0, pagedList.size) 3835f9d120fb2dbcebb87c1caa171320e9246e00c10Chris Craik verifyNoMoreInteractions(callback) 3845dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik } 3855dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik 3865dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik @Test 3875dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik fun addWeakCallbackEmpty() { 3885dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik // Note: ignores Parameterized param 3895dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik val asyncDataSource = AsyncListDataSource(ITEMS) 3905dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik val dataSource = asyncDataSource.wrapAsContiguousWithoutPlaceholders() 3915dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik val pagedList = ContiguousPagedList( 3925dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik dataSource, mMainThread, mBackgroundThread, null, 393e4cede1bc21d8f16cee3c0de2d77f70d7c3fd2cfChris Craik PagedList.Config.Builder().setPageSize(10).build(), null, 394e4cede1bc21d8f16cee3c0de2d77f70d7c3fd2cfChris Craik ContiguousPagedList.LAST_LOAD_UNSPECIFIED) 3955dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik val callback = mock(PagedList.Callback::class.java) 3965dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik 3975dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik // capture empty snapshot 3985dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik val emptySnapshot = pagedList.snapshot() 3995dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik assertTrue(pagedList.isEmpty()) 4005dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik assertTrue(emptySnapshot.isEmpty()) 4015dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik 4025dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik // verify that adding callback notifies nothing going from empty -> empty 4035dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik pagedList.addWeakCallback(emptySnapshot, callback) 4045dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik verifyZeroInteractions(callback) 4055dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik pagedList.removeWeakCallback(callback) 4065dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik 4075dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik // data added in asynchronously 4085dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik asyncDataSource.flush() 4095dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik drain() 4105dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik assertFalse(pagedList.isEmpty()) 4115dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik 4125dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik // verify that adding callback notifies insert going from empty -> content 4135dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik pagedList.addWeakCallback(emptySnapshot, callback) 4145dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik verify(callback).onInserted(0, pagedList.size) 4155dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik verifyNoMoreInteractions(callback) 4165dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik } 4175dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik 4185dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik @Test 41967077406223e49eba5ecd0def10ca80dd6909f16Chris Craik fun boundaryCallback_empty() { 42067077406223e49eba5ecd0def10ca80dd6909f16Chris Craik @Suppress("UNCHECKED_CAST") 42167077406223e49eba5ecd0def10ca80dd6909f16Chris Craik val boundaryCallback = 42267077406223e49eba5ecd0def10ca80dd6909f16Chris Craik mock(PagedList.BoundaryCallback::class.java) as PagedList.BoundaryCallback<Item> 42367077406223e49eba5ecd0def10ca80dd6909f16Chris Craik val pagedList = createCountedPagedList(0, 42467077406223e49eba5ecd0def10ca80dd6909f16Chris Craik listData = ArrayList(), boundaryCallback = boundaryCallback) 42567077406223e49eba5ecd0def10ca80dd6909f16Chris Craik assertEquals(0, pagedList.size) 42667077406223e49eba5ecd0def10ca80dd6909f16Chris Craik 42767077406223e49eba5ecd0def10ca80dd6909f16Chris Craik // nothing yet 42867077406223e49eba5ecd0def10ca80dd6909f16Chris Craik verifyNoMoreInteractions(boundaryCallback) 42967077406223e49eba5ecd0def10ca80dd6909f16Chris Craik 43067077406223e49eba5ecd0def10ca80dd6909f16Chris Craik // onZeroItemsLoaded posted, since creation often happens on BG thread 43167077406223e49eba5ecd0def10ca80dd6909f16Chris Craik drain() 43267077406223e49eba5ecd0def10ca80dd6909f16Chris Craik verify(boundaryCallback).onZeroItemsLoaded() 43367077406223e49eba5ecd0def10ca80dd6909f16Chris Craik verifyNoMoreInteractions(boundaryCallback) 43467077406223e49eba5ecd0def10ca80dd6909f16Chris Craik } 43567077406223e49eba5ecd0def10ca80dd6909f16Chris Craik 43667077406223e49eba5ecd0def10ca80dd6909f16Chris Craik @Test 43768d6c3eb6419ab55acd9ed91b03eb2b25e47c55bChris Craik fun boundaryCallback_singleInitialLoad() { 43868d6c3eb6419ab55acd9ed91b03eb2b25e47c55bChris Craik val shortList = ITEMS.subList(0, 4) 43968d6c3eb6419ab55acd9ed91b03eb2b25e47c55bChris Craik @Suppress("UNCHECKED_CAST") 44068d6c3eb6419ab55acd9ed91b03eb2b25e47c55bChris Craik val boundaryCallback = 44168d6c3eb6419ab55acd9ed91b03eb2b25e47c55bChris Craik mock(PagedList.BoundaryCallback::class.java) as PagedList.BoundaryCallback<Item> 44268d6c3eb6419ab55acd9ed91b03eb2b25e47c55bChris Craik val pagedList = createCountedPagedList(0, listData = shortList, 44368d6c3eb6419ab55acd9ed91b03eb2b25e47c55bChris Craik initLoadSize = shortList.size, boundaryCallback = boundaryCallback) 44468d6c3eb6419ab55acd9ed91b03eb2b25e47c55bChris Craik assertEquals(shortList.size, pagedList.size) 44568d6c3eb6419ab55acd9ed91b03eb2b25e47c55bChris Craik 44668d6c3eb6419ab55acd9ed91b03eb2b25e47c55bChris Craik // nothing yet 44768d6c3eb6419ab55acd9ed91b03eb2b25e47c55bChris Craik verifyNoMoreInteractions(boundaryCallback) 44868d6c3eb6419ab55acd9ed91b03eb2b25e47c55bChris Craik 44968d6c3eb6419ab55acd9ed91b03eb2b25e47c55bChris Craik // onItemAtFrontLoaded / onItemAtEndLoaded posted, since creation often happens on BG thread 45068d6c3eb6419ab55acd9ed91b03eb2b25e47c55bChris Craik drain() 45168d6c3eb6419ab55acd9ed91b03eb2b25e47c55bChris Craik pagedList.loadAround(0) 45268d6c3eb6419ab55acd9ed91b03eb2b25e47c55bChris Craik drain() 45368d6c3eb6419ab55acd9ed91b03eb2b25e47c55bChris Craik verify(boundaryCallback).onItemAtFrontLoaded(shortList.first()) 45468d6c3eb6419ab55acd9ed91b03eb2b25e47c55bChris Craik verify(boundaryCallback).onItemAtEndLoaded(shortList.last()) 45568d6c3eb6419ab55acd9ed91b03eb2b25e47c55bChris Craik verifyNoMoreInteractions(boundaryCallback) 45668d6c3eb6419ab55acd9ed91b03eb2b25e47c55bChris Craik } 45768d6c3eb6419ab55acd9ed91b03eb2b25e47c55bChris Craik 45868d6c3eb6419ab55acd9ed91b03eb2b25e47c55bChris Craik @Test 45967077406223e49eba5ecd0def10ca80dd6909f16Chris Craik fun boundaryCallback_delayed() { 46067077406223e49eba5ecd0def10ca80dd6909f16Chris Craik @Suppress("UNCHECKED_CAST") 46167077406223e49eba5ecd0def10ca80dd6909f16Chris Craik val boundaryCallback = 46267077406223e49eba5ecd0def10ca80dd6909f16Chris Craik mock(PagedList.BoundaryCallback::class.java) as PagedList.BoundaryCallback<Item> 46367077406223e49eba5ecd0def10ca80dd6909f16Chris Craik val pagedList = createCountedPagedList(90, 46467077406223e49eba5ecd0def10ca80dd6909f16Chris Craik initLoadSize = 20, prefetchDistance = 5, boundaryCallback = boundaryCallback) 46567077406223e49eba5ecd0def10ca80dd6909f16Chris Craik verifyRange(80, 20, pagedList) 46667077406223e49eba5ecd0def10ca80dd6909f16Chris Craik 46767077406223e49eba5ecd0def10ca80dd6909f16Chris Craik // nothing yet 46867077406223e49eba5ecd0def10ca80dd6909f16Chris Craik verifyZeroInteractions(boundaryCallback) 46967077406223e49eba5ecd0def10ca80dd6909f16Chris Craik drain() 47067077406223e49eba5ecd0def10ca80dd6909f16Chris Craik verifyZeroInteractions(boundaryCallback) 47167077406223e49eba5ecd0def10ca80dd6909f16Chris Craik 47267077406223e49eba5ecd0def10ca80dd6909f16Chris Craik // loading around last item causes onItemAtEndLoaded 47367077406223e49eba5ecd0def10ca80dd6909f16Chris Craik pagedList.loadAround(if (mCounted) 99 else 19) 47467077406223e49eba5ecd0def10ca80dd6909f16Chris Craik drain() 47567077406223e49eba5ecd0def10ca80dd6909f16Chris Craik verifyRange(80, 20, pagedList) 4765dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik verify(boundaryCallback).onItemAtEndLoaded(ITEMS.last()) 47767077406223e49eba5ecd0def10ca80dd6909f16Chris Craik verifyNoMoreInteractions(boundaryCallback) 47867077406223e49eba5ecd0def10ca80dd6909f16Chris Craik 47967077406223e49eba5ecd0def10ca80dd6909f16Chris Craik // prepending doesn't trigger callback... 48067077406223e49eba5ecd0def10ca80dd6909f16Chris Craik pagedList.loadAround(if (mCounted) 80 else 0) 48167077406223e49eba5ecd0def10ca80dd6909f16Chris Craik drain() 48267077406223e49eba5ecd0def10ca80dd6909f16Chris Craik verifyRange(60, 40, pagedList) 48367077406223e49eba5ecd0def10ca80dd6909f16Chris Craik verifyZeroInteractions(boundaryCallback) 48467077406223e49eba5ecd0def10ca80dd6909f16Chris Craik 48567077406223e49eba5ecd0def10ca80dd6909f16Chris Craik // ...load rest of data, still no dispatch... 48667077406223e49eba5ecd0def10ca80dd6909f16Chris Craik pagedList.loadAround(if (mCounted) 60 else 0) 48767077406223e49eba5ecd0def10ca80dd6909f16Chris Craik drain() 48867077406223e49eba5ecd0def10ca80dd6909f16Chris Craik pagedList.loadAround(if (mCounted) 40 else 0) 48967077406223e49eba5ecd0def10ca80dd6909f16Chris Craik drain() 49067077406223e49eba5ecd0def10ca80dd6909f16Chris Craik pagedList.loadAround(if (mCounted) 20 else 0) 49167077406223e49eba5ecd0def10ca80dd6909f16Chris Craik drain() 49267077406223e49eba5ecd0def10ca80dd6909f16Chris Craik verifyRange(0, 100, pagedList) 49367077406223e49eba5ecd0def10ca80dd6909f16Chris Craik verifyZeroInteractions(boundaryCallback) 49467077406223e49eba5ecd0def10ca80dd6909f16Chris Craik 49567077406223e49eba5ecd0def10ca80dd6909f16Chris Craik // ... finally try prepend, see 0 items, which will dispatch front callback 49667077406223e49eba5ecd0def10ca80dd6909f16Chris Craik pagedList.loadAround(0) 49767077406223e49eba5ecd0def10ca80dd6909f16Chris Craik drain() 4985dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik verify(boundaryCallback).onItemAtFrontLoaded(ITEMS.first()) 49967077406223e49eba5ecd0def10ca80dd6909f16Chris Craik verifyNoMoreInteractions(boundaryCallback) 50067077406223e49eba5ecd0def10ca80dd6909f16Chris Craik } 50167077406223e49eba5ecd0def10ca80dd6909f16Chris Craik 502ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik private fun drain() { 503ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik var executed: Boolean 504ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik do { 505ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik executed = mBackgroundThread.executeAll() 506ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik executed = mMainThread.executeAll() || executed 507ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik } while (executed) 508ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik } 509ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik 510ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik companion object { 511ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik @JvmStatic 512ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik @Parameterized.Parameters(name = "counted:{0}") 513ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik fun parameters(): Array<Array<Boolean>> { 514ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik return arrayOf(arrayOf(true), arrayOf(false)) 515ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik } 516ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik 517ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik private val ITEMS = List(100) { Item(it) } 518ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik } 519ac809222a8350934455ef38980d53f0b89b0f0e9Chris Craik} 520