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) {