blob: c2f10b2caeb68d296734efee22928d290f87c646 [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.
*/
@file:Suppress("DEPRECATION")
package androidx.paging
import androidx.paging.PagedList.Config
import androidx.paging.PagingSource.LoadResult
import androidx.paging.PagingSource.LoadResult.Page
import androidx.testutils.TestDispatcher
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.runBlocking
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 LegacyPagerTest {
private val testDispatcher = TestDispatcher()
private val data = List(9) { "$it" }
inner class ImmediateListDataSource(val data: List<String>) : PagingSource<Int, String>() {
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, String> {
val key = params.key ?: 0
val start = when (params.loadType) {
LoadType.REFRESH -> key
LoadType.START -> key - params.loadSize
LoadType.END -> key
}.coerceAtLeast(0)
val end = when (params.loadType) {
LoadType.REFRESH -> key + params.loadSize
LoadType.START -> key
LoadType.END -> key + params.loadSize
}.coerceAtMost(data.size)
return Page(
data = data.subList(start, end),
prevKey = if (start > 0) start else null,
nextKey = if (end < data.size) end else null,
itemsBefore = start,
itemsAfter = data.size - end
)
}
}
private fun rangeResult(start: Int, end: Int) = Page(
data = data.subList(start, end),
prevKey = if (start > 0) start else null,
nextKey = if (end < data.size) end else null,
itemsBefore = start,
itemsAfter = data.size - end
)
private data class Result(
val type: LoadType,
val pageResult: LoadResult<*, String>
)
private class MockConsumer : LegacyPager.PageConsumer<String> {
private val results: MutableList<Result> = arrayListOf()
private val stateChanges: MutableList<StateChange> = arrayListOf()
var storage: PagedStorage<String>? = null
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: LoadType, page: Page<*, String>): Boolean {
@Suppress("NON_EXHAUSTIVE_WHEN")
when (type) {
LoadType.START -> storage?.prependPage(page)
LoadType.END -> storage?.appendPage(page)
}
results.add(Result(type, page))
return false
}
override fun onStateChanged(type: LoadType, state: LoadState) {
stateChanges.add(StateChange(type, state))
}
}
private fun createPager(
consumer: MockConsumer,
start: Int = 0,
end: Int = 10
): LegacyPager<Int, String> {
val config = Config(2, 2, true, 10, Config.MAX_SIZE_UNBOUNDED)
val pagingSource = ImmediateListDataSource(data)
val initialResult = runBlocking {
pagingSource.load(
PagingSource.LoadParams(
loadType = LoadType.REFRESH,
key = start,
loadSize = end - start,
placeholdersEnabled = config.enablePlaceholders,
pageSize = config.pageSize
)
)
}
val initialData = (initialResult as Page).data
val storage = PagedStorage(
start,
initialResult,
data.size - initialData.size - start
)
consumer.storage = storage
@Suppress("UNCHECKED_CAST")
return LegacyPager(
GlobalScope,
config,
pagingSource,
DirectDispatcher,
testDispatcher,
consumer,
storage as LegacyPager.KeyProvider<Int>
)
}
@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(
listOf(StateChange(LoadType.END, LoadState.Loading)),
consumer.takeStateChanges()
)
testDispatcher.executeAll()
assertEquals(listOf(Result(LoadType.END, rangeResult(6, 8))), consumer.takeResults())
assertEquals(
listOf(StateChange(LoadType.END, LoadState.Idle)),
consumer.takeStateChanges()
)
}
@Test
fun simplePagerPrepend() {
val consumer = MockConsumer()
val pager = createPager(consumer, 4, 8)
pager.trySchedulePrepend()
assertTrue(consumer.takeResults().isEmpty())
assertEquals(
listOf(StateChange(LoadType.START, LoadState.Loading)),
consumer.takeStateChanges()
)
testDispatcher.executeAll()
assertEquals(
listOf(Result(LoadType.START, rangeResult(2, 4))),
consumer.takeResults()
)
assertEquals(
listOf(StateChange(LoadType.START, LoadState.Idle)),
consumer.takeStateChanges()
)
}
@Test
fun doubleAppend() {
val consumer = MockConsumer()
val pager = createPager(consumer, 2, 6)
pager.tryScheduleAppend()
testDispatcher.executeAll()
pager.tryScheduleAppend()
testDispatcher.executeAll()
assertEquals(
listOf(
Result(LoadType.END, rangeResult(6, 8)),
Result(LoadType.END, rangeResult(8, 9))
), consumer.takeResults()
)
assertEquals(
listOf(
StateChange(LoadType.END, LoadState.Loading),
StateChange(LoadType.END, LoadState.Idle),
StateChange(LoadType.END, LoadState.Loading),
StateChange(LoadType.END, LoadState.Idle)
),
consumer.takeStateChanges()
)
}
@Test
fun doublePrepend() {
val consumer = MockConsumer()
val pager = createPager(consumer, 4, 8)
pager.trySchedulePrepend()
testDispatcher.executeAll()
pager.trySchedulePrepend()
testDispatcher.executeAll()
assertEquals(
listOf(
Result(LoadType.START, rangeResult(2, 4)),
Result(LoadType.START, rangeResult(0, 2))
), consumer.takeResults()
)
assertEquals(
listOf(
StateChange(LoadType.START, LoadState.Loading),
StateChange(LoadType.START, LoadState.Idle),
StateChange(LoadType.START, LoadState.Loading),
StateChange(LoadType.START, LoadState.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(LoadType.END, Page.empty<Int, String>())),
consumer.takeResults()
)
assertEquals(
listOf(StateChange(LoadType.END, LoadState.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(LoadType.START, Page.empty<Int, String>())),
consumer.takeResults()
)
assertEquals(
listOf(StateChange(LoadType.START, LoadState.Done)),
consumer.takeStateChanges()
)
}
}