diff --git a/client/client.iml b/client/client.iml index a08f2bd3..fafe275d 100644 --- a/client/client.iml +++ b/client/client.iml @@ -4,13 +4,38 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/pom.xml b/client/pom.xml index ce447e40..532d940a 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -32,6 +32,27 @@ info.picocli picocli + + org.moparforia + shared + + + + + org.junit.jupiter + junit-jupiter-engine + test + + + org.mockito + mockito-core + test + + + org.mockito + mockito-junit-jupiter + test + diff --git a/client/src/main/java/org/moparforia/client/Launcher.java b/client/src/main/java/org/moparforia/client/Launcher.java index 039c53fb..75d983ac 100644 --- a/client/src/main/java/org/moparforia/client/Launcher.java +++ b/client/src/main/java/org/moparforia/client/Launcher.java @@ -2,24 +2,25 @@ import picocli.CommandLine; +import org.moparforia.shared.ManifestVersionProvider; import javax.imageio.ImageIO; import javax.swing.*; import java.awt.*; +import java.io.IOException; import java.text.ParseException; import java.util.concurrent.Callable; -/** - * Playforia - * 28.5.2013 - */ + @CommandLine.Command( description = "Starts Minigolf Client", - name = "client", mixinStandardHelpOptions = true + name = "client", + mixinStandardHelpOptions = true, + versionProvider = ManifestVersionProvider.class ) public class Launcher implements Callable { - private static final String DEFAULT_SERVER = "127.0.0.1"; - private static final int DEFAULT_PORT = 4242; + public static final String DEFAULT_SERVER = "127.0.0.1"; + public static final int DEFAULT_PORT = 4242; // CLI options @CommandLine.Option(names = {"--hostname", "-ip"}, @@ -33,9 +34,8 @@ public class Launcher implements Callable { private int port; @CommandLine.Option(names = {"--lang", "-l"}, - description = "Sets language of the game, available values: ${COMPLETION-CANDIDATES}", - defaultValue = "EN_US") - private Language lang; + description = "Sets language of the game, available values:\n ${COMPLETION-CANDIDATES}") + private Language lang = Language.EN_US; @CommandLine.Option(names = {"--verbose", "-v"}, description = "Set if you want verbose information") private static boolean verbose = false; @@ -51,7 +51,9 @@ public static boolean isUsingCustomServer() { public static void main(String... args) throws Exception { Launcher launcher = new Launcher(); try { - CommandLine.ParseResult parseResult = new CommandLine(launcher).parseArgs(args); + CommandLine.ParseResult parseResult = new CommandLine(launcher) + .setCaseInsensitiveEnumValuesAllowed(true) + .parseArgs(args); if (!CommandLine.printHelpIfRequested(parseResult)) { launcher.call(); } @@ -61,7 +63,7 @@ public static void main(String... args) throws Exception { } } - private boolean showSettingDialog(JFrame frame, String server, int port) throws ParseException { + public boolean showSettingDialog(JFrame frame, String server, int port) throws ParseException { JPanel pane = new JPanel(); pane.setLayout(new GridLayout(4, 1)); @@ -117,10 +119,7 @@ private String[] login(JFrame frame) { @Override public Void call() throws Exception{ - JFrame frame = new JFrame(); - frame.setTitle("Minigolf"); - Image img = ImageIO.read(getClass().getResource("/icons/playforia.png")); - frame.setIconImage(img); + JFrame frame = createFrame(); if (hostname.isEmpty() || port == 0) { // Determine which of these was actually false String temp_hostname = hostname.isEmpty() ? DEFAULT_SERVER : hostname; @@ -130,10 +129,33 @@ public Void call() throws Exception{ } } - new Game(frame, hostname, port, lang.toString(), verbose); + launchGame(frame, hostname, port, lang, verbose); return null; } + public JFrame createFrame() throws IOException { + JFrame frame = new JFrame(); + frame.setTitle("Minigolf"); + Image img = loadIcon(); + frame.setIconImage(img); + return frame; + } + + public Game launchGame(JFrame frame, String hostname, int port, Language lang, boolean verbose) { + return new Game(frame, hostname, port, lang.toString(), verbose); + } + + public Image loadIcon() throws IOException { + return ImageIO.read(getClass().getResource("/icons/playforia.png")); + } + + public void setHostname(String hostname) { + this.hostname = hostname; + } + + public void setPort(int port) { + this.port = port; + } enum Language { EN_US("en_US"), diff --git a/client/src/test/java/org/moparforia/client/LauncherCLITest.java b/client/src/test/java/org/moparforia/client/LauncherCLITest.java new file mode 100644 index 00000000..79aad260 --- /dev/null +++ b/client/src/test/java/org/moparforia/client/LauncherCLITest.java @@ -0,0 +1,126 @@ +package org.moparforia.client; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; +import picocli.CommandLine; + +import javax.swing.*; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.text.ParseException; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +/** + * Tests that CLI parsing works as expected, it doesn't test the main method, but it tests the picocli annotations + */ +@ExtendWith(MockitoExtension.class) +class LauncherCLITest { + + private Launcher launcher; + + private CommandLine cmd; + private StringWriter stdOut; + private StringWriter stdErr; + + @BeforeEach + void setUp() throws ParseException, IOException { + // Mock game + launcher = spy(new Launcher()); + lenient().doReturn(mock(Game.class)).when(launcher).launchGame(any(JFrame.class), anyString(), anyInt(), any(), anyBoolean()); + + // Mock creating JFrame + lenient().doReturn(mock(JFrame.class)).when(launcher).createFrame(); + + // Mock settings dialog + lenient().doAnswer((invocaton) -> { + launcher.setPort(invocaton.getArgument(2)); + launcher.setHostname(invocaton.getArgument(1)); + return true; + }).when(launcher).showSettingDialog(any(JFrame.class), anyString(), anyInt()); + + cmd = new CommandLine(launcher).setCaseInsensitiveEnumValuesAllowed(true); + + stdOut = new StringWriter(); + stdErr = new StringWriter(); + + cmd.setOut(new PrintWriter(stdOut)); + cmd.setErr(new PrintWriter(stdErr)); + } + + @AfterEach + void tearDown() { + clearInvocations(launcher); + } + + @Test + void testInvalidPort() { + assertNotEquals(0, cmd.execute("-p", "test")); + assertNotEquals(0, cmd.execute("--port=test")); + assertNotEquals(0, cmd.execute("-p")); + } + + @Test + void testInvalidLang() { + assertNotEquals(0, cmd.execute("-l", "cs_CZ")); + assertNotEquals(0, cmd.execute("-l", "en")); + } + + @Test + void testValidLang() { + assertEquals(0, cmd.execute("-l", "en_US")); + verify(launcher).launchGame(any(), + eq(Launcher.DEFAULT_SERVER), + eq(Launcher.DEFAULT_PORT), + eq(Launcher.Language.EN_US), + anyBoolean()); + + assertEquals(0, cmd.execute("--lang=Fi_fI")); + verify(launcher).launchGame(any(), + eq(Launcher.DEFAULT_SERVER), + eq(Launcher.DEFAULT_PORT), + eq(Launcher.Language.FI_FI), + anyBoolean()); + } + + @Test + void testValidPortAndHostname() { + assertEquals(0, cmd.execute("-p", "1111", "-ip", "128.128.128.128")); + verify(launcher).launchGame(any(), eq("128.128.128.128"), eq(1111), any(), anyBoolean()); + + assertEquals(0, cmd.execute("-p=2222", "-ip=127.127.127.127")); + verify(launcher).launchGame(any(), eq("127.127.127.127"), eq(2222), any(), anyBoolean()); + + assertEquals(0, cmd.execute("-p=3333", "-ip=126.126.126.126")); + verify(launcher).launchGame(any(), eq("126.126.126.126"), eq(3333), any(), anyBoolean()); + } + + @Test + void testOnlyPort() { + assertEquals(0, cmd.execute("-p", "1111")); + verify(launcher).launchGame(any(), eq(Launcher.DEFAULT_SERVER), eq(1111), any(), anyBoolean()); + } + + @Test + void testOnlyHostname() { + assertEquals(0, cmd.execute("-ip", "127.127.127.127")); + verify(launcher).launchGame(any(), eq("127.127.127.127"), eq(Launcher.DEFAULT_PORT), any(), anyBoolean()); + } + + @Test + void testDefaultValues() { + assertEquals(0, cmd.execute()); + verify(launcher).launchGame(any(), + eq(Launcher.DEFAULT_SERVER), + eq(Launcher.DEFAULT_PORT), + eq(Launcher.Language.EN_US), + eq(false)); + } +} \ No newline at end of file diff --git a/client/src/test/java/org/moparforia/client/ResourceLoadTest.java b/client/src/test/java/org/moparforia/client/ResourceLoadTest.java new file mode 100644 index 00000000..69ed4972 --- /dev/null +++ b/client/src/test/java/org/moparforia/client/ResourceLoadTest.java @@ -0,0 +1,22 @@ +package org.moparforia.client; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + + +/** + * Tests that resources can be loaded + */ +class ResourceLoadTest { + + /** + * Tests that Launcher icon can be loaded + */ + @Test + void testLoadIcon() { + Launcher launcher = new Launcher(); + assertDoesNotThrow(launcher::loadIcon); + } + +} \ No newline at end of file diff --git a/editor/editor.iml b/editor/editor.iml index 03fa41c4..4df9cb35 100644 --- a/editor/editor.iml +++ b/editor/editor.iml @@ -4,7 +4,6 @@ - diff --git a/pom.xml b/pom.xml index 0475be3a..d4c23da5 100644 --- a/pom.xml +++ b/pom.xml @@ -48,6 +48,13 @@ ${project.mainClass} + + ${project.artifactId} + ${project.version} + ${project.artifactId} + ${project.version} + ${project.groupId} + @@ -74,6 +81,10 @@ exec-maven-plugin 3.0.0 + + maven-surefire-plugin + 2.22.2 + @@ -106,6 +117,26 @@ picocli ${picocli.version} + + + + org.junit.jupiter + junit-jupiter-engine + 5.6.2 + test + + + org.mockito + mockito-core + 3.5.10 + test + + + org.mockito + mockito-junit-jupiter + 3.5.10 + test + diff --git a/server/pom.xml b/server/pom.xml index 148de51c..ef406e0e 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -40,6 +40,23 @@ info.picocli picocli + + + + org.junit.jupiter + junit-jupiter-engine + test + + + org.mockito + mockito-core + test + + + org.mockito + mockito-junit-jupiter + test + diff --git a/server/server.iml b/server/server.iml index a8a3312c..37695179 100644 --- a/server/server.iml +++ b/server/server.iml @@ -5,6 +5,8 @@ + + @@ -15,5 +17,18 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/server/src/main/java/org/moparforia/server/Launcher.java b/server/src/main/java/org/moparforia/server/Launcher.java index ae2cd397..236cfb05 100644 --- a/server/src/main/java/org/moparforia/server/Launcher.java +++ b/server/src/main/java/org/moparforia/server/Launcher.java @@ -1,24 +1,28 @@ package org.moparforia.server; +import org.moparforia.shared.ManifestVersionProvider; import picocli.CommandLine; import java.util.concurrent.Callable; @CommandLine.Command( description = "Starts Minigolf Server", - name = "server", mixinStandardHelpOptions = true + name = "server", + mixinStandardHelpOptions = true, + versionProvider = ManifestVersionProvider.class ) public class Launcher implements Callable { + public static final String DEFAULT_HOST = "0.0.0.0"; + public static final int DEFAULT_PORT = 4242; + @CommandLine.Option(names = {"--hostname", "-ip"}, - description = "Sets server hostname", - defaultValue = "0.0.0.0") - private String host; + description = "Sets server hostname") + private String host = DEFAULT_HOST; @CommandLine.Option(names = {"--port", "-p"}, - description = "Sets server port", - defaultValue = "4242") - private int port; + description = "Sets server port") + private int port = DEFAULT_PORT; public static void main(String... args) { Launcher launcher = new Launcher(); @@ -35,7 +39,11 @@ public static void main(String... args) { @Override public Void call() { - new Server(host, port).start(); + getServer(host, port).start(); return null; } + + public Server getServer(String host, int port) { + return new Server(host, port); + } } diff --git a/server/src/test/java/org/moparforia/server/LauncherCLITest.java b/server/src/test/java/org/moparforia/server/LauncherCLITest.java new file mode 100644 index 00000000..a95f2723 --- /dev/null +++ b/server/src/test/java/org/moparforia/server/LauncherCLITest.java @@ -0,0 +1,87 @@ +package org.moparforia.server; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; +import picocli.CommandLine; + +import java.io.PrintWriter; +import java.io.StringWriter; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +/** + * Tests that CLI parsing works as expected, it doesn't test the main method, but it tests the picocli annotations + */ +@ExtendWith(MockitoExtension.class) +class LauncherCLITest { + private Launcher launcher; + + private CommandLine cmd; + private StringWriter stdErr; + private StringWriter stdOut; + + @BeforeEach + void setUp() { + // Mock Launcher instance + launcher = spy(new Launcher()); + lenient().doReturn(mock(Server.class)).when(launcher).getServer(anyString(), anyInt()); + + cmd = new CommandLine(launcher).setCaseInsensitiveEnumValuesAllowed(true); + + stdOut = new StringWriter(); + stdErr = new StringWriter(); + + cmd.setOut(new PrintWriter(stdOut)); + cmd.setErr(new PrintWriter(stdErr)); + } + + @AfterEach + void tearDown() { + clearInvocations(launcher); + } + + @Test + void testInvalidPort() { + assertNotEquals(0, cmd.execute("-p", "test")); + assertNotEquals(0, cmd.execute("--port=test")); + assertNotEquals(0, cmd.execute("-p")); + + verify(launcher, never()).getServer(anyString(), anyInt()); + } + + @Test + void testValidPortAndHostname() { + assertEquals(0, cmd.execute("-p", "1111", "-ip", "128.128.128.128")); + verify(launcher).getServer(eq("128.128.128.128"), eq(1111)); + + assertEquals(0, cmd.execute("-p=2222", "-ip=127.127.127.127")); + verify(launcher).getServer(eq("127.127.127.127"), eq(2222)); + + assertEquals(0, cmd.execute("-p=3333", "-ip=126.126.126.126")); + verify(launcher).getServer(eq("126.126.126.126"), eq(3333)); + } + + @Test + void testOnlyPort() { + assertEquals(0, cmd.execute("-p", "1111")); + verify(launcher).getServer(eq(Launcher.DEFAULT_HOST), eq(1111)); + } + + @Test + void testOnlyHostname() { + assertEquals(0, cmd.execute("-ip", "127.127.127.127")); + verify(launcher).getServer(eq("127.127.127.127"), eq(Launcher.DEFAULT_PORT)); + } + + @Test + void testDefaultValues() { + assertEquals(0, cmd.execute()); + verify(launcher).getServer(eq(Launcher.DEFAULT_HOST), eq(Launcher.DEFAULT_PORT)); + } +} \ No newline at end of file diff --git a/shared/pom.xml b/shared/pom.xml index b64a4355..c7eb7f3e 100644 --- a/shared/pom.xml +++ b/shared/pom.xml @@ -24,9 +24,13 @@ morphia provided + + info.picocli + picocli + provided + - src org.apache.maven.plugins diff --git a/shared/shared.iml b/shared/shared.iml index 84b089b8..9d9c4dbf 100644 --- a/shared/shared.iml +++ b/shared/shared.iml @@ -13,5 +13,6 @@ + \ No newline at end of file diff --git a/shared/src/main/java/org/moparforia/shared/ManifestVersionProvider.java b/shared/src/main/java/org/moparforia/shared/ManifestVersionProvider.java new file mode 100644 index 00000000..ae92aef2 --- /dev/null +++ b/shared/src/main/java/org/moparforia/shared/ManifestVersionProvider.java @@ -0,0 +1,11 @@ +package org.moparforia.shared; + +import picocli.CommandLine; + +public class ManifestVersionProvider implements CommandLine.IVersionProvider { + @Override + public String[] getVersion() { + String version = ManifestVersionProvider.class.getPackage().getImplementationVersion(); + return new String[] {"${COMMAND-FULL-NAME} " + version}; + } +}