blob: fce6cbdfdcf8d718087b1cb4a467d0a441525b63 [file] [log] [blame]
/*
* Copyright 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.paging
import androidx.paging.PagedList.LoadState.DONE
import androidx.paging.PagedList.LoadState.IDLE
import androidx.paging.PagedList.LoadState.LOADING
import androidx.paging.PagedList.LoadType.END
import androidx.paging.PagedList.LoadType.START
import androidx.paging.PagedSource.LoadResult
import androidx.paging.futures.DirectDispatcher
import androidx.testutils.TestExecutor
import kotlinx.coroutines.GlobalScope
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
@RunWith(JUnit4::class)
class PagerTest {
val testExecutor = TestExecutor()
inner class ImmediateListDataSource(private val data: List<String>) :
PositionalDataSource<String>() {
init {
initExecutor(testExecutor)
}
override fun loadInitial(params: LoadInitialParams, callback: LoadInitialCallback<String>) {
executor.execute {
val totalCount = data.size
val position = computeInitialLoadPosition(params, totalCount)
val loadSize = computeInitialLoadSize(params, position, totalCount)
val sublist = data.subList(position, position + loadSize)
callback.onResult(sublist, position, totalCount)
}
}
override fun loadRange(params: LoadRangeParams, callback: LoadRangeCallback<String>) {
executor.execute {
val position = params.startPosition
val end = minOf(position + params.loadSize, data.size)
callback.onResult(data.subList(position, end))
}
}
}
val data = List(9) { "$it" }
private fun rangeResult(start: Int, end: Int) =
PositionalDataSource.RangeResult(data.subList(start, end)).toLoadResult<Int>()
private data class Result(
val type: PagedList.LoadType,
val pageResult: LoadResult<*, String>
)
private data class StateChange(
val type: PagedList.LoadType,
val state: PagedList.LoadState,
val error: Throwable? = null
)
private class MockConsumer : Pager.PageConsumer<String> {
private val results: MutableList<Result> = arrayListOf()
private val stateChanges: MutableList<StateChange> = arrayListOf()
fun takeResults(): List<Result> {
val ret = results.map { it }
results.clear()
return ret
}
fun takeStateChanges(): List<StateChange> {
val ret = stateChanges.map { it }
stateChanges.clear()
return ret
}
override fun onPageResult(
type: PagedList.LoadType,
pageResult: LoadResult<*, String>
): Boolean {
results.add(Result(type, pageResult))
return false
}
override fun onStateChanged(
type: PagedList.LoadType,
state: PagedList.LoadState,
error: Throwable?
) {
stateChanges.add(StateChange(type, state, error))
}
}
private fun createPager(
consumer: MockConsumer,
start: Int = 0,
end: Int = 10
): Pager<Int, String> {
val initialData = data.subList(start, end)
val initialResult = LoadResult<Int, String>(
data = initialData,
offset = 0,
itemsBefore = start,
itemsAfter = data.size - initialData.size - start
)
return Pager(
GlobalScope,
PagedList.Config(2, 2, true, 10, PagedList.Config.MAX_SIZE_UNBOUNDED),
PagedSourceWrapper(ImmediateListDataSource(data)),
DirectDispatcher,
DirectDispatcher,
consumer,
initialResult
)
}
@Test
fun simplePagerAppend() {
val consumer = MockConsumer()
val pager = createPager(consumer, 2, 6)
assertTrue(consumer.takeResults().isEmpty())
assertTrue(consumer.takeStateChanges().isEmpty())
pager.tryScheduleAppend()
assertTrue(consumer.takeResults().isEmpty())
assertEquals(
consumer.takeStateChanges(),
listOf(StateChange(END, LOADING))
)
testExecutor.executeAll()
assertEquals(
listOf(Result(END, rangeResult(6, 8))),
consumer.takeResults()
)
assertEquals(
listOf(StateChange(END, IDLE)),
consumer.takeStateChanges()
)
}
@Test
fun simplePagerPrepend() {
val consumer = MockConsumer()
val pager = createPager(consumer, 4, 8)
pager.trySchedulePrepend()
assertTrue(consumer.takeResults().isEmpty())
assertEquals(
consumer.takeStateChanges(), listOf(
StateChange(START, LOADING)
)
)
testExecutor.executeAll()
assertEquals(
listOf(Result(START, rangeResult(2, 4))),
consumer.takeResults()
)
assertEquals(
listOf(StateChange(START, IDLE)),
consumer.takeStateChanges()
)
}
@Test
fun doubleAppend() {
val consumer = MockConsumer()
val pager = createPager(consumer, 2, 6)
pager.tryScheduleAppend()
testExecutor.executeAll()
pager.tryScheduleAppend()
testExecutor.executeAll()
assertEquals(
listOf(
Result(END, rangeResult(6, 8)),
Result(END, rangeResult(8, 9))
), consumer.takeResults()
)
assertEquals(
listOf(
StateChange(END, LOADING),
StateChange(END, IDLE),
StateChange(END, LOADING),
StateChange(END, IDLE)
), consumer.takeStateChanges()
)
}
@Test
fun doublePrepend() {
val consumer = MockConsumer()
val pager = createPager(consumer, 4, 8)
pager.trySchedulePrepend()
testExecutor.executeAll()
pager.trySchedulePrepend()
testExecutor.executeAll()
assertEquals(
listOf(
Result(START, rangeResult(2, 4)),
Result(START, rangeResult(0, 2))
), consumer.takeResults()
)
assertEquals(
listOf(
StateChange(START, LOADING),
StateChange(START, IDLE),
StateChange(START, LOADING),
StateChange(START, IDLE)
), consumer.takeStateChanges()
)
}
@Test
fun emptyAppend() {
val consumer = MockConsumer()
val pager = createPager(consumer, 0, 9)
pager.tryScheduleAppend()
// Pager triggers an immediate empty response here, so we don't need to flush the executor
assertEquals(
listOf(
Result(END, LoadResult.empty<Int, String>())
), consumer.takeResults()
)
assertEquals(
listOf(
StateChange(END, DONE)
), consumer.takeStateChanges()
)
}
@Test
fun emptyPrepend() {
val consumer = MockConsumer()
val pager = createPager(consumer, 0, 9)
pager.trySchedulePrepend()
// Pager triggers an immediate empty response here, so we don't need to flush the executor
assertEquals(
listOf(
Result(START, LoadResult.empty<Int, String>())
), consumer.takeResults()
)
assertEquals(
listOf(
StateChange(START, DONE)
), consumer.takeStateChanges()
)
}
}