Initial commit

This commit is contained in:
George
2026-06-07 12:18:45 +01:00
commit 9f9b85e1f2
305 changed files with 23050 additions and 0 deletions
@@ -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);
}
}
}
}