diff --git a/Semesterprojekt/src/main/java/thb/jeanluc/adventure/App.java b/Semesterprojekt/src/main/java/thb/jeanluc/adventure/App.java index da51382..2cf6312 100644 --- a/Semesterprojekt/src/main/java/thb/jeanluc/adventure/App.java +++ b/Semesterprojekt/src/main/java/thb/jeanluc/adventure/App.java @@ -64,8 +64,11 @@ public final class App { /** Runs the menu shell on the given IO until the player quits. */ public static void run(GameIO io) { - SaveService saves = new SaveService(); - SettingsStore settingsStore = new SettingsStore(); + run(io, new SaveService(), new SettingsStore()); + } + + /** Runs the menu shell with injectable persistence (for testing). */ + static void run(GameIO io, SaveService saves, SettingsStore settingsStore) { Settings settings = settingsStore.load(); SettingsMenu.apply(settings, io); @@ -88,6 +91,7 @@ public final class App { } } io.write("Farewell."); + io.shutdown(); // GUI: close the window and exit; console: no-op } /** Loads a fresh world and binds it to a newly named session. */ diff --git a/Semesterprojekt/src/main/java/thb/jeanluc/adventure/io/GameIO.java b/Semesterprojekt/src/main/java/thb/jeanluc/adventure/io/GameIO.java index b1a22a1..33e1748 100644 --- a/Semesterprojekt/src/main/java/thb/jeanluc/adventure/io/GameIO.java +++ b/Semesterprojekt/src/main/java/thb/jeanluc/adventure/io/GameIO.java @@ -113,4 +113,14 @@ public interface GameIO { write("Please enter a number between 1 and " + options.size() + "."); } } + + /** + * Tears the frontend down after the main-menu loop ends (the player chose + * Quit). Console mode has nothing to release and exits naturally when + * {@code main} returns; the GUI overrides this to close its window and + * terminate the JVM, which would otherwise stay alive on the Swing EDT. + */ + default void shutdown() { + // no-op in text mode + } } diff --git a/Semesterprojekt/src/main/java/thb/jeanluc/adventure/io/SwingIO.java b/Semesterprojekt/src/main/java/thb/jeanluc/adventure/io/SwingIO.java index 59bee0c..65184df 100644 --- a/Semesterprojekt/src/main/java/thb/jeanluc/adventure/io/SwingIO.java +++ b/Semesterprojekt/src/main/java/thb/jeanluc/adventure/io/SwingIO.java @@ -342,6 +342,21 @@ public class SwingIO implements GameIO { music.level(level); } + /** + * Closes the GUI when the player quits from the main menu. Stops music, + * disposes the window, and terminates the JVM — mirroring the native + * window-close (EXIT_ON_CLOSE); without this the EDT keeps the process + * alive and the dismissed menu overlay just reveals the game underneath. + */ + @Override + public void shutdown() { + music.shutdown(); + SwingUtilities.invokeLater(() -> { + frame.dispose(); + System.exit(0); + }); + } + @Override public void setMap(MapView view) { map.show(view); diff --git a/Semesterprojekt/src/test/java/thb/jeanluc/adventure/AppQuitTest.java b/Semesterprojekt/src/test/java/thb/jeanluc/adventure/AppQuitTest.java new file mode 100644 index 0000000..9629376 --- /dev/null +++ b/Semesterprojekt/src/test/java/thb/jeanluc/adventure/AppQuitTest.java @@ -0,0 +1,52 @@ +package thb.jeanluc.adventure; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import thb.jeanluc.adventure.io.TestIO; +import thb.jeanluc.adventure.save.SaveService; +import thb.jeanluc.adventure.save.SettingsStore; + +import java.nio.file.Path; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Regression test for the main-menu Quit: choosing Quit must end the menu loop + * AND tell the frontend to tear down. Previously {@code run} just returned, + * which exits the console but left the Swing window open (the menu overlay + * closed and the game reappeared underneath). We assert the frontend-agnostic + * contract: the Quit path invokes {@link thb.jeanluc.adventure.io.GameIO#shutdown()}. + */ +class AppQuitTest { + + /** Fake IO that always picks the last menu option (Quit) and records shutdown. */ + private static final class QuitIO extends TestIO { + boolean shutdownCalled; + int chooseCalls; + + @Override + public int choose(String title, List options) { + chooseCalls++; + return options.size() - 1; // Quit is the last main-menu option + } + + @Override + public void shutdown() { + shutdownCalled = true; + } + } + + @Test + void choosingQuit_tearsDownTheFrontend(@TempDir Path tmp) { + QuitIO io = new QuitIO(); + SaveService saves = new SaveService(tmp.resolve("saves")); + SettingsStore settings = new SettingsStore(tmp.resolve("settings.json")); + + App.run(io, saves, settings); + + assertThat(io.shutdownCalled).isTrue(); + assertThat(io.chooseCalls).isEqualTo(1); // straight to Quit, no game started + assertThat(io.allOutput()).contains("Farewell."); + } +}