Introduce EventHandlerPositionManager with (failing) tests
This commit is contained in:
parent
1f10e52131
commit
839fe1100f
16 changed files with 270 additions and 70 deletions
|
@ -14,10 +14,10 @@ abstract class EventEmitter<E: Event> {
|
|||
*/
|
||||
open fun <T: E> on(
|
||||
eventType: KClass<T>,
|
||||
priority: EventPriority = EventPriority.NORMAL,
|
||||
handlerPosition: EventHandlerPosition = EventHandlerPosition.NORMAL,
|
||||
fn: suspend (event: T) -> Unit
|
||||
): EventHandler<T> {
|
||||
val handler = EventHandler(this, eventType, Plugin.getCalling(), priority, fn)
|
||||
val handler = EventHandler(this, eventType, Plugin.getCalling(), handlerPosition, fn)
|
||||
handlers.add(handler)
|
||||
return handler
|
||||
}
|
||||
|
@ -29,12 +29,12 @@ abstract class EventEmitter<E: Event> {
|
|||
*/
|
||||
fun <T: E> onRemovable(
|
||||
eventType: KClass<T>,
|
||||
priority: EventPriority = EventPriority.NORMAL,
|
||||
handlerPosition: EventHandlerPosition = EventHandlerPosition.NORMAL,
|
||||
fn: suspend (event: T, remove: () -> Unit) -> Unit
|
||||
): EventHandler<T> {
|
||||
lateinit var handler: EventHandler<T>
|
||||
|
||||
handler = on(eventType, priority) {
|
||||
handler = on(eventType, handlerPosition) {
|
||||
fn(it, handler::remove)
|
||||
}
|
||||
|
||||
|
@ -46,16 +46,16 @@ abstract class EventEmitter<E: Event> {
|
|||
*/
|
||||
fun <T: E> once(
|
||||
eventType: KClass<T>,
|
||||
priority: EventPriority = EventPriority.NORMAL,
|
||||
handlerPosition: EventHandlerPosition = EventHandlerPosition.NORMAL,
|
||||
fn: suspend (event: T) -> Unit
|
||||
): EventHandler<T> = onRemovable(eventType, priority) { event, remove -> remove(); fn(event) }
|
||||
): EventHandler<T> = onRemovable(eventType, handlerPosition) { event, remove -> remove(); fn(event) }
|
||||
|
||||
/**
|
||||
* Suspends until an event of type [T] is emitted and returns it.
|
||||
*/
|
||||
suspend fun <T: E> waitFor(eventType: KClass<T>, priority: EventPriority = EventPriority.NORMAL): T =
|
||||
suspend fun <T: E> waitFor(eventType: KClass<T>, handlerPosition: EventHandlerPosition = EventHandlerPosition.NORMAL): T =
|
||||
suspendCancellableCoroutine { c ->
|
||||
val handler = EventHandler(this, eventType, Plugin.getCalling(), priority) {
|
||||
val handler = EventHandler(this, eventType, Plugin.getCalling(), handlerPosition) {
|
||||
c.resume(it)
|
||||
}
|
||||
|
||||
|
@ -75,9 +75,9 @@ abstract class EventEmitter<E: Event> {
|
|||
* Registers a new event handler.
|
||||
*/
|
||||
inline fun <reified T: E> on(
|
||||
priority: EventPriority = EventPriority.NORMAL,
|
||||
handlerPosition: EventHandlerPosition = EventHandlerPosition.NORMAL,
|
||||
noinline fn: suspend (event: T) -> Unit
|
||||
): EventHandler<T> = on(T::class, priority, fn)
|
||||
): EventHandler<T> = on(T::class, handlerPosition, fn)
|
||||
|
||||
/**
|
||||
* Registers a new event handler.
|
||||
|
@ -85,28 +85,28 @@ abstract class EventEmitter<E: Event> {
|
|||
* The handler function receives a function to remove the event handler as second parameter.
|
||||
*/
|
||||
inline fun <reified T: E> onRemovable(
|
||||
priority: EventPriority = EventPriority.NORMAL,
|
||||
handlerPosition: EventHandlerPosition = EventHandlerPosition.NORMAL,
|
||||
noinline fn: suspend (event: T, remove: () -> Unit) -> Unit
|
||||
): EventHandler<T> = onRemovable(T::class, priority, fn)
|
||||
): EventHandler<T> = onRemovable(T::class, handlerPosition, fn)
|
||||
|
||||
/**
|
||||
* Registers a new event handler which is automatically removed after it was invoked.
|
||||
*/
|
||||
inline fun <reified T: E> once(
|
||||
priority: EventPriority = EventPriority.NORMAL,
|
||||
handlerPosition: EventHandlerPosition = EventHandlerPosition.NORMAL,
|
||||
noinline fn: suspend (event: T) -> Unit
|
||||
): EventHandler<T> = once(T::class, priority, fn)
|
||||
): EventHandler<T> = once(T::class, handlerPosition, fn)
|
||||
|
||||
/**
|
||||
* Suspends until an event of type [T] is emitted and returns it.
|
||||
*/
|
||||
suspend inline fun <reified T: E> waitFor(priority: EventPriority = EventPriority.NORMAL): T =
|
||||
waitFor(T::class, priority)
|
||||
suspend inline fun <reified T: E> waitFor(position: EventHandlerPosition = EventHandlerPosition.NORMAL): T =
|
||||
waitFor(T::class, position)
|
||||
}
|
||||
|
||||
abstract class EventBus: EventEmitter<Event>() {
|
||||
/**
|
||||
* Invokes all previously registered event handlers sorted by their priority.
|
||||
* Invokes all previously registered event handlers sorted by their handlerPosition.
|
||||
*
|
||||
* The coroutine context is inherited.
|
||||
*
|
||||
|
|
|
@ -6,7 +6,7 @@ import kotlin.reflect.KClass
|
|||
class EventBusWrapper<E>(private val target: Any): EventEmitter<TargetedEvent<E>>() {
|
||||
override fun <T : TargetedEvent<E>> on(
|
||||
eventType: KClass<T>,
|
||||
priority: EventPriority,
|
||||
handlerPosition: EventHandlerPosition,
|
||||
fn: suspend (event: T) -> Unit
|
||||
): EventHandler<T> = Blokk.eventBus.on(eventType, priority) { event -> if (event.target == target) fn(event) }
|
||||
): EventHandler<T> = Blokk.eventBus.on(eventType, handlerPosition) { event -> if (event.target == target) fn(event) }
|
||||
}
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
package space.blokk.event
|
||||
|
||||
import space.blokk.plugin.Plugin
|
||||
import java.util.*
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
data class EventHandler<T : Event> internal constructor(
|
||||
private val eventEmitter: EventEmitter<*>,
|
||||
val eventType: KClass<T>,
|
||||
val plugin: Plugin?,
|
||||
val priority: EventPriority,
|
||||
val handlerPosition: EventHandlerPosition,
|
||||
val fn: suspend (event: T) -> Unit
|
||||
): Comparable<EventHandler<*>> {
|
||||
/**
|
||||
|
@ -15,5 +16,9 @@ data class EventHandler<T : Event> internal constructor(
|
|||
*/
|
||||
fun remove(): Boolean = eventEmitter.remove(this)
|
||||
|
||||
override fun compareTo(other: EventHandler<*>): Int = priority.compareTo(other.priority)
|
||||
override fun compareTo(other: EventHandler<*>): Int {
|
||||
val positionComparison = handlerPosition.compareTo(other.handlerPosition)
|
||||
|
||||
return if (positionComparison == 0) hashCode() - other.hashCode() else positionComparison
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
package space.blokk.event
|
||||
|
||||
import space.blokk.Blokk
|
||||
|
||||
open class EventHandlerPosition: Comparable<EventHandlerPosition> {
|
||||
override fun compareTo(other: EventHandlerPosition): Int =
|
||||
Blokk.eventHandlerPositions.positionOf(this) - Blokk.eventHandlerPositions.positionOf(other)
|
||||
|
||||
object FIRST: EventHandlerPosition()
|
||||
object NORMAL: EventHandlerPosition()
|
||||
object LAST: EventHandlerPosition()
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package space.blokk.event
|
||||
|
||||
interface EventHandlerPositionManager {
|
||||
fun positionOf(eventHandlerPosition: EventHandlerPosition): Int
|
||||
fun positionOfOrNull(eventHandlerPosition: EventHandlerPosition): Int?
|
||||
|
||||
/**
|
||||
* Shorthand for `insertBefore(EventHandlerPosition.LAST, new)`.
|
||||
*/
|
||||
fun insert(vararg positions: EventHandlerPosition)
|
||||
|
||||
/**
|
||||
* Inserts [positions] before [existing].
|
||||
*
|
||||
* @throws IllegalArgumentException When [existing] is [EventHandlerPosition.FIRST]
|
||||
*/
|
||||
fun insertBefore(existing: EventHandlerPosition, vararg positions: EventHandlerPosition)
|
||||
|
||||
/**
|
||||
* Inserts [positions] after [existing].
|
||||
*
|
||||
* @throws IllegalArgumentException When [existing] is [EventHandlerPosition.LAST]
|
||||
*/
|
||||
fun insertAfter(existing: EventHandlerPosition, vararg positions: EventHandlerPosition)
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
package space.blokk.event
|
||||
|
||||
// TODO: Develop a new event ordering concept
|
||||
enum class EventPriority {
|
||||
LOWEST,
|
||||
LOW,
|
||||
LOWER,
|
||||
NORMAL,
|
||||
HIGHER,
|
||||
HIGH,
|
||||
HIGHEST,
|
||||
MONITOR,
|
||||
INTERNAL
|
||||
}
|
|
@ -4,6 +4,7 @@ import space.blokk.Registry
|
|||
import space.blokk.Scheduler
|
||||
import space.blokk.command.Command
|
||||
import space.blokk.event.EventBus
|
||||
import space.blokk.event.EventHandlerPositionManager
|
||||
import space.blokk.logging.Logger
|
||||
import space.blokk.logging.LoggingOutputProvider
|
||||
import space.blokk.net.Session
|
||||
|
@ -17,6 +18,7 @@ import kotlin.coroutines.CoroutineContext
|
|||
|
||||
interface Server {
|
||||
val eventBus: EventBus
|
||||
val eventHandlerPositions: EventHandlerPositionManager
|
||||
|
||||
/**
|
||||
* [CoroutineContext] confined to the server thread.
|
||||
|
|
|
@ -2,9 +2,6 @@ package space.blokk.world.block
|
|||
|
||||
import space.blokk.NamespacedID
|
||||
|
||||
/**
|
||||
* Material: [DAYLIGHT_DETECTOR][Material.DAYLIGHT_DETECTOR]
|
||||
*/
|
||||
data class DaylightDetector(
|
||||
@Attribute
|
||||
val inverted: Boolean = false,
|
||||
|
@ -12,9 +9,9 @@ data class DaylightDetector(
|
|||
val power: Int
|
||||
) : Block() {
|
||||
companion object : Material<DaylightDetector> by material(
|
||||
1,
|
||||
333,
|
||||
NamespacedID("minecraft:daylight_detector"),
|
||||
6158,
|
||||
6698,
|
||||
0.2f,
|
||||
true,
|
||||
0,
|
||||
|
|
|
@ -6,12 +6,12 @@ import strikt.assertions.isEqualTo
|
|||
|
||||
class BlockCodecTest {
|
||||
@Test
|
||||
fun `getStateID returns 6158 for DaylightDetector(inverted=true, power=0)`() {
|
||||
expectThat(DaylightDetector.codec.getStateID(DaylightDetector(true, 0))).isEqualTo(6158)
|
||||
fun `getStateID returns 6698 for DaylightDetector(inverted=true, power=0)`() {
|
||||
expectThat(DaylightDetector.codec.getStateID(DaylightDetector(true, 0))).isEqualTo(6698)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getStateID returns 6189 for DaylightDetector(inverted=false, power=15)`() {
|
||||
expectThat(DaylightDetector.codec.getStateID(DaylightDetector(false, 15))).isEqualTo(6189)
|
||||
fun `getStateID returns 6729 for DaylightDetector(inverted=false, power=15)`() {
|
||||
expectThat(DaylightDetector.codec.getStateID(DaylightDetector(false, 15))).isEqualTo(6729)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ version = rootProject.version
|
|||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
jcenter()
|
||||
maven("https://jitpack.io")
|
||||
}
|
||||
|
||||
|
@ -16,6 +17,8 @@ val coroutinesVersion = properties["version.kotlinx-coroutines"].toString()
|
|||
val slf4jVersion = properties["version.slf4j"].toString()
|
||||
val nettyVersion = properties["version.netty"].toString()
|
||||
val moshiVersion = properties["version.moshi"].toString()
|
||||
val junitVersion = properties["version.junit"].toString()
|
||||
val striktVersion = properties["version.strikt"].toString()
|
||||
|
||||
dependencies {
|
||||
// Kotlin
|
||||
|
@ -46,6 +49,11 @@ dependencies {
|
|||
implementation("com.sksamuel.hoplite:hoplite-core:1.3.9")
|
||||
implementation("com.sksamuel.hoplite:hoplite-yaml:1.3.9")
|
||||
kapt("com.squareup.moshi:moshi-kotlin-codegen:${moshiVersion}")
|
||||
|
||||
// Testing
|
||||
testImplementation("io.strikt:strikt-core:${striktVersion}")
|
||||
testImplementation("org.junit.jupiter:junit-jupiter-api:${junitVersion}")
|
||||
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${junitVersion}")
|
||||
}
|
||||
|
||||
tasks {
|
||||
|
@ -57,6 +65,10 @@ tasks {
|
|||
)
|
||||
}
|
||||
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
shadowJar {
|
||||
archiveClassifier.set(null as String?)
|
||||
|
||||
|
|
|
@ -39,6 +39,8 @@ class BlokkScheduler : Scheduler {
|
|||
fun startTicking() {
|
||||
val interval = 1000L / Server.TICKS_PER_SECOND
|
||||
|
||||
// TODO: Move executor initialization here
|
||||
|
||||
executor.scheduleAtFixedRate({
|
||||
runBlocking {
|
||||
val startTime = System.currentTimeMillis()
|
||||
|
|
|
@ -10,7 +10,9 @@ import kotlinx.coroutines.runBlocking
|
|||
import space.blokk.command.Command
|
||||
import space.blokk.config.BlokkConfig
|
||||
import space.blokk.event.BlokkEventBus
|
||||
import space.blokk.event.BlokkEventHandlerPositionManager
|
||||
import space.blokk.event.EventBus
|
||||
import space.blokk.event.EventHandlerPositionManager
|
||||
import space.blokk.logging.BlokkLoggingOutputProvider
|
||||
import space.blokk.logging.Logger
|
||||
import space.blokk.net.BlokkSocketServer
|
||||
|
@ -28,6 +30,8 @@ import java.util.concurrent.Executors
|
|||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
// TODO: Consider using DI because this improves testability
|
||||
|
||||
class BlokkServer internal constructor() : Server {
|
||||
val logger = Logger("Server")
|
||||
private val socketServer = BlokkSocketServer(this)
|
||||
|
@ -44,8 +48,6 @@ class BlokkServer internal constructor() : Server {
|
|||
override val coroutineContext: CoroutineContext =
|
||||
CoroutineName("Server") + Executors.newSingleThreadExecutor().asCoroutineDispatcher() + SupervisorJob()
|
||||
|
||||
override val eventBus = BlokkEventBus(this)
|
||||
|
||||
override val sessions by socketServer::sessions
|
||||
override val players get() = sessions.mapNotNull { it.player }
|
||||
|
||||
|
@ -82,13 +84,8 @@ class BlokkServer internal constructor() : Server {
|
|||
override val minimumLogLevel = config.minLogLevel
|
||||
override val developmentMode: Boolean = config.developmentMode
|
||||
|
||||
init {
|
||||
val clazz = Class.forName("space.blokk.BlokkKt")
|
||||
val field = clazz.getDeclaredField("serverInstance")
|
||||
field.isAccessible = true
|
||||
field.set(null, this)
|
||||
field.isAccessible = false
|
||||
}
|
||||
override val eventBus = BlokkEventBus(developmentMode)
|
||||
override val eventHandlerPositions = BlokkEventHandlerPositionManager()
|
||||
|
||||
private fun failInitialization(t: Throwable): Nothing {
|
||||
logger.error("Server initialization failed:", t)
|
||||
|
@ -120,7 +117,15 @@ class BlokkServer internal constructor() : Server {
|
|||
|
||||
@JvmStatic
|
||||
fun main(args: Array<String>) {
|
||||
BlokkServer().start()
|
||||
BlokkServer().also { setServerInstance(it) }.start()
|
||||
}
|
||||
|
||||
fun setServerInstance(instance: Server) {
|
||||
val clazz = Class.forName("space.blokk.BlokkKt")
|
||||
val field = clazz.getDeclaredField("serverInstance")
|
||||
field.isAccessible = true
|
||||
field.set(null, instance)
|
||||
field.isAccessible = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,18 +10,18 @@ import kotlin.coroutines.resume
|
|||
import kotlin.reflect.KClass
|
||||
import kotlin.system.measureTimeMillis
|
||||
|
||||
class BlokkEventBus(private val server: BlokkServer) : EventBus() {
|
||||
class BlokkEventBus(private val developmentMode: Boolean) : EventBus() {
|
||||
private val logger = Logger("EventBus", false)
|
||||
|
||||
/**
|
||||
* Invokes all previously registered event handlers sorted by their priority.
|
||||
* Invokes all previously registered event handlers sorted by their handlerPosition.
|
||||
*
|
||||
* The coroutine context is inherited.
|
||||
*
|
||||
* @return [event]
|
||||
*/
|
||||
override suspend fun <T : Event> emit(event: T): T {
|
||||
if (server.developmentMode) {
|
||||
if (developmentMode) {
|
||||
var count = 0
|
||||
val time = measureTimeMillis {
|
||||
for (handler in handlers) {
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
package space.blokk.event
|
||||
|
||||
import java.lang.IllegalArgumentException
|
||||
|
||||
class BlokkEventHandlerPositionManager: EventHandlerPositionManager {
|
||||
internal val positions = ArrayList<EventHandlerPosition>()
|
||||
|
||||
init {
|
||||
positions.add(EventHandlerPosition.FIRST)
|
||||
positions.add(EventHandlerPosition.NORMAL)
|
||||
positions.add(EventHandlerPosition.LAST)
|
||||
}
|
||||
|
||||
override fun positionOfOrNull(eventHandlerPosition: EventHandlerPosition): Int? =
|
||||
positions.indexOf(eventHandlerPosition).let { if (it == -1) null else it }
|
||||
|
||||
override fun positionOf(eventHandlerPosition: EventHandlerPosition): Int = positionOfOrNull(eventHandlerPosition)
|
||||
?: throw IllegalArgumentException("eventHandlerPosition was not registered")
|
||||
|
||||
override fun insert(vararg positions: EventHandlerPosition) {
|
||||
insertBefore(EventHandlerPosition.LAST, *positions)
|
||||
}
|
||||
|
||||
override fun insertBefore(existing: EventHandlerPosition, vararg positions: EventHandlerPosition) {
|
||||
if (existing == EventHandlerPosition.FIRST)
|
||||
throw IllegalArgumentException("You cannot insert positions before EventHandlerPosition.FIRST")
|
||||
|
||||
val index = positionOfOrNull(existing) ?: throw IllegalArgumentException("existing was not registered")
|
||||
insertWithCheck(index, positions.distinct())
|
||||
}
|
||||
|
||||
override fun insertAfter(existing: EventHandlerPosition, vararg positions: EventHandlerPosition) {
|
||||
if (existing == EventHandlerPosition.LAST)
|
||||
throw IllegalArgumentException("You cannot insert positions after EventHandlerPosition.LAST")
|
||||
|
||||
val index = positionOfOrNull(existing) ?: throw IllegalArgumentException("existing was not registered")
|
||||
insertWithCheck(index + 1, positions.distinct())
|
||||
}
|
||||
|
||||
private fun insertWithCheck(index: Int, positions: List<EventHandlerPosition>) {
|
||||
for (position in positions) {
|
||||
if (this.positions.indexOf(position) != -1) throw IllegalArgumentException("$position was already registered")
|
||||
}
|
||||
|
||||
this.positions.addAll(index, positions)
|
||||
}
|
||||
}
|
|
@ -4,21 +4,24 @@ import ch.qos.logback.classic.Level
|
|||
import ch.qos.logback.classic.spi.ILoggingEvent
|
||||
import ch.qos.logback.core.AppenderBase
|
||||
import space.blokk.Blokk
|
||||
import java.lang.Exception
|
||||
|
||||
class LogbackAppender : AppenderBase<ILoggingEvent>() {
|
||||
override fun append(event: ILoggingEvent) {
|
||||
Blokk.loggingOutputProvider.log(
|
||||
true,
|
||||
event.loggerName,
|
||||
when (event.level) {
|
||||
Level.TRACE -> Logger.Level.TRACE
|
||||
Level.DEBUG -> Logger.Level.DEBUG
|
||||
Level.INFO -> Logger.Level.INFO
|
||||
Level.WARN -> Logger.Level.WARN
|
||||
Level.ERROR -> Logger.Level.ERROR
|
||||
else -> error("Should never happen")
|
||||
},
|
||||
event.message
|
||||
)
|
||||
try {
|
||||
Blokk.loggingOutputProvider.log(
|
||||
true,
|
||||
event.loggerName,
|
||||
when (event.level) {
|
||||
Level.TRACE -> Logger.Level.TRACE
|
||||
Level.DEBUG -> Logger.Level.DEBUG
|
||||
Level.INFO -> Logger.Level.INFO
|
||||
Level.WARN -> Logger.Level.WARN
|
||||
Level.ERROR -> Logger.Level.ERROR
|
||||
else -> error("Should never happen")
|
||||
},
|
||||
event.message
|
||||
)
|
||||
} catch(e: Throwable) {}
|
||||
}
|
||||
}
|
||||
|
|
104
blokk-server/src/test/kotlin/space/blokk/event/EventBusTest.kt
Normal file
104
blokk-server/src/test/kotlin/space/blokk/event/EventBusTest.kt
Normal file
|
@ -0,0 +1,104 @@
|
|||
package space.blokk.event
|
||||
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import space.blokk.Blokk
|
||||
import space.blokk.BlokkServer
|
||||
import strikt.api.expectThat
|
||||
import strikt.api.expectThrows
|
||||
import strikt.assertions.isEqualTo
|
||||
import java.lang.IllegalArgumentException
|
||||
|
||||
class EventBusTest {
|
||||
class TestEvent: Event() {
|
||||
private var count = 0
|
||||
|
||||
fun test(expectedCount: Int) {
|
||||
expectThat(count).isEqualTo(expectedCount)
|
||||
count++
|
||||
}
|
||||
}
|
||||
|
||||
object Position1 : EventHandlerPosition()
|
||||
object Position2 : EventHandlerPosition()
|
||||
object Position3 : EventHandlerPosition()
|
||||
object Position4 : EventHandlerPosition()
|
||||
object Position5 : EventHandlerPosition()
|
||||
object Position6 : EventHandlerPosition()
|
||||
|
||||
@BeforeEach
|
||||
fun initServer() {
|
||||
// Does not work because Blokk will not be reinitialized. When we use DI, we do no longer need this.
|
||||
BlokkServer.setServerInstance(BlokkServer())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `handler positions cannot be inserted more than once`() {
|
||||
Blokk.eventHandlerPositions.insert(Position1)
|
||||
|
||||
expectThrows<IllegalArgumentException> { Blokk.eventHandlerPositions.insert(Position1) }
|
||||
expectThrows<IllegalArgumentException> { Blokk.eventHandlerPositions.insertAfter(EventHandlerPosition.FIRST, Position1) }
|
||||
expectThrows<IllegalArgumentException> { Blokk.eventHandlerPositions.insertBefore(EventHandlerPosition.LAST, Position1) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `handler positions are inserted where they should be`() {
|
||||
val expectedOrder = listOf(
|
||||
EventHandlerPosition.FIRST,
|
||||
Position2,
|
||||
Position4,
|
||||
Position1,
|
||||
Position5,
|
||||
EventHandlerPosition.NORMAL,
|
||||
Position3,
|
||||
Position6,
|
||||
EventHandlerPosition.LAST
|
||||
)
|
||||
|
||||
val actualOrder = (Blokk.eventHandlerPositions as BlokkEventHandlerPositionManager).positions
|
||||
println(actualOrder)
|
||||
|
||||
Blokk.eventHandlerPositions.insertAfter(EventHandlerPosition.FIRST, Position1, Position5)
|
||||
println(actualOrder)
|
||||
Blokk.eventHandlerPositions.insertBefore(Position1, Position2, Position4)
|
||||
println(actualOrder)
|
||||
Blokk.eventHandlerPositions.insert(Position3, Position6)
|
||||
println(actualOrder)
|
||||
|
||||
actualOrder.forEachIndexed { index, position ->
|
||||
expectThat(index).isEqualTo(expectedOrder.indexOf(position))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `handler positions cannot be inserted before the first or after the last`() {
|
||||
expectThrows<IllegalArgumentException> {
|
||||
Blokk.eventHandlerPositions.insertBefore(EventHandlerPosition.FIRST, Position1)
|
||||
}
|
||||
|
||||
expectThrows<IllegalArgumentException> {
|
||||
Blokk.eventHandlerPositions.insertAfter(EventHandlerPosition.LAST, Position1)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `handlers are invoked in the right order`() {
|
||||
val order = listOf(
|
||||
EventHandlerPosition.FIRST,
|
||||
Position1,
|
||||
EventHandlerPosition.NORMAL,
|
||||
Position2,
|
||||
Position3,
|
||||
EventHandlerPosition.LAST
|
||||
)
|
||||
|
||||
Blokk.eventHandlerPositions.insertAfter(EventHandlerPosition.FIRST, Position1)
|
||||
Blokk.eventHandlerPositions.insertBefore(EventHandlerPosition.NORMAL, Position2)
|
||||
Blokk.eventHandlerPositions.insert(Position3)
|
||||
|
||||
order.shuffled().forEach { pos -> Blokk.eventBus.on<TestEvent>(pos) { it.test(order.indexOf(pos)) } }
|
||||
|
||||
runBlocking { Blokk.eventBus.emit(TestEvent()) }
|
||||
}
|
||||
}
|
Reference in a new issue