mirror of
https://github.com/DaanVandenBosch/phantasmal-world.git
synced 2025-04-04 22:58:29 +08:00
Improved observable test setup.
This commit is contained in:
parent
18e01f17c7
commit
b78c516b0a
@ -0,0 +1,60 @@
|
|||||||
|
package world.phantasmal.observable
|
||||||
|
|
||||||
|
import world.phantasmal.observable.test.withScope
|
||||||
|
import world.phantasmal.testUtils.TestSuite
|
||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
typealias ObservableAndEmit = Pair<Observable<*>, () -> Unit>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test suite for all [Observable] implementations. There is a subclass of this suite for every
|
||||||
|
* [Observable] implementation.
|
||||||
|
*/
|
||||||
|
abstract class ObservableTests : TestSuite() {
|
||||||
|
abstract fun create(): ObservableAndEmit
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun observable_calls_observers_when_events_are_emitted() {
|
||||||
|
val (observable, emit) = create()
|
||||||
|
val changes = mutableListOf<ChangeEvent<*>>()
|
||||||
|
|
||||||
|
withScope { scope ->
|
||||||
|
observable.observe(scope) { c ->
|
||||||
|
changes.add(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
emit()
|
||||||
|
|
||||||
|
assertEquals(1, changes.size)
|
||||||
|
|
||||||
|
emit()
|
||||||
|
emit()
|
||||||
|
emit()
|
||||||
|
|
||||||
|
assertEquals(4, changes.size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun observable_does_not_call_observers_after_they_are_disposed() {
|
||||||
|
val (observable, emit) = create()
|
||||||
|
val changes = mutableListOf<ChangeEvent<*>>()
|
||||||
|
|
||||||
|
withScope { scope ->
|
||||||
|
observable.observe(scope) { c ->
|
||||||
|
changes.add(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
emit()
|
||||||
|
|
||||||
|
assertEquals(1, changes.size)
|
||||||
|
|
||||||
|
emit()
|
||||||
|
emit()
|
||||||
|
emit()
|
||||||
|
|
||||||
|
assertEquals(4, changes.size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,14 +1,8 @@
|
|||||||
package world.phantasmal.observable
|
package world.phantasmal.observable
|
||||||
|
|
||||||
import world.phantasmal.testUtils.TestSuite
|
class SimpleEmitterTests : ObservableTests() {
|
||||||
import kotlin.test.Test
|
override fun create(): ObservableAndEmit {
|
||||||
|
val observable = SimpleEmitter<Any>()
|
||||||
class SimpleEmitterTests : TestSuite() {
|
return ObservableAndEmit(observable) { observable.emit(ChangeEvent(Any())) }
|
||||||
@Test
|
|
||||||
fun observable_tests() {
|
|
||||||
observableTests {
|
|
||||||
val observable = SimpleEmitter<Any>()
|
|
||||||
ObservableAndEmit(observable) { observable.emit(ChangeEvent(Any())) }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,56 +0,0 @@
|
|||||||
package world.phantasmal.observable
|
|
||||||
|
|
||||||
// Test suite for all Observable implementations.
|
|
||||||
// These functions are called from type-specific unit tests.
|
|
||||||
|
|
||||||
import world.phantasmal.observable.test.withScope
|
|
||||||
import kotlin.test.assertEquals
|
|
||||||
|
|
||||||
typealias ObservableAndEmit = Pair<Observable<*>, () -> Unit>
|
|
||||||
|
|
||||||
fun observableTests(create: () -> ObservableAndEmit) {
|
|
||||||
observableShouldCallObserversWhenEventsAreEmitted(create)
|
|
||||||
observableShouldNotCallObserversAfterTheyAreDisposed(create)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun observableShouldCallObserversWhenEventsAreEmitted(create: () -> ObservableAndEmit) {
|
|
||||||
val (observable, emit) = create()
|
|
||||||
val changes = mutableListOf<ChangeEvent<*>>()
|
|
||||||
|
|
||||||
withScope { scope ->
|
|
||||||
observable.observe(scope) { c ->
|
|
||||||
changes.add(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
emit()
|
|
||||||
|
|
||||||
assertEquals(1, changes.size)
|
|
||||||
|
|
||||||
emit()
|
|
||||||
emit()
|
|
||||||
emit()
|
|
||||||
|
|
||||||
assertEquals(4, changes.size)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun observableShouldNotCallObserversAfterTheyAreDisposed(create: () -> ObservableAndEmit) {
|
|
||||||
val (observable, emit) = create()
|
|
||||||
val changes = mutableListOf<ChangeEvent<*>>()
|
|
||||||
|
|
||||||
withScope { scope ->
|
|
||||||
observable.observe(scope) { c ->
|
|
||||||
changes.add(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
emit()
|
|
||||||
|
|
||||||
assertEquals(1, changes.size)
|
|
||||||
|
|
||||||
emit()
|
|
||||||
emit()
|
|
||||||
emit()
|
|
||||||
|
|
||||||
assertEquals(4, changes.size)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,27 +1,13 @@
|
|||||||
package world.phantasmal.observable.value
|
package world.phantasmal.observable.value
|
||||||
|
|
||||||
import world.phantasmal.observable.observableTests
|
class DelegatingValTests : RegularValTests() {
|
||||||
import world.phantasmal.testUtils.TestSuite
|
override fun create(): ValAndEmit<*> {
|
||||||
import kotlin.test.Test
|
|
||||||
|
|
||||||
class DelegatingValTests : TestSuite() {
|
|
||||||
@Test
|
|
||||||
fun observable_tests() {
|
|
||||||
observableTests(::create)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun val_tests() {
|
|
||||||
valTests(::create, ::createBoolean)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun create(): ValAndEmit<*> {
|
|
||||||
var v = 0
|
var v = 0
|
||||||
val value = DelegatingVal({ v }, { v = it })
|
val value = DelegatingVal({ v }, { v = it })
|
||||||
return ValAndEmit(value) { value.value += 2 }
|
return ValAndEmit(value) { value.value += 2 }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createBoolean(bool: Boolean): ValAndEmit<Boolean> {
|
override fun createBoolean(bool: Boolean): ValAndEmit<Boolean> {
|
||||||
var v = bool
|
var v = bool
|
||||||
val value = DelegatingVal({ v }, { v = it })
|
val value = DelegatingVal({ v }, { v = it })
|
||||||
return ValAndEmit(value) { value.value = !value.value }
|
return ValAndEmit(value) { value.value = !value.value }
|
||||||
|
@ -1,27 +1,13 @@
|
|||||||
package world.phantasmal.observable.value
|
package world.phantasmal.observable.value
|
||||||
|
|
||||||
import world.phantasmal.observable.observableTests
|
class DependentValTests : RegularValTests() {
|
||||||
import world.phantasmal.testUtils.TestSuite
|
override fun create(): ValAndEmit<*> {
|
||||||
import kotlin.test.Test
|
|
||||||
|
|
||||||
class DependentValTests : TestSuite() {
|
|
||||||
@Test
|
|
||||||
fun observable_tests() {
|
|
||||||
observableTests(::create)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun val_tests() {
|
|
||||||
valTests(::create, ::createBoolean)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun create(): ValAndEmit<*> {
|
|
||||||
val v = SimpleVal(0)
|
val v = SimpleVal(0)
|
||||||
val value = DependentVal(listOf(v)) { 2 * v.value }
|
val value = DependentVal(listOf(v)) { 2 * v.value }
|
||||||
return ValAndEmit(value) { v.value += 2 }
|
return ValAndEmit(value) { v.value += 2 }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createBoolean(bool: Boolean): ValAndEmit<Boolean> {
|
override fun createBoolean(bool: Boolean): ValAndEmit<Boolean> {
|
||||||
val v = SimpleVal(bool)
|
val v = SimpleVal(bool)
|
||||||
val value = DependentVal(listOf(v)) { v.value }
|
val value = DependentVal(listOf(v)) { v.value }
|
||||||
return ValAndEmit(value) { v.value = !v.value }
|
return ValAndEmit(value) { v.value = !v.value }
|
||||||
|
@ -0,0 +1,39 @@
|
|||||||
|
package world.phantasmal.observable.value
|
||||||
|
|
||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
import kotlin.test.assertFalse
|
||||||
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test suite for all [Val] implementations that aren't ListVals. There is a subclass of this suite
|
||||||
|
* for every non-ListVal [Val] implementation.
|
||||||
|
*/
|
||||||
|
abstract class RegularValTests : ValTests() {
|
||||||
|
protected abstract fun createBoolean(bool: Boolean): ValAndEmit<Boolean>
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun val_boolean_extensions() {
|
||||||
|
listOf(true, false).forEach { bool ->
|
||||||
|
val (value) = createBoolean(bool)
|
||||||
|
|
||||||
|
// Test the test setup first.
|
||||||
|
assertEquals(bool, value.value)
|
||||||
|
|
||||||
|
// Test `and`.
|
||||||
|
assertEquals(bool, (value and trueVal()).value)
|
||||||
|
assertFalse((value and falseVal()).value)
|
||||||
|
|
||||||
|
// Test `or`.
|
||||||
|
assertTrue((value or trueVal()).value)
|
||||||
|
assertEquals(bool, (value or falseVal()).value)
|
||||||
|
|
||||||
|
// Test `xor`.
|
||||||
|
assertEquals(!bool, (value xor trueVal()).value)
|
||||||
|
assertEquals(bool, (value xor falseVal()).value)
|
||||||
|
|
||||||
|
// Test `!` (unary not).
|
||||||
|
assertEquals(!bool, (!value).value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,26 +1,12 @@
|
|||||||
package world.phantasmal.observable.value
|
package world.phantasmal.observable.value
|
||||||
|
|
||||||
import world.phantasmal.observable.observableTests
|
class SimpleValTests : RegularValTests() {
|
||||||
import world.phantasmal.testUtils.TestSuite
|
override fun create(): ValAndEmit<*> {
|
||||||
import kotlin.test.Test
|
|
||||||
|
|
||||||
class SimpleValTests : TestSuite() {
|
|
||||||
@Test
|
|
||||||
fun observable_tests() {
|
|
||||||
observableTests(::create)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun val_tests() {
|
|
||||||
valTests(::create, ::createBoolean)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun create(): ValAndEmit<*> {
|
|
||||||
val value = SimpleVal(1)
|
val value = SimpleVal(1)
|
||||||
return ValAndEmit(value) { value.value += 2 }
|
return ValAndEmit(value) { value.value += 2 }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createBoolean(bool: Boolean): ValAndEmit<Boolean> {
|
override fun createBoolean(bool: Boolean): ValAndEmit<Boolean> {
|
||||||
val value = SimpleVal(bool)
|
val value = SimpleVal(bool)
|
||||||
return ValAndEmit(value) { value.value = !value.value }
|
return ValAndEmit(value) { value.value = !value.value }
|
||||||
}
|
}
|
||||||
|
@ -1,80 +0,0 @@
|
|||||||
package world.phantasmal.observable.value
|
|
||||||
|
|
||||||
// Test suite for all Val implementations.
|
|
||||||
// These functions are called from type-specific unit tests.
|
|
||||||
|
|
||||||
import world.phantasmal.observable.ChangeEvent
|
|
||||||
import world.phantasmal.observable.test.withScope
|
|
||||||
import kotlin.test.assertEquals
|
|
||||||
import kotlin.test.assertFalse
|
|
||||||
import kotlin.test.assertTrue
|
|
||||||
|
|
||||||
typealias ValAndEmit<T> = Pair<Val<T>, () -> Unit>
|
|
||||||
|
|
||||||
fun valTests(
|
|
||||||
create: () -> ValAndEmit<*>,
|
|
||||||
createBoolean: ((Boolean) -> ValAndEmit<Boolean>)?,
|
|
||||||
) {
|
|
||||||
valShouldRespectCallNowArgument(create)
|
|
||||||
|
|
||||||
if (createBoolean != null) {
|
|
||||||
testValBooleanExtensions(createBoolean)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* When Val::observe is called with callNow = true, it should call the observer immediately.
|
|
||||||
* Otherwise it should only call the observer when it changes.
|
|
||||||
*/
|
|
||||||
private fun valShouldRespectCallNowArgument(create: () -> ValAndEmit<*>) {
|
|
||||||
val (value, emit) = create()
|
|
||||||
val changes = mutableListOf<ChangeEvent<*>>()
|
|
||||||
|
|
||||||
withScope { scope ->
|
|
||||||
// Test callNow = false
|
|
||||||
value.observe(scope, callNow = false) { c ->
|
|
||||||
changes.add(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
emit()
|
|
||||||
|
|
||||||
assertEquals(1, changes.size)
|
|
||||||
}
|
|
||||||
|
|
||||||
withScope { scope ->
|
|
||||||
// Test callNow = true
|
|
||||||
changes.clear()
|
|
||||||
|
|
||||||
value.observe(scope, callNow = true) { c ->
|
|
||||||
changes.add(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
emit()
|
|
||||||
|
|
||||||
assertEquals(2, changes.size)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun testValBooleanExtensions(create: (Boolean) -> ValAndEmit<Boolean>) {
|
|
||||||
listOf(true, false).forEach { bool ->
|
|
||||||
val (value) = create(bool)
|
|
||||||
|
|
||||||
// Test the test setup first.
|
|
||||||
assertEquals(bool, value.value)
|
|
||||||
|
|
||||||
// Test `and`.
|
|
||||||
assertEquals(bool, (value and trueVal()).value)
|
|
||||||
assertFalse((value and falseVal()).value)
|
|
||||||
|
|
||||||
// Test `or`.
|
|
||||||
assertTrue((value or trueVal()).value)
|
|
||||||
assertEquals(bool, (value or falseVal()).value)
|
|
||||||
|
|
||||||
// Test `xor`.
|
|
||||||
assertEquals(!bool, (value xor trueVal()).value)
|
|
||||||
assertEquals(bool, (value xor falseVal()).value)
|
|
||||||
|
|
||||||
// Test `!` (unary not).
|
|
||||||
assertEquals(!bool, (!value).value)
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,51 @@
|
|||||||
|
package world.phantasmal.observable.value
|
||||||
|
|
||||||
|
import world.phantasmal.observable.ChangeEvent
|
||||||
|
import world.phantasmal.observable.ObservableTests
|
||||||
|
import world.phantasmal.observable.test.withScope
|
||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
typealias ValAndEmit<T> = Pair<Val<T>, () -> Unit>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test suite for all [Val] implementations. There is a subclass of this suite for every [Val]
|
||||||
|
* implementation.
|
||||||
|
*/
|
||||||
|
abstract class ValTests : ObservableTests() {
|
||||||
|
abstract override fun create(): ValAndEmit<*>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When [Val.observe] is called with callNow = true, it should call the observer immediately.
|
||||||
|
* Otherwise it should only call the observer when it changes.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
fun val_respects_call_now_argument() {
|
||||||
|
val (value, emit) = create()
|
||||||
|
val changes = mutableListOf<ChangeEvent<*>>()
|
||||||
|
|
||||||
|
withScope { scope ->
|
||||||
|
// Test callNow = false
|
||||||
|
value.observe(scope, callNow = false) { c ->
|
||||||
|
changes.add(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
emit()
|
||||||
|
|
||||||
|
assertEquals(1, changes.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
withScope { scope ->
|
||||||
|
// Test callNow = true
|
||||||
|
changes.clear()
|
||||||
|
|
||||||
|
value.observe(scope, callNow = true) { c ->
|
||||||
|
changes.add(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
emit()
|
||||||
|
|
||||||
|
assertEquals(2, changes.size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
package world.phantasmal.observable.value.list
|
||||||
|
|
||||||
|
import world.phantasmal.observable.test.withScope
|
||||||
|
import world.phantasmal.observable.value.ValTests
|
||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
typealias ListValAndAdd = Pair<ListVal<*>, () -> Unit>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test suite for all [ListVal] implementations. There is a subclass of this suite for every
|
||||||
|
* [ListVal] implementation.
|
||||||
|
*/
|
||||||
|
abstract class ListValTests : ValTests() {
|
||||||
|
abstract override fun create(): ListValAndAdd
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun listVal_updates_sizeVal_correctly() {
|
||||||
|
val (list: List<*>, add) = create()
|
||||||
|
|
||||||
|
assertEquals(0, list.sizeVal.value)
|
||||||
|
|
||||||
|
var observedSize = 0
|
||||||
|
|
||||||
|
withScope { scope ->
|
||||||
|
list.sizeVal.observe(scope) { observedSize = it.value }
|
||||||
|
|
||||||
|
for (i in 1..3) {
|
||||||
|
add()
|
||||||
|
|
||||||
|
assertEquals(i, list.sizeVal.value)
|
||||||
|
assertEquals(i, observedSize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,27 +1,7 @@
|
|||||||
package world.phantasmal.observable.value.list
|
package world.phantasmal.observable.value.list
|
||||||
|
|
||||||
import world.phantasmal.observable.observableTests
|
class SimpleListValTests : ListValTests() {
|
||||||
import world.phantasmal.observable.value.valTests
|
override fun create(): ListValAndAdd {
|
||||||
import world.phantasmal.testUtils.TestSuite
|
|
||||||
import kotlin.test.Test
|
|
||||||
|
|
||||||
class SimpleListValTests : TestSuite() {
|
|
||||||
@Test
|
|
||||||
fun observable_tests() {
|
|
||||||
observableTests(::create)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun val_tests() {
|
|
||||||
valTests(::create, createBoolean = null)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun list_val_tests() {
|
|
||||||
listValTests(::create)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun create(): ListValAndAdd {
|
|
||||||
val value = SimpleListVal(mutableListOf<Int>())
|
val value = SimpleListVal(mutableListOf<Int>())
|
||||||
return ListValAndAdd(value) { value.add(7) }
|
return ListValAndAdd(value) { value.add(7) }
|
||||||
}
|
}
|
||||||
|
@ -1,32 +0,0 @@
|
|||||||
package world.phantasmal.observable.value.list
|
|
||||||
|
|
||||||
// Test suite for all ListVal implementations.
|
|
||||||
// These functions are called from type-specific unit tests.
|
|
||||||
|
|
||||||
import world.phantasmal.observable.test.withScope
|
|
||||||
import kotlin.test.assertEquals
|
|
||||||
|
|
||||||
typealias ListValAndAdd = Pair<ListVal<*>, () -> Unit>
|
|
||||||
|
|
||||||
fun listValTests(create: () -> ListValAndAdd) {
|
|
||||||
listValShouldUpdateSizeValCorrectly(create)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun listValShouldUpdateSizeValCorrectly(create: () -> ListValAndAdd) {
|
|
||||||
val (list: List<*>, add) = create()
|
|
||||||
|
|
||||||
assertEquals(0, list.sizeVal.value)
|
|
||||||
|
|
||||||
var observedSize = 0
|
|
||||||
|
|
||||||
withScope { scope ->
|
|
||||||
list.sizeVal.observe(scope) { observedSize = it.value }
|
|
||||||
|
|
||||||
for (i in 1..3) {
|
|
||||||
add()
|
|
||||||
|
|
||||||
assertEquals(i, list.sizeVal.value)
|
|
||||||
assertEquals(i, observedSize)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user