[go: nahoru, domu]

blob: bd88141acf2f5b08fd7c4054ddb271e140b655d6 [file] [log] [blame]
/*
* Copyright 2020 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.LoadState.NotLoading
import androidx.paging.LoadType.APPEND
import androidx.paging.LoadType.PREPEND
import androidx.paging.LoadType.REFRESH
import androidx.paging.PageEvent.Drop
import androidx.paging.PageEvent.Insert.Companion.Append
import androidx.paging.PageEvent.Insert.Companion.Prepend
import androidx.paging.PageEvent.Insert.Companion.Refresh
import androidx.paging.PageEvent.LoadStateUpdate
import androidx.paging.TerminalSeparatorType.FULLY_COMPLETE
import androidx.paging.TerminalSeparatorType.SOURCE_COMPLETE
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.test.runBlockingTest
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import kotlin.test.assertEquals
private fun <T : Any> List<PageEvent<T>>.getItems() = mapNotNull { event ->
when (event) {
is PageEvent.Insert<T> -> event.pages.map { it.data }
else -> null
}
}
/**
* Simpler assert which only concerns itself with page data, all other event types are ignored.
*/
private fun <T : Any> assertInsertData(
expected: List<PageEvent<T>>,
actual: List<PageEvent<T>>
) {
@Suppress("UNCHECKED_CAST")
assertEquals(
expected.getItems(),
actual.getItems()
)
}
@OptIn(ExperimentalCoroutinesApi::class)
@RunWith(JUnit4::class)
class SeparatorsTest {
@Test
fun refreshFull() = runBlockingTest {
assertThat(
flowOf(
Refresh(
pages = listOf(
listOf("a2", "b1"),
listOf("c1", "c2")
).toTransformablePages(),
placeholdersBefore = 0,
placeholdersAfter = 1,
combinedLoadStates = localLoadStatesOf()
)
).insertEventSeparators(
terminalSeparatorType = FULLY_COMPLETE,
generator = LETTER_SEPARATOR_GENERATOR
).toList()
).isEqualTo(
listOf(
Refresh(
pages = listOf(
TransformablePage(
originalPageOffsets = intArrayOf(0),
data = listOf("a2", "B", "b1"),
hintOriginalPageOffset = 0,
hintOriginalIndices = listOf(0, 1, 1)
),
TransformablePage(
originalPageOffsets = intArrayOf(0, 1),
data = listOf("C"),
hintOriginalPageOffset = 1,
hintOriginalIndices = listOf(0)
),
TransformablePage(
originalPageOffset = 1,
data = listOf("c1", "c2")
)
),
placeholdersBefore = 0,
placeholdersAfter = 1,
combinedLoadStates = localLoadStatesOf()
)
)
)
}
@Test
fun refreshStartFull() = runBlockingTest {
val refresh = Refresh(
pages = listOf(
listOf("c1")
).toTransformablePages(),
placeholdersBefore = 0,
placeholdersAfter = 1,
combinedLoadStates = localLoadStatesOf()
)
assertThat(
flowOf(
refresh,
Prepend(
pages = listOf(
listOf("a1", "b1"),
listOf("b2", "b3")
).toTransformablePages(2),
placeholdersBefore = 1,
combinedLoadStates = localLoadStatesOf()
)
).insertEventSeparators(
terminalSeparatorType = FULLY_COMPLETE,
generator = LETTER_SEPARATOR_GENERATOR
).toList()
).isEqualTo(
listOf(
refresh,
Prepend(
pages = listOf(
TransformablePage(
originalPageOffsets = intArrayOf(-2),
data = listOf("a1", "B", "b1"),
hintOriginalPageOffset = -2,
hintOriginalIndices = listOf(0, 1, 1)
),
TransformablePage(
originalPageOffset = -1,
data = listOf("b2", "b3")
),
TransformablePage(
originalPageOffsets = intArrayOf(-1, 0),
data = listOf("C"),
hintOriginalPageOffset = -1,
hintOriginalIndices = listOf(1)
)
),
placeholdersBefore = 1,
combinedLoadStates = localLoadStatesOf()
)
)
)
}
@Test
fun refreshEndFull() = runBlockingTest {
val refresh = Refresh(
pages = listOf(
listOf("a1", "a2")
).toTransformablePages(),
placeholdersBefore = 0,
placeholdersAfter = 1,
combinedLoadStates = localLoadStatesOf()
)
assertThat(
flowOf(
refresh,
Append(
pages = listOf(
listOf("c1", "d1"),
listOf("d2", "d3")
).toTransformablePages(-1),
placeholdersAfter = 1,
combinedLoadStates = localLoadStatesOf()
)
).insertEventSeparators(
terminalSeparatorType = FULLY_COMPLETE,
generator = LETTER_SEPARATOR_GENERATOR
).toList()
).isEqualTo(
listOf(
refresh,
Append(
pages = listOf(
TransformablePage(
originalPageOffsets = intArrayOf(0, 1),
data = listOf("C"),
hintOriginalPageOffset = 1,
hintOriginalIndices = listOf(0)
),
TransformablePage(
originalPageOffsets = intArrayOf(1),
data = listOf("c1", "D", "d1"),
hintOriginalPageOffset = 1,
hintOriginalIndices = listOf(0, 1, 1)
),
TransformablePage(
originalPageOffset = 2,
data = listOf("d2", "d3")
)
),
placeholdersAfter = 1,
combinedLoadStates = localLoadStatesOf()
)
)
)
}
@Test
fun refreshDropFull() = runBlockingTest {
assertThat(
flowOf(
Refresh(
pages = listOf(
listOf("a1"),
listOf("a2")
).toTransformablePages(),
placeholdersBefore = 0,
placeholdersAfter = 1,
combinedLoadStates = localLoadStatesOf()
),
Drop(APPEND, 1, 1, 4)
).insertEventSeparators(
terminalSeparatorType = FULLY_COMPLETE,
generator = LETTER_SEPARATOR_GENERATOR
).toList()
).isEqualTo(
listOf(
Refresh(
pages = listOf(
TransformablePage(
originalPageOffset = 0,
data = listOf("a1")
),
TransformablePage(
originalPageOffset = 1,
data = listOf("a2")
)
),
placeholdersBefore = 0,
placeholdersAfter = 1,
combinedLoadStates = localLoadStatesOf()
),
Drop<String>(APPEND, 1, 1, 4)
)
)
}
private fun refresh(
pages: List<String?>,
prepend: LoadState = NotLoading.Incomplete,
append: LoadState = NotLoading.Incomplete
) = Refresh(
pages = when {
pages.isEmpty() -> listOf(TransformablePage.empty())
else -> pages.map {
if (it != null) listOf(it)
else listOf()
}.toTransformablePages()
},
placeholdersBefore = 0,
placeholdersAfter = 1,
combinedLoadStates = localLoadStatesOf(prependLocal = prepend, appendLocal = append)
)
private fun prepend(
pages: List<String?>,
prepend: LoadState = NotLoading.Incomplete
) = Prepend(
pages = pages.map {
if (it != null) listOf(it) else listOf()
}.toTransformablePages(),
placeholdersBefore = 0,
combinedLoadStates = localLoadStatesOf(prependLocal = prepend)
)
private fun append(
pages: List<String?>,
append: LoadState = NotLoading.Incomplete
) = Append(
pages = pages.map {
if (it != null) listOf(it) else listOf()
}.toTransformablePages(),
placeholdersAfter = 0,
combinedLoadStates = localLoadStatesOf(appendLocal = append)
)
private fun drop(loadType: LoadType, minPageOffset: Int, maxPageOffset: Int) = Drop<String>(
loadType = loadType,
minPageOffset = minPageOffset,
maxPageOffset = maxPageOffset,
placeholdersRemaining = 0
)
@Test
fun refreshNoop() = runBlockingTest {
assertInsertData(
listOf(
refresh(pages = listOf("a1"))
),
flowOf(
refresh(pages = listOf("a1"))
).insertEventSeparators(
terminalSeparatorType = FULLY_COMPLETE,
generator = LETTER_SEPARATOR_GENERATOR
).toList()
)
}
@Test
fun refreshStartDone() = runBlockingTest {
assertInsertData(
listOf(
refresh(pages = listOf("A", "a1"))
),
flowOf(
refresh(pages = listOf("a1"), prepend = NotLoading.Complete)
).insertEventSeparators(
terminalSeparatorType = FULLY_COMPLETE,
generator = LETTER_SEPARATOR_GENERATOR
).toList()
)
}
@Test
fun refreshEndDone() = runBlockingTest {
assertInsertData(
listOf(
refresh(pages = listOf("a1", "END"))
),
flowOf(
refresh(pages = listOf("a1"), append = NotLoading.Complete)
).insertEventSeparators(
terminalSeparatorType = FULLY_COMPLETE,
generator = LETTER_SEPARATOR_GENERATOR
).toList()
)
}
@Test
fun refreshBothDone() = runBlockingTest {
assertInsertData(
listOf(
refresh(pages = listOf("A", "a1", "END"))
),
flowOf(
refresh(
pages = listOf("a1"),
prepend = NotLoading.Complete,
append = NotLoading.Complete
)
).insertEventSeparators(
terminalSeparatorType = FULLY_COMPLETE,
generator = LETTER_SEPARATOR_GENERATOR
).toList()
)
}
@Test
fun refreshEmptyNoop() = runBlockingTest {
assertInsertData(
listOf(
// should only get one item when initial page is endOfPaginationReached is false on
// both sides.
refresh(pages = listOf<String>())
),
flowOf(
refresh(pages = listOf())
).insertEventSeparators(
terminalSeparatorType = FULLY_COMPLETE,
generator = LETTER_SEPARATOR_GENERATOR
).toList()
)
}
@Test
fun refreshEmptyStartDone() = runBlockingTest {
assertInsertData(
listOf(
// not enough data to create separators yet
refresh(pages = listOf()),
// now we can, since we know what's on the other side
append(pages = listOf("A", "a1")),
// empty should be noop, since it's not append = NotLoading(true)
append(pages = listOf()),
// append = NotLoading(true), so resolve final separator
append(pages = listOf("END"))
),
flowOf(
refresh(pages = listOf(), prepend = NotLoading.Complete),
append(pages = listOf("a1")),
append(pages = listOf()),
append(pages = listOf(), append = NotLoading.Complete)
).insertEventSeparators(
terminalSeparatorType = FULLY_COMPLETE,
generator = LETTER_SEPARATOR_GENERATOR
).toList()
)
}
@Test
fun refreshEmptyEndDone() = runBlockingTest {
assertInsertData(
listOf(
// not enough data to create separators yet
refresh(pages = listOf()),
// now we can, since we know what's on the other side
prepend(pages = listOf("a1", "END")),
// empty should be noop, since it's not prepend = NotLoading(true)
prepend(pages = listOf()),
// prepend = NotLoading(true), so resolve final separator
prepend(pages = listOf("A"))
),
flowOf(
refresh(pages = listOf(), append = NotLoading.Complete),
prepend(pages = listOf("a1")),
prepend(pages = listOf()),
prepend(pages = listOf(), prepend = NotLoading.Complete)
).insertEventSeparators(
terminalSeparatorType = FULLY_COMPLETE,
generator = LETTER_SEPARATOR_GENERATOR
).toList()
)
}
@Test
fun emptyDropResetsDeferredFrontSeparator() = runBlockingTest {
assertInsertData(
listOf(
// not enough data to create separators yet
refresh(pages = listOf()),
// don't insert a separator, since a drop occurred
append(pages = listOf("a1")),
// but now add the separator, since start is done again
prepend(pages = listOf("A"))
),
flowOf(
refresh(pages = listOf(), prepend = NotLoading.Complete),
drop(loadType = PREPEND, minPageOffset = 0, maxPageOffset = 0),
append(pages = listOf("a1")),
prepend(pages = listOf(), prepend = NotLoading.Complete)
).insertEventSeparators(
terminalSeparatorType = FULLY_COMPLETE,
generator = LETTER_SEPARATOR_GENERATOR
).toList()
)
}
@Test
fun emptyDropResetsDeferredEndSeparator() = runBlockingTest {
assertInsertData(
listOf(
// not enough data to create separators yet
refresh(pages = listOf()),
// don't insert a separator, since a drop occurred
prepend(pages = listOf("a1")),
// but now add the separator, since end is done again
append(pages = listOf("END"))
),
flowOf(
refresh(pages = listOf(), append = NotLoading.Complete),
drop(loadType = APPEND, minPageOffset = 0, maxPageOffset = 0),
prepend(pages = listOf("a1")),
append(pages = listOf(), append = NotLoading.Complete)
).insertEventSeparators(
terminalSeparatorType = FULLY_COMPLETE,
generator = LETTER_SEPARATOR_GENERATOR
).toList()
)
}
@Test
fun refreshEmptyStart() = runBlockingTest {
assertInsertData(
listOf(
refresh(pages = listOf()),
// not enough data to create separators yet
prepend(pages = listOf("a1"))
),
flowOf(
refresh(pages = listOf()),
prepend(pages = listOf("a1"))
).insertEventSeparators(
terminalSeparatorType = FULLY_COMPLETE,
generator = LETTER_SEPARATOR_GENERATOR
).toList()
)
}
@Test
fun refreshEmptyEnd() = runBlockingTest {
assertInsertData(
listOf(
refresh(pages = listOf()),
// not enough data to create separators yet
append(pages = listOf("a1"))
),
flowOf(
refresh(pages = listOf()),
append(pages = listOf("a1"))
).insertEventSeparators(
terminalSeparatorType = FULLY_COMPLETE,
generator = LETTER_SEPARATOR_GENERATOR
).toList()
)
}
@Test
fun refreshEmptyStartDropFull() = runBlockingTest {
// when start terminal separator is inserted, we need to drop count*2 + 1
assertThat(
flowOf(
refresh(
pages = listOf("a1", "b1"),
prepend = NotLoading.Complete
),
drop(loadType = PREPEND, minPageOffset = 0, maxPageOffset = 0)
).insertEventSeparators(
terminalSeparatorType = FULLY_COMPLETE,
generator = LETTER_SEPARATOR_GENERATOR
).toList()
).isEqualTo(
listOf(
Refresh(
pages = listOf(
TransformablePage(
originalPageOffsets = intArrayOf(0),
data = listOf("A"),
hintOriginalPageOffset = 0,
hintOriginalIndices = listOf(0)
),
TransformablePage(
originalPageOffset = 0,
data = listOf("a1")
),
TransformablePage(
originalPageOffsets = intArrayOf(0, 1),
data = listOf("B"),
hintOriginalPageOffset = 1,
hintOriginalIndices = listOf(0)
),
TransformablePage(
originalPageOffset = 1,
data = listOf("b1")
)
),
placeholdersBefore = 0,
placeholdersAfter = 1,
combinedLoadStates = localLoadStatesOf(prependLocal = NotLoading.Complete)
),
drop(loadType = PREPEND, minPageOffset = 0, maxPageOffset = 0)
)
)
}
@Test
fun refreshEmptyEndDropFull() = runBlockingTest {
// when end terminal separator is inserted, we need to drop count*2 + 1
assertThat(
flowOf(
refresh(
pages = listOf("a1", "b1"),
append = NotLoading.Complete
),
drop(loadType = APPEND, minPageOffset = 0, maxPageOffset = 0)
).insertEventSeparators(
terminalSeparatorType = FULLY_COMPLETE,
generator = LETTER_SEPARATOR_GENERATOR
).toList()
).isEqualTo(
listOf(
Refresh(
pages = listOf(
TransformablePage(
originalPageOffset = 0,
data = listOf("a1")
),
TransformablePage(
originalPageOffsets = intArrayOf(0, 1),
data = listOf("B"),
hintOriginalPageOffset = 1,
hintOriginalIndices = listOf(0)
),
TransformablePage(
originalPageOffset = 1,
data = listOf("b1")
),
TransformablePage(
originalPageOffsets = intArrayOf(1),
data = listOf("END"),
hintOriginalPageOffset = 1,
hintOriginalIndices = listOf(0)
)
),
placeholdersBefore = 0,
placeholdersAfter = 1,
combinedLoadStates = localLoadStatesOf(appendLocal = NotLoading.Complete)
),
drop(loadType = APPEND, minPageOffset = 0, maxPageOffset = 0)
)
)
}
@Test
fun types() = runBlockingTest {
open class Base
data class PrimaryType(val string: String) : Base()
data class SeparatorType(val string: String) : Base()
assertInsertData(
listOf(
Refresh(
pages = listOf(
listOf(
PrimaryType("a1"),
SeparatorType("B"),
PrimaryType("b1")
)
).toTransformablePages(),
placeholdersBefore = 0,
placeholdersAfter = 1,
combinedLoadStates = localLoadStatesOf()
)
),
flowOf(
Refresh(
pages = listOf(listOf(PrimaryType("a1"), PrimaryType("b1")))
.toTransformablePages(),
placeholdersBefore = 0,
placeholdersAfter = 1,
combinedLoadStates = localLoadStatesOf()
)
).insertEventSeparators(terminalSeparatorType = FULLY_COMPLETE) { before, after ->
return@insertEventSeparators (
if (before != null && after != null) {
SeparatorType("B")
} else null
)
}.toList()
)
}
@Test
fun refreshEmptyPagesExceptOne() = runBlockingTest {
assertThat(
flowOf(
Refresh(
pages = listOf(
listOf(),
listOf(),
listOf("a2", "b1"),
listOf(),
listOf()
).toTransformablePages(),
placeholdersBefore = 0,
placeholdersAfter = 1,
combinedLoadStates = localLoadStatesOf()
)
).insertEventSeparators(
terminalSeparatorType = FULLY_COMPLETE,
generator = LETTER_SEPARATOR_GENERATOR
).toList()
).isEqualTo(
listOf(
Refresh(
pages = listOf(
TransformablePage(
originalPageOffset = 0,
data = listOf()
),
TransformablePage(
originalPageOffset = 1,
data = listOf()
),
TransformablePage(
originalPageOffsets = intArrayOf(2),
data = listOf("a2", "B", "b1"),
hintOriginalPageOffset = 2,
hintOriginalIndices = listOf(0, 1, 1)
),
TransformablePage(
originalPageOffset = 3,
data = listOf()
),
TransformablePage(
originalPageOffset = 4,
data = listOf()
)
),
placeholdersBefore = 0,
placeholdersAfter = 1,
combinedLoadStates = localLoadStatesOf()
)
)
)
}
@Test
fun refreshSparsePages() = runBlockingTest {
assertThat(
flowOf(
Refresh(
pages = listOf(
listOf(),
listOf("a2", "b1"),
listOf(),
listOf("c1", "c2"),
listOf()
).toTransformablePages(),
placeholdersBefore = 0,
placeholdersAfter = 1,
combinedLoadStates = localLoadStatesOf()
)
).insertEventSeparators(
terminalSeparatorType = FULLY_COMPLETE,
generator = LETTER_SEPARATOR_GENERATOR
).toList()
).isEqualTo(
listOf(
Refresh(
pages = listOf(
TransformablePage(
originalPageOffset = 0,
data = listOf()
),
TransformablePage(
originalPageOffsets = intArrayOf(1),
data = listOf("a2", "B", "b1"),
hintOriginalPageOffset = 1,
hintOriginalIndices = listOf(0, 1, 1)
),
TransformablePage(
originalPageOffset = 2,
data = listOf()
),
TransformablePage(
originalPageOffsets = intArrayOf(1, 3),
data = listOf("C"),
hintOriginalPageOffset = 3,
hintOriginalIndices = listOf(0)
),
TransformablePage(
originalPageOffset = 3,
data = listOf("c1", "c2")
),
TransformablePage(
originalPageOffset = 4,
data = listOf()
)
),
placeholdersBefore = 0,
placeholdersAfter = 1,
combinedLoadStates = localLoadStatesOf()
)
)
)
}
@Test
fun prependEmptyPagesExceptOne() = runBlockingTest {
val refresh = Refresh(
pages = listOf(
listOf("c1", "c2")
).toTransformablePages(),
placeholdersBefore = 2,
placeholdersAfter = 0,
combinedLoadStates = localLoadStatesOf()
)
assertThat(
flowOf(
refresh,
Prepend(
pages = listOf(
listOf(),
listOf(),
listOf("a1", "b1"),
listOf(),
listOf()
).toTransformablePages(5),
placeholdersBefore = 0,
combinedLoadStates = localLoadStatesOf()
)
).insertEventSeparators(
terminalSeparatorType = FULLY_COMPLETE,
generator = LETTER_SEPARATOR_GENERATOR
).toList()
).isEqualTo(
listOf(
refresh,
Prepend(
pages = listOf(
TransformablePage(
originalPageOffset = -5,
data = listOf()
),
TransformablePage(
originalPageOffset = -4,
data = listOf()
),
TransformablePage(
originalPageOffsets = intArrayOf(-3),
data = listOf("a1", "B", "b1"),
hintOriginalPageOffset = -3,
hintOriginalIndices = listOf(0, 1, 1)
),
TransformablePage(
originalPageOffsets = intArrayOf(-3, 0),
data = listOf("C"),
hintOriginalPageOffset = -3,
hintOriginalIndices = listOf(1)
),
TransformablePage(
originalPageOffset = -2,
data = listOf()
),
TransformablePage(
originalPageOffset = -1,
data = listOf()
)
),
placeholdersBefore = 0,
combinedLoadStates = localLoadStatesOf()
)
)
)
}
@Test
fun prependSparsePages() = runBlockingTest {
val refresh = Refresh(
pages = listOf(
listOf("d1", "d2")
).toTransformablePages(),
placeholdersBefore = 0,
placeholdersAfter = 4,
combinedLoadStates = localLoadStatesOf()
)
assertThat(
flowOf(
refresh,
Prepend(
pages = listOf(
listOf(),
listOf("a1", "b1"),
listOf(),
listOf("c1", "c2"),
listOf()
).toTransformablePages(5),
placeholdersBefore = 0,
combinedLoadStates = localLoadStatesOf()
)
).insertEventSeparators(
terminalSeparatorType = FULLY_COMPLETE,
generator = LETTER_SEPARATOR_GENERATOR
).toList()
).isEqualTo(
listOf(
refresh,
Prepend(
pages = listOf(
TransformablePage(
originalPageOffset = -5,
data = listOf()
),
TransformablePage(
originalPageOffsets = intArrayOf(-4),
data = listOf("a1", "B", "b1"),
hintOriginalPageOffset = -4,
hintOriginalIndices = listOf(0, 1, 1)
),
TransformablePage(
originalPageOffset = -3,
data = listOf()
),
TransformablePage(
originalPageOffsets = intArrayOf(-4, -2),
data = listOf("C"),
hintOriginalPageOffset = -4,
hintOriginalIndices = listOf(1)
),
TransformablePage(
originalPageOffset = -2,
data = listOf("c1", "c2")
),
TransformablePage(
originalPageOffsets = intArrayOf(-2, 0),
data = listOf("D"),
hintOriginalPageOffset = -2,
hintOriginalIndices = listOf(1)
),
TransformablePage(
originalPageOffset = -1,
data = listOf()
)
),
placeholdersBefore = 0,
combinedLoadStates = localLoadStatesOf()
)
)
)
}
@Test
fun appendEmptyPagesExceptOne() = runBlockingTest {
val refresh = Refresh(
pages = listOf(
listOf("a1", "a2")
).toTransformablePages(),
placeholdersBefore = 0,
placeholdersAfter = 2,
combinedLoadStates = localLoadStatesOf()
)
assertThat(
flowOf(
refresh,
Append(
pages = listOf(
listOf(),
listOf(),
listOf("b1", "c1"),
listOf(),
listOf()
).toTransformablePages(-1),
placeholdersAfter = 0,
combinedLoadStates = localLoadStatesOf()
)
).insertEventSeparators(
terminalSeparatorType = FULLY_COMPLETE,
generator = LETTER_SEPARATOR_GENERATOR
).toList()
).isEqualTo(
listOf(
refresh,
Append(
pages = listOf(
TransformablePage(
originalPageOffset = 1,
data = listOf()
),
TransformablePage(
originalPageOffset = 2,
data = listOf()
),
TransformablePage(
originalPageOffsets = intArrayOf(0, 3),
data = listOf("B"),
hintOriginalPageOffset = 3,
hintOriginalIndices = listOf(0)
),
TransformablePage(
originalPageOffsets = intArrayOf(3),
data = listOf("b1", "C", "c1"),
hintOriginalPageOffset = 3,
hintOriginalIndices = listOf(0, 1, 1)
),
TransformablePage(
originalPageOffset = 4,
data = listOf()
),
TransformablePage(
originalPageOffset = 5,
data = listOf()
)
),
placeholdersAfter = 0,
combinedLoadStates = localLoadStatesOf()
)
)
)
}
@Test
fun appendSparsePages() = runBlockingTest {
val refresh = Refresh(
pages = listOf(
listOf("a1", "a2")
).toTransformablePages(),
placeholdersBefore = 0,
placeholdersAfter = 4,
combinedLoadStates = localLoadStatesOf()
)
assertThat(
flowOf(
refresh,
Append(
pages = listOf(
listOf(),
listOf("b1", "c1"),
listOf(),
listOf("d1", "d2"),
listOf()
).toTransformablePages(-1),
placeholdersAfter = 0,
combinedLoadStates = localLoadStatesOf()
)
).insertEventSeparators(
terminalSeparatorType = FULLY_COMPLETE,
generator = LETTER_SEPARATOR_GENERATOR
).toList()
).isEqualTo(
listOf(
refresh,
Append(
pages = listOf(
TransformablePage(
originalPageOffset = 1,
data = listOf()
),
TransformablePage(
originalPageOffsets = intArrayOf(0, 2),
data = listOf("B"),
hintOriginalPageOffset = 2,
hintOriginalIndices = listOf(0)
),
TransformablePage(
originalPageOffsets = intArrayOf(2),
data = listOf("b1", "C", "c1"),
hintOriginalPageOffset = 2,
hintOriginalIndices = listOf(0, 1, 1)
),
TransformablePage(
originalPageOffset = 3,
data = listOf()
),
TransformablePage(
originalPageOffsets = intArrayOf(2, 4),
data = listOf("D"),
hintOriginalPageOffset = 4,
hintOriginalIndices = listOf(0)
),
TransformablePage(
originalPageOffset = 4,
data = listOf("d1", "d2")
),
TransformablePage(
originalPageOffset = 5,
data = listOf()
)
),
placeholdersAfter = 0,
combinedLoadStates = localLoadStatesOf()
)
)
)
}
@Test
fun remoteRefreshEndOfPaginationReached_fullyComplete() = runBlockingTest {
assertThat(
flowOf(
LoadStateUpdate(
loadType = REFRESH,
fromMediator = true,
loadState = LoadState.Loading
),
LoadStateUpdate(
loadType = REFRESH,
fromMediator = false,
loadState = LoadState.Loading
),
Refresh(
pages = listOf(listOf("a1")).toTransformablePages(),
placeholdersBefore = 1,
placeholdersAfter = 1,
combinedLoadStates = remoteLoadStatesOf(
appendLocal = NotLoading.Complete,
prependLocal = NotLoading.Complete,
)
),
LoadStateUpdate(
loadType = REFRESH,
fromMediator = true,
loadState = NotLoading(endOfPaginationReached = true)
),
LoadStateUpdate(
loadType = PREPEND,
fromMediator = true,
loadState = NotLoading(endOfPaginationReached = true)
),
LoadStateUpdate(
loadType = APPEND,
fromMediator = true,
loadState = NotLoading(endOfPaginationReached = true)
),
).insertEventSeparators(
terminalSeparatorType = FULLY_COMPLETE,
generator = LETTER_SEPARATOR_GENERATOR
).toList()
).isEqualTo(
listOf(
LoadStateUpdate(
loadType = REFRESH,
fromMediator = true,
loadState = LoadState.Loading
),
LoadStateUpdate(
loadType = REFRESH,
fromMediator = false,
loadState = LoadState.Loading
),
Refresh(
pages = listOf(listOf("a1")).toTransformablePages(),
placeholdersBefore = 1,
placeholdersAfter = 1,
combinedLoadStates = remoteLoadStatesOf(
appendLocal = NotLoading.Complete,
prependLocal = NotLoading.Complete,
)
),
LoadStateUpdate(
loadType = REFRESH,
fromMediator = true,
loadState = NotLoading(endOfPaginationReached = true)
),
Prepend(
pages = listOf(
TransformablePage(
originalPageOffsets = intArrayOf(0),
data = listOf("A"),
hintOriginalIndices = listOf(0),
hintOriginalPageOffset = 0,
),
),
placeholdersBefore = 1,
combinedLoadStates = remoteLoadStatesOf(
refresh = NotLoading.Complete,
prepend = NotLoading.Complete,
appendLocal = NotLoading.Complete,
prependLocal = NotLoading.Complete,
refreshRemote = NotLoading.Complete,
prependRemote = NotLoading.Complete,
),
),
Append(
pages = listOf(
TransformablePage(
originalPageOffsets = intArrayOf(0),
data = listOf("END"),
hintOriginalIndices = listOf(0),
hintOriginalPageOffset = 0,
),
),
placeholdersAfter = 1,
combinedLoadStates = remoteLoadStatesOf(
refresh = NotLoading.Complete,
prepend = NotLoading.Complete,
append = NotLoading.Complete,
appendLocal = NotLoading.Complete,
prependLocal = NotLoading.Complete,
refreshRemote = NotLoading.Complete,
prependRemote = NotLoading.Complete,
appendRemote = NotLoading.Complete,
),
),
)
)
}
@Test
fun remoteRefreshEndOfPaginationReached_sourceComplete() = runBlockingTest {
assertThat(
flowOf(
LoadStateUpdate(
loadType = REFRESH,
fromMediator = true,
loadState = LoadState.Loading
),
LoadStateUpdate(
loadType = REFRESH,
fromMediator = false,
loadState = LoadState.Loading
),
Refresh(
pages = listOf(listOf("a1")).toTransformablePages(),
placeholdersBefore = 1,
placeholdersAfter = 1,
combinedLoadStates = remoteLoadStatesOf(
appendLocal = NotLoading.Complete,
prependLocal = NotLoading.Complete,
)
),
LoadStateUpdate(
loadType = REFRESH,
fromMediator = true,
loadState = NotLoading(endOfPaginationReached = true)
),
LoadStateUpdate(
loadType = PREPEND,
fromMediator = true,
loadState = NotLoading(endOfPaginationReached = true)
),
LoadStateUpdate(
loadType = APPEND,
fromMediator = true,
loadState = NotLoading(endOfPaginationReached = true)
),
).insertEventSeparators(
terminalSeparatorType = SOURCE_COMPLETE,
generator = LETTER_SEPARATOR_GENERATOR
).toList()
).isEqualTo(
listOf(
LoadStateUpdate(
loadType = REFRESH,
fromMediator = true,
loadState = LoadState.Loading
),
LoadStateUpdate(
loadType = REFRESH,
fromMediator = false,
loadState = LoadState.Loading
),
Refresh(
pages = listOf(
TransformablePage(
originalPageOffsets = intArrayOf(0),
data = listOf("A"),
hintOriginalIndices = listOf(0),
hintOriginalPageOffset = 0,
),
TransformablePage(
originalPageOffset = 0,
data = listOf("a1"),
),
TransformablePage(
originalPageOffsets = intArrayOf(0),
data = listOf("END"),
hintOriginalIndices = listOf(0),
hintOriginalPageOffset = 0,
),
),
placeholdersBefore = 1,
placeholdersAfter = 1,
combinedLoadStates = remoteLoadStatesOf(
appendLocal = NotLoading.Complete,
prependLocal = NotLoading.Complete,
)
),
LoadStateUpdate(
loadType = REFRESH,
fromMediator = true,
loadState = NotLoading(endOfPaginationReached = true)
),
Prepend(
pages = listOf(),
placeholdersBefore = 1,
combinedLoadStates = remoteLoadStatesOf(
refresh = NotLoading.Complete,
prepend = NotLoading.Complete,
appendLocal = NotLoading.Complete,
prependLocal = NotLoading.Complete,
refreshRemote = NotLoading.Complete,
prependRemote = NotLoading.Complete,
),
),
Append(
pages = listOf(),
placeholdersAfter = 1,
combinedLoadStates = remoteLoadStatesOf(
refresh = NotLoading.Complete,
prepend = NotLoading.Complete,
append = NotLoading.Complete,
appendLocal = NotLoading.Complete,
prependLocal = NotLoading.Complete,
refreshRemote = NotLoading.Complete,
prependRemote = NotLoading.Complete,
appendRemote = NotLoading.Complete,
),
),
)
)
}
@Test
fun remotePrependEndOfPaginationReached_fullyComplete() = runBlockingTest {
assertThat(
flowOf(
Refresh(
pages = listOf(listOf("a1")).toTransformablePages(),
placeholdersBefore = 1,
placeholdersAfter = 1,
combinedLoadStates = remoteLoadStatesOf(
appendLocal = NotLoading.Complete,
prependLocal = NotLoading.Complete,
)
),
// Signalling that remote prepend is done triggers the header to resolve.
LoadStateUpdate(PREPEND, true, NotLoading.Complete),
).insertEventSeparators(
terminalSeparatorType = FULLY_COMPLETE,
generator = LETTER_SEPARATOR_GENERATOR
).toList()
).isEqualTo(
listOf(
Refresh(
pages = listOf(listOf("a1")).toTransformablePages(),
placeholdersBefore = 1,
placeholdersAfter = 1,
combinedLoadStates = remoteLoadStatesOf(
appendLocal = NotLoading.Complete,
prependLocal = NotLoading.Complete,
)
),
Prepend(
pages = listOf(
TransformablePage(
originalPageOffsets = intArrayOf(0),
data = listOf("A"),
hintOriginalIndices = listOf(0),
hintOriginalPageOffset = 0,
),
),
placeholdersBefore = 1,
combinedLoadStates = remoteLoadStatesOf(
prepend = NotLoading.Complete,
appendLocal = NotLoading.Complete,
prependLocal = NotLoading.Complete,
prependRemote = NotLoading.Complete,
),
),
)
)
}
@Test
fun remotePrependEndOfPaginationReached_sourceComplete() = runBlockingTest {
assertThat(
flowOf(
Refresh(
pages = listOf(listOf("a1")).toTransformablePages(),
placeholdersBefore = 1,
placeholdersAfter = 1,
combinedLoadStates = remoteLoadStatesOf(
appendLocal = NotLoading.Complete,
prependLocal = NotLoading.Complete,
)
),
// Signalling that remote prepend is done triggers the header to resolve.
LoadStateUpdate(PREPEND, true, NotLoading.Complete),
).insertEventSeparators(
terminalSeparatorType = SOURCE_COMPLETE,
generator = LETTER_SEPARATOR_GENERATOR
).toList()
).isEqualTo(
listOf(
Refresh(
pages = listOf(
TransformablePage(
originalPageOffsets = intArrayOf(0),
data = listOf("A"),
hintOriginalIndices = listOf(0),
hintOriginalPageOffset = 0,
),
TransformablePage(
originalPageOffset = 0,
data = listOf("a1"),
),
TransformablePage(
originalPageOffsets = intArrayOf(0),
data = listOf("END"),
hintOriginalIndices = listOf(0),
hintOriginalPageOffset = 0,
),
),
placeholdersBefore = 1,
placeholdersAfter = 1,
combinedLoadStates = remoteLoadStatesOf(
appendLocal = NotLoading.Complete,
prependLocal = NotLoading.Complete,
)
),
Prepend(
pages = listOf(),
placeholdersBefore = 1,
combinedLoadStates = remoteLoadStatesOf(
prepend = NotLoading.Complete,
appendLocal = NotLoading.Complete,
prependLocal = NotLoading.Complete,
prependRemote = NotLoading.Complete,
),
),
)
)
}
@Test
fun remotePrependEndOfPaginationReachedWithDrops_fullyComplete() = runBlockingTest {
assertThat(
flowOf(
Refresh(
pages = listOf(listOf("b1")).toTransformablePages(),
placeholdersBefore = 1,
placeholdersAfter = 1,
combinedLoadStates = remoteLoadStatesOf(
appendLocal = NotLoading.Complete,
prependLocal = NotLoading.Incomplete,
)
),
Prepend(
pages = listOf(
TransformablePage(
originalPageOffset = -1,
data = listOf("a1"),
)
),
placeholdersBefore = 0,
combinedLoadStates = remoteLoadStatesOf(
appendLocal = NotLoading.Complete,
prependLocal = NotLoading.Complete,
)
),
// Signalling that remote prepend is done triggers the header to resolve.
LoadStateUpdate(PREPEND, true, NotLoading.Complete),
// Drop the first page, header and separator between "b1" and "a1"
Drop(
loadType = PREPEND,
minPageOffset = -1,
maxPageOffset = -1,
placeholdersRemaining = 1
),
Prepend(
pages = listOf(
TransformablePage(
originalPageOffset = -1,
data = listOf("a1"),
)
),
placeholdersBefore = 0,
combinedLoadStates = remoteLoadStatesOf(
prepend = NotLoading.Complete,
appendLocal = NotLoading.Complete,
prependLocal = NotLoading.Complete,
prependRemote = NotLoading.Complete,
)
),
).insertEventSeparators(
terminalSeparatorType = FULLY_COMPLETE,
generator = LETTER_SEPARATOR_GENERATOR
).toList()
).isEqualTo(
listOf(
Refresh(
pages = listOf(listOf("b1")).toTransformablePages(),
placeholdersBefore = 1,
placeholdersAfter = 1,
combinedLoadStates = remoteLoadStatesOf(
appendLocal = NotLoading.Complete,
prependLocal = NotLoading.Incomplete,
)
),
Prepend(
pages = listOf(
TransformablePage(
originalPageOffset = -1,
data = listOf("a1"),
),
TransformablePage(
originalPageOffsets = intArrayOf(-1, 0),
data = listOf("B"),
hintOriginalIndices = listOf(0),
hintOriginalPageOffset = -1
),
),
placeholdersBefore = 0,
combinedLoadStates = remoteLoadStatesOf(
appendLocal = NotLoading.Complete,
prependLocal = NotLoading.Complete,
)
),
Prepend(
pages = listOf(
TransformablePage(
originalPageOffsets = intArrayOf(-1),
data = listOf("A"),
hintOriginalIndices = listOf(0),
hintOriginalPageOffset = -1,
),
),
placeholdersBefore = 0,
combinedLoadStates = remoteLoadStatesOf(
prepend = NotLoading.Complete,
appendLocal = NotLoading.Complete,
prependLocal = NotLoading.Complete,
prependRemote = NotLoading.Complete,
),
),
Drop(
loadType = PREPEND,
minPageOffset = -1,
maxPageOffset = -1,
placeholdersRemaining = 1
),
Prepend(
pages = listOf(
TransformablePage(
originalPageOffsets = intArrayOf(-1),
data = listOf("A"),
hintOriginalIndices = listOf(0),
hintOriginalPageOffset = -1,
),
TransformablePage(
originalPageOffset = -1,
data = listOf("a1"),
),
TransformablePage(
originalPageOffsets = intArrayOf(-1, 0),
data = listOf("B"),
hintOriginalIndices = listOf(0),
hintOriginalPageOffset = -1
),
),
placeholdersBefore = 0,
combinedLoadStates = remoteLoadStatesOf(
prepend = NotLoading.Complete,
appendLocal = NotLoading.Complete,
prependLocal = NotLoading.Complete,
prependRemote = NotLoading.Complete,
)
),
)
)
}
@Test
fun remotePrependEndOfPaginationReachedWithDrops_sourceComplete() = runBlockingTest {
assertThat(
flowOf(
Refresh(
pages = listOf(listOf("b1")).toTransformablePages(),
placeholdersBefore = 1,
placeholdersAfter = 1,
combinedLoadStates = remoteLoadStatesOf(
appendLocal = NotLoading.Complete,
prependLocal = NotLoading.Incomplete,
)
),
Prepend(
pages = listOf(
TransformablePage(
originalPageOffset = -1,
data = listOf("a1"),
)
),
placeholdersBefore = 0,
combinedLoadStates = remoteLoadStatesOf(
appendLocal = NotLoading.Complete,
prependLocal = NotLoading.Complete,
)
),
// Signalling that remote prepend is done triggers the header to resolve.
LoadStateUpdate(PREPEND, true, NotLoading.Complete),
// Drop the first page, header and separator between "b1" and "a1"
Drop(
loadType = PREPEND,
minPageOffset = -1,
maxPageOffset = -1,
placeholdersRemaining = 1
),
Prepend(
pages = listOf(
TransformablePage(
originalPageOffset = -1,
data = listOf("a1"),
)
),
placeholdersBefore = 0,
combinedLoadStates = remoteLoadStatesOf(
prepend = NotLoading.Complete,
appendLocal = NotLoading.Complete,
prependLocal = NotLoading.Complete,
prependRemote = NotLoading.Complete,
)
),
).insertEventSeparators(
terminalSeparatorType = SOURCE_COMPLETE,
generator = LETTER_SEPARATOR_GENERATOR
).toList()
).isEqualTo(
listOf(
Refresh(
pages = listOf(
TransformablePage(
originalPageOffset = 0,
data = listOf("b1"),
),
TransformablePage(
originalPageOffsets = intArrayOf(0),
data = listOf("END"),
hintOriginalIndices = listOf(0),
hintOriginalPageOffset = 0,
),
),
placeholdersBefore = 1,
placeholdersAfter = 1,
combinedLoadStates = remoteLoadStatesOf(
appendLocal = NotLoading.Complete,
prependLocal = NotLoading.Incomplete,
)
),
Prepend(
pages = listOf(
TransformablePage(
originalPageOffsets = intArrayOf(-1),
data = listOf("A"),
hintOriginalIndices = listOf(0),
hintOriginalPageOffset = -1,
),
TransformablePage(
originalPageOffset = -1,
data = listOf("a1"),
),
TransformablePage(
originalPageOffsets = intArrayOf(-1, 0),
data = listOf("B"),
hintOriginalIndices = listOf(0),
hintOriginalPageOffset = -1
),
),
placeholdersBefore = 0,
combinedLoadStates = remoteLoadStatesOf(
appendLocal = NotLoading.Complete,
prependLocal = NotLoading.Complete,
)
),
Prepend(
pages = listOf(),
placeholdersBefore = 0,
combinedLoadStates = remoteLoadStatesOf(
prepend = NotLoading.Complete,
appendLocal = NotLoading.Complete,
prependLocal = NotLoading.Complete,
prependRemote = NotLoading.Complete,
),
),
Drop(
loadType = PREPEND,
minPageOffset = -1,
maxPageOffset = -1,
placeholdersRemaining = 1
),
Prepend(
pages = listOf(
TransformablePage(
originalPageOffsets = intArrayOf(-1),
data = listOf("A"),
hintOriginalIndices = listOf(0),
hintOriginalPageOffset = -1,
),
TransformablePage(
originalPageOffset = -1,
data = listOf("a1"),
),
TransformablePage(
originalPageOffsets = intArrayOf(-1, 0),
data = listOf("B"),
hintOriginalIndices = listOf(0),
hintOriginalPageOffset = -1
),
),
placeholdersBefore = 0,
combinedLoadStates = remoteLoadStatesOf(
prepend = NotLoading.Complete,
appendLocal = NotLoading.Complete,
prependLocal = NotLoading.Complete,
prependRemote = NotLoading.Complete,
)
),
)
)
}
@Test
fun remoteAppendEndOfPaginationReached_fullyComplete() = runBlockingTest {
assertThat(
flowOf(
Refresh(
pages = listOf(listOf("a1")).toTransformablePages(),
placeholdersBefore = 1,
placeholdersAfter = 1,
combinedLoadStates = remoteLoadStatesOf(
appendLocal = NotLoading.Complete,
prependLocal = NotLoading.Complete,
)
),
// Signalling that remote append is done triggers the footer to resolve.
LoadStateUpdate(APPEND, true, NotLoading.Complete),
).insertEventSeparators(
terminalSeparatorType = FULLY_COMPLETE,
generator = LETTER_SEPARATOR_GENERATOR
).toList()
).isEqualTo(
listOf(
Refresh(
pages = listOf(listOf("a1")).toTransformablePages(),
placeholdersBefore = 1,
placeholdersAfter = 1,
combinedLoadStates = remoteLoadStatesOf(
appendLocal = NotLoading.Complete,
prependLocal = NotLoading.Complete,
)
),
Append(
pages = listOf(
TransformablePage(
originalPageOffsets = intArrayOf(0),
data = listOf("END"),
hintOriginalIndices = listOf(0),
hintOriginalPageOffset = 0,
),
),
placeholdersAfter = 1,
combinedLoadStates = remoteLoadStatesOf(
append = NotLoading.Complete,
appendLocal = NotLoading.Complete,
prependLocal = NotLoading.Complete,
appendRemote = NotLoading.Complete,
),
),
)
)
}
@Test
fun remoteAppendEndOfPaginationReached_sourceComplete() = runBlockingTest {
assertThat(
flowOf(
Refresh(
pages = listOf(listOf("a1")).toTransformablePages(),
placeholdersBefore = 1,
placeholdersAfter = 1,
combinedLoadStates = remoteLoadStatesOf(
appendLocal = NotLoading.Complete,
prependLocal = NotLoading.Complete,
)
),
// Signalling that remote append is done triggers the footer to resolve.
LoadStateUpdate(APPEND, true, NotLoading.Complete),
).insertEventSeparators(
terminalSeparatorType = SOURCE_COMPLETE,
generator = LETTER_SEPARATOR_GENERATOR
).toList()
).isEqualTo(
listOf(
Refresh(
pages = listOf(
TransformablePage(
originalPageOffsets = intArrayOf(0),
data = listOf("A"),
hintOriginalIndices = listOf(0),
hintOriginalPageOffset = 0,
),
TransformablePage(
originalPageOffset = 0,
data = listOf("a1"),
),
TransformablePage(
originalPageOffsets = intArrayOf(0),
data = listOf("END"),
hintOriginalIndices = listOf(0),
hintOriginalPageOffset = 0,
),
),
placeholdersBefore = 1,
placeholdersAfter = 1,
combinedLoadStates = remoteLoadStatesOf(
appendLocal = NotLoading.Complete,
prependLocal = NotLoading.Complete,
)
),
Append(
pages = listOf(),
placeholdersAfter = 1,
combinedLoadStates = remoteLoadStatesOf(
append = NotLoading.Complete,
appendLocal = NotLoading.Complete,
prependLocal = NotLoading.Complete,
appendRemote = NotLoading.Complete,
),
),
)
)
}
@Test
fun remoteAppendEndOfPaginationReachedWithDrops_fullyComplete() = runBlockingTest {
assertThat(
flowOf(
Refresh(
pages = listOf(listOf("b1")).toTransformablePages(),
placeholdersBefore = 1,
placeholdersAfter = 1,
combinedLoadStates = remoteLoadStatesOf(
prependLocal = NotLoading.Complete,
appendLocal = NotLoading.Incomplete,
)
),
Append(
pages = listOf(
TransformablePage(
originalPageOffset = 1,
data = listOf("c1"),
)
),
placeholdersAfter = 0,
combinedLoadStates = remoteLoadStatesOf(
prependLocal = NotLoading.Complete,
appendLocal = NotLoading.Complete,
)
),
// Signalling that remote append is done triggers the footer to resolve.
LoadStateUpdate(APPEND, true, NotLoading.Complete),
// Drop the last page, footer and separator between "b1" and "c1"
Drop(
loadType = APPEND,
minPageOffset = 1,
maxPageOffset = 1,
placeholdersRemaining = 1
),
Append(
pages = listOf(
TransformablePage(
originalPageOffset = 1,
data = listOf("c1"),
)
),
placeholdersAfter = 0,
combinedLoadStates = remoteLoadStatesOf(
append = NotLoading.Complete,
prependLocal = NotLoading.Complete,
appendLocal = NotLoading.Complete,
appendRemote = NotLoading.Complete,
)
),
).insertEventSeparators(
terminalSeparatorType = FULLY_COMPLETE,
generator = LETTER_SEPARATOR_GENERATOR
).toList()
).isEqualTo(
listOf(
Refresh(
pages = listOf(listOf("b1")).toTransformablePages(),
placeholdersBefore = 1,
placeholdersAfter = 1,
combinedLoadStates = remoteLoadStatesOf(
prependLocal = NotLoading.Complete,
appendLocal = NotLoading.Incomplete,
)
),
Append(
pages = listOf(
TransformablePage(
originalPageOffsets = intArrayOf(0, 1),
data = listOf("C"),
hintOriginalIndices = listOf(0),
hintOriginalPageOffset = 1
),
TransformablePage(
originalPageOffset = 1,
data = listOf("c1"),
),
),
placeholdersAfter = 0,
combinedLoadStates = remoteLoadStatesOf(
prependLocal = NotLoading.Complete,
appendLocal = NotLoading.Complete,
)
),
Append(
pages = listOf(
TransformablePage(
originalPageOffsets = intArrayOf(1),
data = listOf("END"),
hintOriginalIndices = listOf(0),
hintOriginalPageOffset = 1,
),
),
placeholdersAfter = 0,
combinedLoadStates = remoteLoadStatesOf(
append = NotLoading.Complete,
prependLocal = NotLoading.Complete,
appendLocal = NotLoading.Complete,
appendRemote = NotLoading.Complete,
),
),
Drop(
loadType = APPEND,
minPageOffset = 1,
maxPageOffset = 1,
placeholdersRemaining = 1
),
Append(
pages = listOf(
TransformablePage(
originalPageOffsets = intArrayOf(0, 1),
data = listOf("C"),
hintOriginalIndices = listOf(0),
hintOriginalPageOffset = 1
),
TransformablePage(
originalPageOffset = 1,
data = listOf("c1"),
),
TransformablePage(
originalPageOffsets = intArrayOf(1),
data = listOf("END"),
hintOriginalIndices = listOf(0),
hintOriginalPageOffset = 1
),
),
placeholdersAfter = 0,
combinedLoadStates = remoteLoadStatesOf(
append = NotLoading.Complete,
prependLocal = NotLoading.Complete,
appendLocal = NotLoading.Complete,
appendRemote = NotLoading.Complete,
)
),
)
)
}
@Test
fun remoteAppendEndOfPaginationReachedWithDrops_sourceComplete() = runBlockingTest {
assertThat(
flowOf(
Refresh(
pages = listOf(listOf("b1")).toTransformablePages(),
placeholdersBefore = 1,
placeholdersAfter = 1,
combinedLoadStates = remoteLoadStatesOf(
prependLocal = NotLoading.Complete,
appendLocal = NotLoading.Incomplete,
)
),
Append(
pages = listOf(
TransformablePage(
originalPageOffset = 1,
data = listOf("c1"),
)
),
placeholdersAfter = 0,
combinedLoadStates = remoteLoadStatesOf(
prependLocal = NotLoading.Complete,
appendLocal = NotLoading.Complete,
)
),
// Signalling that remote append is done triggers the footer to resolve.
LoadStateUpdate(APPEND, true, NotLoading.Complete),
// Drop the last page, footer and separator between "b1" and "c1"
Drop(
loadType = APPEND,
minPageOffset = 1,
maxPageOffset = 1,
placeholdersRemaining = 1
),
Append(
pages = listOf(
TransformablePage(
originalPageOffset = 1,
data = listOf("c1"),
)
),
placeholdersAfter = 0,
combinedLoadStates = remoteLoadStatesOf(
append = NotLoading.Complete,
prependLocal = NotLoading.Complete,
appendLocal = NotLoading.Complete,
appendRemote = NotLoading.Complete,
)
),
).insertEventSeparators(
terminalSeparatorType = SOURCE_COMPLETE,
generator = LETTER_SEPARATOR_GENERATOR
).toList()
).isEqualTo(
listOf(
Refresh(
pages = listOf(
TransformablePage(
originalPageOffsets = intArrayOf(0),
data = listOf("B"),
hintOriginalIndices = listOf(0),
hintOriginalPageOffset = 0
),
TransformablePage(
originalPageOffset = 0,
data = listOf("b1"),
),
),
placeholdersBefore = 1,
placeholdersAfter = 1,
combinedLoadStates = remoteLoadStatesOf(
prependLocal = NotLoading.Complete,
appendLocal = NotLoading.Incomplete,
),
),
Append(
pages = listOf(
TransformablePage(
originalPageOffsets = intArrayOf(0, 1),
data = listOf("C"),
hintOriginalIndices = listOf(0),
hintOriginalPageOffset = 1
),
TransformablePage(
originalPageOffset = 1,
data = listOf("c1"),
),
TransformablePage(
originalPageOffsets = intArrayOf(1),
data = listOf("END"),
hintOriginalIndices = listOf(0),
hintOriginalPageOffset = 1,
),
),
placeholdersAfter = 0,
combinedLoadStates = remoteLoadStatesOf(
prependLocal = NotLoading.Complete,
appendLocal = NotLoading.Complete,
),
),
Append(
pages = listOf(),
placeholdersAfter = 0,
combinedLoadStates = remoteLoadStatesOf(
append = NotLoading.Complete,
prependLocal = NotLoading.Complete,
appendLocal = NotLoading.Complete,
appendRemote = NotLoading.Complete,
),
),
Drop(
loadType = APPEND,
minPageOffset = 1,
maxPageOffset = 1,
placeholdersRemaining = 1
),
Append(
pages = listOf(
TransformablePage(
originalPageOffsets = intArrayOf(0, 1),
data = listOf("C"),
hintOriginalIndices = listOf(0),
hintOriginalPageOffset = 1
),
TransformablePage(
originalPageOffset = 1,
data = listOf("c1"),
),
TransformablePage(
originalPageOffsets = intArrayOf(1),
data = listOf("END"),
hintOriginalIndices = listOf(0),
hintOriginalPageOffset = 1
),
),
placeholdersAfter = 0,
combinedLoadStates = remoteLoadStatesOf(
append = NotLoading.Complete,
prependLocal = NotLoading.Complete,
appendLocal = NotLoading.Complete,
appendRemote = NotLoading.Complete,
)
),
)
)
}
companion object {
/**
* Creates an upper-case letter at the beginning of each section of strings that start
* with the same letter, and the string "END" at the very end.
*/
val LETTER_SEPARATOR_GENERATOR: suspend (String?, String?) -> String? = { before, after ->
if (after == null) {
"END"
} else if (before == null || before.first() != after.first()) {
after.first().uppercaseChar().toString()
} else null
}
}
}
@Suppress("TestFunctionName")
internal fun <T : Any> TransformablePage(data: List<T>) = TransformablePage(
data = data,
originalPageOffset = 0
)
internal fun <T : Any> List<List<T>>.toTransformablePages(
indexOfInitialPage: Int = 0
) = mapIndexed { index, list ->
TransformablePage(
data = list,
originalPageOffset = index - indexOfInitialPage
)
}.toMutableList()