Skip to content

Commit 9b0fad0

Browse files
authored
Merge pull request #570 from namehillsoftware/bugfix/cancellable-aggregate-item-loading
[Bugfix] Cancellable Aggregate Item Loading
2 parents 65f673e + 31e5b82 commit 9b0fad0

File tree

3 files changed

+178
-4
lines changed

3 files changed

+178
-4
lines changed

projectBlueWater/src/main/java/com/lasthopesoftware/bluewater/client/browsing/items/AggregateItemViewModel.kt

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,11 @@ class AggregateItemViewModel(
2727
)
2828
}
2929

30-
override fun loadItem(libraryId: LibraryId, item: IItem?): Promise<Unit> {
30+
override fun loadItem(libraryId: LibraryId, item: IItem?): Promise<Unit> = Promise.Proxy { cs ->
3131
val promisedLoads = itemDataLoaders.map { it.loadItem(libraryId, item) }
3232

33-
return Promise.whenAll(promisedLoads)
33+
Promise.whenAll(promisedLoads)
34+
.also(cs::doCancel)
3435
.then(
3536
UnitResponse.respond(),
3637
{
@@ -41,9 +42,10 @@ class AggregateItemViewModel(
4142
)
4243
}
4344

44-
override fun promiseRefresh(): Promise<Unit> {
45+
override fun promiseRefresh(): Promise<Unit> = Promise.Proxy { cs ->
4546
val promisedRefreshes = itemDataLoaders.map { it.promiseRefresh() }
46-
return Promise.whenAll(promisedRefreshes)
47+
Promise.whenAll(promisedRefreshes)
48+
.also(cs::doCancel)
4749
.then(
4850
UnitResponse.respond(),
4951
{
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
package com.lasthopesoftware.bluewater.client.browsing.items.aggregate.GivenItemDataLoaders.AndLoadingIsCancelled
2+
3+
import com.lasthopesoftware.bluewater.client.browsing.items.AggregateItemViewModel
4+
import com.lasthopesoftware.bluewater.client.browsing.items.Item
5+
import com.lasthopesoftware.bluewater.client.browsing.library.repository.LibraryId
6+
import com.lasthopesoftware.bluewater.shared.observables.MutableInteractionState
7+
import com.lasthopesoftware.bluewater.shared.promises.extensions.toExpiringFuture
8+
import com.namehillsoftware.handoff.promises.Promise
9+
import io.mockk.every
10+
import io.mockk.mockk
11+
import org.assertj.core.api.Assertions.assertThat
12+
import org.junit.jupiter.api.BeforeAll
13+
import org.junit.jupiter.api.Test
14+
import java.util.concurrent.CancellationException
15+
import java.util.concurrent.ExecutionException
16+
17+
class `When Loading an Item` {
18+
19+
companion object {
20+
private const val libraryId = 425
21+
private const val itemId = "U12rH63"
22+
}
23+
24+
private val mut by lazy {
25+
AggregateItemViewModel(
26+
mockk {
27+
every { loadItem(LibraryId(libraryId), Item(itemId)) } returns Promise {
28+
it.awaitCancellation {
29+
isFirstModelCancelled = true
30+
it.sendResolution(Unit)
31+
}
32+
}
33+
34+
every { isLoading } returns MutableInteractionState(false)
35+
},
36+
mockk {
37+
every { loadItem(LibraryId(libraryId), Item(itemId)) } returns Promise<Unit> {
38+
it.awaitCancellation {
39+
isSecondModelCancelled = true
40+
it.sendRejection(CancellationException(":|"))
41+
}
42+
}
43+
every { isLoading } returns MutableInteractionState(false)
44+
},
45+
mockk {
46+
every { loadItem(LibraryId(libraryId), Item(itemId)) } returns Promise<Unit> {
47+
it.awaitCancellation {
48+
isThirdModelCancelled = true
49+
it.sendRejection(CancellationException(":/"))
50+
}
51+
}
52+
every { isLoading } returns MutableInteractionState(true)
53+
},
54+
)
55+
}
56+
57+
private var isFirstModelCancelled = false
58+
private var isSecondModelCancelled = false
59+
private var isThirdModelCancelled = false
60+
private var caughtException: CancellationException? = null
61+
62+
@BeforeAll
63+
fun act() {
64+
try {
65+
val promisedLoadedItem = mut.loadItem(LibraryId(libraryId), Item(itemId))
66+
promisedLoadedItem.cancel()
67+
promisedLoadedItem.toExpiringFuture().get()
68+
} catch (ee: ExecutionException) {
69+
caughtException = ee.cause as? CancellationException
70+
}
71+
}
72+
73+
@Test
74+
fun `then the caught exception is correct`() {
75+
assertThat(caughtException).isNotNull
76+
}
77+
78+
@Test
79+
fun `then the first model is cancelled`() {
80+
assertThat(isFirstModelCancelled).isTrue
81+
}
82+
83+
@Test
84+
fun `then the second model is cancelled`() {
85+
assertThat(isSecondModelCancelled).isTrue
86+
}
87+
88+
@Test
89+
fun `then the third model is cancelled`() {
90+
assertThat(isThirdModelCancelled).isTrue
91+
}
92+
93+
@Test
94+
fun `then the model is loading`() {
95+
assertThat(mut.isLoading.value).isTrue
96+
}
97+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package com.lasthopesoftware.bluewater.client.browsing.items.aggregate.GivenItemDataLoaders.AndLoadingIsCancelled
2+
3+
import com.lasthopesoftware.bluewater.client.browsing.items.AggregateItemViewModel
4+
import com.lasthopesoftware.bluewater.shared.observables.MutableInteractionState
5+
import com.lasthopesoftware.bluewater.shared.promises.extensions.toExpiringFuture
6+
import com.namehillsoftware.handoff.promises.Promise
7+
import io.mockk.every
8+
import io.mockk.mockk
9+
import org.assertj.core.api.Assertions.assertThat
10+
import org.junit.jupiter.api.BeforeAll
11+
import org.junit.jupiter.api.Test
12+
import java.util.concurrent.CancellationException
13+
import java.util.concurrent.ExecutionException
14+
15+
class `When Refreshing an Item` {
16+
17+
private val mut by lazy {
18+
AggregateItemViewModel(
19+
mockk {
20+
every { promiseRefresh() } returns Promise {
21+
it.awaitCancellation {
22+
isFirstModelCancelled = true
23+
it.sendResolution(Unit)
24+
}
25+
}
26+
27+
every { isLoading } returns MutableInteractionState(false)
28+
},
29+
mockk {
30+
every { promiseRefresh() } returns Promise<Unit> {
31+
it.awaitCancellation {
32+
isSecondModelCancelled = true
33+
it.sendRejection(CancellationException())
34+
}
35+
}
36+
every { isLoading } returns MutableInteractionState(false)
37+
},
38+
)
39+
}
40+
41+
private var isFirstModelCancelled = false
42+
private var isSecondModelCancelled = false
43+
private var caughtException: CancellationException? = null
44+
45+
@BeforeAll
46+
fun act() {
47+
try {
48+
val promisedLoadedItem = mut.promiseRefresh()
49+
promisedLoadedItem.cancel()
50+
promisedLoadedItem.toExpiringFuture().get()
51+
} catch (ee: ExecutionException) {
52+
caughtException = ee.cause as? CancellationException
53+
}
54+
}
55+
56+
@Test
57+
fun `then the caught exception is correct`() {
58+
assertThat(caughtException).isNotNull
59+
}
60+
61+
@Test
62+
fun `then the first model is cancelled`() {
63+
assertThat(isFirstModelCancelled).isTrue
64+
}
65+
66+
@Test
67+
fun `then the second model is cancelled`() {
68+
assertThat(isSecondModelCancelled).isTrue
69+
}
70+
71+
@Test
72+
fun `then the model is not loading`() {
73+
assertThat(mut.isLoading.value).isFalse
74+
}
75+
}

0 commit comments

Comments
 (0)