diff --git a/org.springframework.expression/src/test/java/org/springframework/expression/spel/AllTests.java b/org.springframework.expression/src/test/java/org/springframework/expression/spel/AllTests.java index 78089bb8fbb..efcac19dd5f 100644 --- a/org.springframework.expression/src/test/java/org/springframework/expression/spel/AllTests.java +++ b/org.springframework.expression/src/test/java/org/springframework/expression/spel/AllTests.java @@ -30,7 +30,9 @@ public class AllTests { TestSuite suite = new TestSuite("Spring Expression Language tests"); // $JUnit-BEGIN$ suite.addTestSuite(BooleanExpressionTests.class); + suite.addTestSuite(LiteralTests.class); suite.addTestSuite(ParsingTests.class); + suite.addTestSuite(ParserErrorMessagesTests.class); suite.addTestSuite(EvaluationTests.class); suite.addTestSuite(OperatorTests.class); suite.addTestSuite(ConstructorInvocationTests.class); diff --git a/org.springframework.expression/src/test/java/org/springframework/expression/spel/BooleanExpressionTests.java b/org.springframework.expression/src/test/java/org/springframework/expression/spel/BooleanExpressionTests.java index 88b5ec2de9b..741acef7733 100644 --- a/org.springframework.expression/src/test/java/org/springframework/expression/spel/BooleanExpressionTests.java +++ b/org.springframework.expression/src/test/java/org/springframework/expression/spel/BooleanExpressionTests.java @@ -54,4 +54,13 @@ public class BooleanExpressionTests extends ExpressionTestCase { evaluate("true and false or true", Boolean.TRUE, Boolean.class); evaluate("true and false or false", Boolean.FALSE, Boolean.class); } + + public void testBooleanErrors01() { + evaluateAndCheckError("1 or false", SpelMessages.TYPE_CONVERSION_ERROR, 0); + evaluateAndCheckError("false or 39", SpelMessages.TYPE_CONVERSION_ERROR, 9); + evaluateAndCheckError("true and 'hello'", SpelMessages.TYPE_CONVERSION_ERROR, 9); + evaluateAndCheckError(" 'hello' and 'goodbye'", SpelMessages.TYPE_CONVERSION_ERROR, 1); + evaluateAndCheckError("!35", SpelMessages.TYPE_CONVERSION_ERROR, 1); + evaluateAndCheckError("! 'foob'", SpelMessages.TYPE_CONVERSION_ERROR, 2); + } } diff --git a/org.springframework.expression/src/test/java/org/springframework/expression/spel/EvaluationTests.java b/org.springframework.expression/src/test/java/org/springframework/expression/spel/EvaluationTests.java index 0131fbf8a7d..41f06a2110b 100644 --- a/org.springframework.expression/src/test/java/org/springframework/expression/spel/EvaluationTests.java +++ b/org.springframework.expression/src/test/java/org/springframework/expression/spel/EvaluationTests.java @@ -27,98 +27,6 @@ import org.springframework.expression.spel.ast.Lambda; */ public class EvaluationTests extends ExpressionTestCase { - // literals: boolean, integer, string, hex, real, null, date - public void testLiteralBoolean01() { - evaluate("false", "false", Boolean.class); - } - - public void testLiteralBoolean02() { - evaluate("true", "true", Boolean.class); - } - - public void testLiteralInteger01() { - evaluate("1", "1", Integer.class); - } - - public void testLiteralInteger02() { - evaluate("1415", "1415", Integer.class); - } - - public void testLiteralString01() { - evaluate("'Hello World'", "Hello World", String.class); - } - - public void testLiteralString02() { - evaluate("'joe bloggs'", "joe bloggs", String.class); - } - - public void testLiteralString03() { - evaluate("'hello'", "hello", String.class); - } - - public void testLiteralString04() { - evaluate("'Tony''s Pizza'", "Tony's Pizza", String.class); - } - - public void testLiteralString05() { - evaluate("\"Hello World\"", "Hello World", String.class); - } - - public void testLiteralString06() { - evaluate("\"Hello ' World\"", "Hello ' World", String.class); - } - - public void testLiteralHex01() { - evaluate("0x7FFFFFFF", "2147483647", Integer.class); - } - - public void testLiteralReal01() { - evaluate("6.0221415E+23", "6.0221415E23", Double.class); - } - - public void testLiteralNull01() { - evaluate("null", null, null); - } - - // TODO 3 'default' format for date varies too much, we need to standardize on a format for EL - // public void testLiteralDate01() { - // eval("date('3-Feb-2008 4:50:20 PM').getTime()>0", "true", Boolean.class); - // } - - public void testLiteralDate02() { - evaluate("date('19740824131030','yyyyMMddHHmmss').getHours()", "13", Integer.class); - } - - // boolean operators: and or not - public void testBooleanOperators01() { - evaluate("false or false", "false", Boolean.class); - } - - public void testBooleanOperators02() { - evaluate("false or true", "true", Boolean.class); - } - - public void testBooleanOperators03() { - evaluate("true or false", "true", Boolean.class); - } - - public void testBooleanOperators04() { - evaluate("true or true", "true", Boolean.class); - } - - public void testBooleanOperators05() { - evaluate("false or true and false", "false", Boolean.class); - } - - public void testBooleanErrors01() { - evaluateAndCheckError("1 or false", SpelMessages.TYPE_CONVERSION_ERROR, 0); - evaluateAndCheckError("false or 39", SpelMessages.TYPE_CONVERSION_ERROR, 9); - evaluateAndCheckError("true and 'hello'", SpelMessages.TYPE_CONVERSION_ERROR, 9); - evaluateAndCheckError(" 'hello' and 'goodbye'", SpelMessages.TYPE_CONVERSION_ERROR, 1); - evaluateAndCheckError("!35", SpelMessages.TYPE_CONVERSION_ERROR, 1); - evaluateAndCheckError("! 'foob'", SpelMessages.TYPE_CONVERSION_ERROR, 2); - } - // relational operators: lt, le, gt, ge, eq, ne public void testRelOperatorGT01() { evaluate("3 > 6", "false", Boolean.class); @@ -246,14 +154,16 @@ public class EvaluationTests extends ExpressionTestCase { // property access public void testPropertyField01() { - eval("name", "Nikola Tesla", String.class, false); // not writable because (1) name is private (2) there is no setter, only a getter + evaluate("name", "Nikola Tesla", String.class, false); // not writable because (1) name is private (2) there is + // no + // setter, only a getter evaluateAndCheckError("madeup", SpelMessages.PROPERTY_OR_FIELD_NOT_FOUND, 0, "madeup", "org.springframework.expression.spel.testresources.Inventor"); } // nested properties public void testPropertiesNested01() { - eval("placeOfBirth.city", "SmilJan", String.class, true); + evaluate("placeOfBirth.city", "SmilJan", String.class, true); } public void testPropertiesNested02() { @@ -284,7 +194,8 @@ public class EvaluationTests extends ExpressionTestCase { } public void testInlineMapCreation02() { - evaluate("#{1:'January', 2:'February', 3:'March'}.size()", 3, Integer.class);//"{2=February, 1=January, 3=March}", HashMap.class); + evaluate("#{1:'January', 2:'February', 3:'March'}.size()", 3, Integer.class);// "{2=February, 1=January, + // 3=March}", HashMap.class); } public void testInlineMapCreation03() { @@ -444,7 +355,7 @@ public class EvaluationTests extends ExpressionTestCase { } public void testAssignmentToVariables02() { - eval("(#var1='value1';#var1)", "value1", String.class, true); + evaluate("(#var1='value1';#var1)", "value1", String.class, true); } // Property setting @@ -480,11 +391,11 @@ public class EvaluationTests extends ExpressionTestCase { // Bean references public void testReferences01() { - eval("@(apple).name", "Apple", String.class, true); + evaluate("@(apple).name", "Apple", String.class, true); } public void testReferences02() { - eval("@(fruits:banana).name", "Banana", String.class, true); + evaluate("@(fruits:banana).name", "Banana", String.class, true); } public void testReferences03() { @@ -492,7 +403,7 @@ public class EvaluationTests extends ExpressionTestCase { } // null - no context, a.b.c treated as name public void testReferences05() { - eval("@(a/b/c:orange).name", "Orange", String.class, true); + evaluate("@(a/b/c:orange).name", "Orange", String.class, true); } // TODO 4 automatic/default imports for next line? @@ -507,11 +418,11 @@ public class EvaluationTests extends ExpressionTestCase { public void testReferences07() { evaluate("@(apple).color.getRGB().equals(T(java.awt.Color).green.getRGB())", "true", Boolean.class); } - -// value is not public, it is accessed through getRGB() -// public void testStaticRef01() { -// evaluate("T(Color).green.value!=0", "true", Boolean.class); -// } + + // value is not public, it is accessed through getRGB() + // public void testStaticRef01() { + // evaluate("T(Color).green.value!=0", "true", Boolean.class); + // } public void testStaticRef02() { evaluate("T(Color).green.getRGB()!=0", "true", Boolean.class); @@ -519,7 +430,7 @@ public class EvaluationTests extends ExpressionTestCase { // variables and functions public void testVariableAccess01() { - eval("#answer", "42", Integer.class, true); + evaluate("#answer", "42", Integer.class, true); } public void testFunctionAccess01() { @@ -536,7 +447,7 @@ public class EvaluationTests extends ExpressionTestCase { } public void testLambdaNoArgsReferenced() { - eval("(#fn={|| false };#fn)", "{|| false }", Lambda.class, true); + evaluate("(#fn={|| false };#fn)", "{|| false }", Lambda.class, true); } public void testLambda01() { @@ -612,8 +523,8 @@ public class EvaluationTests extends ExpressionTestCase { } public void testVariableReferences() { - eval("(#answer=42;#answer)", "42", Integer.class, true); - eval("($answer=42;$answer)", "42", Integer.class, true); + evaluate("(#answer=42;#answer)", "42", Integer.class, true); + evaluate("($answer=42;$answer)", "42", Integer.class, true); } // type references @@ -636,15 +547,15 @@ public class EvaluationTests extends ExpressionTestCase { evaluate("T(String)", "class java.lang.String", Class.class); } - public void testStringType() { - evaluateAndAskForReturnType("getPlaceOfBirth().getCity()", "SmilJan", String.class); + public void testStringType() { + evaluateAndAskForReturnType("getPlaceOfBirth().getCity()", "SmilJan", String.class); + } + + public void testNumbers01() { + evaluateAndAskForReturnType("3*4+5", 17, Integer.class); + evaluateAndAskForReturnType("3*4+5", 17L, Long.class); + evaluateAndAskForReturnType("65", 'A', Character.class); + evaluateAndAskForReturnType("3*4+5", (short) 17, Short.class); + evaluateAndAskForReturnType("3*4+5", "17", String.class); } - - public void testNumbers01() { - evaluateAndAskForReturnType("3*4+5",17,Integer.class); - evaluateAndAskForReturnType("3*4+5",17L,Long.class); - evaluateAndAskForReturnType("65",'A',Character.class); - evaluateAndAskForReturnType("3*4+5",(short)17,Short.class); - evaluateAndAskForReturnType("3*4+5","17",String.class); - } } diff --git a/org.springframework.expression/src/test/java/org/springframework/expression/spel/ExpressionTestCase.java b/org.springframework.expression/src/test/java/org/springframework/expression/spel/ExpressionTestCase.java index 55c3065de39..5546354db6c 100644 --- a/org.springframework.expression/src/test/java/org/springframework/expression/spel/ExpressionTestCase.java +++ b/org.springframework.expression/src/test/java/org/springframework/expression/spel/ExpressionTestCase.java @@ -140,7 +140,8 @@ public abstract class ExpressionTestCase extends TestCase { * @param expectedClassOfResult the expected class of the evaluation result * @param shouldBeWritable should the parsed expression be writable? */ - public void eval(String expression, Object expectedValue, Class expectedClassOfResult, boolean shouldBeWritable) { + public void evaluate(String expression, Object expectedValue, Class expectedClassOfResult, + boolean shouldBeWritable) { try { SpelExpression e = parser.parseExpression(expression); if (e == null) { @@ -191,13 +192,33 @@ public abstract class ExpressionTestCase extends TestCase { * @param otherProperties The expected inserts within the message */ protected void evaluateAndCheckError(String expression, SpelMessages expectedMessage, Object... otherProperties) { + evaluateAndCheckError(expression, null, expectedMessage, otherProperties); + } + + /** + * Evaluate the specified expression and ensure the expected message comes out. The message may have inserts and + * they will be checked if otherProperties is specified. The first entry in otherProperties should always be the + * position. + * @param expression The expression to evaluate + * @param expectedReturnType Ask the expression return value to be of this type if possible (null indicates don't + * ask for conversion) + * @param expectedMessage The expected message + * @param otherProperties The expected inserts within the message + */ + protected void evaluateAndCheckError(String expression, Class expectedReturnType, SpelMessages expectedMessage, + Object... otherProperties) { try { Expression expr = parser.parseExpression(expression); if (expr == null) { fail("Parser returned null for expression"); } - @SuppressWarnings("unused") - Object value = expr.getValue(eContext); + if (expectedReturnType != null) { + @SuppressWarnings("unused") + Object value = expr.getValue(eContext, expectedReturnType); + } else { + @SuppressWarnings("unused") + Object value = expr.getValue(eContext); + } fail("Should have failed with message " + expectedMessage); } catch (EvaluationException ee) { SpelException ex = (SpelException) ee; @@ -236,9 +257,56 @@ public abstract class ExpressionTestCase extends TestCase { } } + /** + * Parse the specified expression and ensure the expected message comes out. The message may have inserts and they + * will be checked if otherProperties is specified. The first entry in otherProperties should always be the + * position. + * @param expression The expression to evaluate + * @param expectedMessage The expected message + * @param otherProperties The expected inserts within the message + */ + protected void parseAndCheckError(String expression, SpelMessages expectedMessage, Object... otherProperties) { + try { + Expression expr = parser.parseExpression(expression); + fail("Parsing should have failed!"); + } catch (ParseException pe) { + SpelException ex = (SpelException) pe.getCause(); + if (ex.getMessageUnformatted() != expectedMessage) { + System.out.println(ex.getMessage()); + ex.printStackTrace(); + assertEquals("Failed to get expected message", expectedMessage, ex.getMessageUnformatted()); + } + if (otherProperties != null && otherProperties.length != 0) { + // first one is expected position of the error within the string + int pos = ((Integer) otherProperties[0]).intValue(); + assertEquals("Did not get correct position reported in error ", pos, ex.getPosition()); + if (otherProperties.length > 1) { + // Check inserts match + Object[] inserts = ex.getInserts(); + if (inserts == null) { + inserts = new Object[0]; + } + if (inserts.length < otherProperties.length - 1) { + ex.printStackTrace(); + fail("Cannot check " + (otherProperties.length - 1) + + " properties of the exception, it only has " + inserts.length + " inserts"); + } + for (int i = 1; i < otherProperties.length; i++) { + if (!inserts[i - 1].equals(otherProperties[i])) { + ex.printStackTrace(); + fail("Insert does not match, expected '" + otherProperties[i] + "' but insert value was '" + + inserts[i - 1] + "'"); + } + } + } + } + } + } + public static String stringValueOf(Object value) { // do something nice for arrays - if (value==null) return "null"; + if (value == null) + return "null"; if (value.getClass().isArray()) { StringBuilder sb = new StringBuilder(); if (value.getClass().getComponentType().isPrimitive()) { diff --git a/org.springframework.expression/src/test/java/org/springframework/expression/spel/LiteralTests.java b/org.springframework.expression/src/test/java/org/springframework/expression/spel/LiteralTests.java new file mode 100644 index 00000000000..91471a74028 --- /dev/null +++ b/org.springframework.expression/src/test/java/org/springframework/expression/spel/LiteralTests.java @@ -0,0 +1,109 @@ +/* + * Copyright 2004-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.expression.spel; + +/** + * Tests the evaluation of basic literals: boolean, integer, hex integer, long, real, null, date + * + * @author Andy Clement + */ +public class LiteralTests extends ExpressionTestCase { + + public void testLiteralBoolean01() { + evaluate("false", "false", Boolean.class); + } + + public void testLiteralBoolean02() { + evaluate("true", "true", Boolean.class); + } + + public void testLiteralInteger01() { + evaluate("1", "1", Integer.class); + } + + public void testLiteralInteger02() { + evaluate("1415", "1415", Integer.class); + } + + public void testLiteralString01() { + evaluate("'Hello World'", "Hello World", String.class); + } + + public void testLiteralString02() { + evaluate("'joe bloggs'", "joe bloggs", String.class); + } + + public void testLiteralString03() { + evaluate("'hello'", "hello", String.class); + } + + public void testLiteralString04() { + evaluate("'Tony''s Pizza'", "Tony's Pizza", String.class); + } + + public void testLiteralString05() { + evaluate("\"Hello World\"", "Hello World", String.class); + } + + public void testLiteralString06() { + evaluate("\"Hello ' World\"", "Hello ' World", String.class); + } + + public void testHexIntLiteral01() { + evaluate("0x7FFFF", "524287", Integer.class); + evaluate("0x7FFFFL", 524287L, Long.class); + evaluate("0X7FFFF", "524287", Integer.class); + evaluate("0X7FFFFl", 524287L, Long.class); + } + + public void testLongIntLiteral01() { + evaluate("0xCAFEBABEL", 3405691582L, Long.class); + } + + public void testLongIntInteractions01() { + evaluate("0x20 * 2L", 64L, Long.class); + // ask for the result to be made into an Integer + evaluateAndAskForReturnType("0x20 * 2L", 64, Integer.class); + // ask for the result to be made into an Integer knowing that it will not fit + evaluateAndCheckError("0x1220 * 0xffffffffL", Integer.class, SpelMessages.PROBLEM_DURING_TYPE_CONVERSION, -1, + "long value '19928648248800' cannot be represented as an int"); + } + + public void testSignedIntLiterals() { + evaluate("-1", -1, Integer.class); + evaluate("-0xa", -10, Integer.class); + evaluate("-1L", -1L, Long.class); + evaluate("-0x20l", -32L, Long.class); + } + + public void testLiteralReal01() { + evaluate("6.0221415E+23", "6.0221415E23", Double.class); + } + + public void testLiteralNull01() { + evaluate("null", null, null); + } + + // TODO 3 'default' format for date varies too much, we need to standardize on a format for EL + // public void testLiteralDate01() { + // eval("date('3-Feb-2008 4:50:20 PM').getTime()>0", "true", Boolean.class); + // } + + public void testLiteralDate02() { + evaluate("date('19740824131030','yyyyMMddHHmmss').getHours()", "13", Integer.class); + } + +} diff --git a/org.springframework.expression/src/test/java/org/springframework/expression/spel/ParserErrorMessagesTests.java b/org.springframework.expression/src/test/java/org/springframework/expression/spel/ParserErrorMessagesTests.java new file mode 100644 index 00000000000..becd8c412dc --- /dev/null +++ b/org.springframework.expression/src/test/java/org/springframework/expression/spel/ParserErrorMessagesTests.java @@ -0,0 +1,39 @@ +/* + * Copyright 2004-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.expression.spel; + +/** + * Tests the messages and exceptions that come out for badly formed expressions + * + * @author Andy Clement + */ +public class ParserErrorMessagesTests extends ExpressionTestCase { + + public void testBrokenExpression01() { + // Expression: 0xCAFEBABE - too big to be processed as an int, needs the L suffix + parseAndCheckError("0xCAFEBABE", SpelMessages.NOT_AN_INTEGER); + } + + // parseCheck("true or "); + // parseCheck("tru or false"); + // parseCheck("1 + "); + // parseCheck("0xCAFEBABEG"); + // TODO 3 too many close brackets - parser recover + // public void testExpressionLists07a() { parseCheck("((3;4;)+(5;6;)))","((3;4) + // + (5;6))");} + // } + // --- +} diff --git a/org.springframework.expression/src/test/java/org/springframework/expression/spel/ParsingTests.java b/org.springframework.expression/src/test/java/org/springframework/expression/spel/ParsingTests.java index 37bcc84d636..1d896a5a067 100644 --- a/org.springframework.expression/src/test/java/org/springframework/expression/spel/ParsingTests.java +++ b/org.springframework.expression/src/test/java/org/springframework/expression/spel/ParsingTests.java @@ -29,6 +29,7 @@ public class ParsingTests extends TestCase { private SpelExpressionParser parser; + @Override public void setUp() { parser = new SpelExpressionParser(); } @@ -37,9 +38,9 @@ public class ParsingTests extends TestCase { public void testLiteralBoolean01() { parseCheck("false"); } - + public void testLiteralLong01() { - parseCheck("37L","37"); + parseCheck("37L", "37"); } public void testLiteralBoolean02() { @@ -413,20 +414,6 @@ public class ParsingTests extends TestCase { parseCheck("((3;4;)+(5;6;))", "((3;4) + (5;6))"); } - // TODO 3 too many close brackets - parser recover - // public void testExpressionLists07a() { parseCheck("((3;4;)+(5;6;)))","((3;4) - // + (5;6))");} - - - // parser warnings/errors -// public void testBrokenExpression01() { -// parseCheck("1 + "); -// -// } - - - // --- - /** * Parse the supplied expression and then create a string representation of the resultant AST, it should be the same * as the original expression.