Initial commit
This commit is contained in:
@@ -0,0 +1,181 @@
|
||||
package com.livingworld.events;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* Tests for {@link LivingWorldEventBus}.
|
||||
*/
|
||||
public class LivingWorldEventBusTest {
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Registration tests
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
void registerAddsListenerForEventType() {
|
||||
LivingWorldEventBus bus = new LivingWorldEventBus();
|
||||
LivingWorldEventListener listener = event -> {};
|
||||
|
||||
assertDoesNotThrow(() -> bus.register("weather_change", listener));
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerPreservesRegistrationOrder() {
|
||||
LivingWorldEventBus bus = new LivingWorldEventBus();
|
||||
AtomicBoolean firstCalled = new AtomicBoolean(false);
|
||||
AtomicBoolean secondCalled = new AtomicBoolean(false);
|
||||
|
||||
LivingWorldEventListener first = event -> firstCalled.set(true);
|
||||
LivingWorldEventListener second = event -> secondCalled.set(true);
|
||||
|
||||
bus.register("weather_change", first);
|
||||
bus.register("weather_change", second);
|
||||
|
||||
BaseLivingWorldEvent event = new BaseLivingWorldEvent("weather_change", 0L, "core");
|
||||
publishEvent(bus, "weather_change", event);
|
||||
|
||||
assertTrue(firstCalled.get());
|
||||
assertTrue(secondCalled.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerBlankEventTypeThrows() {
|
||||
LivingWorldEventBus bus = new LivingWorldEventBus();
|
||||
|
||||
IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> {
|
||||
bus.register(" ", null);
|
||||
});
|
||||
|
||||
assertEquals("eventType must not be null or blank", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerNullListenerThrows() {
|
||||
LivingWorldEventBus bus = new LivingWorldEventBus();
|
||||
|
||||
IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> {
|
||||
bus.register("weather_change", null);
|
||||
});
|
||||
|
||||
assertEquals("listener must not be null", exception.getMessage());
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Publishing tests
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
void publishDispatchesToRegisteredListeners() {
|
||||
LivingWorldEventBus bus = new LivingWorldEventBus();
|
||||
AtomicBoolean listenerCalled = new AtomicBoolean(false);
|
||||
|
||||
bus.register("weather_change", event -> listenerCalled.set(true));
|
||||
|
||||
BaseLivingWorldEvent event = new BaseLivingWorldEvent(
|
||||
"weather_change", 100L, "core"
|
||||
);
|
||||
|
||||
publishEvent(bus, "weather_change", event);
|
||||
|
||||
assertTrue(listenerCalled.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
void publishUnknownEventTypeDoesNotCrash() {
|
||||
LivingWorldEventBus bus = new LivingWorldEventBus();
|
||||
BaseLivingWorldEvent event = new BaseLivingWorldEvent("unknown_type_123", 100L, "core");
|
||||
|
||||
assertDoesNotThrow(() -> publishEvent(bus, "unknown_type_123", event));
|
||||
}
|
||||
|
||||
@Test
|
||||
void publishNullEventThrows() {
|
||||
LivingWorldEventBus bus = new LivingWorldEventBus();
|
||||
|
||||
assertThrowsWithMessage(() -> { try { publishEvent(bus, "weather_change", null); } catch (RuntimeException e) { throw e; } },
|
||||
IllegalArgumentException.class, "event must not be null");
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Metrics tests
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
void getPublishedEventCountIncrementsOnPublish() {
|
||||
LivingWorldEventBus bus = new LivingWorldEventBus();
|
||||
|
||||
BaseLivingWorldEvent event1 = new BaseLivingWorldEvent("weather_change", 100L, "core");
|
||||
bus.publish(event1);
|
||||
|
||||
assertEquals(1, bus.getPublishedEventCount());
|
||||
|
||||
// Clear suppression to allow publishing events with different ticks
|
||||
bus.clearSuppressionForCurrentTick();
|
||||
|
||||
BaseLivingWorldEvent event2 = new BaseLivingWorldEvent("soil_moisture", 200L, "core");
|
||||
bus.publish(event2);
|
||||
|
||||
assertEquals(2, bus.getPublishedEventCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getListenerCountReturnsCorrectNumberOfListeners() {
|
||||
LivingWorldEventBus bus = new LivingWorldEventBus();
|
||||
|
||||
bus.register("weather_change", event -> {});
|
||||
bus.register("weather_change", event -> {});
|
||||
bus.register("pollution_level", event -> {});
|
||||
|
||||
assertEquals(2, bus.getListenerCount("weather_change"));
|
||||
assertEquals(1, bus.getListenerCount("pollution_level"));
|
||||
assertEquals(0, bus.getListenerCount("unknown_event"));
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Helper methods (kept in test file for simplicity)
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
private void publishEvent(LivingWorldEventBus bus, String eventType, LivingWorldEvent event) {
|
||||
// Use reflection to call the package-private publish method
|
||||
try {
|
||||
java.lang.reflect.Method method = LivingWorldEventBus.class.getDeclaredMethod(
|
||||
"publish", LivingWorldEvent.class
|
||||
);
|
||||
method.setAccessible(true);
|
||||
if (event == null) {
|
||||
method.invoke(bus, new Object[]{null});
|
||||
} else {
|
||||
method.invoke(bus, event);
|
||||
}
|
||||
} catch (java.lang.reflect.InvocationTargetException e) {
|
||||
// Unwrap the target exception for proper test assertions
|
||||
Throwable cause = e.getCause();
|
||||
if (cause instanceof RuntimeException) {
|
||||
throw (RuntimeException) cause;
|
||||
} else if (cause != null) {
|
||||
throw new RuntimeException(cause);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void assertThrowsWithMessage(Runnable runnable, Class<? extends Throwable> expectedType, String expectedMessage) {
|
||||
try {
|
||||
runnable.run();
|
||||
fail("Expected " + expectedType.getSimpleName() + " but no exception was thrown");
|
||||
} catch (AssertionError e) {
|
||||
throw e;
|
||||
} catch (Throwable t) {
|
||||
if (expectedType.isInstance(t)) {
|
||||
assertEquals(expectedMessage, t.getMessage());
|
||||
} else {
|
||||
throw new AssertionError("Expected " + expectedType.getSimpleName() + " but got " + t.getClass().getSimpleName() + ": " + t.getMessage(), t);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user