-
Notifications
You must be signed in to change notification settings - Fork 80
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement CircuitReceiveTurbine (#1597)
This is an alternative to the distinctUntilChanged approach we added, where this exposes a new `CircuitReceiveTurbine` that overrides `awaitItem()` to emulate this behavior instead. This also offers helpers like `awaitUnchanged()` and policy controls.
- Loading branch information
Showing
8 changed files
with
137 additions
and
68 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
62 changes: 62 additions & 0 deletions
62
circuit-test/src/commonMain/kotlin/com/slack/circuit/test/CircuitReceiveTurbine.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
// Copyright (C) 2024 Slack Technologies, LLC | ||
// SPDX-License-Identifier: Apache-2.0 | ||
package com.slack.circuit.test | ||
|
||
import androidx.compose.runtime.SnapshotMutationPolicy | ||
import app.cash.turbine.ReceiveTurbine | ||
import com.slack.circuit.runtime.CircuitUiState | ||
|
||
/** | ||
* A Circuit-specific extension to [ReceiveTurbine] with extra helper functions for Circuit testing. | ||
* | ||
* This implementation of [ReceiveTurbine] slightly alters the behavior of [awaitItem] by only | ||
* emitting items that are _different_ from the previously emitted item. | ||
*/ | ||
@OptIn(ExperimentalSubclassOptIn::class) | ||
@SubclassOptInRequired(ExperimentalForInheritanceCircuitTestApi::class) | ||
public interface CircuitReceiveTurbine<UiState : CircuitUiState> : ReceiveTurbine<UiState> { | ||
/** | ||
* Awaits the next item and asserts that it is _unchanged_ from the previous emission. Essentially | ||
* this is a sort of escape-hatch from the altered "distinct until changed" behavior of | ||
* [awaitItem] in this implementation and can be used to more or less assert no change in state | ||
* after the next recomposition. | ||
*/ | ||
public suspend fun awaitUnchanged() | ||
} | ||
|
||
internal fun <UiState : CircuitUiState> ReceiveTurbine<UiState>.asCircuitReceiveTurbine( | ||
policy: SnapshotMutationPolicy<UiState> | ||
): CircuitReceiveTurbine<UiState> { | ||
return CircuitReceiveTurbineImpl(this, policy) | ||
} | ||
|
||
@OptIn(ExperimentalForInheritanceCircuitTestApi::class) | ||
private class CircuitReceiveTurbineImpl<UiState : CircuitUiState>( | ||
private val delegate: ReceiveTurbine<UiState>, | ||
private val policy: SnapshotMutationPolicy<UiState>, | ||
) : CircuitReceiveTurbine<UiState>, ReceiveTurbine<UiState> by delegate { | ||
|
||
private var lastItem: UiState? = null | ||
|
||
override suspend fun awaitUnchanged() { | ||
val next = delegate.awaitItem() | ||
if (next != lastItem) { | ||
throw AssertionError( | ||
"Expected unchanged item but received ${next}. Previous was ${lastItem}." | ||
) | ||
} | ||
} | ||
|
||
override suspend fun awaitItem(): UiState { | ||
while (true) { | ||
val last = lastItem | ||
val next = delegate.awaitItem() | ||
lastItem = next | ||
if (last == null) { | ||
return next | ||
} else if (!policy.equivalent(last, next)) { | ||
return next | ||
} | ||
} | ||
} | ||
} |
13 changes: 13 additions & 0 deletions
13
.../src/commonMain/kotlin/com/slack/circuit/test/ExperimentalForInheritanceCircuitTestApi.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
// Copyright (C) 2024 Slack Technologies, LLC | ||
// SPDX-License-Identifier: Apache-2.0 | ||
package com.slack.circuit.test | ||
|
||
@Target(AnnotationTarget.CLASS) | ||
@RequiresOptIn( | ||
level = RequiresOptIn.Level.WARNING, | ||
message = | ||
"Inheriting from this circuit-test API is unstable. " + | ||
"Either new methods may be added in the future, which would break the inheritance, " + | ||
"or correctly inheriting from it requires fulfilling contracts that may change in the future.", | ||
) | ||
public annotation class ExperimentalForInheritanceCircuitTestApi |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters