1 /* <lambda>null2 * Copyright 2019 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 androidx.paging.LoadType.PREPEND 20 import androidx.paging.LoadType.REFRESH 21 import androidx.paging.PageEvent.Drop 22 import kotlin.coroutines.EmptyCoroutineContext 23 import kotlin.test.Test 24 import kotlin.test.assertEquals 25 import kotlin.test.assertFailsWith 26 import kotlin.test.assertSame 27 import kotlinx.coroutines.ExperimentalCoroutinesApi 28 import kotlinx.coroutines.test.UnconfinedTestDispatcher 29 import kotlinx.coroutines.test.runTest 30 31 internal fun <T : Any> adjacentInsertEvent( 32 isPrepend: Boolean, 33 page: List<T>, 34 originalPageOffset: Int, 35 placeholdersRemaining: Int 36 ) = 37 if (isPrepend) { 38 localPrepend( 39 pages = listOf(TransformablePage(originalPageOffset = originalPageOffset, data = page)), 40 placeholdersBefore = placeholdersRemaining, 41 ) 42 } else { 43 localAppend( 44 pages = listOf(TransformablePage(originalPageOffset = originalPageOffset, data = page)), 45 placeholdersAfter = placeholdersRemaining, 46 ) 47 } 48 49 @OptIn(ExperimentalCoroutinesApi::class) 50 class PageEventTest { 51 @Test placeholdersExceptionnull52 fun placeholdersException() { 53 assertFailsWith<IllegalArgumentException> { 54 localRefresh<Char>( 55 pages = listOf(), 56 placeholdersBefore = 1, 57 placeholdersAfter = -1, 58 ) 59 } 60 assertFailsWith<IllegalArgumentException> { 61 localRefresh<Char>( 62 pages = listOf(), 63 placeholdersBefore = -1, 64 placeholdersAfter = 1, 65 ) 66 } 67 } 68 69 @Test dropTypenull70 fun dropType() { 71 assertFailsWith<IllegalArgumentException> { 72 Drop<Char>( 73 loadType = REFRESH, 74 minPageOffset = 0, 75 maxPageOffset = 0, 76 placeholdersRemaining = 4 77 ) 78 } 79 } 80 81 @Test dropRangenull82 fun dropRange() { 83 assertFailsWith<IllegalArgumentException> { 84 Drop<Char>( 85 loadType = REFRESH, 86 minPageOffset = 2, 87 maxPageOffset = 0, 88 placeholdersRemaining = 4 89 ) 90 } 91 } 92 93 @Test dropPlaceholdersnull94 fun dropPlaceholders() { 95 assertFailsWith<IllegalArgumentException> { 96 Drop<Char>( 97 loadType = REFRESH, 98 minPageOffset = 0, 99 maxPageOffset = 0, 100 placeholdersRemaining = -1 101 ) 102 } 103 } 104 105 @Test dropTransformnull106 fun dropTransform() = 107 runTest(UnconfinedTestDispatcher()) { 108 val drop = 109 Drop<Char>( 110 loadType = PREPEND, 111 minPageOffset = 0, 112 maxPageOffset = 0, 113 placeholdersRemaining = 0 114 ) 115 116 assertSame(drop, drop.map { it + 1 }) 117 assertSame(drop, drop.flatMap { listOf(it, it) }) 118 assertSame(drop, drop.filter { false }) 119 } 120 121 @Test stateTransformnull122 fun stateTransform() = 123 runTest(UnconfinedTestDispatcher()) { 124 val state = localLoadStateUpdate<Char>(refreshLocal = LoadState.Loading) 125 126 assertSame(state, state.map { it + 1 }) 127 assertSame(state, state.flatMap { listOf(it, it) }) 128 assertSame(state, state.filter { false }) 129 } 130 131 @Test insertMapnull132 fun insertMap() = 133 runTest(UnconfinedTestDispatcher()) { 134 val insert = 135 localAppend( 136 pages = listOf(TransformablePage(listOf('a', 'b'))), 137 placeholdersAfter = 4, 138 ) 139 assertEquals( 140 localAppend( 141 pages = listOf(TransformablePage(listOf("a", "b"))), 142 placeholdersAfter = 4, 143 ), 144 insert.map { it.toString() } 145 ) 146 147 assertEquals(insert, insert.map { it.toString() }.map { it[0] }) 148 } 149 150 @Test insertMapTransformednull151 fun insertMapTransformed() = 152 runTest(UnconfinedTestDispatcher()) { 153 assertEquals( 154 localAppend( 155 pages = 156 listOf( 157 TransformablePage( 158 originalPageOffsets = intArrayOf(0), 159 data = listOf("a", "b"), 160 hintOriginalPageOffset = 0, 161 hintOriginalIndices = listOf(0, 2) 162 ) 163 ), 164 placeholdersAfter = 4, 165 ), 166 localAppend( 167 pages = 168 listOf( 169 TransformablePage( 170 originalPageOffsets = intArrayOf(0), 171 data = listOf("a", "b"), 172 hintOriginalPageOffset = 0, 173 hintOriginalIndices = listOf(0, 2) 174 ) 175 ), 176 placeholdersAfter = 4, 177 ) 178 .map { it.toString() } 179 ) 180 } 181 182 @Test insertFilternull183 fun insertFilter() = 184 runTest(UnconfinedTestDispatcher()) { 185 val insert = 186 localAppend( 187 pages = listOf(TransformablePage(listOf('a', 'b', 'c', 'd'))), 188 placeholdersAfter = 4, 189 ) 190 191 // filter out C 192 val insertNoC = insert.filter { it != 'c' } 193 194 assertEquals( 195 localAppend( 196 pages = 197 listOf( 198 TransformablePage( 199 originalPageOffsets = intArrayOf(0), 200 data = listOf('a', 'b', 'd'), 201 hintOriginalPageOffset = 0, 202 hintOriginalIndices = listOf(0, 1, 3) 203 ) 204 ), 205 placeholdersAfter = 4, 206 ), 207 insertNoC 208 ) 209 210 // now filter out A, to validate filtration when lookup present 211 assertEquals( 212 localAppend( 213 pages = 214 listOf( 215 TransformablePage( 216 originalPageOffsets = intArrayOf(0), 217 data = listOf('b', 'd'), 218 hintOriginalPageOffset = 0, 219 hintOriginalIndices = listOf(1, 3) 220 ) 221 ), 222 placeholdersAfter = 4, 223 ), 224 insertNoC.filter { it != 'a' } 225 ) 226 } 227 228 @Test insertFlatMapnull229 fun insertFlatMap() = 230 runTest(UnconfinedTestDispatcher()) { 231 val insert = 232 localAppend( 233 pages = listOf(TransformablePage(listOf('a', 'b'))), 234 placeholdersAfter = 4, 235 ) 236 237 val flatMapped = insert.flatMap { listOf("${it}1", "${it}2") } 238 239 assertEquals( 240 localAppend( 241 pages = 242 listOf( 243 TransformablePage( 244 originalPageOffsets = intArrayOf(0), 245 data = listOf("a1", "a2", "b1", "b2"), 246 hintOriginalPageOffset = 0, 247 hintOriginalIndices = listOf(0, 0, 1, 1) 248 ) 249 ), 250 placeholdersAfter = 4, 251 ), 252 flatMapped 253 ) 254 255 val flatMappedAgain = flatMapped.flatMap { listOf(it, "-") } 256 257 assertEquals( 258 localAppend( 259 pages = 260 listOf( 261 TransformablePage( 262 originalPageOffsets = intArrayOf(0), 263 data = listOf("a1", "-", "a2", "-", "b1", "-", "b2", "-"), 264 hintOriginalPageOffset = 0, 265 hintOriginalIndices = listOf(0, 0, 0, 0, 1, 1, 1, 1) 266 ) 267 ), 268 placeholdersAfter = 4, 269 ), 270 flatMappedAgain 271 ) 272 } 273 274 class StaticPagingData { 275 companion object { initParametersnull276 fun initParameters() = 277 listOf( 278 listOf("a", "b", "c"), 279 emptyList(), 280 ) 281 } 282 283 private val presenter = TestPagingDataPresenter<String>(EmptyCoroutineContext) 284 285 @Test fun map_nonEmpty() = map(PagingData.from(listOf("a", "b", "c"))) 286 287 @Test fun map_empty() = map(PagingData.empty()) 288 289 private fun map(pagingData: PagingData<String>) = 290 runTest(UnconfinedTestDispatcher()) { 291 val transform = { it: String -> it + it } 292 presenter.collectFrom(pagingData) 293 val originalItems = presenter.snapshot().items 294 val expectedItems = originalItems.map(transform) 295 val transformedPagingData = pagingData.map { transform(it) } 296 presenter.collectFrom(transformedPagingData) 297 assertEquals(expectedItems, presenter.snapshot().items) 298 } 299 flatMap_nonEmptynull300 @Test fun flatMap_nonEmpty() = flatMap(PagingData.from(listOf("a", "b", "c"))) 301 302 @Test fun flatMap_empty() = flatMap(PagingData.empty()) 303 304 private fun flatMap(pagingData: PagingData<String>) = 305 runTest(UnconfinedTestDispatcher()) { 306 val transform = { it: String -> listOf(it, it) } 307 presenter.collectFrom(pagingData) 308 val originalItems = presenter.snapshot().items 309 val expectedItems = originalItems.flatMap(transform) 310 val transformedPagingData = pagingData.flatMap { transform(it) } 311 presenter.collectFrom(transformedPagingData) 312 assertEquals(expectedItems, presenter.snapshot().items) 313 } 314 filter_nonEmptynull315 @Test fun filter_nonEmpty() = filter(PagingData.from(listOf("a", "b", "c"))) 316 317 @Test fun filter_empty() = filter(PagingData.empty()) 318 319 private fun filter(pagingData: PagingData<String>) = 320 runTest(UnconfinedTestDispatcher()) { 321 val predicate = { it: String -> it != "b" } 322 presenter.collectFrom(pagingData) 323 val originalItems = presenter.snapshot().items 324 val expectedItems = originalItems.filter(predicate) 325 val transformedPagingData = pagingData.filter { predicate(it) } 326 presenter.collectFrom(transformedPagingData) 327 assertEquals(expectedItems, presenter.snapshot().items) 328 } 329 330 @Test insertSeparators_nonEmptynull331 fun insertSeparators_nonEmpty() = insertSeparators(PagingData.from(listOf("a", "b", "c"))) 332 333 @Test fun insertSeparators_empty() = insertSeparators(PagingData.empty()) 334 335 private fun insertSeparators(pagingData: PagingData<String>) = 336 runTest(UnconfinedTestDispatcher()) { 337 val transform = { left: String?, right: String? -> 338 if (left == null || right == null) null else "|" 339 } 340 presenter.collectFrom(pagingData) 341 val originalItems = presenter.snapshot().items 342 val expectedItems = 343 originalItems.flatMapIndexed { index, s -> 344 val result = mutableListOf<String>() 345 if (index == 0) { 346 transform(null, s)?.let(result::add) 347 } 348 result.add(s) 349 transform(s, originalItems.getOrNull(index + 1))?.let(result::add) 350 if (index == originalItems.lastIndex) { 351 transform(s, null)?.let(result::add) 352 } 353 result 354 } 355 val transformedPagingData = 356 pagingData.insertSeparators { left, right -> transform(left, right) } 357 presenter.collectFrom(transformedPagingData) 358 assertEquals(expectedItems, presenter.snapshot().items) 359 } 360 } 361 } 362