1 /* 2 * Copyright 2020 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 17 package androidx.paging 18 19 import kotlin.coroutines.CoroutineContext 20 import kotlin.coroutines.EmptyCoroutineContext 21 import kotlinx.coroutines.delay 22 import kotlinx.coroutines.withContext 23 24 /** 25 * [PagingSource] for testing which pages through a list of conesecutive integers from 0..99 where 26 * position == key == value. 27 * 28 * Note: This class has a delay of 1000ms is built into its load method and is meant to be used with 29 * APIs from [kotlinx.coroutines.test.DelayController]. 30 */ 31 class TestPagingSource( 32 counted: Boolean = true, 33 override val jumpingSupported: Boolean = true, 34 val items: List<Int> = ITEMS, 35 private val loadDelay: Long = 1000, 36 private val loadContext: CoroutineContext = EmptyCoroutineContext, 37 private val placeholdersEnabled: Boolean = true, 38 ) : PagingSource<Int, Int>() { 39 var errorNextLoad = false 40 var nextLoadResult: LoadResult<Int, Int>? = null 41 42 var getRefreshKeyResult: Int? = null 43 val getRefreshKeyCalls = mutableListOf<PagingState<Int, Int>>() 44 val loadedPages = mutableListOf<LoadResult.Page<Int, Int>>() 45 46 init { 47 if (!counted) { 48 throw NotImplementedError( 49 "TODO: Implement this for uncounted case, and add " + 50 "appropriate test cases to PageFetcher, Pager, and PagerState." 51 ) 52 } 53 } 54 55 override val keyReuseSupported: Boolean 56 get() = true 57 loadnull58 override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Int> { 59 // This delay allows tests running within DelayController APIs to control the order of 60 // execution of events. 61 delay(loadDelay) 62 63 return withContext(loadContext) { getLoadResult(params) } 64 } 65 getLoadResultnull66 private fun getLoadResult(params: LoadParams<Int>): LoadResult<Int, Int> { 67 val key = params.key ?: 0 68 69 val isPrepend = params is LoadParams.Prepend 70 val start = (if (isPrepend) key - params.loadSize + 1 else key).coerceAtLeast(0) 71 val end = (if (isPrepend) key + 1 else key + params.loadSize).coerceAtMost(items.size) 72 73 if (errorNextLoad) { 74 errorNextLoad = false 75 return LoadResult.Error(LOAD_ERROR) 76 } 77 78 val nextLoadResult = nextLoadResult 79 if (nextLoadResult != null) { 80 this.nextLoadResult = null 81 return nextLoadResult 82 } 83 84 return LoadResult.Page( 85 items.subList(start, end), 86 if (start > 0) start - 1 else null, 87 if (end < items.size) end else null, 88 if (placeholdersEnabled) start else Int.MIN_VALUE, 89 if (placeholdersEnabled) (items.size - end) else Int.MIN_VALUE 90 ) 91 .also { loadedPages.add(it) } 92 } 93 getRefreshKeynull94 override fun getRefreshKey(state: PagingState<Int, Int>): Int? { 95 getRefreshKeyCalls.add(state) 96 return getRefreshKeyResult ?: state.anchorPosition 97 } 98 99 companion object { <lambda>null100 val ITEMS = List(100) { it } 101 val LOAD_ERROR = Exception("Exception from TestPagingSource.errorNextLoad") 102 } 103 } 104