From a53e54572a804c3446a9a918502d28776b925026 Mon Sep 17 00:00:00 2001 From: Ali Khorami <33497115+sabotack@users.noreply.github.com> Date: Tue, 16 May 2023 13:51:47 +0200 Subject: [PATCH 01/17] feat: added visitAssign unit tests (#84) --- src/test/interpreter/VisitorUnitTest.java | 61 +++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/src/test/interpreter/VisitorUnitTest.java b/src/test/interpreter/VisitorUnitTest.java index d285165..39a1fa1 100644 --- a/src/test/interpreter/VisitorUnitTest.java +++ b/src/test/interpreter/VisitorUnitTest.java @@ -7,6 +7,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; import org.junit.jupiter.params.provider.ValueSource; import org.mockito.Mock; import org.mockito.Mockito; @@ -51,6 +52,66 @@ void visitAssignStoresCorrectNumber(int value) { Assertions.assertNull(result); } + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void visitAssignStoresCorrectBoolean(boolean value) { + // Mock functions ID(), expression(), and visit() + Mockito.when(mockAssignContext.ID()).thenReturn(new MockTerminalNode("varName")); + Mockito.when(mockAssignContext.expression()).thenReturn(mockExpressionContext); + Mockito.when(spyVisitor.visit(mockExpressionContext)).thenReturn(new MSBool(value)); + + // Call the visitAssign method on the spy + MSType result = spyVisitor.visitAssign(mockAssignContext); + + // Initialize mock value and assert that no exceptions are thrown when value is retrieved + AtomicReference mockValue = new AtomicReference<>(); + Assertions.assertDoesNotThrow(() -> mockValue.set(symbolTable.retrieveSymbolValue(symbolTable.retrieveSymbol("varName")))); + + // Assert that the symbol value is equal to the initial assign value, and assert that result is null + Assertions.assertEquals(value, ((MSBool) mockValue.get()).getValue()); + Assertions.assertNull(result); + } + + @ParameterizedTest + @EnumSource(MSRelDir.Direction.class) + void visitAssignStoresCorrectRelDir(MSRelDir.Direction value) { + // Mock functions ID(), expression(), and visit() + Mockito.when(mockAssignContext.ID()).thenReturn(new MockTerminalNode("varName")); + Mockito.when(mockAssignContext.expression()).thenReturn(mockExpressionContext); + Mockito.when(spyVisitor.visit(mockExpressionContext)).thenReturn(new MSRelDir(value.toString().toLowerCase())); + + // Call the visitAssign method on the spy + MSType result = spyVisitor.visitAssign(mockAssignContext); + + // Initialize mock value and assert that no exceptions are thrown when value is retrieved + AtomicReference mockValue = new AtomicReference<>(); + Assertions.assertDoesNotThrow(() -> mockValue.set(symbolTable.retrieveSymbolValue(symbolTable.retrieveSymbol("varName")))); + + // Assert that the symbol value is equal to the initial assign value, and assert that result is null + Assertions.assertEquals(value, ((MSRelDir) mockValue.get()).getValue()); + Assertions.assertNull(result); + } + + @ParameterizedTest + @EnumSource(MSAbsDir.Direction.class) + void visitAssignStoresCorrectAbsDir(MSAbsDir.Direction value) { + // Mock functions ID(), expression(), and visit() + Mockito.when(mockAssignContext.ID()).thenReturn(new MockTerminalNode("varName")); + Mockito.when(mockAssignContext.expression()).thenReturn(mockExpressionContext); + Mockito.when(spyVisitor.visit(mockExpressionContext)).thenReturn(new MSAbsDir(value.toString().toLowerCase())); + + // Call the visitAssign method on the spy + MSType result = spyVisitor.visitAssign(mockAssignContext); + + // Initialize mock value and assert that no exceptions are thrown when value is retrieved + AtomicReference mockValue = new AtomicReference<>(); + Assertions.assertDoesNotThrow(() -> mockValue.set(symbolTable.retrieveSymbolValue(symbolTable.retrieveSymbol("varName")))); + + // Assert that the symbol value is equal to the initial assign value, and assert that result is null + Assertions.assertEquals(value, ((MSAbsDir) mockValue.get()).getValue()); + Assertions.assertNull(result); + } + @Test void visitNotExprValidBoolReturnsNegatedBool() { Mockito.when(mockNotExprContext.expression()).thenReturn(mockExpressionContext); From 5b0b7a6e9ae95414deabe414d8b078e1d1e3c7c5 Mon Sep 17 00:00:00 2001 From: Ali Khorami <33497115+sabotack@users.noreply.github.com> Date: Tue, 16 May 2023 13:57:16 +0200 Subject: [PATCH 02/17] Test for AddSubVisitor (#86) Co-authored-by: Viktor Platz --- src/test/interpreter/VisitorUnitTest.java | 69 ++++++++++++++++------- src/test/interpreter/utils/MockToken.java | 58 +++++++++++++++++++ 2 files changed, 106 insertions(+), 21 deletions(-) create mode 100644 src/test/interpreter/utils/MockToken.java diff --git a/src/test/interpreter/VisitorUnitTest.java b/src/test/interpreter/VisitorUnitTest.java index 39a1fa1..ccc9fdc 100644 --- a/src/test/interpreter/VisitorUnitTest.java +++ b/src/test/interpreter/VisitorUnitTest.java @@ -3,10 +3,14 @@ import interpreter.antlr.MineScriptParser; import interpreter.types.*; import interpreter.utils.MockTerminalNode; +import interpreter.utils.MockToken; +import org.antlr.v4.runtime.Token; +import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; import org.junit.jupiter.params.provider.EnumSource; import org.junit.jupiter.params.provider.ValueSource; import org.mockito.Mock; @@ -14,6 +18,7 @@ import org.mockito.Spy; import org.mockito.junit.jupiter.MockitoExtension; +import java.util.Objects; import java.util.concurrent.atomic.AtomicReference; @ExtendWith(MockitoExtension.class) @@ -23,7 +28,8 @@ class VisitorUnitTest { @Spy private final Visitor spyVisitor = visitor; @Mock private MineScriptParser.AssignContext mockAssignContext; - @Mock private MineScriptParser.ExpressionContext mockExpressionContext; + @Mock private MineScriptParser.ExpressionContext mockExpressionContext1; + @Mock private MineScriptParser.ExpressionContext mockExpressionContext2; @Mock private MineScriptParser.BoolContext mockBoolContext; @Mock private MineScriptParser.AbsDirContext mockAbsDirContext; @Mock private MineScriptParser.RelDirContext mockRelDirContext; @@ -31,14 +37,17 @@ class VisitorUnitTest { @Mock private MineScriptParser.IdContext mockIdContext; @Mock private MineScriptParser.NegContext mockNegContext; @Mock private MineScriptParser.NotExprContext mockNotExprContext; + @Mock private MineScriptParser.AddSubContext mockAddSubContext; + + @ParameterizedTest @ValueSource(ints = {-1000, -10, 0, 10, 1000}) void visitAssignStoresCorrectNumber(int value) { // Mock functions ID(), expression(), and visit() Mockito.when(mockAssignContext.ID()).thenReturn(new MockTerminalNode("varName")); - Mockito.when(mockAssignContext.expression()).thenReturn(mockExpressionContext); - Mockito.when(spyVisitor.visit(mockExpressionContext)).thenReturn(new MSNumber(value)); + Mockito.when(mockAssignContext.expression()).thenReturn(mockExpressionContext1); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSNumber(value)); // Call the visitAssign method on the spy MSType result = spyVisitor.visitAssign(mockAssignContext); @@ -57,8 +66,8 @@ void visitAssignStoresCorrectNumber(int value) { void visitAssignStoresCorrectBoolean(boolean value) { // Mock functions ID(), expression(), and visit() Mockito.when(mockAssignContext.ID()).thenReturn(new MockTerminalNode("varName")); - Mockito.when(mockAssignContext.expression()).thenReturn(mockExpressionContext); - Mockito.when(spyVisitor.visit(mockExpressionContext)).thenReturn(new MSBool(value)); + Mockito.when(mockAssignContext.expression()).thenReturn(mockExpressionContext1); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSBool(value)); // Call the visitAssign method on the spy MSType result = spyVisitor.visitAssign(mockAssignContext); @@ -77,8 +86,8 @@ void visitAssignStoresCorrectBoolean(boolean value) { void visitAssignStoresCorrectRelDir(MSRelDir.Direction value) { // Mock functions ID(), expression(), and visit() Mockito.when(mockAssignContext.ID()).thenReturn(new MockTerminalNode("varName")); - Mockito.when(mockAssignContext.expression()).thenReturn(mockExpressionContext); - Mockito.when(spyVisitor.visit(mockExpressionContext)).thenReturn(new MSRelDir(value.toString().toLowerCase())); + Mockito.when(mockAssignContext.expression()).thenReturn(mockExpressionContext1); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSRelDir(value.toString().toLowerCase())); // Call the visitAssign method on the spy MSType result = spyVisitor.visitAssign(mockAssignContext); @@ -97,8 +106,8 @@ void visitAssignStoresCorrectRelDir(MSRelDir.Direction value) { void visitAssignStoresCorrectAbsDir(MSAbsDir.Direction value) { // Mock functions ID(), expression(), and visit() Mockito.when(mockAssignContext.ID()).thenReturn(new MockTerminalNode("varName")); - Mockito.when(mockAssignContext.expression()).thenReturn(mockExpressionContext); - Mockito.when(spyVisitor.visit(mockExpressionContext)).thenReturn(new MSAbsDir(value.toString().toLowerCase())); + Mockito.when(mockAssignContext.expression()).thenReturn(mockExpressionContext1); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSAbsDir(value.toString().toLowerCase())); // Call the visitAssign method on the spy MSType result = spyVisitor.visitAssign(mockAssignContext); @@ -114,8 +123,8 @@ void visitAssignStoresCorrectAbsDir(MSAbsDir.Direction value) { @Test void visitNotExprValidBoolReturnsNegatedBool() { - Mockito.when(mockNotExprContext.expression()).thenReturn(mockExpressionContext); - Mockito.when(spyVisitor.visit(mockExpressionContext)).thenReturn(new MSBool(false)); + Mockito.when(mockNotExprContext.expression()).thenReturn(mockExpressionContext1); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSBool(false)); MSType result = spyVisitor.visitNotExpr(mockNotExprContext); Assertions.assertTrue(((MSBool) result).getValue()); @@ -123,8 +132,8 @@ void visitNotExprValidBoolReturnsNegatedBool() { @Test void visitNotExprPassZeroReturnsTrue() { - Mockito.when(mockNotExprContext.expression()).thenReturn(mockExpressionContext); - Mockito.when(spyVisitor.visit(mockExpressionContext)).thenReturn(new MSNumber(0)); + Mockito.when(mockNotExprContext.expression()).thenReturn(mockExpressionContext1); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSNumber(0)); MSType result = spyVisitor.visitNotExpr(mockNotExprContext); Assertions.assertTrue(((MSBool) result).getValue()); @@ -133,8 +142,8 @@ void visitNotExprPassZeroReturnsTrue() { @ParameterizedTest @ValueSource(ints = {-1000, -100, 100, 1000}) void visitNotExprPassNonZeroNumberReturnsFalse(int value) { - Mockito.when(mockNotExprContext.expression()).thenReturn(mockExpressionContext); - Mockito.when(spyVisitor.visit(mockExpressionContext)).thenReturn(new MSNumber(value)); + Mockito.when(mockNotExprContext.expression()).thenReturn(mockExpressionContext1); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSNumber(value)); MSType result = spyVisitor.visitNotExpr(mockNotExprContext); Assertions.assertFalse(((MSBool) result).getValue()); @@ -142,8 +151,8 @@ void visitNotExprPassNonZeroNumberReturnsFalse(int value) { @Test void visitNotExprInvalidTypeThrowsRuntimeException() { - Mockito.when(mockNotExprContext.expression()).thenReturn(mockExpressionContext); - Mockito.when(spyVisitor.visit(mockExpressionContext)).thenReturn(new MSRelDir("right")); + Mockito.when(mockNotExprContext.expression()).thenReturn(mockExpressionContext1); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSRelDir("right")); Assertions.assertThrows(RuntimeException.class, () -> spyVisitor.visitNotExpr(mockNotExprContext)); } @@ -151,8 +160,8 @@ void visitNotExprInvalidTypeThrowsRuntimeException() { @ParameterizedTest @ValueSource(ints = {-1000, -10, 0, 10, 1000}) void visitNegNegatesNumber(int value) { - Mockito.when(mockNegContext.expression()).thenReturn(mockExpressionContext); - Mockito.when(spyVisitor.visit(mockExpressionContext)).thenReturn(new MSNumber(value)); + Mockito.when(mockNegContext.expression()).thenReturn(mockExpressionContext1); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSNumber(value)); MSType result = spyVisitor.visitNeg(mockNegContext); Assertions.assertEquals(-value, ((MSNumber) result).getValue()); @@ -160,8 +169,8 @@ void visitNegNegatesNumber(int value) { @Test void visitNegInvalidTypeThrowsRuntimeException() { - Mockito.when(mockNegContext.expression()).thenReturn(mockExpressionContext); - Mockito.when(spyVisitor.visit(mockExpressionContext)).thenReturn(new MSBool(false)); + Mockito.when(mockNegContext.expression()).thenReturn(mockExpressionContext1); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSBool(false)); Assertions.assertThrows(RuntimeException.class, () -> spyVisitor.visitNeg(mockNegContext)); } @@ -250,4 +259,22 @@ void visitNumberInvalidInputThrowsRuntimeException(){ Mockito.when(mockNumberContext.NUMBER()).thenReturn(new MockTerminalNode("abc")); Assertions.assertThrows(RuntimeException.class, () -> spyVisitor.visitNumber(mockNumberContext)); } + + @ParameterizedTest + @CsvSource ({"1, 2, +", "2, 1, -", "0, 0, +", "-1, -2, +", "-2, -1, -"}) + void visitAddSubValidInputReturnsNumber(int value1, int value2, String operator){ + mockAddSubContext.op = new MockToken(operator); + Mockito.when(mockAddSubContext.expression(0)).thenReturn(mockExpressionContext1); + Mockito.when(mockAddSubContext.expression(1)).thenReturn(mockExpressionContext2); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSNumber(value1)); + Mockito.when(spyVisitor.visit(mockExpressionContext2)).thenReturn(new MSNumber(value2)); + + MSType result = spyVisitor.visitAddSub(mockAddSubContext); + if (operator.equals("+")){ + Assertions.assertEquals(value1 + value2, ((MSNumber) result).getValue()); + } else if (operator.equals("-")){ + Assertions.assertEquals(value1 - value2, ((MSNumber) result).getValue()); + } + } + } diff --git a/src/test/interpreter/utils/MockToken.java b/src/test/interpreter/utils/MockToken.java new file mode 100644 index 0000000..b28687f --- /dev/null +++ b/src/test/interpreter/utils/MockToken.java @@ -0,0 +1,58 @@ +package interpreter.utils; + +import org.antlr.v4.runtime.CharStream; +import org.antlr.v4.runtime.Token; +import org.antlr.v4.runtime.TokenSource; + +public record MockToken(String token) implements Token { + + @Override + public String getText() { + return token; + } + + @Override + public int getType() { + return 0; + } + + @Override + public int getLine() { + return 0; + } + + @Override + public int getCharPositionInLine() { + return 0; + } + + @Override + public int getChannel() { + return 0; + } + + @Override + public int getTokenIndex() { + return 0; + } + + @Override + public int getStartIndex() { + return 0; + } + + @Override + public int getStopIndex() { + return 0; + } + + @Override + public TokenSource getTokenSource() { + return null; + } + + @Override + public CharStream getInputStream() { + return null; + } +} From 2314c8a85cd0cb2f5a805709f7aa7bb581c37c32 Mon Sep 17 00:00:00 2001 From: Simon Mikkelsen Bejer <93765117+sBejer@users.noreply.github.com> Date: Wed, 17 May 2023 08:11:03 +0200 Subject: [PATCH 03/17] Add VisitAnd Unit tests (#88) --- src/test/interpreter/VisitorUnitTest.java | 34 ++++++++++++++++++++--- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/src/test/interpreter/VisitorUnitTest.java b/src/test/interpreter/VisitorUnitTest.java index ccc9fdc..4e861b6 100644 --- a/src/test/interpreter/VisitorUnitTest.java +++ b/src/test/interpreter/VisitorUnitTest.java @@ -28,6 +28,7 @@ class VisitorUnitTest { @Spy private final Visitor spyVisitor = visitor; @Mock private MineScriptParser.AssignContext mockAssignContext; + @Mock private MineScriptParser.ExpressionContext mockExpressionContext; @Mock private MineScriptParser.ExpressionContext mockExpressionContext1; @Mock private MineScriptParser.ExpressionContext mockExpressionContext2; @Mock private MineScriptParser.BoolContext mockBoolContext; @@ -37,10 +38,8 @@ class VisitorUnitTest { @Mock private MineScriptParser.IdContext mockIdContext; @Mock private MineScriptParser.NegContext mockNegContext; @Mock private MineScriptParser.NotExprContext mockNotExprContext; + @Mock private MineScriptParser.AndContext mockAndContext; @Mock private MineScriptParser.AddSubContext mockAddSubContext; - - - @ParameterizedTest @ValueSource(ints = {-1000, -10, 0, 10, 1000}) void visitAssignStoresCorrectNumber(int value) { @@ -259,6 +258,34 @@ void visitNumberInvalidInputThrowsRuntimeException(){ Mockito.when(mockNumberContext.NUMBER()).thenReturn(new MockTerminalNode("abc")); Assertions.assertThrows(RuntimeException.class, () -> spyVisitor.visitNumber(mockNumberContext)); } + @Test + void visitAndValidBoolsReturnsTrue(){ + Mockito.when(mockAndContext.expression(0)).thenReturn(mockExpressionContext1); + Mockito.when(mockAndContext.expression(1)).thenReturn(mockExpressionContext2); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSBool(true)); + Mockito.when(spyVisitor.visit(mockExpressionContext2)).thenReturn(new MSBool(true)); + + MSType result = spyVisitor.visitAnd(mockAndContext); + Assertions.assertTrue(((MSBool) result).getValue()); + } + @Test + void visitAndBoolsTrueAndFalseReturnsFalse(){ + Mockito.when(mockAndContext.expression(0)).thenReturn(mockExpressionContext1); + Mockito.when(mockAndContext.expression(1)).thenReturn(mockExpressionContext2); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSBool(true)); + Mockito.when(spyVisitor.visit(mockExpressionContext2)).thenReturn(new MSBool(false)); + + MSType result = spyVisitor.visitAnd(mockAndContext); + Assertions.assertFalse(((MSBool) result).getValue()); + } + @Test + void visitAndFalseWithShortCircuitReturnsFalse() { + Mockito.when(mockAndContext.expression(0)).thenReturn(mockExpressionContext1); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSBool(false)); + + MSType result = spyVisitor.visitAnd(mockAndContext); + Assertions.assertFalse(((MSBool) result).getValue()); + } @ParameterizedTest @CsvSource ({"1, 2, +", "2, 1, -", "0, 0, +", "-1, -2, +", "-2, -1, -"}) @@ -276,5 +303,4 @@ void visitAddSubValidInputReturnsNumber(int value1, int value2, String operator) Assertions.assertEquals(value1 - value2, ((MSNumber) result).getValue()); } } - } From 2203c3c1258f0c9f4e46050f95df044ad57f9279 Mon Sep 17 00:00:00 2001 From: Ali Khorami <33497115+sabotack@users.noreply.github.com> Date: Wed, 17 May 2023 08:46:26 +0200 Subject: [PATCH 04/17] Removed usage of ExpressionParser and fixed negation-tests (#89) --- src/main/interpreter/ExpressionParser.java | 28 ---------- src/main/interpreter/Visitor.java | 61 ++++++++++++++++------ src/test/interpreter/VisitorUnitTest.java | 20 ++----- 3 files changed, 49 insertions(+), 60 deletions(-) delete mode 100644 src/main/interpreter/ExpressionParser.java diff --git a/src/main/interpreter/ExpressionParser.java b/src/main/interpreter/ExpressionParser.java deleted file mode 100644 index af987c5..0000000 --- a/src/main/interpreter/ExpressionParser.java +++ /dev/null @@ -1,28 +0,0 @@ -package interpreter; - -import interpreter.antlr.MineScriptBaseVisitor; -import interpreter.types.MSBool; -import interpreter.types.MSNumber; - -public class ExpressionParser extends MineScriptBaseVisitor { - - public MSBool getBoolean(T ctx) { - if (ctx instanceof MSBool b) { - return b; - } else if (ctx instanceof MSNumber i) { - return new MSBool(i.getValue() != 0); - } else { - throw new RuntimeException("Condition must be a bool"); - } - } - - public MSBool getNegatedBoolean(T ctx) { - if (ctx instanceof MSBool b) { - return new MSBool(!b.getValue()); - } else if (ctx instanceof MSNumber i) { - return new MSBool(i.getValue() == 0); - } else { - throw new RuntimeException("Condition must be a bool"); - } - } -} diff --git a/src/main/interpreter/Visitor.java b/src/main/interpreter/Visitor.java index 35325fc..082b626 100644 --- a/src/main/interpreter/Visitor.java +++ b/src/main/interpreter/Visitor.java @@ -22,7 +22,6 @@ import java.util.stream.Collectors; public class Visitor extends MineScriptBaseVisitor { - private final ExpressionParser parser = new ExpressionParser(); private final Random random = new Random(System.currentTimeMillis()); private final SymbolTable symbolTable; private boolean hasReturned = false; @@ -136,10 +135,13 @@ public MSType visitWhile(MineScriptParser.WhileContext ctx) { MSType value = null; /*Also checks for the returned state in case a return has been called inside the loop body*/ - while (parser.getBoolean(visit(ctx.expression())).getValue() && !hasReturned) { + while (visit(ctx.expression()) instanceof MSBool c && c.getValue() && !hasReturned) { value = visit(ctx.statements()); } + if (!(visit(ctx.expression()) instanceof MSBool)) + throw new RuntimeException("While condition must be a boolean"); + return value; } @@ -147,7 +149,12 @@ public MSType visitWhile(MineScriptParser.WhileContext ctx) { public MSType visitIf(MineScriptParser.IfContext ctx) { /*Handle if and else if*/ for (var expression : ctx.expression()) { - if (parser.getBoolean(visit(expression)).getValue()) { + MSType condition = visit(expression); + + if (!(condition instanceof MSBool)) + throw new RuntimeException("If condition must be a boolean"); + + if (((MSBool) condition).getValue()) { return visit(ctx.statements(ctx.expression().indexOf(expression))); } } @@ -186,7 +193,13 @@ public MSType visitBool(MineScriptParser.BoolContext ctx) { @Override public MSType visitNotExpr(MineScriptParser.NotExprContext ctx) { - return parser.getNegatedBoolean(visit(ctx.expression())); + MSType value = visit(ctx.expression()); + + if (!(value instanceof MSBool)) + throw new RuntimeException("Cannot negate non-boolean value"); + + boolean bool = ((MSBool) value).getValue(); + return new MSBool(!bool); } @Override @@ -257,6 +270,7 @@ public MSType visitAddSub(MineScriptParser.AddSubContext ctx) { MSType left = visit(ctx.expression(0)); MSType right = visit(ctx.expression(1)); + /*Check that both sides are numbers otherwise throw an error*/ if (!(left instanceof MSNumber l) || !(right instanceof MSNumber r)) { throw new RuntimeException("Cannot use '" + ctx.op.getText() + "' operator on " + left.getTypeName() + " and " + right.getTypeName()); } @@ -312,32 +326,46 @@ public MSType visitPow(MineScriptParser.PowContext ctx) { } return calculateArithmeticExpression(l, r, "^"); - } @Override public MSType visitAnd(MineScriptParser.AndContext ctx) { /*Handles and with short-circuit, that's why the left side is handled first*/ MSType left = visit(ctx.expression(0)); - /*If the left part is false, it returns without the need to evaluate the right side*/ - if (!parser.getBoolean(left).getValue()) { - return new MSBool(false); - } - MSType right = visit(ctx.expression(1)); - return new MSBool(parser.getBoolean(right).getValue()); + if (left instanceof MSBool l) {/*If the left part is false, it returns without the need to evaluate the right side*/ + if (!l.getValue()) { + return l; + } + + MSType right = visit(ctx.expression(1)); + if (!(right instanceof MSBool r)) { + throw new RuntimeException("Cannot use 'and' operator on " + left.getTypeName() + " and " + right.getTypeName()); + } + return r; + } else { + throw new RuntimeException("Cannot use 'and' operator on " + left.getTypeName() + " and " + visit(ctx.expression(1)).getTypeName()); + } } @Override public MSType visitOr(MineScriptParser.OrContext ctx) { /*Handles or with short-circuit, much like in the visitAnd method*/ MSType left = visit(ctx.expression(0)); - if (parser.getBoolean(left).getValue()) { - return new MSBool(true); - } - MSType right = visit(ctx.expression(1)); - return new MSBool(parser.getBoolean(right).getValue()); + if (left instanceof MSBool l) { + if (l.getValue()) { + return l; + } + + MSType right = visit(ctx.expression(1)); + if (!(right instanceof MSBool r)) { + throw new RuntimeException("Cannot use 'or' operator on " + left.getTypeName() + " and " + right.getTypeName()); + } + return r; + } else { + throw new RuntimeException("Cannot use 'or' operator on " + left.getTypeName() + " and " + visit(ctx.expression(1)).getTypeName()); + } } @Override @@ -702,7 +730,6 @@ private MSNumber calculateArithmeticExpression(MSNumber left, MSNumber right, St throw new RuntimeException("Result of '" + leftBig + " " + operator + " " + rightBig + "' is too small. Minimum number is " + Integer.MIN_VALUE); } return new MSNumber(result.intValue()); - } /** diff --git a/src/test/interpreter/VisitorUnitTest.java b/src/test/interpreter/VisitorUnitTest.java index 4e861b6..c1b7d91 100644 --- a/src/test/interpreter/VisitorUnitTest.java +++ b/src/test/interpreter/VisitorUnitTest.java @@ -129,23 +129,13 @@ void visitNotExprValidBoolReturnsNegatedBool() { Assertions.assertTrue(((MSBool) result).getValue()); } - @Test - void visitNotExprPassZeroReturnsTrue() { - Mockito.when(mockNotExprContext.expression()).thenReturn(mockExpressionContext1); - Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSNumber(0)); - - MSType result = spyVisitor.visitNotExpr(mockNotExprContext); - Assertions.assertTrue(((MSBool) result).getValue()); - } - @ParameterizedTest - @ValueSource(ints = {-1000, -100, 100, 1000}) - void visitNotExprPassNonZeroNumberReturnsFalse(int value) { - Mockito.when(mockNotExprContext.expression()).thenReturn(mockExpressionContext1); - Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSNumber(value)); + @ValueSource(ints = {-1000, -100, 0, 100, 1000}) + void visitNotExprPassNumberThrowsRuntimeException(int value) { + Mockito.when(mockNotExprContext.expression()).thenReturn(mockExpressionContext); + Mockito.when(spyVisitor.visit(mockExpressionContext)).thenReturn(new MSNumber(value)); - MSType result = spyVisitor.visitNotExpr(mockNotExprContext); - Assertions.assertFalse(((MSBool) result).getValue()); + Assertions.assertThrows(RuntimeException.class, () -> spyVisitor.visitNotExpr(mockNotExprContext)); } @Test From 7da6e579143b97d4e28a06322f203e3cc89bbcf2 Mon Sep 17 00:00:00 2001 From: Emil Monrad Laursen <92304713+EmilML@users.noreply.github.com> Date: Wed, 17 May 2023 08:47:41 +0200 Subject: [PATCH 05/17] Add tests for all cases of MultDivMod visitor (#87) --- src/test/interpreter/VisitorUnitTest.java | 46 +++++++++++++++++++++-- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/src/test/interpreter/VisitorUnitTest.java b/src/test/interpreter/VisitorUnitTest.java index c1b7d91..943078d 100644 --- a/src/test/interpreter/VisitorUnitTest.java +++ b/src/test/interpreter/VisitorUnitTest.java @@ -4,8 +4,6 @@ import interpreter.types.*; import interpreter.utils.MockTerminalNode; import interpreter.utils.MockToken; -import org.antlr.v4.runtime.Token; -import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -18,7 +16,6 @@ import org.mockito.Spy; import org.mockito.junit.jupiter.MockitoExtension; -import java.util.Objects; import java.util.concurrent.atomic.AtomicReference; @ExtendWith(MockitoExtension.class) @@ -40,6 +37,8 @@ class VisitorUnitTest { @Mock private MineScriptParser.NotExprContext mockNotExprContext; @Mock private MineScriptParser.AndContext mockAndContext; @Mock private MineScriptParser.AddSubContext mockAddSubContext; + @Mock private MineScriptParser.MultDivModContext mockMultDivModContext; + @ParameterizedTest @ValueSource(ints = {-1000, -10, 0, 10, 1000}) void visitAssignStoresCorrectNumber(int value) { @@ -293,4 +292,45 @@ void visitAddSubValidInputReturnsNumber(int value1, int value2, String operator) Assertions.assertEquals(value1 - value2, ((MSNumber) result).getValue()); } } + + @ParameterizedTest + @CsvSource ({"2,2,*", "2,2,/", "2,2,%"}) + void visitMultDivModValidInputReturnsNumber(int left, int right, String operator) { + mockMultDivModContext.op = new MockToken(operator); + Mockito.when(mockMultDivModContext.expression(0)).thenReturn(mockExpressionContext1); + Mockito.when(mockMultDivModContext.expression(1)).thenReturn(mockExpressionContext2); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSNumber(left)); + Mockito.when(spyVisitor.visit(mockExpressionContext2)).thenReturn(new MSNumber(right)); + + MSType result = spyVisitor.visitMultDivMod(mockMultDivModContext); + switch (operator) { + case "*" -> Assertions.assertEquals(left * right, ((MSNumber) result).getValue()); + case "/" -> Assertions.assertEquals(left / right, ((MSNumber) result).getValue()); + case "%" -> Assertions.assertEquals(left % right, ((MSNumber) result).getValue()); + } + } + + @ParameterizedTest + @CsvSource ({"2,0,/", "2,0,%"}) + void visitMultDivModDivByZeroThrowsRuntimeException(int left, int right, String operator) { + mockMultDivModContext.op = new MockToken(operator); + Mockito.when(mockMultDivModContext.expression(0)).thenReturn(mockExpressionContext1); + Mockito.when(mockMultDivModContext.expression(1)).thenReturn(mockExpressionContext2); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSNumber(left)); + Mockito.when(spyVisitor.visit(mockExpressionContext2)).thenReturn(new MSNumber(right)); + + Assertions.assertThrows(RuntimeException.class, () -> spyVisitor.visitMultDivMod(mockMultDivModContext)); + } + + @ParameterizedTest + @CsvSource ({"2,true,/", "2,true,%", "2,false,/", "2,false,%", "true,2,/", "true,2,%", "false,2,/", "false,2,%"}) + void visitMultDivModInvalidInputThrowsRuntimeException(String left, String right, String operator) { + mockMultDivModContext.op = new MockToken(operator); + Mockito.when(mockMultDivModContext.expression(0)).thenReturn(mockExpressionContext1); + Mockito.when(mockMultDivModContext.expression(1)).thenReturn(mockExpressionContext2); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSBool(Boolean.parseBoolean(left))); + Mockito.when(spyVisitor.visit(mockExpressionContext2)).thenReturn(new MSBool(Boolean.parseBoolean(right))); + + Assertions.assertThrows(RuntimeException.class, () -> spyVisitor.visitMultDivMod(mockMultDivModContext)); + } } From df63332af004ea15e2dd69dda94edcf6d6bae3a2 Mon Sep 17 00:00:00 2001 From: Ali Khorami <33497115+sabotack@users.noreply.github.com> Date: Wed, 17 May 2023 09:14:28 +0200 Subject: [PATCH 06/17] added visitOr unit test (#90) --- src/test/interpreter/VisitorUnitTest.java | 36 +++++++++++++++++------ 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/src/test/interpreter/VisitorUnitTest.java b/src/test/interpreter/VisitorUnitTest.java index 943078d..9d60d4b 100644 --- a/src/test/interpreter/VisitorUnitTest.java +++ b/src/test/interpreter/VisitorUnitTest.java @@ -37,6 +37,7 @@ class VisitorUnitTest { @Mock private MineScriptParser.NotExprContext mockNotExprContext; @Mock private MineScriptParser.AndContext mockAndContext; @Mock private MineScriptParser.AddSubContext mockAddSubContext; + @Mock private MineScriptParser.OrContext mockOrContext; @Mock private MineScriptParser.MultDivModContext mockMultDivModContext; @ParameterizedTest @@ -247,6 +248,7 @@ void visitNumberInvalidInputThrowsRuntimeException(){ Mockito.when(mockNumberContext.NUMBER()).thenReturn(new MockTerminalNode("abc")); Assertions.assertThrows(RuntimeException.class, () -> spyVisitor.visitNumber(mockNumberContext)); } + @Test void visitAndValidBoolsReturnsTrue(){ Mockito.when(mockAndContext.expression(0)).thenReturn(mockExpressionContext1); @@ -257,8 +259,9 @@ void visitAndValidBoolsReturnsTrue(){ MSType result = spyVisitor.visitAnd(mockAndContext); Assertions.assertTrue(((MSBool) result).getValue()); } - @Test - void visitAndBoolsTrueAndFalseReturnsFalse(){ + + @Test + void visitAndBoolsTrueAndFalseReturnsFalse(){ Mockito.when(mockAndContext.expression(0)).thenReturn(mockExpressionContext1); Mockito.when(mockAndContext.expression(1)).thenReturn(mockExpressionContext2); Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSBool(true)); @@ -266,15 +269,15 @@ void visitAndBoolsTrueAndFalseReturnsFalse(){ MSType result = spyVisitor.visitAnd(mockAndContext); Assertions.assertFalse(((MSBool) result).getValue()); - } - @Test + } + @Test void visitAndFalseWithShortCircuitReturnsFalse() { - Mockito.when(mockAndContext.expression(0)).thenReturn(mockExpressionContext1); - Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSBool(false)); + Mockito.when(mockAndContext.expression(0)).thenReturn(mockExpressionContext1); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSBool(false)); - MSType result = spyVisitor.visitAnd(mockAndContext); - Assertions.assertFalse(((MSBool) result).getValue()); - } + MSType result = spyVisitor.visitAnd(mockAndContext); + Assertions.assertFalse(((MSBool) result).getValue()); + } @ParameterizedTest @CsvSource ({"1, 2, +", "2, 1, -", "0, 0, +", "-1, -2, +", "-2, -1, -"}) @@ -292,6 +295,21 @@ void visitAddSubValidInputReturnsNumber(int value1, int value2, String operator) Assertions.assertEquals(value1 - value2, ((MSNumber) result).getValue()); } } + + @ParameterizedTest + @CsvSource({"false, false", "false, true", "true, false", "true, true"}) + void visitOrValidInputReturnsOrValue(boolean value1, boolean value2) { + Mockito.when(mockOrContext.expression(0)).thenReturn(mockExpressionContext1); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSBool(value1)); + + if (!value1) { + Mockito.when(mockOrContext.expression(1)).thenReturn(mockExpressionContext2); + Mockito.when(spyVisitor.visit(mockExpressionContext2)).thenReturn(new MSBool(value2)); + } + + MSType result = spyVisitor.visitOr(mockOrContext); + Assertions.assertEquals(value1 || value2, ((MSBool) result).getValue()); + } @ParameterizedTest @CsvSource ({"2,2,*", "2,2,/", "2,2,%"}) From 5efa5edf4bca66f9241583efebfe48c7b4f559cf Mon Sep 17 00:00:00 2001 From: Ali Khorami <33497115+sabotack@users.noreply.github.com> Date: Wed, 17 May 2023 09:53:55 +0200 Subject: [PATCH 07/17] added visitIsIsNot tests (#91) --- src/main/interpreter/Visitor.java | 1 - src/test/interpreter/VisitorUnitTest.java | 67 ++++++++++++++++++++++- 2 files changed, 66 insertions(+), 2 deletions(-) diff --git a/src/main/interpreter/Visitor.java b/src/main/interpreter/Visitor.java index 082b626..59e206a 100644 --- a/src/main/interpreter/Visitor.java +++ b/src/main/interpreter/Visitor.java @@ -190,7 +190,6 @@ public MSType visitBool(MineScriptParser.BoolContext ctx) { return new MSBool(Boolean.parseBoolean(ctx.getText())); } - @Override public MSType visitNotExpr(MineScriptParser.NotExprContext ctx) { MSType value = visit(ctx.expression()); diff --git a/src/test/interpreter/VisitorUnitTest.java b/src/test/interpreter/VisitorUnitTest.java index 9d60d4b..ab414e2 100644 --- a/src/test/interpreter/VisitorUnitTest.java +++ b/src/test/interpreter/VisitorUnitTest.java @@ -39,6 +39,7 @@ class VisitorUnitTest { @Mock private MineScriptParser.AddSubContext mockAddSubContext; @Mock private MineScriptParser.OrContext mockOrContext; @Mock private MineScriptParser.MultDivModContext mockMultDivModContext; + @Mock private MineScriptParser.IsIsNotContext mockIsIsNotContext; @ParameterizedTest @ValueSource(ints = {-1000, -10, 0, 10, 1000}) @@ -310,7 +311,71 @@ void visitOrValidInputReturnsOrValue(boolean value1, boolean value2) { MSType result = spyVisitor.visitOr(mockOrContext); Assertions.assertEquals(value1 || value2, ((MSBool) result).getValue()); } - + + @ParameterizedTest + @CsvSource({"0,0,is", "0,1,is not", "1,0,is not", "1,1,is"}) + void visitIsIsNotNumberReturnsCorrectBool(int left, int right, String operator) { + mockIsIsNotContext.op = new MockToken(operator); + Mockito.when(mockIsIsNotContext.expression(0)).thenReturn(mockExpressionContext1); + Mockito.when(mockIsIsNotContext.expression(1)).thenReturn(mockExpressionContext2); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSNumber(left)); + Mockito.when(spyVisitor.visit(mockExpressionContext2)).thenReturn(new MSNumber(right)); + + MSType result = spyVisitor.visitIsIsNot(mockIsIsNotContext); + switch (operator) { + case "is" -> Assertions.assertEquals(left == right, ((MSBool) result).getValue()); + case "is not" -> Assertions.assertEquals(left != right, ((MSBool) result).getValue()); + } + } + + @ParameterizedTest + @CsvSource({"false,false,is", "false,true,is not", "true,false,is not", "true,true,is"}) + void visitIsIsNotBoolReturnsCorrectBool(boolean left, boolean right, String operator) { + mockIsIsNotContext.op = new MockToken(operator); + Mockito.when(mockIsIsNotContext.expression(0)).thenReturn(mockExpressionContext1); + Mockito.when(mockIsIsNotContext.expression(1)).thenReturn(mockExpressionContext2); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSBool(left)); + Mockito.when(spyVisitor.visit(mockExpressionContext2)).thenReturn(new MSBool(right)); + + MSType result = spyVisitor.visitIsIsNot(mockIsIsNotContext); + switch (operator) { + case "is" -> Assertions.assertEquals(left == right, ((MSBool) result).getValue()); + case "is not" -> Assertions.assertEquals(left != right, ((MSBool) result).getValue()); + } + } + + @ParameterizedTest + @CsvSource({"north,south,is", "east,west,is not", "top,bottom,is not", "north,north,is"}) + void visitIsIsNotAbsDirReturnsCorrectBool(String left, String right, String operator) { + mockIsIsNotContext.op = new MockToken(operator); + Mockito.when(mockIsIsNotContext.expression(0)).thenReturn(mockExpressionContext1); + Mockito.when(mockIsIsNotContext.expression(1)).thenReturn(mockExpressionContext2); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSAbsDir(left)); + Mockito.when(spyVisitor.visit(mockExpressionContext2)).thenReturn(new MSAbsDir(right)); + + MSType result = spyVisitor.visitIsIsNot(mockIsIsNotContext); + switch (operator) { + case "is" -> Assertions.assertEquals(left.equals(right), ((MSBool) result).getValue()); + case "is not" -> Assertions.assertEquals(!left.equals(right), ((MSBool) result).getValue()); + } + } + + @ParameterizedTest + @CsvSource({"left,right,is", "up,down,is not", "left,left,is not", "right,right,is"}) + void visitIsIsNotRelDirReturnCorrectBool(String left, String right, String operator) { + mockIsIsNotContext.op = new MockToken(operator); + Mockito.when(mockIsIsNotContext.expression(0)).thenReturn(mockExpressionContext1); + Mockito.when(mockIsIsNotContext.expression(1)).thenReturn(mockExpressionContext2); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSRelDir(left)); + Mockito.when(spyVisitor.visit(mockExpressionContext2)).thenReturn(new MSRelDir(right)); + + MSType result = spyVisitor.visitIsIsNot(mockIsIsNotContext); + switch (operator) { + case "is" -> Assertions.assertEquals(left.equals(right), ((MSBool) result).getValue()); + case "is not" -> Assertions.assertEquals(!left.equals(right), ((MSBool) result).getValue()); + } + } + @ParameterizedTest @CsvSource ({"2,2,*", "2,2,/", "2,2,%"}) void visitMultDivModValidInputReturnsNumber(int left, int right, String operator) { From d2ebf9ae01ca424602a0732ce5117eb6c4724b85 Mon Sep 17 00:00:00 2001 From: sabotack Date: Wed, 17 May 2023 10:37:09 +0200 Subject: [PATCH 08/17] removed extra spacing --- src/main/interpreter/Visitor.java | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/main/interpreter/Visitor.java b/src/main/interpreter/Visitor.java index 59e206a..27330b7 100644 --- a/src/main/interpreter/Visitor.java +++ b/src/main/interpreter/Visitor.java @@ -661,16 +661,6 @@ public MSType visitFuncDecl(MineScriptParser.FuncDeclContext ctx) { return null; } - @Override - public MSType visitActual_parameters(MineScriptParser.Actual_parametersContext ctx) { - return null; - } - - @Override - public MSType visitFormal_paramaters(MineScriptParser.Formal_paramatersContext ctx) { - return null; - } - /** * @param ctx Formal parameters context * @return List of formal parameters From bf4ca5fd8e263f8dddb34c481c790b0a0cb5799e Mon Sep 17 00:00:00 2001 From: sabotack Date: Wed, 17 May 2023 11:03:15 +0200 Subject: [PATCH 09/17] refactor: optimize visitIsIsNot tests --- src/test/interpreter/VisitorUnitTest.java | 92 ++++++++--------------- 1 file changed, 32 insertions(+), 60 deletions(-) diff --git a/src/test/interpreter/VisitorUnitTest.java b/src/test/interpreter/VisitorUnitTest.java index ab414e2..9047c9d 100644 --- a/src/test/interpreter/VisitorUnitTest.java +++ b/src/test/interpreter/VisitorUnitTest.java @@ -282,92 +282,64 @@ void visitAndFalseWithShortCircuitReturnsFalse() { @ParameterizedTest @CsvSource ({"1, 2, +", "2, 1, -", "0, 0, +", "-1, -2, +", "-2, -1, -"}) - void visitAddSubValidInputReturnsNumber(int value1, int value2, String operator){ + void visitAddSubValidInputReturnsNumber(int left, int right, String operator){ mockAddSubContext.op = new MockToken(operator); Mockito.when(mockAddSubContext.expression(0)).thenReturn(mockExpressionContext1); Mockito.when(mockAddSubContext.expression(1)).thenReturn(mockExpressionContext2); - Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSNumber(value1)); - Mockito.when(spyVisitor.visit(mockExpressionContext2)).thenReturn(new MSNumber(value2)); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSNumber(left)); + Mockito.when(spyVisitor.visit(mockExpressionContext2)).thenReturn(new MSNumber(right)); MSType result = spyVisitor.visitAddSub(mockAddSubContext); if (operator.equals("+")){ - Assertions.assertEquals(value1 + value2, ((MSNumber) result).getValue()); + Assertions.assertEquals(left + right, ((MSNumber) result).getValue()); } else if (operator.equals("-")){ - Assertions.assertEquals(value1 - value2, ((MSNumber) result).getValue()); + Assertions.assertEquals(left - right, ((MSNumber) result).getValue()); } } @ParameterizedTest @CsvSource({"false, false", "false, true", "true, false", "true, true"}) - void visitOrValidInputReturnsOrValue(boolean value1, boolean value2) { + void visitOrValidInputReturnsOrValue(boolean left, boolean right) { Mockito.when(mockOrContext.expression(0)).thenReturn(mockExpressionContext1); - Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSBool(value1)); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSBool(left)); - if (!value1) { + if (!left) { Mockito.when(mockOrContext.expression(1)).thenReturn(mockExpressionContext2); - Mockito.when(spyVisitor.visit(mockExpressionContext2)).thenReturn(new MSBool(value2)); + Mockito.when(spyVisitor.visit(mockExpressionContext2)).thenReturn(new MSBool(right)); } MSType result = spyVisitor.visitOr(mockOrContext); - Assertions.assertEquals(value1 || value2, ((MSBool) result).getValue()); - } - - @ParameterizedTest - @CsvSource({"0,0,is", "0,1,is not", "1,0,is not", "1,1,is"}) - void visitIsIsNotNumberReturnsCorrectBool(int left, int right, String operator) { - mockIsIsNotContext.op = new MockToken(operator); - Mockito.when(mockIsIsNotContext.expression(0)).thenReturn(mockExpressionContext1); - Mockito.when(mockIsIsNotContext.expression(1)).thenReturn(mockExpressionContext2); - Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSNumber(left)); - Mockito.when(spyVisitor.visit(mockExpressionContext2)).thenReturn(new MSNumber(right)); - - MSType result = spyVisitor.visitIsIsNot(mockIsIsNotContext); - switch (operator) { - case "is" -> Assertions.assertEquals(left == right, ((MSBool) result).getValue()); - case "is not" -> Assertions.assertEquals(left != right, ((MSBool) result).getValue()); - } - } - - @ParameterizedTest - @CsvSource({"false,false,is", "false,true,is not", "true,false,is not", "true,true,is"}) - void visitIsIsNotBoolReturnsCorrectBool(boolean left, boolean right, String operator) { - mockIsIsNotContext.op = new MockToken(operator); - Mockito.when(mockIsIsNotContext.expression(0)).thenReturn(mockExpressionContext1); - Mockito.when(mockIsIsNotContext.expression(1)).thenReturn(mockExpressionContext2); - Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSBool(left)); - Mockito.when(spyVisitor.visit(mockExpressionContext2)).thenReturn(new MSBool(right)); - - MSType result = spyVisitor.visitIsIsNot(mockIsIsNotContext); - switch (operator) { - case "is" -> Assertions.assertEquals(left == right, ((MSBool) result).getValue()); - case "is not" -> Assertions.assertEquals(left != right, ((MSBool) result).getValue()); - } + Assertions.assertEquals(left || right, ((MSBool) result).getValue()); } @ParameterizedTest - @CsvSource({"north,south,is", "east,west,is not", "top,bottom,is not", "north,north,is"}) - void visitIsIsNotAbsDirReturnsCorrectBool(String left, String right, String operator) { + @CsvSource({"num,0,0,is", "num,0,1,is not", "num,1,0,is not", "num,1,1,is", "num,-1,1,is not", + "rel,left,right,is", "rel,up,down,is not", "rel,left,left,is not", "rel,right,right,is", + "abs,north,south,is", "abs,east,west,is not", "abs,top,bottom,is not", "abs,north,north,is", + "bool,false,false,is", "bool,false,true,is not", "bool,true,false,is not", "bool,true,true,is"}) + void visitIsIsNotDirectionReturnsCorrectBool(String type, String left, String right, String operator) { mockIsIsNotContext.op = new MockToken(operator); Mockito.when(mockIsIsNotContext.expression(0)).thenReturn(mockExpressionContext1); Mockito.when(mockIsIsNotContext.expression(1)).thenReturn(mockExpressionContext2); - Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSAbsDir(left)); - Mockito.when(spyVisitor.visit(mockExpressionContext2)).thenReturn(new MSAbsDir(right)); - MSType result = spyVisitor.visitIsIsNot(mockIsIsNotContext); - switch (operator) { - case "is" -> Assertions.assertEquals(left.equals(right), ((MSBool) result).getValue()); - case "is not" -> Assertions.assertEquals(!left.equals(right), ((MSBool) result).getValue()); + switch (type) { + case "num" -> { + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSNumber(Integer.parseInt(left))); + Mockito.when(spyVisitor.visit(mockExpressionContext2)).thenReturn(new MSNumber(Integer.parseInt(right))); + } + case "rel" -> { + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSRelDir(left)); + Mockito.when(spyVisitor.visit(mockExpressionContext2)).thenReturn(new MSRelDir(right)); + } + case "abs" -> { + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSAbsDir(left)); + Mockito.when(spyVisitor.visit(mockExpressionContext2)).thenReturn(new MSAbsDir(right)); + } + case "bool" -> { + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSBool(Boolean.parseBoolean(left))); + Mockito.when(spyVisitor.visit(mockExpressionContext2)).thenReturn(new MSBool(Boolean.parseBoolean(right))); + } } - } - - @ParameterizedTest - @CsvSource({"left,right,is", "up,down,is not", "left,left,is not", "right,right,is"}) - void visitIsIsNotRelDirReturnCorrectBool(String left, String right, String operator) { - mockIsIsNotContext.op = new MockToken(operator); - Mockito.when(mockIsIsNotContext.expression(0)).thenReturn(mockExpressionContext1); - Mockito.when(mockIsIsNotContext.expression(1)).thenReturn(mockExpressionContext2); - Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSRelDir(left)); - Mockito.when(spyVisitor.visit(mockExpressionContext2)).thenReturn(new MSRelDir(right)); MSType result = spyVisitor.visitIsIsNot(mockIsIsNotContext); switch (operator) { From 75745f8c2605dd7c7d80790ecc89f2ef850ccf60 Mon Sep 17 00:00:00 2001 From: sabotack Date: Wed, 17 May 2023 11:05:45 +0200 Subject: [PATCH 10/17] rename test --- src/test/interpreter/VisitorUnitTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/interpreter/VisitorUnitTest.java b/src/test/interpreter/VisitorUnitTest.java index 9047c9d..6d33b38 100644 --- a/src/test/interpreter/VisitorUnitTest.java +++ b/src/test/interpreter/VisitorUnitTest.java @@ -317,7 +317,7 @@ void visitOrValidInputReturnsOrValue(boolean left, boolean right) { "rel,left,right,is", "rel,up,down,is not", "rel,left,left,is not", "rel,right,right,is", "abs,north,south,is", "abs,east,west,is not", "abs,top,bottom,is not", "abs,north,north,is", "bool,false,false,is", "bool,false,true,is not", "bool,true,false,is not", "bool,true,true,is"}) - void visitIsIsNotDirectionReturnsCorrectBool(String type, String left, String right, String operator) { + void visitIsIsNotCorrectTypesReturnsCorrectBool(String type, String left, String right, String operator) { mockIsIsNotContext.op = new MockToken(operator); Mockito.when(mockIsIsNotContext.expression(0)).thenReturn(mockExpressionContext1); Mockito.when(mockIsIsNotContext.expression(1)).thenReturn(mockExpressionContext2); From d9d1f2142db2881896e498756216ebcc71b685a2 Mon Sep 17 00:00:00 2001 From: Ali Khorami <33497115+sabotack@users.noreply.github.com> Date: Wed, 17 May 2023 11:28:28 +0200 Subject: [PATCH 11/17] added visitReturn tests (#93) --- src/test/interpreter/VisitorUnitTest.java | 53 +++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/src/test/interpreter/VisitorUnitTest.java b/src/test/interpreter/VisitorUnitTest.java index 6d33b38..b4c9fc1 100644 --- a/src/test/interpreter/VisitorUnitTest.java +++ b/src/test/interpreter/VisitorUnitTest.java @@ -16,6 +16,7 @@ import org.mockito.Spy; import org.mockito.junit.jupiter.MockitoExtension; +import java.lang.reflect.Field; import java.util.concurrent.atomic.AtomicReference; @ExtendWith(MockitoExtension.class) @@ -40,6 +41,7 @@ class VisitorUnitTest { @Mock private MineScriptParser.OrContext mockOrContext; @Mock private MineScriptParser.MultDivModContext mockMultDivModContext; @Mock private MineScriptParser.IsIsNotContext mockIsIsNotContext; + @Mock private MineScriptParser.ReturnContext mockReturnContext; @ParameterizedTest @ValueSource(ints = {-1000, -10, 0, 10, 1000}) @@ -388,4 +390,55 @@ void visitMultDivModInvalidInputThrowsRuntimeException(String left, String right Assertions.assertThrows(RuntimeException.class, () -> spyVisitor.visitMultDivMod(mockMultDivModContext)); } + + @ParameterizedTest + @CsvSource ({"num,-10", "num,0", "num,10", + "rel,left", "rel,right", "rel,up", "rel,down", + "abs,north", "abs,south", "abs,east", "abs,west", "abs,top", "abs,bottom", + "bool,true", "bool,false"}) + void visitReturnCorrectTypesReturnValue(String type, String value) { + Field hasReturnedField = null; + boolean hasReturned = false; + + // Setup mocks for the context and visit method + Mockito.when(mockReturnContext.expression()).thenReturn(mockExpressionContext1); + switch (type) { + case "num" -> Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSNumber(Integer.parseInt(value))); + case "rel" -> Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSRelDir(value)); + case "abs" -> Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSAbsDir(value)); + case "bool" -> Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSBool(Boolean.parseBoolean(value))); + } + + // Get the initial value of the hasReturned field of the visitor + try { + hasReturnedField = spyVisitor.getClass().getDeclaredField("hasReturned"); + hasReturnedField.setAccessible(true); + hasReturned = hasReturnedField.getBoolean(spyVisitor); + } catch (Exception e) { + throw new RuntimeException(e); + } + + // Check that the hasReturned field is false before the visit method is called + Assertions.assertFalse(hasReturned); + + // Call the visit method and check that the correct type and value is returned + MSType result = spyVisitor.visitReturn(mockReturnContext); + switch (type) { + case "num" -> Assertions.assertEquals(Integer.parseInt(value), ((MSNumber) result).getValue()); + case "rel" -> Assertions.assertEquals(MSRelDir.Direction.valueOf(value.toUpperCase()), ((MSRelDir) result).getValue()); + case "abs" -> Assertions.assertEquals(MSAbsDir.Direction.valueOf(value.toUpperCase()), ((MSAbsDir) result).getValue()); + case "bool" -> Assertions.assertEquals(Boolean.parseBoolean(value), ((MSBool) result).getValue()); + } + + // Check that the hasReturned field is true after the visit method is called + try { hasReturned = hasReturnedField.getBoolean(spyVisitor); } + catch (Exception e) { throw new RuntimeException(e); } + Assertions.assertTrue(hasReturned); + } + + @Test + void visitReturnNullExpressionThrowsRuntimeException() { + Mockito.when(mockReturnContext.expression()).thenReturn(null); + Assertions.assertThrows(RuntimeException.class, () -> spyVisitor.visitReturn(mockReturnContext)); + } } From e655135410b0702bc32a7246903d7cdecfa0415a Mon Sep 17 00:00:00 2001 From: Viktor Platz <62332353+ViktorPlatz@users.noreply.github.com> Date: Wed, 17 May 2023 13:30:51 +0200 Subject: [PATCH 12/17] Unit tests viktor (#98) * Test for AddSubVisitor * Test for AddSubVisitor, Comp, FuncCall, ParenExpr, etc * Update VisitorUnitTest.java --- src/test/interpreter/VisitorUnitTest.java | 242 ++++++++++++++++++++-- 1 file changed, 221 insertions(+), 21 deletions(-) diff --git a/src/test/interpreter/VisitorUnitTest.java b/src/test/interpreter/VisitorUnitTest.java index b4c9fc1..052f59d 100644 --- a/src/test/interpreter/VisitorUnitTest.java +++ b/src/test/interpreter/VisitorUnitTest.java @@ -4,18 +4,23 @@ import interpreter.types.*; import interpreter.utils.MockTerminalNode; import interpreter.utils.MockToken; +import org.antlr.v4.runtime.Token; +import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.EnumSource; import org.junit.jupiter.params.provider.ValueSource; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.Spy; import org.mockito.junit.jupiter.MockitoExtension; - +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; import java.lang.reflect.Field; import java.util.concurrent.atomic.AtomicReference; @@ -36,8 +41,14 @@ class VisitorUnitTest { @Mock private MineScriptParser.IdContext mockIdContext; @Mock private MineScriptParser.NegContext mockNegContext; @Mock private MineScriptParser.NotExprContext mockNotExprContext; - @Mock private MineScriptParser.AndContext mockAndContext; @Mock private MineScriptParser.AddSubContext mockAddSubContext; + @Mock private MineScriptParser.PowContext mockPowContext; + @Mock private MineScriptParser.ParenExprContext mockParenExprContext; + @Mock private MineScriptParser.CompContext mockCompContext; + @Mock private MineScriptParser.FuncCallContext mockFuncCallContext; + @Mock private MineScriptParser.Actual_parametersContext mockActualParametersContext; + @Mock private MineScriptParser.StatementsContext mockStatementsContext; + @Mock private MineScriptParser.AndContext mockAndContext; @Mock private MineScriptParser.OrContext mockOrContext; @Mock private MineScriptParser.MultDivModContext mockMultDivModContext; @Mock private MineScriptParser.IsIsNotContext mockIsIsNotContext; @@ -63,6 +74,7 @@ void visitAssignStoresCorrectNumber(int value) { Assertions.assertNull(result); } + @ParameterizedTest @ValueSource(booleans = {true, false}) void visitAssignStoresCorrectBoolean(boolean value) { @@ -124,6 +136,13 @@ void visitAssignStoresCorrectAbsDir(MSAbsDir.Direction value) { } @Test + void visitNotExprPassZeroReturnsTrue() { + Mockito.when(mockNotExprContext.expression()).thenReturn(mockExpressionContext1); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSNumber(0)); + + MSType result = spyVisitor.visitNotExpr(mockNotExprContext); + Assertions.assertTrue(((MSBool) result).getValue()); + } void visitNotExprValidBoolReturnsNegatedBool() { Mockito.when(mockNotExprContext.expression()).thenReturn(mockExpressionContext1); Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSBool(false)); @@ -133,6 +152,15 @@ void visitNotExprValidBoolReturnsNegatedBool() { } @ParameterizedTest + @ValueSource(ints = {-1000, -100, 100, 1000}) + void visitNotExprPassNonZeroNumberReturnsFalse(int value) { + Mockito.when(mockNotExprContext.expression()).thenReturn(mockExpressionContext1); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSNumber(value)); + + MSType result = spyVisitor.visitNotExpr(mockNotExprContext); + Assertions.assertFalse(((MSBool) result).getValue()); + } + @ValueSource(ints = {-1000, -100, 0, 100, 1000}) void visitNotExprPassNumberThrowsRuntimeException(int value) { Mockito.when(mockNotExprContext.expression()).thenReturn(mockExpressionContext); @@ -251,7 +279,196 @@ void visitNumberInvalidInputThrowsRuntimeException(){ Mockito.when(mockNumberContext.NUMBER()).thenReturn(new MockTerminalNode("abc")); Assertions.assertThrows(RuntimeException.class, () -> spyVisitor.visitNumber(mockNumberContext)); } - + + @ParameterizedTest + @CsvSource ({"1, 2, +", "2, 1, -", "0, 0, +", "-1, -2, +", "-2, -1, -"}) + void visitAddSubValidInputReturnsNumber(int value1, int value2, String operator){ + mockAddSubContext.op = new MockToken(operator); + Mockito.when(mockAddSubContext.expression(0)).thenReturn(mockExpressionContext1); + Mockito.when(mockAddSubContext.expression(1)).thenReturn(mockExpressionContext2); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSNumber(value1)); + Mockito.when(spyVisitor.visit(mockExpressionContext2)).thenReturn(new MSNumber(value2)); + + MSType result = spyVisitor.visitAddSub(mockAddSubContext); + if (operator.equals("+")){ + Assertions.assertEquals(value1 + value2, ((MSNumber) result).getValue()); + } else if (operator.equals("-")){ + Assertions.assertEquals(value1 - value2, ((MSNumber) result).getValue()); + } + } + + @Test + void visitAddSubInvalidTypeThrowsRuntimeException(){ + mockAddSubContext.op = new MockToken("+"); + Mockito.when(mockAddSubContext.expression(0)).thenReturn(mockExpressionContext1); + Mockito.when(mockAddSubContext.expression(1)).thenReturn(mockExpressionContext2); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSBool(false)); + Mockito.when(spyVisitor.visit(mockExpressionContext2)).thenReturn(new MSNumber(2)); + + Assertions.assertThrows(RuntimeException.class, () -> spyVisitor.visitAddSub(mockAddSubContext)); + } + + @ParameterizedTest + @CsvSource ({"1, 2", "2, 1", "0, 0", "-1, 1", "-2, 2"}) + void visitPowValidInputReturnsNumber(int value1, int value2){ + Mockito.when(mockPowContext.expression(0)).thenReturn(mockExpressionContext1); + Mockito.when(mockPowContext.expression(1)).thenReturn(mockExpressionContext2); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSNumber(value1)); + Mockito.when(spyVisitor.visit(mockExpressionContext2)).thenReturn(new MSNumber(value2)); + + MSType result = spyVisitor.visitPow(mockPowContext); + Assertions.assertEquals(Math.pow(value1, value2), ((MSNumber) result).getValue()); + } + + @Test + void visitPowInvalidInputThrowsRuntimeException(){ + Mockito.when(mockPowContext.expression(0)).thenReturn(mockExpressionContext1); + Mockito.when(mockPowContext.expression(1)).thenReturn(mockExpressionContext2); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSNumber(1)); + Mockito.when(spyVisitor.visit(mockExpressionContext2)).thenReturn(new MSNumber(-1)); + + Assertions.assertThrows(RuntimeException.class, () -> spyVisitor.visitPow(mockPowContext)); + } + + @ParameterizedTest + @CsvSource ({"true, false", "false, false"}) + void visitPowInvalidTypeThrowsRuntimeException(boolean bool1, boolean bool2){ + Mockito.when(mockPowContext.expression(0)).thenReturn(mockExpressionContext1); + Mockito.when(mockPowContext.expression(1)).thenReturn(mockExpressionContext2); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSBool(bool1)); + Mockito.when(spyVisitor.visit(mockExpressionContext2)).thenReturn(new MSBool(bool2)); + + Assertions.assertThrows(RuntimeException.class, () -> spyVisitor.visitPow(mockPowContext)); + + } + + @ParameterizedTest + @CsvSource ({"1", "2", "0", "-1", "-2"}) + void visitParenExprValidInputReturnsNumber(int value){ + Mockito.when(mockParenExprContext.expression()).thenReturn(mockExpressionContext1); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSNumber(value)); + + MSType result = spyVisitor.visitParenExpr(mockParenExprContext); + Assertions.assertEquals(value, ((MSNumber) result).getValue()); + } + + @ParameterizedTest + @ValueSource (booleans = {true, false}) + void visitParenExprValidInputReturnsBoolean(boolean value){ + Mockito.when(mockParenExprContext.expression()).thenReturn(mockExpressionContext1); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSBool(value)); + + MSType result = spyVisitor.visitParenExpr(mockParenExprContext); + Assertions.assertEquals(value, ((MSBool) result).getValue()); + } + + @ParameterizedTest + @CsvSource ({"1, 2, <", "2, 1, >", "0, 0, >=", "-1, 1, <", "-2, 2, <="}) + void visitCompValidInputReturnsTrue(int value1, int value2, String operator){ + mockCompContext.op = new MockToken(operator); + Mockito.when(mockCompContext.expression(0)).thenReturn(mockExpressionContext1); + Mockito.when(mockCompContext.expression(1)).thenReturn(mockExpressionContext2); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSNumber(value1)); + Mockito.when(spyVisitor.visit(mockExpressionContext2)).thenReturn(new MSNumber(value2)); + + MSType result = spyVisitor.visitComp(mockCompContext); + Assertions.assertEquals(true, ((MSBool) result).getValue()); + } + + @ParameterizedTest + @CsvSource ({"1, 2, >=", "2, 1, <", "0, 0, <", "-1, 1, >=", "-2, 2, >"}) + void visitCompValidInputReturnsFalse(int value1, int value2, String operator){ + mockCompContext.op = new MockToken(operator); + Mockito.when(mockCompContext.expression(0)).thenReturn(mockExpressionContext1); + Mockito.when(mockCompContext.expression(1)).thenReturn(mockExpressionContext2); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSNumber(value1)); + Mockito.when(spyVisitor.visit(mockExpressionContext2)).thenReturn(new MSNumber(value2)); + + MSType result = spyVisitor.visitComp(mockCompContext); + Assertions.assertEquals(false, ((MSBool) result).getValue()); + } + + @Test + void visitCompInvalidTypeThrowsException(){ + mockCompContext.op = new MockToken(">"); + Mockito.when(mockCompContext.expression(0)).thenReturn(mockExpressionContext1); + Mockito.when(mockCompContext.expression(1)).thenReturn(mockExpressionContext2); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSBool(true)); + Mockito.when(spyVisitor.visit(mockExpressionContext2)).thenReturn(new MSNumber(14)); + + Assertions.assertThrows(RuntimeException.class, () -> spyVisitor.visitComp(mockCompContext)); + } + + @Test + void visitFuncCallValidInputNoParamsReturnsNumber(){ + Mockito.when(mockFuncCallContext.actual_parameters()).thenReturn(mockActualParametersContext); + Mockito.when(mockFuncCallContext.ID()).thenReturn(new MockTerminalNode("testFunction")); + symbolTable.enterSymbol("testFunction", new MSFunction("testFunction", new ArrayList(), mockStatementsContext)); + + Mockito.when(spyVisitor.visit(mockStatementsContext)).thenReturn(new MSNumber(1)); + + MSType result = spyVisitor.visitFuncCall(mockFuncCallContext); + Assertions.assertEquals(1, ((MSNumber) result).getValue()); + } + @ParameterizedTest + @CsvSource ({"1", "2", "0", "-1", "-2", "123", "-123"}) + void visitFuncCallValidInputOneParamReturnsNumber(int value){ + + Mockito.when(mockFuncCallContext.ID()).thenReturn(new MockTerminalNode("testFunction")); + Mockito.when(mockFuncCallContext.actual_parameters()).thenReturn(mockActualParametersContext); + Mockito.when(mockFuncCallContext.actual_parameters().expression()).thenReturn(new ArrayList(){{add(mockExpressionContext1);}}); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSNumber(value)); + + symbolTable.enterSymbol("testFunction", new MSFunction("testFunction", new ArrayList(){{add("Param1");}}, mockStatementsContext)); + + Mockito.when(spyVisitor.visit(mockStatementsContext)).thenReturn(new MSNumber(value)); + + MSType result = spyVisitor.visitFuncCall(mockFuncCallContext); + Assertions.assertEquals(value, ((MSNumber) result).getValue()); + } + + @ParameterizedTest + @CsvSource ({"1, 2", "2, 1", "0, 0", "-1, 1", "-2, 2", "123, 122", "123, 124", "123, 123"}) + void visitFuncCallValidInputTwoParamReturnsNumber(int value1, int value2){ + + Mockito.when(mockFuncCallContext.ID()).thenReturn(new MockTerminalNode("testFunction")); + Mockito.when(mockFuncCallContext.actual_parameters()).thenReturn(mockActualParametersContext); + Mockito.when(mockFuncCallContext.actual_parameters().expression()).thenReturn(new ArrayList(){{add(mockExpressionContext1); add(mockExpressionContext2);}}); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSNumber(value1)); + Mockito.when(spyVisitor.visit(mockExpressionContext2)).thenReturn(new MSNumber(value2)); + + symbolTable.enterSymbol("testFunction", new MSFunction("testFunction", new ArrayList(){{add("Param1"); add("Param2");}}, mockStatementsContext)); + + Mockito.when(spyVisitor.visit(mockStatementsContext)).thenReturn(new MSNumber(value1+value2)); + + MSType result = spyVisitor.visitFuncCall(mockFuncCallContext); + Assertions.assertEquals(value1+value2, ((MSNumber) result).getValue()); + } + + @ParameterizedTest + @CsvSource ({"1, 2", "2, 1", "0, 0", "-1, 1", "-2, 2"}) + void visitFuncCallInvalidInputThrowsException(int value1, int value2){ + + Mockito.when(mockFuncCallContext.ID()).thenReturn(new MockTerminalNode("testFunction")); + Mockito.when(mockFuncCallContext.actual_parameters()).thenReturn(mockActualParametersContext); + Mockito.when(mockFuncCallContext.actual_parameters().expression()).thenReturn(new ArrayList(){{add(mockExpressionContext1); add(mockExpressionContext2);}}); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSNumber(value1)); + Mockito.when(spyVisitor.visit(mockExpressionContext2)).thenReturn(new MSNumber(value2)); + + symbolTable.enterSymbol("testFunction", new MSFunction("testFunction", new ArrayList(){{add("Param1");}}, mockStatementsContext)); + + Assertions.assertThrows(RuntimeException.class, () -> spyVisitor.visitFuncCall(mockFuncCallContext)); + } + + @Test + void visitFuncCallInvalidFunction(){ + Mockito.when(mockFuncCallContext.ID()).thenReturn(new MockTerminalNode("testFunctionFake")); + Mockito.when(mockFuncCallContext.actual_parameters()).thenReturn(mockActualParametersContext); + symbolTable.enterSymbol("testFunction", new MSFunction("testFunction", new ArrayList(){{add("Param1"); }}, mockStatementsContext)); + + Assertions.assertThrows(RuntimeException.class, () -> spyVisitor.visitFuncCall(mockFuncCallContext)); + } + @Test void visitAndValidBoolsReturnsTrue(){ Mockito.when(mockAndContext.expression(0)).thenReturn(mockExpressionContext1); @@ -262,7 +479,7 @@ void visitAndValidBoolsReturnsTrue(){ MSType result = spyVisitor.visitAnd(mockAndContext); Assertions.assertTrue(((MSBool) result).getValue()); } - + @Test void visitAndBoolsTrueAndFalseReturnsFalse(){ Mockito.when(mockAndContext.expression(0)).thenReturn(mockExpressionContext1); @@ -282,23 +499,6 @@ void visitAndFalseWithShortCircuitReturnsFalse() { Assertions.assertFalse(((MSBool) result).getValue()); } - @ParameterizedTest - @CsvSource ({"1, 2, +", "2, 1, -", "0, 0, +", "-1, -2, +", "-2, -1, -"}) - void visitAddSubValidInputReturnsNumber(int left, int right, String operator){ - mockAddSubContext.op = new MockToken(operator); - Mockito.when(mockAddSubContext.expression(0)).thenReturn(mockExpressionContext1); - Mockito.when(mockAddSubContext.expression(1)).thenReturn(mockExpressionContext2); - Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSNumber(left)); - Mockito.when(spyVisitor.visit(mockExpressionContext2)).thenReturn(new MSNumber(right)); - - MSType result = spyVisitor.visitAddSub(mockAddSubContext); - if (operator.equals("+")){ - Assertions.assertEquals(left + right, ((MSNumber) result).getValue()); - } else if (operator.equals("-")){ - Assertions.assertEquals(left - right, ((MSNumber) result).getValue()); - } - } - @ParameterizedTest @CsvSource({"false, false", "false, true", "true, false", "true, true"}) void visitOrValidInputReturnsOrValue(boolean left, boolean right) { From 83324af2e5d315705e5895cb40870769fe5b1c8f Mon Sep 17 00:00:00 2001 From: Viktor Platz Date: Wed, 17 May 2023 13:34:28 +0200 Subject: [PATCH 13/17] Remove old tests --- src/test/interpreter/VisitorUnitTest.java | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/src/test/interpreter/VisitorUnitTest.java b/src/test/interpreter/VisitorUnitTest.java index 052f59d..22840cc 100644 --- a/src/test/interpreter/VisitorUnitTest.java +++ b/src/test/interpreter/VisitorUnitTest.java @@ -134,15 +134,6 @@ void visitAssignStoresCorrectAbsDir(MSAbsDir.Direction value) { Assertions.assertEquals(value, ((MSAbsDir) mockValue.get()).getValue()); Assertions.assertNull(result); } - - @Test - void visitNotExprPassZeroReturnsTrue() { - Mockito.when(mockNotExprContext.expression()).thenReturn(mockExpressionContext1); - Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSNumber(0)); - - MSType result = spyVisitor.visitNotExpr(mockNotExprContext); - Assertions.assertTrue(((MSBool) result).getValue()); - } void visitNotExprValidBoolReturnsNegatedBool() { Mockito.when(mockNotExprContext.expression()).thenReturn(mockExpressionContext1); Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSBool(false)); @@ -151,16 +142,6 @@ void visitNotExprValidBoolReturnsNegatedBool() { Assertions.assertTrue(((MSBool) result).getValue()); } - @ParameterizedTest - @ValueSource(ints = {-1000, -100, 100, 1000}) - void visitNotExprPassNonZeroNumberReturnsFalse(int value) { - Mockito.when(mockNotExprContext.expression()).thenReturn(mockExpressionContext1); - Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSNumber(value)); - - MSType result = spyVisitor.visitNotExpr(mockNotExprContext); - Assertions.assertFalse(((MSBool) result).getValue()); - } - @ValueSource(ints = {-1000, -100, 0, 100, 1000}) void visitNotExprPassNumberThrowsRuntimeException(int value) { Mockito.when(mockNotExprContext.expression()).thenReturn(mockExpressionContext); From 46c529ab7f10233fb59c50814a4b2ccc6cae6c39 Mon Sep 17 00:00:00 2001 From: sabotack Date: Wed, 17 May 2023 13:52:57 +0200 Subject: [PATCH 14/17] refactor: removed redundant statements --- src/test/interpreter/VisitorUnitTest.java | 50 +++++++++-------------- 1 file changed, 20 insertions(+), 30 deletions(-) diff --git a/src/test/interpreter/VisitorUnitTest.java b/src/test/interpreter/VisitorUnitTest.java index 22840cc..e4c113a 100644 --- a/src/test/interpreter/VisitorUnitTest.java +++ b/src/test/interpreter/VisitorUnitTest.java @@ -4,24 +4,20 @@ import interpreter.types.*; import interpreter.utils.MockTerminalNode; import interpreter.utils.MockToken; -import org.antlr.v4.runtime.Token; -import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; -import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.EnumSource; import org.junit.jupiter.params.provider.ValueSource; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.Spy; import org.mockito.junit.jupiter.MockitoExtension; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; + import java.lang.reflect.Field; +import java.util.ArrayList; import java.util.concurrent.atomic.AtomicReference; @ExtendWith(MockitoExtension.class) @@ -31,7 +27,6 @@ class VisitorUnitTest { @Spy private final Visitor spyVisitor = visitor; @Mock private MineScriptParser.AssignContext mockAssignContext; - @Mock private MineScriptParser.ExpressionContext mockExpressionContext; @Mock private MineScriptParser.ExpressionContext mockExpressionContext1; @Mock private MineScriptParser.ExpressionContext mockExpressionContext2; @Mock private MineScriptParser.BoolContext mockBoolContext; @@ -134,20 +129,15 @@ void visitAssignStoresCorrectAbsDir(MSAbsDir.Direction value) { Assertions.assertEquals(value, ((MSAbsDir) mockValue.get()).getValue()); Assertions.assertNull(result); } - void visitNotExprValidBoolReturnsNegatedBool() { + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void visitNotExprValidBoolReturnsNegatedBool(boolean value) { Mockito.when(mockNotExprContext.expression()).thenReturn(mockExpressionContext1); - Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSBool(false)); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSBool(value)); MSType result = spyVisitor.visitNotExpr(mockNotExprContext); - Assertions.assertTrue(((MSBool) result).getValue()); - } - - @ValueSource(ints = {-1000, -100, 0, 100, 1000}) - void visitNotExprPassNumberThrowsRuntimeException(int value) { - Mockito.when(mockNotExprContext.expression()).thenReturn(mockExpressionContext); - Mockito.when(spyVisitor.visit(mockExpressionContext)).thenReturn(new MSNumber(value)); - - Assertions.assertThrows(RuntimeException.class, () -> spyVisitor.visitNotExpr(mockNotExprContext)); + Assertions.assertEquals(!value, ((MSBool) result).getValue()); } @Test @@ -353,7 +343,7 @@ void visitCompValidInputReturnsTrue(int value1, int value2, String operator){ Mockito.when(spyVisitor.visit(mockExpressionContext2)).thenReturn(new MSNumber(value2)); MSType result = spyVisitor.visitComp(mockCompContext); - Assertions.assertEquals(true, ((MSBool) result).getValue()); + Assertions.assertTrue(((MSBool) result).getValue()); } @ParameterizedTest @@ -366,7 +356,7 @@ void visitCompValidInputReturnsFalse(int value1, int value2, String operator){ Mockito.when(spyVisitor.visit(mockExpressionContext2)).thenReturn(new MSNumber(value2)); MSType result = spyVisitor.visitComp(mockCompContext); - Assertions.assertEquals(false, ((MSBool) result).getValue()); + Assertions.assertFalse(((MSBool) result).getValue()); } @Test @@ -384,7 +374,7 @@ void visitCompInvalidTypeThrowsException(){ void visitFuncCallValidInputNoParamsReturnsNumber(){ Mockito.when(mockFuncCallContext.actual_parameters()).thenReturn(mockActualParametersContext); Mockito.when(mockFuncCallContext.ID()).thenReturn(new MockTerminalNode("testFunction")); - symbolTable.enterSymbol("testFunction", new MSFunction("testFunction", new ArrayList(), mockStatementsContext)); + symbolTable.enterSymbol("testFunction", new MSFunction("testFunction", new ArrayList<>(), mockStatementsContext)); Mockito.when(spyVisitor.visit(mockStatementsContext)).thenReturn(new MSNumber(1)); @@ -397,10 +387,10 @@ void visitFuncCallValidInputOneParamReturnsNumber(int value){ Mockito.when(mockFuncCallContext.ID()).thenReturn(new MockTerminalNode("testFunction")); Mockito.when(mockFuncCallContext.actual_parameters()).thenReturn(mockActualParametersContext); - Mockito.when(mockFuncCallContext.actual_parameters().expression()).thenReturn(new ArrayList(){{add(mockExpressionContext1);}}); + Mockito.when(mockFuncCallContext.actual_parameters().expression()).thenReturn(new ArrayList<>(){{add(mockExpressionContext1);}}); Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSNumber(value)); - symbolTable.enterSymbol("testFunction", new MSFunction("testFunction", new ArrayList(){{add("Param1");}}, mockStatementsContext)); + symbolTable.enterSymbol("testFunction", new MSFunction("testFunction", new ArrayList<>(){{add("Param1");}}, mockStatementsContext)); Mockito.when(spyVisitor.visit(mockStatementsContext)).thenReturn(new MSNumber(value)); @@ -414,11 +404,11 @@ void visitFuncCallValidInputTwoParamReturnsNumber(int value1, int value2){ Mockito.when(mockFuncCallContext.ID()).thenReturn(new MockTerminalNode("testFunction")); Mockito.when(mockFuncCallContext.actual_parameters()).thenReturn(mockActualParametersContext); - Mockito.when(mockFuncCallContext.actual_parameters().expression()).thenReturn(new ArrayList(){{add(mockExpressionContext1); add(mockExpressionContext2);}}); + Mockito.when(mockFuncCallContext.actual_parameters().expression()).thenReturn(new ArrayList<>(){{add(mockExpressionContext1); add(mockExpressionContext2);}}); Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSNumber(value1)); Mockito.when(spyVisitor.visit(mockExpressionContext2)).thenReturn(new MSNumber(value2)); - symbolTable.enterSymbol("testFunction", new MSFunction("testFunction", new ArrayList(){{add("Param1"); add("Param2");}}, mockStatementsContext)); + symbolTable.enterSymbol("testFunction", new MSFunction("testFunction", new ArrayList<>(){{add("Param1"); add("Param2");}}, mockStatementsContext)); Mockito.when(spyVisitor.visit(mockStatementsContext)).thenReturn(new MSNumber(value1+value2)); @@ -432,11 +422,11 @@ void visitFuncCallInvalidInputThrowsException(int value1, int value2){ Mockito.when(mockFuncCallContext.ID()).thenReturn(new MockTerminalNode("testFunction")); Mockito.when(mockFuncCallContext.actual_parameters()).thenReturn(mockActualParametersContext); - Mockito.when(mockFuncCallContext.actual_parameters().expression()).thenReturn(new ArrayList(){{add(mockExpressionContext1); add(mockExpressionContext2);}}); + Mockito.when(mockFuncCallContext.actual_parameters().expression()).thenReturn(new ArrayList<>(){{add(mockExpressionContext1); add(mockExpressionContext2);}}); Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSNumber(value1)); Mockito.when(spyVisitor.visit(mockExpressionContext2)).thenReturn(new MSNumber(value2)); - symbolTable.enterSymbol("testFunction", new MSFunction("testFunction", new ArrayList(){{add("Param1");}}, mockStatementsContext)); + symbolTable.enterSymbol("testFunction", new MSFunction("testFunction", new ArrayList<>(){{add("Param1");}}, mockStatementsContext)); Assertions.assertThrows(RuntimeException.class, () -> spyVisitor.visitFuncCall(mockFuncCallContext)); } @@ -445,7 +435,7 @@ void visitFuncCallInvalidInputThrowsException(int value1, int value2){ void visitFuncCallInvalidFunction(){ Mockito.when(mockFuncCallContext.ID()).thenReturn(new MockTerminalNode("testFunctionFake")); Mockito.when(mockFuncCallContext.actual_parameters()).thenReturn(mockActualParametersContext); - symbolTable.enterSymbol("testFunction", new MSFunction("testFunction", new ArrayList(){{add("Param1"); }}, mockStatementsContext)); + symbolTable.enterSymbol("testFunction", new MSFunction("testFunction", new ArrayList<>(){{add("Param1"); }}, mockStatementsContext)); Assertions.assertThrows(RuntimeException.class, () -> spyVisitor.visitFuncCall(mockFuncCallContext)); } @@ -578,8 +568,8 @@ void visitMultDivModInvalidInputThrowsRuntimeException(String left, String right "abs,north", "abs,south", "abs,east", "abs,west", "abs,top", "abs,bottom", "bool,true", "bool,false"}) void visitReturnCorrectTypesReturnValue(String type, String value) { - Field hasReturnedField = null; - boolean hasReturned = false; + Field hasReturnedField; + boolean hasReturned; // Setup mocks for the context and visit method Mockito.when(mockReturnContext.expression()).thenReturn(mockExpressionContext1); From 4e273f494d5b012a65f4170eae2cca230b6a971a Mon Sep 17 00:00:00 2001 From: Ali Khorami <33497115+sabotack@users.noreply.github.com> Date: Wed, 17 May 2023 14:31:01 +0200 Subject: [PATCH 15/17] added visitFuncDecl tests (#99) --- src/test/interpreter/VisitorUnitTest.java | 62 ++++++++++++++++++++--- 1 file changed, 55 insertions(+), 7 deletions(-) diff --git a/src/test/interpreter/VisitorUnitTest.java b/src/test/interpreter/VisitorUnitTest.java index e4c113a..a5feb2a 100644 --- a/src/test/interpreter/VisitorUnitTest.java +++ b/src/test/interpreter/VisitorUnitTest.java @@ -41,7 +41,9 @@ class VisitorUnitTest { @Mock private MineScriptParser.ParenExprContext mockParenExprContext; @Mock private MineScriptParser.CompContext mockCompContext; @Mock private MineScriptParser.FuncCallContext mockFuncCallContext; + @Mock private MineScriptParser.FuncDeclContext mockFuncDeclContext; @Mock private MineScriptParser.Actual_parametersContext mockActualParametersContext; + @Mock private MineScriptParser.Formal_paramatersContext mockFormalParametersContext; @Mock private MineScriptParser.StatementsContext mockStatementsContext; @Mock private MineScriptParser.AndContext mockAndContext; @Mock private MineScriptParser.OrContext mockOrContext; @@ -431,6 +433,52 @@ void visitFuncCallInvalidInputThrowsException(int value1, int value2){ Assertions.assertThrows(RuntimeException.class, () -> spyVisitor.visitFuncCall(mockFuncCallContext)); } + @Test + void visitFuncDeclNoParamsStoresCorrectFunction() { + Mockito.when(mockFuncDeclContext.ID()).thenReturn(new MockTerminalNode("testFunction")); + Mockito.when(mockFuncDeclContext.formal_paramaters()).thenReturn(mockFormalParametersContext); + + spyVisitor.visitFuncDecl(mockFuncDeclContext); + + AtomicReference mockValue = new AtomicReference<>(); + Assertions.assertDoesNotThrow(() -> mockValue.set(symbolTable.retrieveSymbolValue(symbolTable.retrieveSymbol("testFunction")))); + Assertions.assertTrue(mockValue.get() instanceof MSFunction); + Assertions.assertEquals("testFunction", ((MSFunction) mockValue.get()).getName()); + } + + @Test + void visitFuncDeclOneParamStoresCorrectFunction() { + Mockito.when(mockFuncDeclContext.ID()).thenReturn(new MockTerminalNode("testFunction")); + Mockito.when(mockFuncDeclContext.formal_paramaters()).thenReturn(mockFormalParametersContext); + Mockito.when(mockFormalParametersContext.ID()).thenReturn(new ArrayList<>() {{add(new MockTerminalNode("Param1"));}}); + + spyVisitor.visitFuncDecl(mockFuncDeclContext); + + AtomicReference mockValue = new AtomicReference<>(); + Assertions.assertDoesNotThrow(() -> mockValue.set(symbolTable.retrieveSymbolValue(symbolTable.retrieveSymbol("testFunction")))); + Assertions.assertTrue(mockValue.get() instanceof MSFunction); + Assertions.assertEquals("testFunction", ((MSFunction) mockValue.get()).getName()); + Assertions.assertEquals(1, ((MSFunction) mockValue.get()).getParameters().size()); + Assertions.assertEquals("Param1", ((MSFunction) mockValue.get()).getParameters().get(0)); + } + + @Test + void visitFuncDeclTwoParamsStoresCorrectFunction() { + Mockito.when(mockFuncDeclContext.ID()).thenReturn(new MockTerminalNode("testFunction")); + Mockito.when(mockFuncDeclContext.formal_paramaters()).thenReturn(mockFormalParametersContext); + Mockito.when(mockFormalParametersContext.ID()).thenReturn(new ArrayList<>() {{add(new MockTerminalNode("Param1")); add(new MockTerminalNode("Param2"));}}); + + spyVisitor.visitFuncDecl(mockFuncDeclContext); + + AtomicReference mockValue = new AtomicReference<>(); + Assertions.assertDoesNotThrow(() -> mockValue.set(symbolTable.retrieveSymbolValue(symbolTable.retrieveSymbol("testFunction")))); + Assertions.assertTrue(mockValue.get() instanceof MSFunction); + Assertions.assertEquals("testFunction", ((MSFunction) mockValue.get()).getName()); + Assertions.assertEquals(2, ((MSFunction) mockValue.get()).getParameters().size()); + Assertions.assertEquals("Param1", ((MSFunction) mockValue.get()).getParameters().get(0)); + Assertions.assertEquals("Param2", ((MSFunction) mockValue.get()).getParameters().get(1)); + } + @Test void visitFuncCallInvalidFunction(){ Mockito.when(mockFuncCallContext.ID()).thenReturn(new MockTerminalNode("testFunctionFake")); @@ -487,9 +535,9 @@ void visitOrValidInputReturnsOrValue(boolean left, boolean right) { @ParameterizedTest @CsvSource({"num,0,0,is", "num,0,1,is not", "num,1,0,is not", "num,1,1,is", "num,-1,1,is not", - "rel,left,right,is", "rel,up,down,is not", "rel,left,left,is not", "rel,right,right,is", - "abs,north,south,is", "abs,east,west,is not", "abs,top,bottom,is not", "abs,north,north,is", - "bool,false,false,is", "bool,false,true,is not", "bool,true,false,is not", "bool,true,true,is"}) + "rel,left,right,is", "rel,up,down,is not", "rel,left,left,is not", "rel,right,right,is", + "abs,north,south,is", "abs,east,west,is not", "abs,top,bottom,is not", "abs,north,north,is", + "bool,false,false,is", "bool,false,true,is not", "bool,true,false,is not", "bool,true,true,is"}) void visitIsIsNotCorrectTypesReturnsCorrectBool(String type, String left, String right, String operator) { mockIsIsNotContext.op = new MockToken(operator); Mockito.when(mockIsIsNotContext.expression(0)).thenReturn(mockExpressionContext1); @@ -563,10 +611,10 @@ void visitMultDivModInvalidInputThrowsRuntimeException(String left, String right } @ParameterizedTest - @CsvSource ({"num,-10", "num,0", "num,10", - "rel,left", "rel,right", "rel,up", "rel,down", - "abs,north", "abs,south", "abs,east", "abs,west", "abs,top", "abs,bottom", - "bool,true", "bool,false"}) + @CsvSource ({"bool,true", "bool,false", + "num,-10", "num,0", "num,10", + "rel,left", "rel,right", "rel,up", "rel,down", + "abs,north", "abs,south", "abs,east", "abs,west", "abs,top", "abs,bottom"}) void visitReturnCorrectTypesReturnValue(String type, String value) { Field hasReturnedField; boolean hasReturned; From 9c92f40ab1294e46f574151b6bd7e2aec2b3226b Mon Sep 17 00:00:00 2001 From: Viktor Platz Date: Wed, 17 May 2023 21:00:21 +0200 Subject: [PATCH 16/17] Tests for While, Repeat and If statements --- src/test/interpreter/VisitorUnitTest.java | 164 ++++++++++++++++++++-- 1 file changed, 150 insertions(+), 14 deletions(-) diff --git a/src/test/interpreter/VisitorUnitTest.java b/src/test/interpreter/VisitorUnitTest.java index 22840cc..eeb4aa5 100644 --- a/src/test/interpreter/VisitorUnitTest.java +++ b/src/test/interpreter/VisitorUnitTest.java @@ -4,23 +4,21 @@ import interpreter.types.*; import interpreter.utils.MockTerminalNode; import interpreter.utils.MockToken; -import org.antlr.v4.runtime.Token; -import org.jetbrains.annotations.NotNull; +import org.antlr.v4.runtime.tree.TerminalNode; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; -import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.EnumSource; import org.junit.jupiter.params.provider.ValueSource; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.Spy; import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.stubbing.OngoingStubbing; + import java.util.ArrayList; -import java.util.List; -import java.util.Objects; import java.lang.reflect.Field; import java.util.concurrent.atomic.AtomicReference; @@ -47,12 +45,18 @@ class VisitorUnitTest { @Mock private MineScriptParser.CompContext mockCompContext; @Mock private MineScriptParser.FuncCallContext mockFuncCallContext; @Mock private MineScriptParser.Actual_parametersContext mockActualParametersContext; - @Mock private MineScriptParser.StatementsContext mockStatementsContext; + @Mock private MineScriptParser.StatementsContext mockStatementsContext1; + @Mock private MineScriptParser.StatementsContext mockStatementsContext2; + @Mock private MineScriptParser.StatementsContext mockStatementsContext3; @Mock private MineScriptParser.AndContext mockAndContext; @Mock private MineScriptParser.OrContext mockOrContext; @Mock private MineScriptParser.MultDivModContext mockMultDivModContext; @Mock private MineScriptParser.IsIsNotContext mockIsIsNotContext; @Mock private MineScriptParser.ReturnContext mockReturnContext; + @Mock private MineScriptParser.IfContext mockIfContext; + @Mock private MineScriptParser.StatementContext mockStatementContext; + @Mock private MineScriptParser.WhileContext mockWhileContext; + @Mock private MineScriptParser.RepeatContext mockRepeatContext; @ParameterizedTest @ValueSource(ints = {-1000, -10, 0, 10, 1000}) @@ -142,6 +146,7 @@ void visitNotExprValidBoolReturnsNegatedBool() { Assertions.assertTrue(((MSBool) result).getValue()); } + @ParameterizedTest @ValueSource(ints = {-1000, -100, 0, 100, 1000}) void visitNotExprPassNumberThrowsRuntimeException(int value) { Mockito.when(mockNotExprContext.expression()).thenReturn(mockExpressionContext); @@ -289,6 +294,19 @@ void visitAddSubInvalidTypeThrowsRuntimeException(){ Assertions.assertThrows(RuntimeException.class, () -> spyVisitor.visitAddSub(mockAddSubContext)); } + @ParameterizedTest + @CsvSource ({"2147483641, 2, *", "2147483647, 12, +", "2147483647, 4, *", "2147483647, 1000, ^", "2147483647, -12, -"}) + void calculateArithmeticExpressionInvalidInputOverflowError(int value1, int value2, String operator){ + mockAddSubContext.op = new MockToken(operator); + Mockito.when(mockAddSubContext.expression(0)).thenReturn(mockExpressionContext1); + Mockito.when(mockAddSubContext.expression(1)).thenReturn(mockExpressionContext2); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSNumber(value1)); + Mockito.when(spyVisitor.visit(mockExpressionContext2)).thenReturn(new MSNumber(value2)); + + Assertions.assertThrows(RuntimeException.class, () -> spyVisitor.visitAddSub(mockAddSubContext)); + + } + @ParameterizedTest @CsvSource ({"1, 2", "2, 1", "0, 0", "-1, 1", "-2, 2"}) void visitPowValidInputReturnsNumber(int value1, int value2){ @@ -384,9 +402,9 @@ void visitCompInvalidTypeThrowsException(){ void visitFuncCallValidInputNoParamsReturnsNumber(){ Mockito.when(mockFuncCallContext.actual_parameters()).thenReturn(mockActualParametersContext); Mockito.when(mockFuncCallContext.ID()).thenReturn(new MockTerminalNode("testFunction")); - symbolTable.enterSymbol("testFunction", new MSFunction("testFunction", new ArrayList(), mockStatementsContext)); + symbolTable.enterSymbol("testFunction", new MSFunction("testFunction", new ArrayList(), mockStatementsContext1)); - Mockito.when(spyVisitor.visit(mockStatementsContext)).thenReturn(new MSNumber(1)); + Mockito.when(spyVisitor.visit(mockStatementsContext1)).thenReturn(new MSNumber(1)); MSType result = spyVisitor.visitFuncCall(mockFuncCallContext); Assertions.assertEquals(1, ((MSNumber) result).getValue()); @@ -400,9 +418,9 @@ void visitFuncCallValidInputOneParamReturnsNumber(int value){ Mockito.when(mockFuncCallContext.actual_parameters().expression()).thenReturn(new ArrayList(){{add(mockExpressionContext1);}}); Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSNumber(value)); - symbolTable.enterSymbol("testFunction", new MSFunction("testFunction", new ArrayList(){{add("Param1");}}, mockStatementsContext)); + symbolTable.enterSymbol("testFunction", new MSFunction("testFunction", new ArrayList(){{add("Param1");}}, mockStatementsContext1)); - Mockito.when(spyVisitor.visit(mockStatementsContext)).thenReturn(new MSNumber(value)); + Mockito.when(spyVisitor.visit(mockStatementsContext1)).thenReturn(new MSNumber(value)); MSType result = spyVisitor.visitFuncCall(mockFuncCallContext); Assertions.assertEquals(value, ((MSNumber) result).getValue()); @@ -418,9 +436,9 @@ void visitFuncCallValidInputTwoParamReturnsNumber(int value1, int value2){ Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSNumber(value1)); Mockito.when(spyVisitor.visit(mockExpressionContext2)).thenReturn(new MSNumber(value2)); - symbolTable.enterSymbol("testFunction", new MSFunction("testFunction", new ArrayList(){{add("Param1"); add("Param2");}}, mockStatementsContext)); + symbolTable.enterSymbol("testFunction", new MSFunction("testFunction", new ArrayList(){{add("Param1"); add("Param2");}}, mockStatementsContext1)); - Mockito.when(spyVisitor.visit(mockStatementsContext)).thenReturn(new MSNumber(value1+value2)); + Mockito.when(spyVisitor.visit(mockStatementsContext1)).thenReturn(new MSNumber(value1+value2)); MSType result = spyVisitor.visitFuncCall(mockFuncCallContext); Assertions.assertEquals(value1+value2, ((MSNumber) result).getValue()); @@ -436,7 +454,7 @@ void visitFuncCallInvalidInputThrowsException(int value1, int value2){ Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSNumber(value1)); Mockito.when(spyVisitor.visit(mockExpressionContext2)).thenReturn(new MSNumber(value2)); - symbolTable.enterSymbol("testFunction", new MSFunction("testFunction", new ArrayList(){{add("Param1");}}, mockStatementsContext)); + symbolTable.enterSymbol("testFunction", new MSFunction("testFunction", new ArrayList(){{add("Param1");}}, mockStatementsContext1)); Assertions.assertThrows(RuntimeException.class, () -> spyVisitor.visitFuncCall(mockFuncCallContext)); } @@ -445,11 +463,23 @@ void visitFuncCallInvalidInputThrowsException(int value1, int value2){ void visitFuncCallInvalidFunction(){ Mockito.when(mockFuncCallContext.ID()).thenReturn(new MockTerminalNode("testFunctionFake")); Mockito.when(mockFuncCallContext.actual_parameters()).thenReturn(mockActualParametersContext); - symbolTable.enterSymbol("testFunction", new MSFunction("testFunction", new ArrayList(){{add("Param1"); }}, mockStatementsContext)); + symbolTable.enterSymbol("testFunction", new MSFunction("testFunction", new ArrayList(){{add("Param1"); }}, mockStatementsContext1)); Assertions.assertThrows(RuntimeException.class, () -> spyVisitor.visitFuncCall(mockFuncCallContext)); } + @Test + void visitBreakBuiltInFuncCall(){ + Mockito.when(mockFuncCallContext.ID()).thenReturn(new MockTerminalNode("Break")); + Mockito.when(mockFuncCallContext.actual_parameters()).thenReturn(mockActualParametersContext); + + Mockito.when(mockFuncCallContext.actual_parameters().expression()).thenReturn(new ArrayList(){{add(mockExpressionContext1);}}); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSBool(true)); + + MSType result = spyVisitor.visitFuncCall(mockFuncCallContext); + Assertions.assertEquals(true, ((MSBool) result).getValue()); + } + @Test void visitAndValidBoolsReturnsTrue(){ Mockito.when(mockAndContext.expression(0)).thenReturn(mockExpressionContext1); @@ -622,4 +652,110 @@ void visitReturnNullExpressionThrowsRuntimeException() { Mockito.when(mockReturnContext.expression()).thenReturn(null); Assertions.assertThrows(RuntimeException.class, () -> spyVisitor.visitReturn(mockReturnContext)); } + + @Test + void visitIfStatements() { + Mockito.when(mockIfContext.expression()).thenReturn(new ArrayList(){{add(mockExpressionContext1);}}); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSBool(true)); + Mockito.when(mockIfContext.statements(0)).thenReturn(mockStatementsContext1); + Mockito.when(spyVisitor.visit(mockStatementsContext1)).thenReturn(new MSNumber(1)); + Mockito.when(spyVisitor.visit(mockStatementsContext1)).thenReturn(new MSNumber(1)); + + MSType result = spyVisitor.visitIf(mockIfContext); + + Assertions.assertEquals(1, ((MSNumber) result).getValue()); + } + + @Test + void visitElseStatementsInIfElseCase() { + Mockito.when(mockIfContext.expression()).thenReturn(new ArrayList(){{add(mockExpressionContext1);}}); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSBool(false)); + Mockito.when(mockIfContext.statements()).thenReturn(new ArrayList(){{add(mockStatementsContext1);add(mockStatementsContext2);}}); + Mockito.when(mockIfContext.statements(1)).thenReturn(mockStatementsContext2); + Mockito.when(spyVisitor.visit(mockStatementsContext2)).thenReturn(new MSNumber(2)); + + MSType result = spyVisitor.visitIf(mockIfContext); + + Assertions.assertEquals(2, ((MSNumber) result).getValue()); + } + + @Test + void visitElseIfStatements() { + Mockito.when(mockIfContext.expression()).thenReturn(new ArrayList(){{add(mockExpressionContext1);add(mockExpressionContext2);}}); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSBool(false)); + Mockito.when(spyVisitor.visit(mockExpressionContext2)).thenReturn(new MSBool(true)); + + Mockito.when(mockIfContext.statements(1)).thenReturn(mockStatementsContext2); + Mockito.when(spyVisitor.visit(mockStatementsContext2)).thenReturn(new MSNumber(2)); + + MSType result = spyVisitor.visitIf(mockIfContext); + + Assertions.assertEquals(2, ((MSNumber) result).getValue()); + } + + @Test + void visitIfNonBooleanCondition() { + Mockito.when(mockIfContext.expression()).thenReturn(new ArrayList(){{add(mockExpressionContext1);}}); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSNumber(1)); + + Assertions.assertThrows(RuntimeException.class, () -> spyVisitor.visitIf(mockIfContext)); + } + + @Test + void visitWhileConditionTrue(){ + Mockito.when(mockWhileContext.expression()).thenReturn(mockExpressionContext1); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSBool(true), new MSBool(false)); + Mockito.when(mockWhileContext.statements()).thenReturn(mockStatementsContext1); + Mockito.when(spyVisitor.visit(mockStatementsContext1)).thenReturn(new MSNumber(1)); + + MSType result = spyVisitor.visitWhile(mockWhileContext); + + Assertions.assertEquals(1, ((MSNumber) result).getValue()); + } + + @Test + void visitWhileConditionFalse(){ + Mockito.when(mockWhileContext.expression()).thenReturn(mockExpressionContext1); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSBool(false)); + + MSType result = spyVisitor.visitWhile(mockWhileContext); + + Assertions.assertNull(result); + } + + @Test + void visitWhileConditionNonBooleanCondition(){ + Mockito.when(mockWhileContext.expression()).thenReturn(mockExpressionContext1); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSNumber(1)); + + Assertions.assertThrows(RuntimeException.class, () -> spyVisitor.visitWhile(mockWhileContext)); + } + + @ParameterizedTest + @CsvSource({ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "100", "1000"}) + void visitRepeatStatementValueTimes(int value) { + Mockito.when(mockRepeatContext.expression()).thenReturn(mockExpressionContext1); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSNumber(value)); + Mockito.when(mockRepeatContext.statements()).thenReturn(mockStatementsContext1); + + OngoingStubbing stubbing = Mockito.when(spyVisitor.visit(mockStatementsContext1)); + for (int i = 1; i <= value; i++) { + stubbing = stubbing.thenReturn(new MSNumber(i)); + } + + MSType result = spyVisitor.visitRepeat(mockRepeatContext); + + Assertions.assertEquals(value, ((MSNumber) result).getValue()); + } + + @Test + void visitRepeatStatementNonIntegerValue() { + Mockito.when(mockRepeatContext.expression()).thenReturn(mockExpressionContext1); + Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSBool(true)); + + Assertions.assertThrows(RuntimeException.class, () -> spyVisitor.visitRepeat(mockRepeatContext)); + } + + + } From f360a3ddc536efc11018b913e87b929aaa5bd542 Mon Sep 17 00:00:00 2001 From: Viktor Platz Date: Wed, 17 May 2023 21:01:11 +0200 Subject: [PATCH 17/17] Small error fix --- src/test/interpreter/VisitorUnitTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/interpreter/VisitorUnitTest.java b/src/test/interpreter/VisitorUnitTest.java index eeb4aa5..ad09f9f 100644 --- a/src/test/interpreter/VisitorUnitTest.java +++ b/src/test/interpreter/VisitorUnitTest.java @@ -732,7 +732,7 @@ void visitWhileConditionNonBooleanCondition(){ } @ParameterizedTest - @CsvSource({ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "100", "1000"}) + @CsvSource({"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "100", "1000"}) void visitRepeatStatementValueTimes(int value) { Mockito.when(mockRepeatContext.expression()).thenReturn(mockExpressionContext1); Mockito.when(spyVisitor.visit(mockExpressionContext1)).thenReturn(new MSNumber(value));