From d49f18c3aa2343e20665b85ffb79292e8c24cb7c Mon Sep 17 00:00:00 2001 From: Lars Simon Winzer Date: Sat, 25 Apr 2026 10:44:09 +0200 Subject: [PATCH 1/3] Test: Unit tests for Tokenizer --- .../parser/tokenizer/TokenizerTest.java | 204 ++++++++++++++++++ 1 file changed, 204 insertions(+) create mode 100644 src/test/java/ch/unibas/dmi/dbis/cs108/casono/server/network/protocol/parser/tokenizer/TokenizerTest.java diff --git a/src/test/java/ch/unibas/dmi/dbis/cs108/casono/server/network/protocol/parser/tokenizer/TokenizerTest.java b/src/test/java/ch/unibas/dmi/dbis/cs108/casono/server/network/protocol/parser/tokenizer/TokenizerTest.java new file mode 100644 index 0000000..5c5a2d3 --- /dev/null +++ b/src/test/java/ch/unibas/dmi/dbis/cs108/casono/server/network/protocol/parser/tokenizer/TokenizerTest.java @@ -0,0 +1,204 @@ +package ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.parser.tokenizer; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; +import org.junit.jupiter.api.Test; + +class TokenizerTest { + @Test + void testEofAlwaysPresent() { + List tokens = Tokenizer.tokenize(""); + + assertEquals(1, tokens.size()); + assertEquals(RawTokenType.EOF, tokens.get(0).type()); + } + + @Test + void testSimpleCommand() { + List tokens = Tokenizer.tokenize("PING"); + assertEquals(2, tokens.size()); + assertEquals(RawTokenType.WORD, tokens.get(0).type()); + assertEquals("PING", tokens.get(0).value()); + assertEquals(RawTokenType.EOF, tokens.get(1).type()); + } + + @Test + void testSimpleCommandWithUnderscore() { + List tokens = Tokenizer.tokenize("JOIN_GAME"); + assertEquals(2, tokens.size()); + assertEquals(RawTokenType.WORD, tokens.get(0).type()); + assertEquals("JOIN_GAME", tokens.get(0).value()); + assertEquals(RawTokenType.EOF, tokens.get(1).type()); + } + + @Test + void testCommandWithOneParameter() { + List tokens = Tokenizer.tokenize("GET VALUE=42"); + assertEquals(5, tokens.size()); + + assertEquals(RawTokenType.WORD, tokens.get(0).type()); + assertEquals("GET", tokens.get(0).value()); + + // Parameter + assertEquals(RawTokenType.WORD, tokens.get(1).type()); + assertEquals("VALUE", tokens.get(1).value()); + + assertEquals(RawTokenType.SEPARATOR, tokens.get(2).type()); + assertEquals("=", tokens.get(2).value()); + + assertEquals(RawTokenType.WORD, tokens.get(3).type()); + assertEquals("42", tokens.get(3).value()); + + assertEquals(RawTokenType.EOF, tokens.get(4).type()); + } + + @Test + void testCommandWithOneParameterWhitespacesBetweenSeperator() { + List tokens = Tokenizer.tokenize("GET VALUE = 42"); + assertEquals(5, tokens.size()); + + assertEquals(RawTokenType.WORD, tokens.get(0).type()); + assertEquals("GET", tokens.get(0).value()); + + // Parameter + assertEquals(RawTokenType.WORD, tokens.get(1).type()); + assertEquals("VALUE", tokens.get(1).value()); + + assertEquals(RawTokenType.SEPARATOR, tokens.get(2).type()); + assertEquals("=", tokens.get(2).value()); + + assertEquals(RawTokenType.WORD, tokens.get(3).type()); + assertEquals("42", tokens.get(3).value()); + + assertEquals(RawTokenType.EOF, tokens.get(4).type()); + } + + @Test + void testCommandWithOneParameterAndNewline() { + List tokens = Tokenizer.tokenize("GET\nVALUE=42"); + assertEquals(6, tokens.size()); + + assertEquals(RawTokenType.WORD, tokens.get(0).type()); + assertEquals("GET", tokens.get(0).value()); + + // Parameter + assertEquals(RawTokenType.WORD, tokens.get(2).type()); + assertEquals("VALUE", tokens.get(2).value()); + + assertEquals(RawTokenType.SEPARATOR, tokens.get(3).type()); + assertEquals("=", tokens.get(3).value()); + + assertEquals(RawTokenType.WORD, tokens.get(4).type()); + assertEquals("42", tokens.get(4).value()); + + assertEquals(RawTokenType.EOF, tokens.get(5).type()); + } + + @Test + void testCommandWithStringParameter() { + List tokens = Tokenizer.tokenize("GREET MSG='Hello World'"); + assertEquals(5, tokens.size()); + + assertEquals(RawTokenType.WORD, tokens.get(0).type()); + assertEquals("GREET", tokens.get(0).value()); + + // First parameter + assertEquals(RawTokenType.WORD, tokens.get(1).type()); + assertEquals("MSG", tokens.get(1).value()); + + assertEquals(RawTokenType.SEPARATOR, tokens.get(2).type()); + assertEquals("=", tokens.get(2).value()); + + assertEquals(RawTokenType.STRING, tokens.get(3).type()); + assertEquals("Hello World", tokens.get(3).value()); + + assertEquals(RawTokenType.EOF, tokens.get(4).type()); + } + + @Test + void testCommandWithMultilineStringParameter() { + List tokens = Tokenizer.tokenize("GREET MSG='Hello\nWorld'"); + assertEquals(5, tokens.size()); + + assertEquals(RawTokenType.WORD, tokens.get(0).type()); + assertEquals("GREET", tokens.get(0).value()); + + // First parameter + assertEquals(RawTokenType.WORD, tokens.get(1).type()); + assertEquals("MSG", tokens.get(1).value()); + + assertEquals(RawTokenType.SEPARATOR, tokens.get(2).type()); + assertEquals("=", tokens.get(2).value()); + + assertEquals(RawTokenType.STRING, tokens.get(3).type()); + assertEquals("Hello\nWorld", tokens.get(3).value()); + + assertEquals(RawTokenType.EOF, tokens.get(4).type()); + } + + @Test + void testWhitespace() { + List tokens = Tokenizer.tokenize(" \t PING"); + assertEquals(2, tokens.size()); + assertEquals(RawTokenType.WORD, tokens.get(0).type()); + assertEquals("PING", tokens.get(0).value()); + } + + @Test + void testStringWithEscapedQuote() { + List tokens = Tokenizer.tokenize("WONDERFUL_GREETING MSG='it\\'s a wonderful day'"); + assertEquals(5, tokens.size()); + assertEquals(RawTokenType.STRING, tokens.get(3).type()); + assertEquals("it's a wonderful day", tokens.get(3).value()); + } + + @Test + void testStringWithWronglyEscapedQuote() { + TokenizerException ex = assertThrows(TokenizerException.class, () -> Tokenizer.tokenize("WONDERFUL_GREETING MSG='it\'s a wonderful day'")); + assertEquals("Unterminated string literal", ex.getMessage()); + } + + @Test + void testStringWithEscapedBackslash() { + List tokens = Tokenizer.tokenize("EXECUTE_COMMAND CMD='\\whoami'"); + assertEquals(5, tokens.size()); + assertEquals(RawTokenType.STRING, tokens.get(3).type()); + assertEquals("\\whoami", tokens.get(3).value()); + } + + @Test + void testColumnTracking() { + List tokens = Tokenizer.tokenize("GET VAL=1"); + assertEquals(5, tokens.size()); + assertEquals(1, tokens.get(0).column()); + assertEquals(5, tokens.get(1).column()); + assertEquals(8, tokens.get(2).column()); + assertEquals(9, tokens.get(3).column()); + } + + @Test + void testUnterminatedStringThrows() { + TokenizerException ex = assertThrows(TokenizerException.class, () -> Tokenizer.tokenize("GREETING = 'unclosed")); + assertEquals(1, ex.getLine()); + assertEquals(12, ex.getColumn()); + assertTrue(ex.getMessage().contains("Unterminated string literal")); + } + + @Test + void testUnexpectedCharacterThrows() { + TokenizerException ex = assertThrows(TokenizerException.class, () -> Tokenizer.tokenize("CMD @ KEY=VALUE")); + assertTrue(ex.getMessage().contains("Unexpected character '@'")); + assertEquals(1, ex.getLine()); + } + + @Test + void testEmptyStringValue() { + List tokens = Tokenizer.tokenize("cmd = ''"); + assertEquals(4, tokens.size()); + assertEquals(RawTokenType.STRING, tokens.get(2).type()); + assertEquals("", tokens.get(2).value()); + } +} -- 2.52.0 From 4bfb59f234b54de0a2c2a14bae85ca8822cd710d Mon Sep 17 00:00:00 2001 From: Lars Simon Winzer Date: Sat, 25 Apr 2026 13:28:10 +0200 Subject: [PATCH 2/3] Test: Unit tests for TokenClassifier Co-authored-by: Copilot --- .../parser/tokenizer/TokenClassifierTest.java | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 src/test/java/ch/unibas/dmi/dbis/cs108/casono/server/network/protocol/parser/tokenizer/TokenClassifierTest.java diff --git a/src/test/java/ch/unibas/dmi/dbis/cs108/casono/server/network/protocol/parser/tokenizer/TokenClassifierTest.java b/src/test/java/ch/unibas/dmi/dbis/cs108/casono/server/network/protocol/parser/tokenizer/TokenClassifierTest.java new file mode 100644 index 0000000..9a3dc21 --- /dev/null +++ b/src/test/java/ch/unibas/dmi/dbis/cs108/casono/server/network/protocol/parser/tokenizer/TokenClassifierTest.java @@ -0,0 +1,85 @@ +package ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.parser.tokenizer; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.ArrayList; +import java.util.List; +import org.junit.jupiter.api.Test; + +public class TokenClassifierTest { + @Test + void testClassifySimpleCommand() { + List raw = Tokenizer.tokenize("PING"); + List tokens = TokenClassifier.classify(raw); + + assertEquals(2, tokens.size()); + assertEquals(TokenType.COMMAND, tokens.get(0).type()); + assertEquals("PING", tokens.get(0).value()); + assertEquals(TokenType.EOF, tokens.get(1).type()); + } + + @Test + void testCommandWithParameter() { + List raw = Tokenizer.tokenize("ANSWER VALUE=42"); + List tokens = TokenClassifier.classify(raw); + + assertEquals(5, tokens.size()); + assertEquals(TokenType.COMMAND, tokens.get(0).type()); + assertEquals(TokenType.KEY, tokens.get(1).type()); + assertEquals(TokenType.SEPARATOR, tokens.get(2).type()); + assertEquals(TokenType.VALUE, tokens.get(3).type()); + assertEquals("42", tokens.get(3).value()); + assertEquals(TokenType.EOF, tokens.get(4).type()); + } + + @Test + void testStringValue() { + List raw = Tokenizer.tokenize("GREET MSG='Hello World'"); + List tokens = TokenClassifier.classify(raw); + + assertEquals(5, tokens.size()); + assertEquals(TokenType.COMMAND, tokens.get(0).type()); + assertEquals(TokenType.KEY, tokens.get(1).type()); + assertEquals(TokenType.SEPARATOR, tokens.get(2).type()); + assertEquals(TokenType.VALUE, tokens.get(3).type()); + assertEquals("Hello World", tokens.get(3).value()); + } + + @Test + void testUnexpectedStringLiteralThrows() { + List raw = Tokenizer.tokenize("CMD 'oops'"); + + TokenizerException ex = + assertThrows(TokenizerException.class, () -> TokenClassifier.classify(raw)); + assertTrue(ex.getMessage().contains("Unexpected string literal")); + } + + @Test + void testMissingValueAfterSeparatorThrows() { + List raw = Tokenizer.tokenize("CMD KEY="); + + TokenizerException ex = + assertThrows(TokenizerException.class, () -> TokenClassifier.classify(raw)); + assertEquals("Expected VALUE after '='", ex.getMessage()); + } + + @Test + void testNextWordIsKeyThrows() { + List raw = Tokenizer.tokenize("CMD KEY1=KEY2=42"); + + TokenizerException ex = + assertThrows(TokenizerException.class, () -> TokenClassifier.classify(raw)); + assertEquals("Expected VALUE after '='", ex.getMessage()); + } + + @Test + void testEmptyRawTokensThrows() { + List raw = new ArrayList<>(); + + TokenizerException ex = + assertThrows(TokenizerException.class, () -> TokenClassifier.classify(raw)); + assertEquals("Expected COMMAND as first token", ex.getMessage()); + assertEquals(1, ex.getLine()); + assertEquals(1, ex.getColumn()); + } +} -- 2.52.0 From 2190fccd8f990e9de0bc75b0cc35b93058ef80fb Mon Sep 17 00:00:00 2001 From: Lars Simon Winzer Date: Sat, 25 Apr 2026 13:28:34 +0200 Subject: [PATCH 3/3] Style: Apply Spotless --- .../protocol/parser/tokenizer/TokenizerTest.java | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/test/java/ch/unibas/dmi/dbis/cs108/casono/server/network/protocol/parser/tokenizer/TokenizerTest.java b/src/test/java/ch/unibas/dmi/dbis/cs108/casono/server/network/protocol/parser/tokenizer/TokenizerTest.java index 5c5a2d3..bd5e486 100644 --- a/src/test/java/ch/unibas/dmi/dbis/cs108/casono/server/network/protocol/parser/tokenizer/TokenizerTest.java +++ b/src/test/java/ch/unibas/dmi/dbis/cs108/casono/server/network/protocol/parser/tokenizer/TokenizerTest.java @@ -149,7 +149,8 @@ class TokenizerTest { @Test void testStringWithEscapedQuote() { - List tokens = Tokenizer.tokenize("WONDERFUL_GREETING MSG='it\\'s a wonderful day'"); + List tokens = + Tokenizer.tokenize("WONDERFUL_GREETING MSG='it\\'s a wonderful day'"); assertEquals(5, tokens.size()); assertEquals(RawTokenType.STRING, tokens.get(3).type()); assertEquals("it's a wonderful day", tokens.get(3).value()); @@ -157,7 +158,10 @@ class TokenizerTest { @Test void testStringWithWronglyEscapedQuote() { - TokenizerException ex = assertThrows(TokenizerException.class, () -> Tokenizer.tokenize("WONDERFUL_GREETING MSG='it\'s a wonderful day'")); + TokenizerException ex = + assertThrows( + TokenizerException.class, + () -> Tokenizer.tokenize("WONDERFUL_GREETING MSG='it\'s a wonderful day'")); assertEquals("Unterminated string literal", ex.getMessage()); } @@ -181,7 +185,9 @@ class TokenizerTest { @Test void testUnterminatedStringThrows() { - TokenizerException ex = assertThrows(TokenizerException.class, () -> Tokenizer.tokenize("GREETING = 'unclosed")); + TokenizerException ex = + assertThrows( + TokenizerException.class, () -> Tokenizer.tokenize("GREETING = 'unclosed")); assertEquals(1, ex.getLine()); assertEquals(12, ex.getColumn()); assertTrue(ex.getMessage().contains("Unterminated string literal")); @@ -189,7 +195,8 @@ class TokenizerTest { @Test void testUnexpectedCharacterThrows() { - TokenizerException ex = assertThrows(TokenizerException.class, () -> Tokenizer.tokenize("CMD @ KEY=VALUE")); + TokenizerException ex = + assertThrows(TokenizerException.class, () -> Tokenizer.tokenize("CMD @ KEY=VALUE")); assertTrue(ex.getMessage().contains("Unexpected character '@'")); assertEquals(1, ex.getLine()); } -- 2.52.0