diff --git a/spring-expression/src/test/java/org/springframework/expression/spel/ListTests.java b/spring-expression/src/test/java/org/springframework/expression/spel/ListTests.java index c9d23d8140..3908c97791 100644 --- a/spring-expression/src/test/java/org/springframework/expression/spel/ListTests.java +++ b/spring-expression/src/test/java/org/springframework/expression/spel/ListTests.java @@ -20,20 +20,22 @@ import java.util.ArrayList; import java.util.Collections; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; import org.springframework.expression.spel.ast.InlineList; import org.springframework.expression.spel.standard.SpelExpression; -import org.springframework.expression.spel.standard.SpelExpressionParser; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.springframework.expression.spel.SpelMessage.RESULT_OF_SELECTION_CRITERIA_IS_NOT_BOOLEAN; +import static org.assertj.core.api.InstanceOfAssertFactories.type; /** * Test usage of inline lists. * * @author Andy Clement * @author Giovanni Dall'Oglio Risso + * @author Sam Brannen * @since 3.0.4 */ class ListTests extends AbstractExpressionTests { @@ -44,49 +46,61 @@ class ListTests extends AbstractExpressionTests { @Test - void testInlineListCreation01() { + void inlineListCreation() { evaluate("{1, 2, 3, 4, 5}", "[1, 2, 3, 4, 5]", unmodifiableClass); - } - - @Test - void testInlineListCreation02() { evaluate("{'abc', 'xyz'}", "[abc, xyz]", unmodifiableClass); - } - - @Test - void testInlineListCreation03() { evaluate("{}", "[]", unmodifiableClass); - } - - @Test - void testInlineListCreation04() { evaluate("{'abc'=='xyz'}", "[false]", ArrayList.class); } @Test - void testInlineListAndNesting() { + void inlineListAndNesting() { evaluate("{{1,2,3},{4,5,6}}", "[[1, 2, 3], [4, 5, 6]]", unmodifiableClass); evaluate("{{1,'2',3},{4,{'a','b'},5,6}}", "[[1, 2, 3], [4, [a, b], 5, 6]]", unmodifiableClass); } @Test - void testInlineListError() { + void inlineListError() { parseAndCheckError("{'abc'", SpelMessage.OOD); } @Test - void testRelOperatorsIs02() { + void inlineListAndInstanceofOperator() { evaluate("{1, 2, 3, 4, 5} instanceof T(java.util.List)", "true", Boolean.class); } @Test - void testInlineListCreation05() { + void inlineListAndBetweenOperatorForIntegers() { + evaluate("1 between {1,5}", "true", Boolean.class); evaluate("3 between {1,5}", "true", Boolean.class); + evaluate("5 between {1,5}", "true", Boolean.class); + evaluate("0 between {1,5}", "false", Boolean.class); + evaluate("8 between {1,5}", "false", Boolean.class); } @Test - void testInlineListCreation06() { - evaluate("8 between {1,5}", "false", Boolean.class); + void inlineListAndBetweenOperatorForStrings() { + evaluate("'a' between {'a', 'c'}", "true", Boolean.class); + evaluate("'b' between {'a', 'c'}", "true", Boolean.class); + evaluate("'c' between {'a', 'c'}", "true", Boolean.class); + evaluate("'z' between {'a', 'c'}", "false", Boolean.class); + + evaluate("'efg' between {'abc', 'xyz'}", "true", Boolean.class); + } + + @Test + void inlineListAndBetweenOperatorForBigDecimals() { + String oneToFive = "{new java.math.BigDecimal('1'),new java.math.BigDecimal('5')}"; + + evaluate("new java.math.BigDecimal('1') between " + oneToFive, "true", Boolean.class); + evaluate("new java.math.BigDecimal('3') between " + oneToFive, "true", Boolean.class); + evaluate("new java.math.BigDecimal('5') between " + oneToFive, "true", Boolean.class); + evaluate("new java.math.BigDecimal('8') between " + oneToFive, "false", Boolean.class); + } + + @Test + void inlineListAndBetweenOperatorWithNonMatchingTypes() { + evaluateAndCheckError("'abc' between {5,7}", SpelMessage.NOT_COMPARABLE, 6); } @Test @@ -108,7 +122,7 @@ class ListTests extends AbstractExpressionTests { @Test void selectionOnListWithNonBooleanSelectionCriteria() { - evaluateAndCheckError("listOfNumbersUpToTen.?['nonboolean']", RESULT_OF_SELECTION_CRITERIA_IS_NOT_BOOLEAN); + evaluateAndCheckError("listOfNumbersUpToTen.?['nonboolean']", SpelMessage.RESULT_OF_SELECTION_CRITERIA_IS_NOT_BOOLEAN); } @Test @@ -126,67 +140,33 @@ class ListTests extends AbstractExpressionTests { evaluate("new java.util.HashSet().addAll({'a','b','c'})", "true", Boolean.class); } - @Test - void testRelOperatorsBetween01() { - evaluate("32 between {32, 42}", "true", Boolean.class); + @ParameterizedTest + @CsvSource(quoteCharacter = '"', delimiterString = "->", textBlock = """ + "{1,2,3,4,5}" -> true + "{'abc'}" -> true + "{}" -> true + + "{#a,2,3}" -> false + "{1,2,Integer.valueOf(4)}" -> false + "{1,2,{#a}}" -> false + """) + void constantRepresentation(String expression, boolean isConstant) { + SpelExpression expression1 = (SpelExpression) parser.parseExpression(expression); + assertThat(expression1.getAST()).asInstanceOf(type(InlineList.class)).satisfies(inlineList -> { + if (isConstant) { + assertThat(inlineList.isConstant()).isTrue(); + } + else { + assertThat(inlineList.isConstant()).isFalse(); + } + }); } @Test - void testRelOperatorsBetween02() { - evaluate("'efg' between {'abc', 'xyz'}", "true", Boolean.class); - } - - @Test - void testRelOperatorsBetween03() { - evaluate("42 between {32, 42}", "true", Boolean.class); - } - - @Test - void testRelOperatorsBetween04() { - evaluate("new java.math.BigDecimal('1') between {new java.math.BigDecimal('1'),new java.math.BigDecimal('5')}", - "true", Boolean.class); - evaluate("new java.math.BigDecimal('3') between {new java.math.BigDecimal('1'),new java.math.BigDecimal('5')}", - "true", Boolean.class); - evaluate("new java.math.BigDecimal('5') between {new java.math.BigDecimal('1'),new java.math.BigDecimal('5')}", - "true", Boolean.class); - evaluate("new java.math.BigDecimal('8') between {new java.math.BigDecimal('1'),new java.math.BigDecimal('5')}", - "false", Boolean.class); - } - - @Test - void testRelOperatorsBetweenErrors02() { - evaluateAndCheckError("'abc' between {5,7}", SpelMessage.NOT_COMPARABLE, 6); - } - - @Test - void testConstantRepresentation1() { - checkConstantList("{1,2,3,4,5}", true); - checkConstantList("{'abc'}", true); - checkConstantList("{}", true); - checkConstantList("{#a,2,3}", false); - checkConstantList("{1,2,Integer.valueOf(4)}", false); - checkConstantList("{1,2,{#a}}", false); - } - - private void checkConstantList(String expressionText, boolean expectedToBeConstant) { - SpelExpressionParser parser = new SpelExpressionParser(); - SpelExpression expression = (SpelExpression) parser.parseExpression(expressionText); - SpelNode node = expression.getAST(); - boolean condition = node instanceof InlineList; - assertThat(condition).isTrue(); - InlineList inlineList = (InlineList) node; - if (expectedToBeConstant) { - assertThat(inlineList.isConstant()).isTrue(); - } - else { - assertThat(inlineList.isConstant()).isFalse(); - } - } - - @Test - void testInlineListWriting() { + void inlineListWriting() { // list should be unmodifiable assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() -> evaluate("{1, 2, 3, 4, 5}[0]=6", "[1, 2, 3, 4, 5]", unmodifiableClass)); } + } diff --git a/spring-expression/src/test/java/org/springframework/expression/spel/ParsingTests.java b/spring-expression/src/test/java/org/springframework/expression/spel/ParsingTests.java index e9ca2419d7..ca9c6d6df1 100644 --- a/spring-expression/src/test/java/org/springframework/expression/spel/ParsingTests.java +++ b/spring-expression/src/test/java/org/springframework/expression/spel/ParsingTests.java @@ -16,6 +16,7 @@ package org.springframework.expression.spel; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -318,6 +319,29 @@ class ParsingTests { } } + @Nested + class StringOperators { + + @Test + void stringConcatenation() { + parseCheck("'a' + 'b'", "('a' + 'b')"); + parseCheck("'hello' + ' ' + 'world'", "(('hello' + ' ') + 'world')"); + } + + @Test + void characterSubtraction() { + parseCheck("'X' - 3", "('X' - 3)"); + parseCheck("'X' - 2 - 1", "(('X' - 2) - 1)"); + } + + @Test + void stringRepeat() { + parseCheck("'abc' * 2", "('abc' * 2)"); + parseCheck("'abc' * 2 * 2", "(('abc' * 2) * 2)"); + } + + } + @Nested class MathematicalOperators { @@ -326,16 +350,6 @@ class ParsingTests { parseCheck("2+4", "(2 + 4)"); } - @Test - void mathOperatorsAddStrings() { - parseCheck("'a' + 'b'", "('a' + 'b')"); - } - - @Test - void mathOperatorsAddMultipleStrings() { - parseCheck("'hello' + ' ' + 'world'", "(('hello' + ' ') + 'world')"); - } - @Test void mathOperatorsSubtract() { parseCheck("5-4", "(5 - 4)"); @@ -355,6 +369,32 @@ class ParsingTests { void mathOperatorModulus() { parseCheck("7 % 4", "(7 % 4)"); } + + @Disabled("Disabled due to a bug in OpInc.toStringAST()") + @Test + void mathOperatorIncrementPrefix() { + parseCheck("++7", "++7"); + parseCheck("++foo", "++foo"); + } + + @Test + void mathOperatorIncrementPostfix() { + parseCheck("7++", "7++"); + parseCheck("foo++", "foo++"); + } + + @Disabled("Disabled due to a bug in OpDec.toStringAST()") + @Test + void mathOperatorDecrementPrefix() { + parseCheck("--7", "--7"); + parseCheck("--foo", "--foo"); + } + + @Test + void mathOperatorDecrementPostfix() { + parseCheck("7--", "7--"); + parseCheck("foo--", "foo--"); + } } @Nested diff --git a/spring-expression/src/test/java/org/springframework/expression/spel/SelectionAndProjectionTests.java b/spring-expression/src/test/java/org/springframework/expression/spel/SelectionAndProjectionTests.java index 3ae88937b7..bd9e9f9ecf 100644 --- a/spring-expression/src/test/java/org/springframework/expression/spel/SelectionAndProjectionTests.java +++ b/spring-expression/src/test/java/org/springframework/expression/spel/SelectionAndProjectionTests.java @@ -34,6 +34,8 @@ import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.InstanceOfAssertFactories.INTEGER; +import static org.assertj.core.api.InstanceOfAssertFactories.LIST; import static org.springframework.expression.spel.SpelMessage.INVALID_TYPE_FOR_SELECTION; import static org.springframework.expression.spel.SpelMessage.PROJECTION_NOT_SUPPORTED_ON_TYPE; import static org.springframework.expression.spel.SpelMessage.RESULT_OF_SELECTION_CRITERIA_IS_NOT_BOOLEAN; @@ -94,28 +96,21 @@ class SelectionAndProjectionTests extends AbstractExpressionTests { void selectionWithList() { Expression expression = new SpelExpressionParser().parseRaw("integers.?[#this<5]"); EvaluationContext context = new StandardEvaluationContext(new ListTestBean()); - Object value = expression.getValue(context); - assertThat(value).isInstanceOf(List.class); - List list = (List) value; - assertThat(list).containsExactly(0, 1, 2, 3, 4); + assertThat(expression.getValue(context)).asInstanceOf(LIST).containsExactly(0, 1, 2, 3, 4); } @Test void selectFirstItemInList() { Expression expression = new SpelExpressionParser().parseRaw("integers.^[#this<5]"); EvaluationContext context = new StandardEvaluationContext(new ListTestBean()); - Object value = expression.getValue(context); - assertThat(value).isInstanceOf(Integer.class); - assertThat(value).isEqualTo(0); + assertThat(expression.getValue(context)).asInstanceOf(INTEGER).isZero(); } @Test void selectLastItemInList() { Expression expression = new SpelExpressionParser().parseRaw("integers.$[#this<5]"); EvaluationContext context = new StandardEvaluationContext(new ListTestBean()); - Object value = expression.getValue(context); - assertThat(value).isInstanceOf(Integer.class); - assertThat(value).isEqualTo(4); + assertThat(expression.getValue(context)).asInstanceOf(INTEGER).isEqualTo(4); } @Test @@ -130,28 +125,21 @@ class SelectionAndProjectionTests extends AbstractExpressionTests { void selectionWithSet() { Expression expression = new SpelExpressionParser().parseRaw("integers.?[#this<5]"); EvaluationContext context = new StandardEvaluationContext(new SetTestBean()); - Object value = expression.getValue(context); - assertThat(value).isInstanceOf(List.class); - List list = (List) value; - assertThat(list).containsExactly(0, 1, 2, 3, 4); + assertThat(expression.getValue(context)).asInstanceOf(LIST).containsExactly(0, 1, 2, 3, 4); } @Test void selectFirstItemInSet() { Expression expression = new SpelExpressionParser().parseRaw("integers.^[#this<5]"); EvaluationContext context = new StandardEvaluationContext(new SetTestBean()); - Object value = expression.getValue(context); - assertThat(value).isInstanceOf(Integer.class); - assertThat(value).isEqualTo(0); + assertThat(expression.getValue(context)).asInstanceOf(INTEGER).isZero(); } @Test void selectLastItemInSet() { Expression expression = new SpelExpressionParser().parseRaw("integers.$[#this<5]"); EvaluationContext context = new StandardEvaluationContext(new SetTestBean()); - Object value = expression.getValue(context); - assertThat(value).isInstanceOf(Integer.class); - assertThat(value).isEqualTo(4); + assertThat(expression.getValue(context)).asInstanceOf(INTEGER).isEqualTo(4); } @Test @@ -159,10 +147,7 @@ class SelectionAndProjectionTests extends AbstractExpressionTests { void selectionWithIterable() { Expression expression = new SpelExpressionParser().parseRaw("integers.?[#this<5]"); EvaluationContext context = new StandardEvaluationContext(new IterableTestBean()); - Object value = expression.getValue(context); - assertThat(value).isInstanceOf(List.class); - List list = (List) value; - assertThat(list).containsExactly(0, 1, 2, 3, 4); + assertThat(expression.getValue(context)).asInstanceOf(LIST).containsExactly(0, 1, 2, 3, 4); } @Test @@ -173,26 +158,21 @@ class SelectionAndProjectionTests extends AbstractExpressionTests { assertThat(value.getClass().isArray()).isTrue(); TypedValue typedValue = new TypedValue(value); assertThat(typedValue.getTypeDescriptor().getElementTypeDescriptor().getType()).isEqualTo(Integer.class); - Integer[] array = (Integer[]) value; - assertThat(array).containsExactly(0, 1, 2, 3, 4); + assertThat((Integer[]) value).containsExactly(0, 1, 2, 3, 4); } @Test void selectFirstItemInArray() { Expression expression = new SpelExpressionParser().parseRaw("integers.^[#this<5]"); EvaluationContext context = new StandardEvaluationContext(new ArrayTestBean()); - Object value = expression.getValue(context); - assertThat(value).isInstanceOf(Integer.class); - assertThat(value).isEqualTo(0); + assertThat(expression.getValue(context)).asInstanceOf(INTEGER).isZero(); } @Test void selectLastItemInArray() { Expression expression = new SpelExpressionParser().parseRaw("integers.$[#this<5]"); EvaluationContext context = new StandardEvaluationContext(new ArrayTestBean()); - Object value = expression.getValue(context); - assertThat(value).isInstanceOf(Integer.class); - assertThat(value).isEqualTo(4); + assertThat(expression.getValue(context)).asInstanceOf(INTEGER).isEqualTo(4); } @Test @@ -203,26 +183,21 @@ class SelectionAndProjectionTests extends AbstractExpressionTests { assertThat(value.getClass().isArray()).isTrue(); TypedValue typedValue = new TypedValue(value); assertThat(typedValue.getTypeDescriptor().getElementTypeDescriptor().getType()).isEqualTo(Integer.class); - Integer[] array = (Integer[]) value; - assertThat(array).containsExactly(0, 1, 2, 3, 4); + assertThat((Integer[]) value).containsExactly(0, 1, 2, 3, 4); } @Test void selectFirstItemInPrimitiveArray() { Expression expression = new SpelExpressionParser().parseRaw("ints.^[#this<5]"); EvaluationContext context = new StandardEvaluationContext(new ArrayTestBean()); - Object value = expression.getValue(context); - assertThat(value).isInstanceOf(Integer.class); - assertThat(value).isEqualTo(0); + assertThat(expression.getValue(context)).asInstanceOf(INTEGER).isZero(); } @Test void selectLastItemInPrimitiveArray() { Expression expression = new SpelExpressionParser().parseRaw("ints.$[#this<5]"); EvaluationContext context = new StandardEvaluationContext(new ArrayTestBean()); - Object value = expression.getValue(context); - assertThat(value).isInstanceOf(Integer.class); - assertThat(value).isEqualTo(4); + assertThat(expression.getValue(context)).asInstanceOf(INTEGER).isEqualTo(4); } @Test @@ -269,10 +244,7 @@ class SelectionAndProjectionTests extends AbstractExpressionTests { Expression expression = new SpelExpressionParser().parseRaw("#testList.![wrapper.value]"); EvaluationContext context = new StandardEvaluationContext(); context.setVariable("testList", IntegerTestBean.createList()); - Object value = expression.getValue(context); - assertThat(value).isInstanceOf(List.class); - List list = (List) value; - assertThat(list).containsExactly(5, 6, 7); + assertThat(expression.getValue(context)).asInstanceOf(LIST).containsExactly(5, 6, 7); } @Test @@ -287,10 +259,7 @@ class SelectionAndProjectionTests extends AbstractExpressionTests { Expression expression = new SpelExpressionParser().parseRaw("#testList.![wrapper.value]"); EvaluationContext context = new StandardEvaluationContext(); context.setVariable("testList", IntegerTestBean.createSet()); - Object value = expression.getValue(context); - assertThat(value).isInstanceOf(List.class); - List list = (List) value; - assertThat(list).containsExactly(5, 6, 7); + assertThat(expression.getValue(context)).asInstanceOf(LIST).containsExactly(5, 6, 7); } @Test @@ -299,10 +268,7 @@ class SelectionAndProjectionTests extends AbstractExpressionTests { Expression expression = new SpelExpressionParser().parseRaw("#testList.![wrapper.value]"); EvaluationContext context = new StandardEvaluationContext(); context.setVariable("testList", IntegerTestBean.createIterable()); - Object value = expression.getValue(context); - assertThat(value).isInstanceOf(List.class); - List list = (List) value; - assertThat(list).containsExactly(5, 6, 7); + assertThat(expression.getValue(context)).asInstanceOf(LIST).containsExactly(5, 6, 7); } @Test @@ -314,8 +280,7 @@ class SelectionAndProjectionTests extends AbstractExpressionTests { assertThat(value.getClass().isArray()).isTrue(); TypedValue typedValue = new TypedValue(value); assertThat(typedValue.getTypeDescriptor().getElementTypeDescriptor().getType()).isEqualTo(Number.class); - Number[] array = (Number[]) value; - assertThat(array).containsExactly(5, 5.9f, 7); + assertThat((Number[]) value).containsExactly(5, 5.9f, 7); }