/**
* Copyright (C) 2019 Adrien Hopkins
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package sevenUnits.utils;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import java.util.List;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
/**
* A test for the {@code ExpressionParser} class. This is NOT part of this
* program's public API.
*
* @author Adrien Hopkins
* @since 2019-03-22
* @since v0.2.0
*/
class ExpressionParserTest {
private static final ExpressionParser numberParser = new ExpressionParser.Builder<>(
Integer::parseInt).addBinaryOperator("+", (o1, o2) -> o1 + o2, 0)
.addBinaryOperator("-", (o1, o2) -> o1 - o2, 0)
.addUnaryOperator("neg", o1 -> -o1, 1)
.addBinaryOperator("*", (o1, o2) -> o1 * o2, 2)
.addBinaryOperator("/", (o1, o2) -> o1 / o2, 2)
.addUnaryOperator("recip", o1 -> 1 / o1, 3)
.addBinaryOperator("^", (o1, o2) -> (int) Math.pow(o1, o2), 4).build();
/**
* The expressions used in the expression parsing tests
*/
private static final List TEST_EXPRESSIONS = List.of(
// test parsing of expressions
"1 + 2 ^ 5 * 3", "(1 + 2) ^ 5 * 3",
"12 * 5 + (3 ^ (2 * 3) - 72) / (3 + 3 * 2)",
// ensure it normally goes from left to right
"1 + 2 + 3 + 4", "12 - 4 - 3", "12 - (4 - 3)", "1 / 2 + 3");
/**
* The expected results for evaluating these expressions
*/
private static final int[] RESULTS = { 97, 729, 133, 10, 5, 11, 3 };
/**
* @return A stream of objects, where each one is an expression and the
* expected result
* @since 2021-09-27
*/
private static final Stream testParseExpressionData() {
return IntStream.range(0, TEST_EXPRESSIONS.size())
.mapToObj(i -> Arguments.of(TEST_EXPRESSIONS.get(i), RESULTS[i]));
}
private static final Stream testConvertExpressionToRPN() {
return Stream.of(Arguments.of("1 + 2 ^ 5 * 3", "1 2 5 ^ 3 * +"),
Arguments.of("(1 + 2) ^ 5 * 3", "1 2 + 5 ^ 3 *"),
Arguments.of("12 * 5 + (3 ^ (2 * 3) - 72) / (3 + 3 * 2)",
"12 5 * 3 2 3 * ^ 72 - 3 3 2 * + / +"),
Arguments.of("1 + 2 + 3 + 4", "1 2 + 3 + 4 +"),
Arguments.of("12 - 4 - 3", "12 4 - 3 -"),
Arguments.of("12 - (4 - 3)", "12 4 3 - -"),
Arguments.of("1 / 2 + 3", "1 2 / 3 +"), Arguments.of("12", "12"),
Arguments.of("2 * 3 + 4", "2 3 * 4 +"),
Arguments.of("(2 * 3) + 4", "2 3 * 4 +"),
Arguments.of("2 * 3 - 4", "2 3 * 4 -"),
Arguments.of("(2 * 3) - 4", "2 3 * 4 -"),
Arguments.of("2 * (3 + 4)", "2 3 4 + *"),
Arguments.of("2 * (3 - 4)", "2 3 4 - *"),
Arguments.of("neg 2", "2 neg"),
Arguments.of("1 + neg 2", "1 2 neg +"));
}
private static final Stream testParseRPN() {
return Stream.of(Arguments.of("1 2 5 ^ 3 * +", 97),
Arguments.of("1 2 + 5 ^ 3 *", 729),
Arguments.of("12 5 * 3 2 3 * ^ 72 - 3 3 2 * + / +", 133),
Arguments.of("1 2 + 3 + 4 +", 10), Arguments.of("12 4 - 3 -", 5),
Arguments.of("12 4 3 - -", 11), Arguments.of("1 2 / 3 +", 3),
Arguments.of("12", 12), Arguments.of("2 3 * 4 +", 10),
Arguments.of("2 3 * 4 -", 2), Arguments.of("2 3 4 + *", 14),
Arguments.of("2 3 4 - *", -2), Arguments.of("2 neg", -2),
Arguments.of("1 2 neg +", -1));
}
private static final Stream testInvalidExpression() {
return Stream.of("+", "1 +", "1 + * 2", "1 (+ 1)", "neg");
}
private static final Stream testInvalidRPN() {
return Stream.of("+", "1 +", "1 + * 2", "1 * 2", "1 2", "neg");
}
/**
* Test method for
* {@link sevenUnits.utils.ExpressionParser#parseExpression(java.lang.String)}.
*/
@ParameterizedTest
@MethodSource("testParseExpressionData")
public void testParseExpression(String expression, int value) {
assertEquals(value, numberParser.parseExpression(expression));
}
@ParameterizedTest
@MethodSource
public void testConvertExpressionToRPN(String expression,
String expectedRPN) {
assertEquals(expectedRPN,
numberParser.convertExpressionToReversePolish(expression));
}
@ParameterizedTest
@MethodSource
public void testInvalidExpression(String expression) {
assertThrows(RuntimeException.class,
() -> numberParser.convertExpressionToReversePolish(expression));
}
@ParameterizedTest
@MethodSource
public void testParseRPN(String expressionRPN, int value) {
assertEquals(value,
numberParser.parseReversePolishExpression(expressionRPN));
}
@ParameterizedTest
@MethodSource
public void testInvalidRPN(String expressionRPN) {
assertThrows(RuntimeException.class,
() -> numberParser.parseReversePolishExpression(expressionRPN));
}
}