aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md5
-rw-r--r--src/main/kotlin/me/bush/illnamethislater/Config.kt10
-rw-r--r--src/main/kotlin/me/bush/illnamethislater/EventBus.kt47
-rw-r--r--src/main/kotlin/me/bush/illnamethislater/EventListener.kt6
-rw-r--r--src/main/kotlin/me/bush/illnamethislater/Listener.kt2
-rw-r--r--src/main/kotlin/me/bush/illnamethislater/ListenerGroup.kt41
-rw-r--r--src/main/kotlin/me/bush/illnamethislater/ReflectUtil.kt45
-rw-r--r--src/test/java/JavaTest.java62
-rw-r--r--src/test/java/TestJava.java80
-rw-r--r--src/test/kotlin/KotlinTest.kt252
-rw-r--r--src/test/kotlin/TestKotlin.kt215
11 files changed, 402 insertions, 363 deletions
diff --git a/README.md b/README.md
index 389cbfd..e43d9f5 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1 @@
-#EVENTBUS-KOTLIN
- this isnt done yet i just wanted to get the gh up
-
-still not done dfsdofdfomsdkflmsdfvdfdfsdfsdf
+readme soon
diff --git a/src/main/kotlin/me/bush/illnamethislater/Config.kt b/src/main/kotlin/me/bush/illnamethislater/Config.kt
index ef946c5..31c2b63 100644
--- a/src/main/kotlin/me/bush/illnamethislater/Config.kt
+++ b/src/main/kotlin/me/bush/illnamethislater/Config.kt
@@ -1,5 +1,6 @@
package me.bush.illnamethislater
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import org.apache.logging.log4j.LogManager
import org.apache.logging.log4j.Logger
@@ -29,7 +30,7 @@ data class Config(
*
* [Information and examples](https://github.com/therealbush/eventbus-kotlin#tododothething)
*/
- val parallelContext: CoroutineContext = Dispatchers.Default,
+ val parallelScope: CoroutineScope = CoroutineScope(Dispatchers.Default),
/**
* Whether this [EventBus] should try to find a "cancelled" field in events being listened for that
@@ -40,10 +41,7 @@ data class Config(
val thirdPartyCompatibility: Boolean = true,
/**
- * Whether parallel listeners should be called before or after sequential listeners. Parallel listeners
- * will always finish before sequential listeners are called, or before [EventBus.post] returns.
- *
- * [Information and examples](https://github.com/therealbush/eventbus-kotlin#tododothething)
+ * todo doc
*/
- val parallelFirst: Boolean = true
+ val annotationRequired: Boolean = false
)
diff --git a/src/main/kotlin/me/bush/illnamethislater/EventBus.kt b/src/main/kotlin/me/bush/illnamethislater/EventBus.kt
index 5b278e3..7affa4a 100644
--- a/src/main/kotlin/me/bush/illnamethislater/EventBus.kt
+++ b/src/main/kotlin/me/bush/illnamethislater/EventBus.kt
@@ -1,6 +1,10 @@
package me.bush.illnamethislater
+import kotlinx.coroutines.delay
import kotlin.reflect.KClass
+import kotlin.reflect.full.companionObject
+import kotlin.reflect.full.companionObjectInstance
+import kotlin.reflect.full.hasAnnotation
/**
* [A simple event dispatcher.](https://github.com/therealbush/eventbus-kotlin#tododothething)
@@ -13,22 +17,36 @@ class EventBus(private val config: Config = Config()) {
private val subscribers = hashMapOf<Any, List<Listener>>()
/**
+ * Returns the current count of active subscribers.
+ */
+ val subscriberCount get() = subscribers.size
+
+ /**
+ * Returns the current count of all listeners, regardless of type.
+ */
+ val listenerCount get() = listeners.values.sumOf { it.parallel.size + it.sequential.size }
+
+ /**
* Searches [subscriber] for members that return [Listener] and registers them.
*
* This will not find top level listeners, use [register] instead.
*
- * Returns `false` if [subscriber] was already subscribed, `true` otherwise.
+ * Returns `true` if [subscriber] was successfully subscribed,
+ * `false` if it was already subscribed, or could not be.
*
* [Information and examples](https://github.com/therealbush/eventbus-kotlin#tododothething)
*/
fun subscribe(subscriber: Any): Boolean {
return if (subscriber in subscribers) false
else runCatching {
- // Register every listener into a group, but also
- // keep a separate list just for this subscriber.
- subscribers[subscriber] = subscriber::class.listeners.map { member ->
- register(member.handleCall(subscriber).also { it.subscriber = subscriber })
- }.toList()
+ // Keep a separate list just for this subscriber.
+ subscribers[subscriber] = subscriber::class.listeners
+ .filter { !config.annotationRequired || it.hasAnnotation<EventListener>() }.map { member ->
+ // Register listener to a group.
+ println("${member.name}, ${member.returnType}")
+ member.parameters.forEach { println(it) }
+ register(member.handleCall(subscriber).also { it.subscriber = subscriber })
+ }.toList()
true
}.getOrElse {
config.logger.error("Unable to register listeners for subscriber $subscriber", it)
@@ -41,18 +59,13 @@ class EventBus(private val config: Config = Config()) {
*
* This will not remove top level listeners, use [unregister] instead.
*
- * Returns `true` if [subscriber] was subscribed, `false` otherwise.
+ * Returns `true` if [subscriber] was subscribed.
*
* [Information and examples](https://github.com/therealbush/eventbus-kotlin#tododothething)
*/
fun unsubscribe(subscriber: Any): Boolean {
val contained = subscriber in subscribers
- // Unregister every listener for this subscriber,
- // and return null so the map entry is removed.
- subscribers.computeIfPresent(subscriber) { _, listeners ->
- listeners.forEach { unregister(it) }
- null
- }
+ subscribers.remove(subscriber)?.forEach { unregister(it) }
return contained
}
@@ -69,7 +82,7 @@ class EventBus(private val config: Config = Config()) {
}
/**
- * Unregisters a [Listener] from this [EventBus].
+ * Unregisters a [Listener] from this [EventBus]. Returns `true` if [Listener] was registered.
*
* [Information and examples](https://github.com/therealbush/eventbus-kotlin#tododothething)
*/
@@ -115,11 +128,13 @@ class EventBus(private val config: Config = Config()) {
* String: 3, 0
*/
fun debugInfo() {
- config.logger.info("Subscribers: ${subscribers.size}")
+ config.logger.info("Subscribers: ${subscribers.keys.size}")
val sequential = listeners.values.sumOf { it.sequential.size }
val parallel = listeners.values.sumOf { it.parallel.size }
config.logger.info("Listeners: $sequential sequential, $parallel parallel")
- listeners.values.sortedByDescending { it.sequential.size + it.parallel.size }.forEach { it.debugInfo() }
+ listeners.values.sortedByDescending { it.sequential.size + it.parallel.size }.forEach {
+ config.logger.info(it.toString())
+ }
}
}
diff --git a/src/main/kotlin/me/bush/illnamethislater/EventListener.kt b/src/main/kotlin/me/bush/illnamethislater/EventListener.kt
new file mode 100644
index 0000000..e96b1db
--- /dev/null
+++ b/src/main/kotlin/me/bush/illnamethislater/EventListener.kt
@@ -0,0 +1,6 @@
+package me.bush.illnamethislater
+
+/**
+ * todo docs
+ */
+annotation class EventListener
diff --git a/src/main/kotlin/me/bush/illnamethislater/Listener.kt b/src/main/kotlin/me/bush/illnamethislater/Listener.kt
index b2bb2ce..c9251d2 100644
--- a/src/main/kotlin/me/bush/illnamethislater/Listener.kt
+++ b/src/main/kotlin/me/bush/illnamethislater/Listener.kt
@@ -70,4 +70,4 @@ fun <T : Any> listener(
// This might introduce some overhead, but its worth
// not manually having to return "Kotlin.UNIT" from every Java listener.
listener: Consumer<T>
-) = Listener({ event: T -> listener.accept(event) }, type.kotlin, priority, parallel, receiveCancelled)
+) = Listener(listener::accept, type.kotlin, priority, parallel, receiveCancelled)
diff --git a/src/main/kotlin/me/bush/illnamethislater/ListenerGroup.kt b/src/main/kotlin/me/bush/illnamethislater/ListenerGroup.kt
index fdcc7bc..b8116cb 100644
--- a/src/main/kotlin/me/bush/illnamethislater/ListenerGroup.kt
+++ b/src/main/kotlin/me/bush/illnamethislater/ListenerGroup.kt
@@ -1,7 +1,6 @@
package me.bush.illnamethislater
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.*
import java.util.concurrent.CopyOnWriteArrayList
import kotlin.reflect.KClass
@@ -23,9 +22,10 @@ internal class ListenerGroup(
* Adds [listener] to this [ListenerGroup], and sorts its list.
*/
fun register(listener: Listener) {
- with(if (listener.parallel) parallel else sequential) {
- add(listener)
- sortedByDescending { it.priority }
+ (if (listener.parallel) parallel else sequential).let {
+ if (it.addIfAbsent(listener)) {
+ it.sortedByDescending(Listener::priority)
+ }
}
}
@@ -41,36 +41,25 @@ internal class ListenerGroup(
* Posts an event to every listener. Returns true of the event was cancelled.
*/
fun post(event: Any): Boolean {
- if (config.parallelFirst) postParallel(event)
sequential.forEach {
if (it.receiveCancelled || !cancelledState.isCancelled(event)) {
it.listener(event)
}
}
- if (!config.parallelFirst) postParallel(event)
- return cancelledState.isCancelled(event)
- }
-
- /**
- * Posts an event to all parallel listeners. Cancel state of the event is checked once before
- * posting the event as opposed to before calling each listener, to avoid inconsistencies.
- */
- private fun postParallel(event: Any) {
- if (parallel.isEmpty()) return
- // We check this once, because listener order is not consistent
- val cancelled = cancelledState.isCancelled(event)
- // Credit to KB for the idea
- runBlocking(config.parallelContext) {
+ if (parallel.isNotEmpty()) {
+ // We check this once, because listener order is not guaranteed.
+ val cancelled = cancelledState.isCancelled(event)
+ // Credit to KB for the idea
parallel.forEach {
- if (it.receiveCancelled || !cancelled) launch {
- it.listener(event)
+ if (it.receiveCancelled || !cancelled) {
+ config.parallelScope.launch {
+ it.listener(event)
+ }
}
}
}
+ return cancelledState.isCancelled(event)
}
- /**
- * Logs information about this [ListenerGroup].
- */
- fun debugInfo() = config.logger.info("${type.simpleName}: ${sequential.size}, ${parallel.size}")
+ override fun toString() = "${type.simpleName}: ${sequential.size}, ${parallel.size}"
}
diff --git a/src/main/kotlin/me/bush/illnamethislater/ReflectUtil.kt b/src/main/kotlin/me/bush/illnamethislater/ReflectUtil.kt
index ba42f76..2b16379 100644
--- a/src/main/kotlin/me/bush/illnamethislater/ReflectUtil.kt
+++ b/src/main/kotlin/me/bush/illnamethislater/ReflectUtil.kt
@@ -1,15 +1,15 @@
package me.bush.illnamethislater
import java.lang.reflect.Modifier
-import kotlin.reflect.KCallable
-import kotlin.reflect.KClass
-import kotlin.reflect.KProperty
+import kotlin.reflect.*
import kotlin.reflect.full.allSuperclasses
import kotlin.reflect.full.declaredMembers
+import kotlin.reflect.full.valueParameters
+import kotlin.reflect.full.withNullability
import kotlin.reflect.jvm.isAccessible
import kotlin.reflect.jvm.javaField
import kotlin.reflect.jvm.javaGetter
-import kotlin.reflect.typeOf
+import kotlin.reflect.jvm.javaMethod
// by bush, unchanged since 1.0.0
@@ -22,26 +22,41 @@ internal val <T : Any> KClass<T>.allMembers
/**
* Checks if a [KCallable] is static on the jvm, and handles invocation accordingly.
- * I am not aware of a better alternative that works with `object` classes.
+ *
+ * I have tried to check if the callable needs a receiver, and have left my code
+ * below, but for some reason a property (private, no getter) of a companion object
+ * (which is static in bytecode) requires a receiver, while an identical property in a
+ * non companion object does not, and will throw if one is passed.
+ *
+ * I am not aware of a way to check if a property belongs to a companion object, is static,
+ * or requires certain arguments. (instanceParameter exists, but it will throw if it is an argument)
+ * I thought maybe I was using the wrong methods, but apart from KProperty#get, (which is only for
+ * properties, and only accepts arguments of type `Nothing` when `T` is star projected or covariant)
+ * I could not find any other way to do this, not even on StackOverFlow.
+ *
+ * Funny how this solution is 1/10th the lines and always works.
*/
internal fun <R> KCallable<R>.handleCall(receiver: Any? = null): R {
isAccessible = true
- return if (static) call() else call(receiver)
+ return runCatching { call(receiver) }.getOrElse { call() }
}
-/**
- * Checks if the calling [KCallable] is a static java field. Because Kotlin likes to be funny, properties
- * belonging to `object` classes are static, but their getters are not. If there is a getter (the property
- * is not private), we will be accessing that, otherwise we check if the field is static with Java reflection.
- * This also lets us support static listeners in Java code.
+/*
+internal val KCallable<*>.isJvmStatic
+ get() = when (this) {
+ is KFunction -> Modifier.isStatic(javaMethod?.modifiers ?: 0)
+ is KProperty -> this.javaGetter == null && Modifier.isStatic(javaField?.modifiers ?: 0)
+ else -> false
+ }
*/
-internal val KCallable<*>.static
- get() = if (this !is KProperty<*> || javaGetter != null) false
- else javaField?.let { Modifier.isStatic(it.modifiers) } ?: false
/**
* Finds all members of return type [Listener]. (properties and methods)
*/
@Suppress("UNCHECKED_CAST") // This cannot fail
internal inline val KClass<*>.listeners
- get() = allMembers.filter { it.returnType == typeOf<Listener>() } as Sequence<KCallable<Listener>>
+ // Force nullability to false, so this will detect listeners in Java
+ // with "!" nullability. Also make sure there are no parameters.
+ get() = allMembers.filter {
+ it.returnType.withNullability(false) == typeOf<Listener>() && it.valueParameters.isEmpty()
+ } as Sequence<KCallable<Listener>>
diff --git a/src/test/java/JavaTest.java b/src/test/java/JavaTest.java
new file mode 100644
index 0000000..0b942b6
--- /dev/null
+++ b/src/test/java/JavaTest.java
@@ -0,0 +1,62 @@
+import me.bush.illnamethislater.Listener;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.config.Configurator;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.TestInstance;
+import org.junit.jupiter.api.TestInstance.Lifecycle;
+import me.bush.illnamethislater.EventBus;
+import org.apache.logging.log4j.LogManager;
+import org.junit.jupiter.api.Test;
+
+import static me.bush.illnamethislater.ListenerKt.listener;
+
+/**
+ * I was getting NCDFE when trying to load this class
+ * from the other test and I don't care enough to fix it.
+ *
+ * @author bush
+ * @since 1.0.0
+ */
+@TestInstance(Lifecycle.PER_CLASS)
+public class JavaTest {
+ private EventBus eventBus;
+ private final Logger logger = LogManager.getLogger();
+
+ @BeforeAll
+ public void setup() {
+ Configurator.setRootLevel(Level.ALL);
+ logger.info("Running Java tests");
+ eventBus = new EventBus();
+ eventBus.subscribe(this);
+ }
+
+ @Test
+ public void javaSubscriberTest() {
+ eventBus.subscribe(this);
+ SimpleEvent event = new SimpleEvent();
+ eventBus.post(event);
+ Assertions.assertEquals(event.getCount(), 4);
+ }
+
+ public Listener someInstanceListenerField = listener(SimpleEvent.class, event -> {
+ event.setCount(event.getCount() + 1);
+ });
+
+ public Listener someInstanceListenerMethod() {
+ return listener(SimpleEvent.class, event -> {
+ event.setCount(event.getCount() + 1);
+ });
+ }
+
+ public static Listener someStaticListenerMethod() {
+ return listener(SimpleEvent.class, event -> {
+ event.setCount(event.getCount() + 1);
+ });
+ }
+
+ public static Listener someStaticListenerField = listener(SimpleEvent.class, event -> {
+ event.setCount(event.getCount() + 1);
+ });
+}
diff --git a/src/test/java/TestJava.java b/src/test/java/TestJava.java
deleted file mode 100644
index c6c77a8..0000000
--- a/src/test/java/TestJava.java
+++ /dev/null
@@ -1,80 +0,0 @@
-import me.bush.illnamethislater.Listener;
-import org.apache.logging.log4j.Level;
-import org.apache.logging.log4j.Logger;
-import org.apache.logging.log4j.core.config.Configurator;
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.TestInstance;
-import org.junit.jupiter.api.TestInstance.Lifecycle;
-import me.bush.illnamethislater.EventBus;
-import org.apache.logging.log4j.LogManager;
-import org.junit.jupiter.api.AfterAll;
-import org.junit.jupiter.api.Test;
-
-import static me.bush.illnamethislater.ListenerKt.listener;
-
-/**
- * I was getting noclassdeffound when trying to load this Java
- * class in the other test and I don't care enough to fix it.
- *
- * @author bush
- * @since 1.0.0
- */
-@TestInstance(Lifecycle.PER_CLASS)
-public class TestJava {
- private static boolean thisShouldChange;
- private boolean thisShouldChangeToo;
- private EventBus eventBus;
- private final Logger logger = LogManager.getLogger();
-
- @BeforeAll
- public void setup() {
- Configurator.setRootLevel(Level.ALL);
-
- // Test that init works
- logger.info("Initializing");
- eventBus = new EventBus();
-
- // Ensure no npe
- logger.info("Logging empty debug info");
- eventBus.debugInfo();
-
- // Test that basic subscribing reflection works
- logger.info("Subscribing");
- eventBus.subscribe(this);
-
- logger.info("Testing Events");
- }
-
- @AfterAll
- public void unsubscribe() {
- logger.info("Unsubscribing");
- eventBus.unsubscribe(this);
- eventBus.debugInfo();
- }
-
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
- @Test
- public void javaSubscriberTest() {
- eventBus.subscribe(this);
- eventBus.post(new MyEvent());
- Assertions.assertTrue(TestJava.thisShouldChange);
- Assertions.assertTrue(this.thisShouldChangeToo);
- // TODO: 4/2/2022 fix calling from java
- }
-
- public Listener listener = listener(MyEvent.class, 200, event -> {
- Assertions.assertEquals(event.getSomeString(), "donda");
- event.setSomeString("donda 2");
- this.thisShouldChangeToo = true;
- });
-
- public static Listener someStaticListener() {
- return listener(MyEvent.class, 100, event -> {
- Assertions.assertEquals(event.getSomeString(), "donda 2");
- thisShouldChange = true;
- });
- }
-}
diff --git a/src/test/kotlin/KotlinTest.kt b/src/test/kotlin/KotlinTest.kt
new file mode 100644
index 0000000..6ad3273
--- /dev/null
+++ b/src/test/kotlin/KotlinTest.kt
@@ -0,0 +1,252 @@
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.runBlocking
+import me.bush.illnamethislater.*
+import org.apache.logging.log4j.Level
+import org.apache.logging.log4j.LogManager
+import org.apache.logging.log4j.core.config.Configurator
+import org.junit.jupiter.api.Assertions
+import org.junit.jupiter.api.BeforeAll
+import org.junit.jupiter.api.TestInstance
+import org.junit.jupiter.api.Test
+import org.opentest4j.AssertionFailedError
+import sun.misc.Unsafe
+import kotlin.jvm.internal.PropertyReference0Impl
+import kotlin.random.Random
+import kotlin.reflect.KCallable
+import kotlin.reflect.KProperty
+import kotlin.reflect.full.*
+import kotlin.reflect.javaType
+import kotlin.reflect.jvm.isAccessible
+import kotlin.reflect.jvm.javaField
+import kotlin.reflect.jvm.javaGetter
+
+/**
+ * I don't know how to do these....
+ *
+ * @author bush
+ * @since 1.0.0
+ */
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+class KotlinTest {
+ private lateinit var eventBus: EventBus
+ private val logger = LogManager.getLogger("Kotlin Test")
+
+ @BeforeAll
+ fun `setup logger and initialize eventbus` () {
+ // Log level defaults to only error
+ Configurator.setRootLevel(Level.ALL)
+ eventBus = EventBus(
+ // Defaults
+ Config(
+ logger = LogManager.getLogger("Eventbus"),
+ parallelScope = CoroutineScope(Dispatchers.Default),
+ thirdPartyCompatibility = true,
+ annotationRequired = false
+ )
+ )
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ @Test
+ fun `test listener priority and ability to cancel events or receive cancelled events` () {
+ eventBus.subscribe(this)
+ val event = SimpleEvent()
+ eventBus.post(event)
+ Assertions.assertEquals(3, event.count)
+ eventBus.unsubscribe(this)
+ }
+
+ // Last to be called; does not have receiveCancelled, but the last listener un-cancelled the event.
+ fun listener4() = listener<SimpleEvent>(priority = Int.MIN_VALUE) {
+ Assertions.assertEquals(2, it.count)
+ it.count++
+ }
+
+ // Will not be called; second-highest priority, no receiveCancelled.
+ val listener2
+ get() = listener<SimpleEvent>(priority = 0) {
+ Assertions.fail("This should not be called")
+ }
+
+ // First to be called; highest priority.
+ private val listener1 = listener<SimpleEvent>(priority = 10) {
+ Assertions.assertEquals(0, it.count)
+ it.count++
+ // Cancel, so next listener shouldn't receive it.
+ it.cancel()
+ }
+
+ // Second to be called; has receiveCancelled and can un-cancel the event.
+ fun listener3() = listener<SimpleEvent>(priority = Int.MIN_VALUE + 100, receiveCancelled = true) {
+ Assertions.assertEquals(1, it.count)
+ it.count++
+ it.cancelled = false
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ @Test
+ fun `test subscribing on a kotlin singleton object` () {
+ eventBus.subscribe(ObjectTest)
+ val event = SimpleEvent()
+ eventBus.post(event)
+ Assertions.assertEquals(3, event.count)
+ eventBus.unsubscribe(ObjectTest)
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ @Test
+ fun `test primitive types and listeners which don't belong to a class` () {
+ val random = Random.nextInt()
+ var changed = 0
+ val listener = listener<Int> {
+ changed = it
+ }
+ eventBus.register(listener)
+ eventBus.post(random)
+ Assertions.assertEquals(random, changed)
+ eventBus.unregister(listener)
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ @Test
+ fun `test that we can detect if an external event is cancelled` () {
+ eventBus.subscribe(this)
+ val event = ExternalEvent()
+ eventBus.post(event)
+ eventBus.unsubscribe(this)
+ }
+
+ @EventListener
+ fun externalListener1() = listener<ExternalEvent>(priority = 1) {
+ it.canceled = true
+ }
+
+ // Should not be called
+ fun externalListener2() = listener<ExternalEvent>(priority = -1) {
+ Assertions.fail("This should not be called")
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ @Test
+ fun `test parallel event posting` () {
+ runBlocking {
+ sus()
+ }
+ }
+
+ suspend fun sus() {
+ println()
+ }
+
+ fun sussy() {
+ println()
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ @Test
+ fun `call every method on multiple threads concurrently to ensure no CME is thrown` () {
+
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ @Test
+ fun `test that inheritance doesn't affect events or listeners` () {
+ val superTest = listener<SimpleEvent> {
+ Assertions.fail("This should not be called")
+ }
+ eventBus.register(superTest)
+ // No listeners should be called when a superclass is posted.
+ eventBus.post(Any())
+ eventBus.unregister(superTest)
+ val subTest = listener<Any> {
+ Assertions.fail("This should not be called")
+ }
+ eventBus.register(subTest)
+ // No listeners should be called when a subclass is posted.
+ eventBus.post(SimpleEvent())
+ eventBus.unregister(subTest)
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ @Test
+ fun `test that require annotation mode works` () {
+ val eventBus = EventBus(Config(annotationRequired = true))
+ eventBus.subscribe(this)
+ eventBus.post(Unit)
+ Assertions.assertTrue(called)
+ }
+
+ var called = false
+
+ @EventListener
+ val annotation = listener<Unit> {
+ called = true
+ }
+
+ val noAnnotation = listener<Unit> {
+ Assertions.fail("This should not be called")
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ @Test
+ fun `test subscribing on a companion object` () {
+ eventBus.subscribe(KotlinTest)
+ val string = "i love bush's eventbus <3"
+ eventBus.post(string)
+ Assertions.assertEquals(string, value)
+ eventBus.unsubscribe(KotlinTest)
+ }
+
+ companion object {
+ var value = ""
+
+ @EventListener
+ private val listener = listener<String> {
+ value = it
+ }
+ }
+}
+
+object ObjectTest {
+ private val listener1 = listener<SimpleEvent> {
+ it.count++
+ }
+
+ @EventListener
+ private val listener2 get() = listener<SimpleEvent> {
+ it.count++
+ }
+
+ private fun listener3() = listener<SimpleEvent> {
+ it.count++
+ }
+}
+
+class SimpleEvent : Event() {
+ override val cancellable = true
+
+ var count = 0
+}
+
+class ExternalEvent {
+ var canceled = false
+}
diff --git a/src/test/kotlin/TestKotlin.kt b/src/test/kotlin/TestKotlin.kt
deleted file mode 100644
index 2ae6313..0000000
--- a/src/test/kotlin/TestKotlin.kt
+++ /dev/null
@@ -1,215 +0,0 @@
-import me.bush.illnamethislater.Event
-import me.bush.illnamethislater.EventBus
-import me.bush.illnamethislater.listener
-import org.apache.logging.log4j.Level
-import org.apache.logging.log4j.LogManager
-import org.apache.logging.log4j.core.config.Configurator
-import org.junit.jupiter.api.AfterAll
-import org.junit.jupiter.api.Assertions
-import org.junit.jupiter.api.BeforeAll
-import org.junit.jupiter.api.Test
-import org.junit.jupiter.api.TestInstance
-import org.junit.jupiter.api.TestInstance.Lifecycle
-import kotlin.random.Random
-
-/**
- * I don't know how to do these....
- *
- * @author bush
- * @since 1.0.0
- */
-@TestInstance(Lifecycle.PER_CLASS)
-class Test {
- private lateinit var eventBus: EventBus
- private val logger = LogManager.getLogger()
-
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
- @BeforeAll
- fun setup() {
- Configurator.setRootLevel(Level.ALL)
-
- // Test that init works
- logger.info("Initializing")
- eventBus = EventBus()
-
- // Ensure no npe
- logger.info("Logging empty debug info")
- eventBus.debugInfo()
-
- // Test that basic subscribing reflection works
- logger.info("Subscribing")
- eventBus.subscribe(this)
-
- logger.info("Testing Events")
- }
-
- @AfterAll
- fun unsubscribe() {
- logger.info("Unsubscribing")
- eventBus.unsubscribe(this)
- eventBus.debugInfo()
- }
-
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
- // Tests debug info format
- @Test
- fun debugInfoTest() {
- eventBus.debugInfo()
- }
-
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
- // Tests autoboxing and internal data structure against primitives.
- @Test
- fun primitiveListenerTest() {
- val random = Random.nextInt()
- eventBus.post(random)
- Assertions.assertEquals(random, primitiveTestValue)
- }
-
- private var primitiveTestValue = 0
-
- val primitiveListener = listener<Int> {
- primitiveTestValue = it
- }
-
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
- // Tests unsubscribing of listeners which don't belong to a subscriber.
- @Test
- fun freeListenerTest() {
- // Register listener and keep the value
- val listener = eventBus.register(listener<String> {
- freeListenerTestValue = it
- })
- val valueOne = "i love bush's eventbus <3"
- val valueTwo = "sdklasdjsakdsadlksadlksdl"
- // Will change the value
- eventBus.post(valueOne)
- Assertions.assertEquals(valueOne, freeListenerTestValue)
- // Remove the listener
- eventBus.unregister(listener)
- // No effect
- eventBus.post(valueTwo)
- // Value will not change
- Assertions.assertEquals(valueOne, freeListenerTestValue)
- }
-
- private var freeListenerTestValue: String? = null
-
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
- // Tests priority and receiveCancelled functionality.
- @Test
- fun myEventListenerTest() {
- val event = MyEvent()
- eventBus.post(event)
- Assertions.assertEquals(event.lastListener, "myEventListener3")
- }
-
- // First to be called; highest priority.
- val myEventListener0 = listener<MyEvent>(priority = 10) {
- Assertions.assertEquals(it.lastListener, "")
- it.lastListener = "myEventListener0"
- it.cancel()
- }
-
- // Will not be called; second-highest priority, no receiveCancelled.
- val myEventListener1
- get() = listener<MyEvent>(priority = 0) {
- Assertions.assertTrue(false)
- }
-
- // Second to be called; has receiveCancelled and can un-cancel the event.
- fun myEventListener2() = listener<MyEvent>(priority = Int.MIN_VALUE + 100, receiveCancelled = true) {
- Assertions.assertEquals(it.lastListener, "myEventListener0")
- it.lastListener = "myEventListener2"
- it.cancelled = false
- }
-
- // Last to be called; does not have receiveCancelled, but the last listener un-cancelled the event.
- fun myEventListener3() = listener<MyEvent>(priority = Int.MIN_VALUE) {
- Assertions.assertEquals(it.lastListener, "myEventListener2")
- it.lastListener = "myEventListener3"
- }
-
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
- // Tests external event cancel state functionality.
- @Test
- fun externalEventListenerTest() {
- var unchanged = "this will not change"
- // Cancels the event
- eventBus.register(listener<ExternalEvent>(200) {
- it.canceled = true
- })
- // This shouldn't be called
- eventBus.register(listener<ExternalEvent>(100) {
- unchanged = "changed"
- })
- eventBus.post(ExternalEvent())
- Assertions.assertEquals(unchanged, "this will not change")
- // Tests that duplicates are detected, and that both
- // "canceled" and "cancelled" are detected as valid fields
- eventBus.register(listener<ExternalDuplicates> {})
- }
-
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
- // Tests parallel invocation functionality.
- @Test
- fun parallelListenerTest() {
-
- }
-
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
- // Tests reflection against singleton object classes.
- @Test
- fun objectSubscriberTest() {
- eventBus.subscribe(ObjectSubscriber)
- eventBus.post(Unit)
- Assertions.assertTrue(ObjectSubscriber.willChange)
- }
-
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
- // todo test thread safety
- // todo ensure boolean functions return proper value (subscribe, unsubscribe, etc)
-}
-
-object ObjectSubscriber {
- var willChange = false
-
- fun listener() = listener<Unit> {
- willChange = true
- }
-}
-
-class MyEvent : Event() {
- override val cancellable = true
-
- var lastListener = ""
- var someString = "donda"
-}
-
-class ExternalEvent {
- var canceled = false
-}
-
-// Should give us a warning about duplicates
-class ExternalDuplicates {
- var canceled = false
- var cancelled = false
-}