diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/service/event/EventChannel.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/service/event/EventChannel.java
new file mode 100644
index 0000000..e67e360
--- /dev/null
+++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/service/event/EventChannel.java
@@ -0,0 +1,18 @@
+package edu.ntnu.idi.idatt2003.g40.mappe.service.event;
+
+/**
+ * Interface representing an event channel.
+ *
+ * Used to separate event types into groups.
+ *
+ * Decreases coupling and enables testing of event types.
+ * */
+public interface EventChannel {
+
+ /**
+ * Getter method for enum name.
+ *
+ * @return String name of enum.
+ * */
+ String getName();
+}
diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/service/event/EventData.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/service/event/EventData.java
index 70465f6..b35f098 100644
--- a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/service/event/EventData.java
+++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/service/event/EventData.java
@@ -4,10 +4,10 @@
* Data object sent through the event system by the {@link EventManager}.
*
* @param the type of data this object represents.
- * @param eventType the event type this object represents.
+ * @param channel the event type this object represents.
* @param data the data this object contains.
*
*/
-public record EventData(EventType eventType, T data) {
+public record EventData(EventChannel channel, T data) {
}
diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/service/event/EventManager.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/service/event/EventManager.java
index a2bf1b5..a4a2bad 100644
--- a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/service/event/EventManager.java
+++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/service/event/EventManager.java
@@ -1,9 +1,6 @@
package edu.ntnu.idi.idatt2003.g40.mappe.service.event;
-import java.util.ArrayList;
-import java.util.EnumMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
/**
* Event broker in the pub/sub model.
@@ -26,19 +23,19 @@ public final class EventManager {
* Used to identify which subscribers to fire the event towards.
*
*/
- private final Map> subscriberMap =
- new EnumMap<>(EventType.class);
+ private final Map> subscriberMap =
+ new HashMap<>();
/**
* Method for adding a subscriber to the subscriber map given an event type.
*
- * @param subscriber the {@link EventSubscriber} object to add.
- * @param eventType the {@link EventType} this subscriber should subscribe to.
+ * @param subscriber the {@link EventSubscriber} object to add.
+ * @param eventChannel the {@link EventChannel} type this subscriber should subscribe to.
*
*
*/
- public void addSubscriber(final EventSubscriber subscriber, final EventType eventType) {
- subscriberMap.computeIfAbsent(eventType, k -> new ArrayList<>()).add(subscriber);
+ public void addSubscriber(final EventSubscriber subscriber, final EventChannel eventChannel) {
+ subscriberMap.computeIfAbsent(eventChannel, k -> new ArrayList<>()).add(subscriber);
}
/**
@@ -47,11 +44,19 @@ public void addSubscriber(final EventSubscriber subscriber, final EventType even
* @param the type of event data to send.
* @param data the data to send.
*
- *
+ * @throws IllegalArgumentException if no subscriber is found of the
+ * event type.
*/
- public void invokeEvent(final EventData data) {
- for (EventSubscriber e : subscriberMap.get(data.eventType())) {
- e.handleEvent(data);
+ public void invokeEvent(final EventData data)
+ throws IllegalArgumentException {
+ if (data == null || !subscriberMap.containsKey(data.channel())) {
+ throw new IllegalArgumentException(
+ "No subscriber listening to this event!"
+ );
+ } else {
+ for (EventSubscriber e : subscriberMap.get(data.channel())) {
+ e.handleEvent(data);
+ }
}
}
}
diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/service/event/EventType.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/service/event/EventType.java
index 52a60b9..4231866 100644
--- a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/service/event/EventType.java
+++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/service/event/EventType.java
@@ -10,9 +10,8 @@
* Other
*
*
- *
*/
-public enum EventType {
+public enum EventType implements EventChannel {
/**
* Event type representing events that causes the current scene to change.
@@ -23,16 +22,13 @@ public enum EventType {
* @see edu.ntnu.idi.idatt2003.g40.mappe.view.ViewManager
*
*/
- SCENE_CHANGE,
+ SCENE_CHANGE;
/**
- * Event type representing events that causes the scene to change to the previous one.
- *
- * Primarily handled by the active
- * {@link edu.ntnu.idi.idatt2003.g40.mappe.view.ViewManager} object.
- *
- * @see edu.ntnu.idi.idatt2003.g40.mappe.view.ViewManager
- *
- */
- SCENE_BACK,
+ * {@inheritDoc}
+ * */
+ @Override
+ public String getName() {
+ return this.name();
+ }
}
diff --git a/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/service/event/EventManagerTest.java b/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/service/event/EventManagerTest.java
new file mode 100644
index 0000000..58d6d0e
--- /dev/null
+++ b/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/service/event/EventManagerTest.java
@@ -0,0 +1,98 @@
+package edu.ntnu.idi.idatt2003.g40.mappe.service.event;
+
+import edu.ntnu.idi.idatt2003.g40.mappe.view.ViewData;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class EventManagerTest {
+ private enum TestEventTypes implements EventChannel {
+ TEST_TYPE_1,
+ TEST_TYPE_2,
+ TEST_TYPE_3;
+
+ @Override
+ public String getName() {
+ return this.name();
+ }
+ }
+
+ private GenericEventPublisher testEventPublisher;
+ private GenericEventPublisher testEventPublisher2;
+ private GenericEventPublisher testEventPublisher3;
+
+ private GenericEventSubscriber testEventSubscriber;
+ private GenericEventSubscriber testEventSubscriber2;
+
+ private EventManager testEventManager;
+
+ @BeforeEach
+ void setUp() {
+ testEventManager = new EventManager();
+
+ testEventSubscriber = new GenericEventSubscriber();
+ testEventSubscriber2 = new GenericEventSubscriber();
+
+ testEventPublisher = new GenericEventPublisher(testEventManager, TestEventTypes.TEST_TYPE_1);
+ testEventPublisher2 = new GenericEventPublisher(testEventManager, TestEventTypes.TEST_TYPE_2);
+ testEventPublisher3 = new GenericEventPublisher(testEventManager, TestEventTypes.TEST_TYPE_3);
+
+ testEventManager.addSubscriber(testEventSubscriber, TestEventTypes.TEST_TYPE_1);
+ testEventManager.addSubscriber(testEventSubscriber2, TestEventTypes.TEST_TYPE_2);
+ }
+
+ @Test
+ void firedEventCaughtByCorrectSubscriber() {
+ assertFalse(testEventSubscriber.invokedEvent);
+ testEventPublisher.fireEvent();
+ assertTrue(testEventSubscriber.invokedEvent);
+ }
+
+ @Test
+ void firedEventNotCaughtByIncorrectSubscriber() {
+ assertFalse(testEventSubscriber.invokedEvent);
+ testEventPublisher2.fireEvent();
+ assertFalse(testEventSubscriber.invokedEvent);
+ }
+
+ @Test
+ void firedEventThrowsErrorWhenNoSubscribers() {
+ assertFalse(testEventSubscriber.invokedEvent);
+ assertThrows(IllegalArgumentException.class, () -> {
+ testEventPublisher3.fireEvent();
+ });
+ assertFalse(testEventSubscriber.invokedEvent);
+ }
+
+ private class GenericEventPublisher implements EventPublisher {
+
+ private final ViewData viewData;
+ private final EventData eventData;
+ private final EventManager eventManager;
+
+ public GenericEventPublisher(final EventManager eventManager, final TestEventTypes eventType) {
+ viewData = new ViewData("Test");
+ eventData = new EventData(eventType, viewData);
+ this.eventManager = eventManager;
+ }
+
+ public void fireEvent() {
+ invoke(eventData, eventManager);
+ }
+
+ @Override
+ public void invoke(EventData data, EventManager eventManager) {
+ eventManager.invokeEvent(data);
+ }
+ }
+
+ private class GenericEventSubscriber implements EventSubscriber {
+ public boolean invokedEvent = false;
+
+ @Override
+ public void handleEvent(EventData data) {
+ invokedEvent = true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/view/ViewControllerTest.java b/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/view/ViewControllerTest.java
new file mode 100644
index 0000000..abf0158
--- /dev/null
+++ b/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/view/ViewControllerTest.java
@@ -0,0 +1,69 @@
+package edu.ntnu.idi.idatt2003.g40.mappe.view;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import edu.ntnu.idi.idatt2003.g40.mappe.service.event.EventManager;
+import javafx.scene.control.Button;
+import javafx.scene.layout.Pane;
+import javafx.stage.Stage;
+import org.junit.jupiter.api.Test;
+import org.testfx.framework.junit5.ApplicationTest;
+
+class ViewControllerTest extends ApplicationTest {
+ private EventManager testEventManager;
+ private GenericViewController testViewController;
+ private GenericViewElement testViewElement;
+
+ @Override
+ public void start(Stage stage) {
+ testEventManager = new EventManager();
+ testViewElement = new ViewControllerTest.GenericViewElement(new Pane());
+ testViewController = new GenericViewController(testViewElement, testEventManager);
+ }
+
+ @Test
+ void controllerElementSetsButtonBehavior() {
+ assertFalse(testViewElement.buttonPressed);
+ testViewElement.getInteractableButton().fire();
+ assertTrue(testViewElement.buttonPressed);
+ }
+
+ private class GenericViewElement extends ViewElement {
+ public Boolean buttonPressed = false;
+ private Button interactableButton;
+
+ protected GenericViewElement(final Pane rootPane) {
+ super(rootPane);
+ }
+
+ @Override
+ protected void initLayout() {
+ interactableButton = new Button("Click me!");
+ getButtons().add(interactableButton);
+ }
+
+ public Button getInteractableButton() {
+ return interactableButton;
+ }
+
+ @Override
+ protected void initStyling() { }
+ }
+
+ private class GenericViewController extends ViewController {
+
+ protected GenericViewController(final ViewControllerTest.GenericViewElement viewElement,
+ final EventManager eventManager)
+ throws IllegalArgumentException {
+ super(viewElement, eventManager);
+ }
+
+ @Override
+ protected void initInteractions() {
+ getViewElement().getInteractableButton().setOnAction(e -> {
+ getViewElement().buttonPressed = true;
+ });
+ }
+ }
+}
diff --git a/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/view/ViewManagerTest.java b/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/view/ViewManagerTest.java
new file mode 100644
index 0000000..1f9aeb3
--- /dev/null
+++ b/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/view/ViewManagerTest.java
@@ -0,0 +1,108 @@
+package edu.ntnu.idi.idatt2003.g40.mappe.view;
+
+import edu.ntnu.idi.idatt2003.g40.mappe.service.event.EventManager;
+import javafx.scene.Scene;
+import javafx.scene.control.Button;
+import javafx.scene.layout.Pane;
+import javafx.stage.Stage;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.testfx.framework.junit5.ApplicationTest;
+
+/**
+ * Tests the view manager.
+ *
+ * Uses the testfx framework
+ *
+ */
+class ViewManagerTest extends ApplicationTest {
+
+ private ViewManager testViewManager;
+ private EventManager testEventManager;
+ private ViewManagerTest.GenericViewElement genericViewElement;
+
+ @Override
+ public void start(final Stage stage) {
+ stage.setScene(new Scene(new Pane()));
+ testEventManager = new EventManager();
+ testViewManager = new ViewManager(stage, testEventManager);
+ genericViewElement = new GenericViewElement(new Pane(), "Test View");
+ }
+
+ @Test
+ void testViewManagerHoldsNoViewAtStart() {
+ Assertions.assertNull(testViewManager.getCurrentView());
+ }
+
+ @Test
+ void addingMultipleOfSameViewThrowsException() {
+ testViewManager.addView(genericViewElement);
+ Assertions.assertThrows(IllegalArgumentException.class, () -> {
+ testViewManager.addView(genericViewElement);
+ });
+ }
+
+ @Test
+ void testViewManagerSettingCorrectView() {
+ testViewManager.addView(genericViewElement);
+ testViewManager.setScene(genericViewElement);
+ Assertions.assertEquals(genericViewElement, testViewManager.getCurrentView());
+ }
+
+ @Test
+ void testViewManagerThrowingErrorWhenSettingNonExistentView() {
+ Assertions.assertThrows(IllegalArgumentException.class, () -> {
+ testViewManager.setScene(genericViewElement);
+ });
+ }
+
+ @Test
+ void throwsErrorWhenAddingWidgetWithNoViewName() {
+ genericViewElement = new GenericViewElement(new Pane());
+ Assertions.assertThrows(IllegalArgumentException.class, () -> {
+ testViewManager.addView(genericViewElement);
+ });
+ }
+
+ private class GenericViewElement extends ViewElement {
+ public Boolean buttonPressed = false;
+ private Button interactableButton;
+
+ protected GenericViewElement(final Pane rootPane, final String viewName) {
+ super(rootPane, viewName);
+ }
+
+ protected GenericViewElement(final Pane rootPane) {
+ super(rootPane);
+ }
+
+ @Override
+ protected void initLayout() {
+ interactableButton = new Button("Click me!");
+ getButtons().add(interactableButton);
+ }
+
+ public Button getInteractableButton() {
+ return interactableButton;
+ }
+
+ @Override
+ protected void initStyling() { }
+ }
+
+ private class GenericViewController extends ViewController {
+
+ protected GenericViewController(final ViewManagerTest.GenericViewElement viewElement,
+ final EventManager eventManager)
+ throws IllegalArgumentException {
+ super(viewElement, eventManager);
+ }
+
+ @Override
+ protected void initInteractions() {
+ getViewElement().getInteractableButton().setOnAction(e -> {
+ getViewElement().buttonPressed = true;
+ });
+ }
+ }
+}