791 lines
22 KiB
Kotlin
791 lines
22 KiB
Kotlin
package com.keylesspalace.tusky.components.timeline
|
|
|
|
import android.content.SharedPreferences
|
|
import android.net.ConnectivityManager
|
|
import com.keylesspalace.tusky.appstore.EventHub
|
|
import com.keylesspalace.tusky.components.timeline.TimelineViewModel.Companion.LOAD_AT_ONCE
|
|
import com.keylesspalace.tusky.db.AccountEntity
|
|
import com.keylesspalace.tusky.db.AccountManager
|
|
import com.keylesspalace.tusky.entity.Poll
|
|
import com.keylesspalace.tusky.entity.PollOption
|
|
import com.keylesspalace.tusky.entity.Status
|
|
import com.keylesspalace.tusky.network.FilterModel
|
|
import com.keylesspalace.tusky.network.MastodonApi
|
|
import com.keylesspalace.tusky.network.TimelineCases
|
|
import com.keylesspalace.tusky.util.Either
|
|
import com.keylesspalace.tusky.util.toViewData
|
|
import com.keylesspalace.tusky.viewdata.StatusViewData
|
|
import com.nhaarman.mockitokotlin2.*
|
|
import io.reactivex.rxjava3.annotations.NonNull
|
|
import io.reactivex.rxjava3.core.Observable
|
|
import io.reactivex.rxjava3.core.Single
|
|
import io.reactivex.rxjava3.observers.TestObserver
|
|
import io.reactivex.rxjava3.subjects.PublishSubject
|
|
import kotlinx.coroutines.runBlocking
|
|
import net.accelf.yuito.streaming.StreamingManager
|
|
import org.junit.Assert.*
|
|
import org.junit.Before
|
|
import org.junit.Test
|
|
import org.robolectric.annotation.Config
|
|
import org.robolectric.shadows.ShadowLog
|
|
import retrofit2.Response
|
|
import java.io.IOException
|
|
|
|
@Config(sdk = [29])
|
|
class TimelineViewModelTest {
|
|
lateinit var timelineRepository: TimelineRepository
|
|
lateinit var timelineCases: TimelineCases
|
|
lateinit var mastodonApi: MastodonApi
|
|
lateinit var eventHub: EventHub
|
|
lateinit var viewModel: TimelineViewModel
|
|
lateinit var accountManager: AccountManager
|
|
lateinit var sharedPreference: SharedPreferences
|
|
lateinit var connectivityManager: ConnectivityManager
|
|
lateinit var streamingManager: StreamingManager
|
|
|
|
@Before
|
|
fun setup() {
|
|
ShadowLog.stream = System.out
|
|
timelineRepository = mock()
|
|
timelineCases = mock()
|
|
mastodonApi = mock()
|
|
eventHub = mock {
|
|
on { events } doReturn Observable.never()
|
|
}
|
|
val account = AccountEntity(
|
|
0,
|
|
"domain",
|
|
"accessToken",
|
|
isActive = true,
|
|
)
|
|
|
|
accountManager = mock {
|
|
on { activeAccount } doReturn account
|
|
}
|
|
sharedPreference = mock()
|
|
connectivityManager = mock()
|
|
streamingManager = mock()
|
|
viewModel = TimelineViewModel(
|
|
timelineRepository,
|
|
timelineCases,
|
|
mastodonApi,
|
|
eventHub,
|
|
accountManager,
|
|
sharedPreference,
|
|
FilterModel(),
|
|
connectivityManager,
|
|
streamingManager,
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun `loadInitial, home, without cache, empty response`() {
|
|
val initialResponse = listOf<Status>()
|
|
setCachedResponse(initialResponse)
|
|
|
|
// loadAbove -> loadBelow
|
|
whenever(
|
|
timelineRepository.getStatuses(
|
|
maxId = null,
|
|
sinceId = null,
|
|
sincedIdMinusOne = null,
|
|
requestMode = TimelineRequestMode.ANY,
|
|
limit = LOAD_AT_ONCE
|
|
)
|
|
).thenReturn(Single.just(listOf()))
|
|
|
|
runBlocking {
|
|
viewModel.loadInitial()
|
|
}
|
|
|
|
verify(timelineRepository).getStatuses(
|
|
null,
|
|
null,
|
|
null,
|
|
LOAD_AT_ONCE,
|
|
TimelineRequestMode.ANY
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun `loadInitial, home, without cache, single item in response`() {
|
|
setCachedResponse(listOf())
|
|
|
|
val status = makeStatus("1")
|
|
whenever(
|
|
timelineRepository.getStatuses(
|
|
isNull(),
|
|
isNull(),
|
|
isNull(),
|
|
eq(LOAD_AT_ONCE),
|
|
eq(TimelineRequestMode.ANY)
|
|
)
|
|
).thenReturn(
|
|
Single.just(
|
|
listOf(
|
|
Either.Right(status)
|
|
)
|
|
)
|
|
)
|
|
|
|
val updates = viewModel.viewUpdates.test()
|
|
|
|
runBlocking {
|
|
viewModel.loadInitial()
|
|
}
|
|
|
|
verify(timelineRepository).getStatuses(
|
|
isNull(),
|
|
isNull(),
|
|
isNull(),
|
|
eq(LOAD_AT_ONCE),
|
|
eq(TimelineRequestMode.ANY)
|
|
)
|
|
|
|
assertViewUpdated(updates)
|
|
|
|
assertHasList(listOf(status).toViewData())
|
|
}
|
|
|
|
@Test
|
|
fun `loadInitial, list`() {
|
|
val listId = "1"
|
|
viewModel.init(TimelineViewModel.Kind.LIST, listId, listOf(), false)
|
|
val status = makeStatus("1")
|
|
|
|
whenever(
|
|
mastodonApi.listTimeline(
|
|
listId,
|
|
null,
|
|
null,
|
|
LOAD_AT_ONCE,
|
|
)
|
|
).thenReturn(
|
|
Single.just(
|
|
Response.success(
|
|
listOf(
|
|
status
|
|
)
|
|
)
|
|
)
|
|
)
|
|
|
|
val updates = viewModel.viewUpdates.test()
|
|
|
|
runBlocking {
|
|
viewModel.loadInitial().join()
|
|
}
|
|
assertViewUpdated(updates)
|
|
|
|
assertHasList(listOf(status).toViewData())
|
|
assertFalse("loading", viewModel.isLoadingInitially)
|
|
}
|
|
|
|
@Test
|
|
fun `loadInitial, home, without cache, error on load`() {
|
|
setCachedResponse(listOf())
|
|
|
|
whenever(
|
|
timelineRepository.getStatuses(
|
|
maxId = null,
|
|
sinceId = null,
|
|
sincedIdMinusOne = null,
|
|
limit = LOAD_AT_ONCE,
|
|
TimelineRequestMode.ANY,
|
|
)
|
|
).thenReturn(Single.error(IOException("test")))
|
|
|
|
val updates = viewModel.viewUpdates.test()
|
|
|
|
runBlocking {
|
|
viewModel.loadInitial()
|
|
}
|
|
|
|
verify(timelineRepository).getStatuses(
|
|
isNull(),
|
|
isNull(),
|
|
isNull(),
|
|
eq(LOAD_AT_ONCE),
|
|
eq(TimelineRequestMode.ANY)
|
|
)
|
|
|
|
assertViewUpdated(updates)
|
|
|
|
assertHasList(listOf())
|
|
assertEquals(TimelineViewModel.FailureReason.NETWORK, viewModel.failure)
|
|
}
|
|
|
|
@Test
|
|
fun `loadInitial, home, with cache, error on load above`() {
|
|
val statuses = (5 downTo 1).map { makeStatus(it.toString()) }
|
|
setCachedResponse(statuses)
|
|
setInitialRefresh("6", statuses)
|
|
|
|
whenever(
|
|
timelineRepository.getStatuses(
|
|
maxId = null,
|
|
sinceId = "5",
|
|
sincedIdMinusOne = "4",
|
|
limit = LOAD_AT_ONCE,
|
|
TimelineRequestMode.NETWORK,
|
|
)
|
|
).thenReturn(Single.error(IOException("test")))
|
|
|
|
val updates = viewModel.viewUpdates.test()
|
|
|
|
runBlocking {
|
|
viewModel.loadInitial()
|
|
}
|
|
|
|
assertViewUpdated(updates)
|
|
|
|
assertHasList(statuses.toViewData())
|
|
// No failure set since we had statuses
|
|
assertNull(viewModel.failure)
|
|
}
|
|
|
|
@Test
|
|
fun `loadInitial, home, with cache, error on refresh`() {
|
|
val statuses = (5 downTo 2).map { makeStatus(it.toString()) }
|
|
setCachedResponse(statuses)
|
|
|
|
// Error on refreshing cached
|
|
whenever(
|
|
timelineRepository.getStatuses(
|
|
maxId = "6",
|
|
sinceId = null,
|
|
sincedIdMinusOne = null,
|
|
limit = LOAD_AT_ONCE,
|
|
TimelineRequestMode.NETWORK,
|
|
)
|
|
).thenReturn(Single.error(IOException("test")))
|
|
|
|
// Empty on loading above
|
|
setLoadAbove("5", "4", listOf())
|
|
|
|
val updates = viewModel.viewUpdates.test()
|
|
|
|
runBlocking {
|
|
viewModel.loadInitial()
|
|
}
|
|
|
|
assertViewUpdated(updates)
|
|
|
|
assertHasList(statuses.toViewData())
|
|
assertNull(viewModel.failure)
|
|
}
|
|
|
|
@Test
|
|
fun `loads above cached`() {
|
|
val cachedStatuses = (5 downTo 1).map { makeStatus(it.toString()) }
|
|
setCachedResponse(cachedStatuses)
|
|
setInitialRefresh("6", cachedStatuses)
|
|
|
|
val additionalStatuses = (10 downTo 6)
|
|
.map { makeStatus(it.toString()) }
|
|
|
|
whenever(
|
|
timelineRepository.getStatuses(
|
|
null,
|
|
"5",
|
|
"4",
|
|
LOAD_AT_ONCE,
|
|
TimelineRequestMode.NETWORK
|
|
)
|
|
).thenReturn(Single.just(additionalStatuses.toEitherList()))
|
|
|
|
runBlocking {
|
|
viewModel.loadInitial()
|
|
}
|
|
|
|
// We could also check refresh progress here but it's a bit cumbersome
|
|
|
|
assertHasList(additionalStatuses.plus(cachedStatuses).toViewData())
|
|
}
|
|
|
|
@Test
|
|
fun refresh() {
|
|
val cachedStatuses = (5 downTo 1).map { makeStatus(it.toString()) }
|
|
setCachedResponse(cachedStatuses)
|
|
setInitialRefresh("6", cachedStatuses)
|
|
|
|
val additionalStatuses = listOf(makeStatus("6"))
|
|
|
|
whenever(
|
|
timelineRepository.getStatuses(
|
|
null,
|
|
"5",
|
|
"4",
|
|
LOAD_AT_ONCE,
|
|
TimelineRequestMode.NETWORK
|
|
)
|
|
).thenReturn(Single.just(additionalStatuses.toEitherList()))
|
|
|
|
runBlocking {
|
|
viewModel.loadInitial()
|
|
}
|
|
|
|
clearInvocations(timelineRepository)
|
|
|
|
val newStatuses = (8 downTo 7).map { makeStatus(it.toString()) }
|
|
|
|
// Loading above the cached manually
|
|
whenever(
|
|
timelineRepository.getStatuses(
|
|
null,
|
|
"6",
|
|
"5",
|
|
LOAD_AT_ONCE,
|
|
TimelineRequestMode.NETWORK
|
|
)
|
|
).thenReturn(Single.just(newStatuses.toEitherList()))
|
|
|
|
runBlocking {
|
|
viewModel.refresh()
|
|
}
|
|
|
|
val allStatuses = newStatuses + additionalStatuses + cachedStatuses
|
|
assertHasList(allStatuses.toViewData())
|
|
}
|
|
|
|
@Test
|
|
fun `refresh failed`() {
|
|
val cachedStatuses = (5 downTo 1).map { makeStatus(it.toString()) }
|
|
setCachedResponse(cachedStatuses)
|
|
setInitialRefresh("6", cachedStatuses)
|
|
setLoadAbove("5", "4", listOf())
|
|
|
|
runBlocking {
|
|
viewModel.loadInitial()
|
|
}
|
|
|
|
clearInvocations(timelineRepository)
|
|
|
|
// Loading above the cached manually
|
|
whenever(
|
|
timelineRepository.getStatuses(
|
|
null,
|
|
"6",
|
|
"5",
|
|
LOAD_AT_ONCE,
|
|
TimelineRequestMode.NETWORK
|
|
)
|
|
).thenReturn(Single.error(IOException("test")))
|
|
|
|
runBlocking {
|
|
viewModel.refresh().join()
|
|
}
|
|
|
|
assertHasList(cachedStatuses.map { it.toViewData(false, false) })
|
|
assertFalse("refreshing", viewModel.isRefreshing)
|
|
assertNull("failure is not set", viewModel.failure)
|
|
}
|
|
|
|
@Test
|
|
fun loadMore() {
|
|
val cachedStatuses = (10 downTo 5).map { makeStatus(it.toString()) }
|
|
setCachedResponse(cachedStatuses)
|
|
setInitialRefresh("11", cachedStatuses)
|
|
|
|
// Nothing above
|
|
setLoadAbove("10", "9", listOf())
|
|
|
|
runBlocking {
|
|
viewModel.loadInitial().join()
|
|
}
|
|
|
|
clearInvocations(timelineRepository)
|
|
|
|
val oldStatuses = (4 downTo 1).map { makeStatus(it.toString()) }
|
|
|
|
// Loading below the cached
|
|
whenever(
|
|
timelineRepository.getStatuses(
|
|
"5",
|
|
null,
|
|
null,
|
|
LOAD_AT_ONCE,
|
|
TimelineRequestMode.ANY
|
|
)
|
|
).thenReturn(Single.just(oldStatuses.toEitherList()))
|
|
|
|
runBlocking {
|
|
viewModel.loadMore().join()
|
|
}
|
|
|
|
val allStatuses = cachedStatuses + oldStatuses
|
|
assertHasList(allStatuses.toViewData())
|
|
}
|
|
|
|
@Test
|
|
fun `loadMore parallel`() {
|
|
val cachedStatuses = (10 downTo 5).map { makeStatus(it.toString()) }
|
|
setCachedResponse(cachedStatuses)
|
|
setInitialRefresh("11", cachedStatuses)
|
|
|
|
// Nothing above
|
|
setLoadAbove("10", "9", listOf())
|
|
|
|
runBlocking {
|
|
viewModel.loadInitial().join()
|
|
}
|
|
|
|
clearInvocations(timelineRepository)
|
|
|
|
val oldStatuses = (4 downTo 1).map { makeStatus(it.toString()) }
|
|
|
|
val responseSubject = PublishSubject.create<List<TimelineStatus>>()
|
|
// Loading below the cached
|
|
whenever(
|
|
timelineRepository.getStatuses(
|
|
"5",
|
|
null,
|
|
null,
|
|
LOAD_AT_ONCE,
|
|
TimelineRequestMode.ANY
|
|
)
|
|
).thenReturn(responseSubject.firstOrError())
|
|
|
|
clearInvocations(timelineRepository)
|
|
|
|
runBlocking {
|
|
// Trigger them in parallel
|
|
val job1 = viewModel.loadMore()
|
|
val job2 = viewModel.loadMore()
|
|
// Send the response
|
|
responseSubject.onNext(oldStatuses.toEitherList())
|
|
// Wait for both
|
|
job1.join()
|
|
job2.join()
|
|
}
|
|
|
|
val allStatuses = cachedStatuses + oldStatuses
|
|
assertHasList(allStatuses.toViewData())
|
|
|
|
verify(timelineRepository, times(1)).getStatuses(
|
|
"5",
|
|
null,
|
|
null,
|
|
LOAD_AT_ONCE,
|
|
TimelineRequestMode.ANY
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun `loadMore failed`() {
|
|
val cachedStatuses = (10 downTo 5).map { makeStatus(it.toString()) }
|
|
setCachedResponse(cachedStatuses)
|
|
setInitialRefresh("11", cachedStatuses)
|
|
|
|
// Nothing above
|
|
setLoadAbove("10", "9", listOf())
|
|
|
|
runBlocking {
|
|
viewModel.loadInitial().join()
|
|
}
|
|
|
|
clearInvocations(timelineRepository)
|
|
|
|
// Loading below the cached
|
|
whenever(
|
|
timelineRepository.getStatuses(
|
|
"5",
|
|
null,
|
|
null,
|
|
LOAD_AT_ONCE,
|
|
TimelineRequestMode.ANY
|
|
)
|
|
).thenReturn(Single.error(IOException("test")))
|
|
|
|
runBlocking {
|
|
viewModel.loadMore().join()
|
|
}
|
|
|
|
assertHasList(cachedStatuses.toViewData())
|
|
|
|
// Check that we can still load after that
|
|
|
|
val oldStatuses = listOf(makeStatus("4"))
|
|
whenever(
|
|
timelineRepository.getStatuses(
|
|
"5",
|
|
null,
|
|
null,
|
|
LOAD_AT_ONCE,
|
|
TimelineRequestMode.ANY
|
|
)
|
|
).thenReturn(Single.just(oldStatuses.toEitherList()))
|
|
|
|
runBlocking {
|
|
viewModel.loadMore().join()
|
|
}
|
|
assertHasList((cachedStatuses + oldStatuses).toViewData())
|
|
}
|
|
|
|
@Test
|
|
fun loadGap() {
|
|
val status5 = makeStatus("5")
|
|
val status4 = makeStatus("4")
|
|
val status3 = makeStatus("3")
|
|
val status1 = makeStatus("1")
|
|
|
|
val cachedStatuses: List<TimelineStatus> = listOf(
|
|
Either.Right(status5),
|
|
Either.Left(Placeholder("4")),
|
|
Either.Right(status1)
|
|
)
|
|
val laterFetchedStatuses = listOf<TimelineStatus>(
|
|
Either.Right(status4),
|
|
Either.Right(status3),
|
|
)
|
|
|
|
setCachedResponseWithGaps(cachedStatuses)
|
|
setInitialRefreshWithGaps("6", cachedStatuses)
|
|
|
|
// Nothing above
|
|
setLoadAbove("5", items = listOf())
|
|
|
|
whenever(
|
|
timelineRepository.getStatuses(
|
|
"5",
|
|
"1",
|
|
null,
|
|
LOAD_AT_ONCE,
|
|
TimelineRequestMode.NETWORK
|
|
)
|
|
).thenReturn(Single.just(laterFetchedStatuses))
|
|
|
|
runBlocking {
|
|
viewModel.loadInitial().join()
|
|
|
|
viewModel.loadGap(1).join()
|
|
}
|
|
|
|
assertHasList(
|
|
listOf(
|
|
status5,
|
|
status4,
|
|
status3,
|
|
status1
|
|
).toViewData()
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun `loadGap failed`() {
|
|
val status5 = makeStatus("5")
|
|
val status1 = makeStatus("1")
|
|
|
|
val cachedStatuses: List<TimelineStatus> = listOf(
|
|
Either.Right(status5),
|
|
Either.Left(Placeholder("4")),
|
|
Either.Right(status1)
|
|
)
|
|
setCachedResponseWithGaps(cachedStatuses)
|
|
setInitialRefreshWithGaps("6", cachedStatuses)
|
|
|
|
setLoadAbove("5", items = listOf())
|
|
|
|
whenever(
|
|
timelineRepository.getStatuses(
|
|
"5",
|
|
"1",
|
|
null,
|
|
LOAD_AT_ONCE,
|
|
TimelineRequestMode.NETWORK
|
|
)
|
|
).thenReturn(Single.error(IOException("test")))
|
|
|
|
runBlocking {
|
|
viewModel.loadInitial().join()
|
|
|
|
viewModel.loadGap(1).join()
|
|
}
|
|
|
|
assertHasList(
|
|
listOf(
|
|
status5.toViewData(false, false),
|
|
StatusViewData.Placeholder("4", false),
|
|
status1.toViewData(false, false),
|
|
)
|
|
)
|
|
}
|
|
|
|
@Test
|
|
fun favorite() {
|
|
val status5 = makeStatus("5")
|
|
val status4 = makeStatus("4")
|
|
val status3 = makeStatus("3")
|
|
val statuses = listOf(status5, status4, status3)
|
|
setCachedResponse(statuses)
|
|
setInitialRefresh("6", statuses)
|
|
setLoadAbove("5", "4", listOf())
|
|
|
|
runBlocking { viewModel.loadInitial() }
|
|
|
|
whenever(timelineCases.favourite("4", true))
|
|
.thenReturn(Single.just(status4.copy(favourited = true)))
|
|
|
|
runBlocking {
|
|
viewModel.favorite(true, 1).join()
|
|
}
|
|
|
|
verify(timelineCases).favourite("4", true)
|
|
|
|
assertHasList(listOf(status5, status4.copy(favourited = true), status3).toViewData())
|
|
}
|
|
|
|
@Test
|
|
fun reblog() {
|
|
val status5 = makeStatus("5")
|
|
val status4 = makeStatus("4")
|
|
val status3 = makeStatus("3")
|
|
val statuses = listOf(status5, status4, status3)
|
|
setCachedResponse(statuses)
|
|
setInitialRefresh("6", statuses)
|
|
setLoadAbove("5", "4", listOf())
|
|
|
|
runBlocking { viewModel.loadInitial() }
|
|
|
|
whenever(timelineCases.reblog("4", true))
|
|
.thenReturn(Single.just(status4.copy(reblogged = true)))
|
|
|
|
runBlocking {
|
|
viewModel.reblog(true, 1).join()
|
|
}
|
|
|
|
verify(timelineCases).reblog("4", true)
|
|
|
|
assertHasList(listOf(status5, status4.copy(reblogged = true), status3).toViewData())
|
|
}
|
|
|
|
@Test
|
|
fun bookmark() {
|
|
val status5 = makeStatus("5")
|
|
val status4 = makeStatus("4")
|
|
val status3 = makeStatus("3")
|
|
val statuses = listOf(status5, status4, status3)
|
|
setCachedResponse(statuses)
|
|
setInitialRefresh("6", statuses)
|
|
setLoadAbove("5", "4", listOf())
|
|
|
|
runBlocking { viewModel.loadInitial() }
|
|
|
|
whenever(timelineCases.bookmark("4", true))
|
|
.thenReturn(Single.just(status4.copy(bookmarked = true)))
|
|
|
|
runBlocking {
|
|
viewModel.bookmark(true, 1).join()
|
|
}
|
|
|
|
verify(timelineCases).bookmark("4", true)
|
|
|
|
assertHasList(listOf(status5, status4.copy(bookmarked = true), status3).toViewData())
|
|
}
|
|
|
|
@Test
|
|
fun voteInPoll() {
|
|
val status5 = makeStatus("5")
|
|
val poll = Poll(
|
|
"1",
|
|
expiresAt = null,
|
|
expired = false,
|
|
multiple = false,
|
|
votersCount = 1,
|
|
votesCount = 1,
|
|
voted = false,
|
|
options = listOf(PollOption("1", 1), PollOption("2", 2)),
|
|
ownVotes = null
|
|
)
|
|
val status4 = makeStatus("4").copy(poll = poll)
|
|
val status3 = makeStatus("3")
|
|
val statuses = listOf(status5, status4, status3)
|
|
setCachedResponse(statuses)
|
|
setInitialRefresh("6", statuses)
|
|
setLoadAbove("5", "4", listOf())
|
|
|
|
runBlocking { viewModel.loadInitial() }
|
|
|
|
val votedPoll = poll.votedCopy(listOf(0))
|
|
whenever(timelineCases.voteInPoll("4", poll.id, listOf(0)))
|
|
.thenReturn(Single.just(votedPoll))
|
|
|
|
runBlocking {
|
|
viewModel.voteInPoll(1, listOf(0)).join()
|
|
}
|
|
|
|
verify(timelineCases).voteInPoll("4", poll.id, listOf(0))
|
|
|
|
assertHasList(listOf(status5, status4.copy(poll = votedPoll), status3).toViewData())
|
|
}
|
|
|
|
private fun setLoadAbove(
|
|
above: String,
|
|
aboveMinusOne: String? = null,
|
|
items: List<TimelineStatus>
|
|
) {
|
|
whenever(
|
|
timelineRepository.getStatuses(
|
|
null,
|
|
above,
|
|
aboveMinusOne,
|
|
LOAD_AT_ONCE,
|
|
TimelineRequestMode.NETWORK
|
|
)
|
|
).thenReturn(Single.just(items))
|
|
}
|
|
|
|
private fun assertHasList(aList: List<StatusViewData>) {
|
|
assertEquals(
|
|
aList,
|
|
viewModel.statuses.toList()
|
|
)
|
|
}
|
|
|
|
private fun assertViewUpdated(updates: @NonNull TestObserver<Unit>) {
|
|
assertTrue("There were view updates", updates.values().isNotEmpty())
|
|
}
|
|
|
|
private fun setInitialRefresh(maxId: String?, statuses: List<Status>) {
|
|
setInitialRefreshWithGaps(maxId, statuses.toEitherList())
|
|
}
|
|
|
|
private fun setCachedResponse(initialResponse: List<Status>) {
|
|
setCachedResponseWithGaps(initialResponse.toEitherList())
|
|
}
|
|
|
|
private fun setCachedResponseWithGaps(initialResponse: List<TimelineStatus>) {
|
|
whenever(
|
|
timelineRepository.getStatuses(
|
|
isNull(),
|
|
isNull(),
|
|
isNull(),
|
|
eq(LOAD_AT_ONCE),
|
|
eq(TimelineRequestMode.DISK)
|
|
)
|
|
)
|
|
.thenReturn(Single.just(initialResponse))
|
|
}
|
|
|
|
private fun setInitialRefreshWithGaps(maxId: String?, statuses: List<TimelineStatus>) {
|
|
whenever(
|
|
timelineRepository.getStatuses(
|
|
maxId,
|
|
null,
|
|
null,
|
|
LOAD_AT_ONCE,
|
|
TimelineRequestMode.NETWORK
|
|
)
|
|
).thenReturn(Single.just(statuses))
|
|
}
|
|
|
|
private fun List<Status>.toViewData(): List<StatusViewData> = map {
|
|
it.toViewData(
|
|
alwaysShowSensitiveMedia = false,
|
|
alwaysOpenSpoiler = false
|
|
)
|
|
}
|
|
|
|
private fun List<Status>.toEitherList() = map { Either.Right<Placeholder, Status>(it) }
|
|
}
|