|
|
|
import kotlinx.coroutines.CoroutineScope |
|
import kotlinx.coroutines.GlobalScope |
|
import kotlinx.coroutines.Job |
|
import kotlinx.coroutines.flow.* |
|
import kotlinx.coroutines.launch |
|
import kotlinx.coroutines.test.TestCoroutineScope |
|
import org.junit.Assert.* |
|
import kotlin.coroutines.coroutineContext |
|
import kotlin.reflect.KClass |
|
|
|
class TestFlowCollector<T> { |
|
|
|
private var job: Job? = null |
|
private val _values = mutableListOf<T>() |
|
var error: Throwable? = null |
|
get() = synchronized(this) { |
|
field |
|
} |
|
private set(value) { |
|
synchronized(this) { |
|
field = value |
|
} |
|
} |
|
var completed: Boolean = false |
|
get() = synchronized(this) { field } |
|
private set(value) { |
|
synchronized(this) { |
|
field = value |
|
} |
|
} |
|
val values: List<T> |
|
get() = synchronized(this) { |
|
_values.toList() |
|
} |
|
|
|
internal fun testOn(scope: CoroutineScope, flow: Flow<T>) { |
|
synchronized(this) { |
|
check(job == null) { "this TestFlowCollector testOn has already been used" } |
|
job = scope.launch { |
|
flow |
|
.catch { e -> error = e } |
|
.onCompletion { completed = true } |
|
.collect { item -> |
|
synchronized(this) { |
|
_values.add(item) |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
fun cancel() { |
|
val j = job |
|
check(j != null) { "this TestFlowCollector testOn has never been used" } |
|
j.cancel() |
|
} |
|
} |
|
|
|
fun <T> Flow<T>.test(scope: CoroutineScope): TestFlowCollector<T> { |
|
val testCollector = TestFlowCollector<T>() |
|
testCollector.testOn(scope, this) |
|
return testCollector |
|
} |
|
|
|
fun <T> TestFlowCollector<T>.lastValue(): T? = values.lastOrNull() |
|
|
|
fun <T> TestFlowCollector<T>.assertEmittedCount( |
|
count: Int, |
|
message: String = "Emission count does not match expected" |
|
) { |
|
require(count >= 0) { "asserting emitted count less then zero makes no sense" } |
|
assertEquals(message, count, values.size) |
|
} |
|
|
|
fun <T> TestFlowCollector<T>.assertNothingEmitted(message: String = "Expected nothing emitted") { |
|
assertEmittedCount(0, message) |
|
} |
|
|
|
fun <T> TestFlowCollector<T>.assertEmittedValuesEquals( |
|
values: List<T>, |
|
message: String = "Emitted values expectation is not met" |
|
) { |
|
assertEquals(message, values, this.values) |
|
} |
|
|
|
fun <T> TestFlowCollector<T>.assertEmittedValuesEquals( |
|
first: T, |
|
vararg others: T, |
|
message: String = "Emitted values expectation is not met" |
|
) { |
|
assertEquals(message, listOf(first, *others), values) |
|
} |
|
|
|
fun <T> TestFlowCollector<T>.assertEmittedValuesSame( |
|
values: List<T>, |
|
message: String = "Emitted values expectation is not met" |
|
) { |
|
values |
|
.forEachIndexed { idx, item -> |
|
assertSame("$message (on index $idx)", values[idx], item) |
|
} |
|
} |
|
|
|
fun <T> TestFlowCollector<T>.assertEmittedValuesContains( |
|
value: T, |
|
message: String = "Value expected to be emitted was not" |
|
) { |
|
val found = values.firstOrNull { it == value } |
|
assertEquals(message, value, found) |
|
} |
|
|
|
fun <T> TestFlowCollector<T>.assertCompleted(message: String = "Expected completed but was not") { |
|
assertTrue(message, completed) |
|
} |
|
|
|
fun <T> TestFlowCollector<T>.assertNotCompleted(message: String = "Expected not completed but was") { |
|
assertFalse(message, completed) |
|
} |
|
|
|
fun <T> TestFlowCollector<T>.assertNoError(message: String = "Expected no error but there was one") { |
|
assertNull(message, error) |
|
} |
|
|
|
fun <T, E : Throwable> TestFlowCollector<T>.assertErrorThrown( |
|
clazz: KClass<E>, |
|
message: String = "Error expectation was not met" |
|
): E? { |
|
val e = error |
|
assertNotNull(message, e) |
|
if (e != null) { |
|
assertEquals(message, clazz, e::class) |
|
} |
|
return e as? E |
|
} |
|
|
|
fun <T, E : Throwable> TestFlowCollector<T>.assertErrorThrown( |
|
clazz: Class<E>, |
|
message: String = "Error expectation was not met" |
|
): E? { |
|
val e = error |
|
assertNotNull(message, e) |
|
if (e != null) { |
|
assertEquals(message, clazz, e::class.java) |
|
} |
|
return e as? E |
|
} |
|
|
|
fun <T> TestFlowCollector<T>.assertLastEmittedValueEquals( |
|
v: T, |
|
message: String = "Last Emitted value did not met expectations" |
|
) { |
|
assertEquals(message, v, lastValue()) |
|
} |