You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by he...@apache.org on 2025/09/01 13:00:18 UTC
(commons-jexl) branch master updated: JEXL-440: moving tests in dedicated class; - refined parsing exception error reporting;
This is an automated email from the ASF dual-hosted git repository.
henrib pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-jexl.git
The following commit(s) were added to refs/heads/master by this push:
new 8461d83e JEXL-440: moving tests in dedicated class; - refined parsing exception error reporting;
8461d83e is described below
commit 8461d83ed53b90637c94c29492cc5dd72f839d84
Author: Henrib <hb...@gmail.com>
AuthorDate: Mon Sep 1 15:00:08 2025 +0200
JEXL-440: moving tests in dedicated class;
- refined parsing exception error reporting;
---
.../apache/commons/jexl3/internal/Interpreter.java | 15 +-
.../org/apache/commons/jexl3/parser/Parser.jjt | 6 +-
.../org/apache/commons/jexl3/Issues400Test.java | 75 +--------
.../java/org/apache/commons/jexl3/JexlTest.java | 29 ++--
.../java/org/apache/commons/jexl3/SwitchTest.java | 174 +++++++++++++++++++++
5 files changed, 199 insertions(+), 100 deletions(-)
diff --git a/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java b/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
index 486e2e12..4046f761 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
@@ -977,7 +977,7 @@ public class Interpreter extends InterpreterBase {
* Short-circuit evaluation of logical expression.
* @param check the fuse value that will stop evaluation, true for OR, false for AND
* @param node a ASTAndNode or a ASTOrNode
- * @param data
+ * @param data the data, usually null and unused
* @return true or false if boolean logical option is true, the last evaluated argument otherwise
*/
private Object shortCircuit(final boolean check, final JexlNode node, final Object data) {
@@ -2016,7 +2016,13 @@ public class Interpreter extends InterpreterBase {
protected Object visit(final ASTSwitchExpression node, final Object data) {
final Object value = node.jjtGetChild(0).jjtAccept(this, data);
final int index = node.switchIndex(value);
- return index >= 0 ? node.jjtGetChild(index).jjtAccept(this, data) : null;
+ if (index >= 0) {
+ return node.jjtGetChild(index).jjtAccept(this, data);
+ }
+ if (isStrictEngine()) {
+ throw new JexlException(node, "no case in switch expression for: " + value);
+ }
+ return null;
}
@Override
@@ -2037,6 +2043,9 @@ public class Interpreter extends InterpreterBase {
}
return value;
}
+ if (isStrictEngine()) {
+ throw new JexlException(node, "no case in switch statement for: " + value);
+ }
return null;
}
@@ -2288,7 +2297,7 @@ public class Interpreter extends InterpreterBase {
/**
* Runs a node.
* @param node the node
- * @param data the usual data
+ * @param data the usual data, always null
* @return the return value
*/
protected Object visitLexicalNode(final JexlNode node, final Object data) {
diff --git a/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt b/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt
index 5537abc9..1e11a37b 100644
--- a/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt
+++ b/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt
@@ -92,11 +92,7 @@ public final class Parser extends JexlParser
throw new JexlException.Tokenization(info, xtme).clean();
} catch (ParseException xparse) {
Token errortok = errorToken(jj_lastpos, jj_scanpos, token.next, token);
- String msg = xparse.getMessage();
- if (msg.endsWith(";")) {
- msg = errortok.image;
- }
- throw new JexlException.Parsing(info.at(errortok.beginLine, errortok.beginColumn), msg).clean();
+ throw new JexlException.Parsing(info.at(errortok.beginLine, errortok.beginColumn), errortok.image).clean();
} finally {
token_source.defaultLexState = DEFAULT;
cleanup(previous);
diff --git a/src/test/java/org/apache/commons/jexl3/Issues400Test.java b/src/test/java/org/apache/commons/jexl3/Issues400Test.java
index efc84ae4..3c5eb7ad 100644
--- a/src/test/java/org/apache/commons/jexl3/Issues400Test.java
+++ b/src/test/java/org/apache/commons/jexl3/Issues400Test.java
@@ -192,7 +192,7 @@ public class Issues400Test {
final Map<String, Object> b = Collections.singletonMap("c", 42);
final Map<String, Object> a = Collections.singletonMap("b", b);
JexlScript script;
- Object result = -42;
+ Object result;
script = jexl.createScript("a?['B']?['C']", "a");
result = script.execute(null, a);
assertEquals(script.getSourceText(), script.getParsedText());
@@ -769,79 +769,6 @@ public class Issues400Test {
Assertions.assertFalse((boolean) sqle.createScript("a != 25", "a").execute(null, 25));
}
- @Test
- void test440a() {
- JexlFeatures f = JexlFeatures.createDefault().ambiguousStatement(true);
- JexlEngine jexl = new JexlBuilder().features(f).safe(false).strict(true).create();
- String src = "let y = switch (x) { case 10,11 -> 3 case 20, 21 -> 4\n" +
- "default -> { let z = 4; z + x } } y";
- JexlScript script = jexl.createScript(src, "x");
- assertNotNull(script);
- String dbgStr = script.getParsedText();
- assertNotNull(dbgStr);
-
- Object result = script.execute(null, 10);
- Assertions.assertEquals(3, result);
- result = script.execute(null, 11);
- Assertions.assertEquals(3, result);
- result = script.execute(null, 20);
- Assertions.assertEquals(4, result);
- result = script.execute(null, 21);
- Assertions.assertEquals(4, result);
- result = script.execute(null, 38);
- Assertions.assertEquals(42, result);
- src = "let y = switch (x) { case 10,11 -> break; case 20, 21 -> 4; } y";
- try {
- script = jexl.createScript(src, "x");
- fail("should not be able to create script with break in switch");
- } catch (JexlException.Parsing xparse) {
- assertTrue(xparse.getMessage().contains("break"));
- }
- assertNotNull(script);
- }
- @Test
- void test440b() {
- JexlEngine jexl = new JexlBuilder().safe(false).strict(true).create();
- final String src =
-"switch (x) { case 10 : return 3; case 20 : case 21 : return 4; case 32: break; default : return x + 4; } 169";
- final JexlScript script = jexl.createScript(src, "x");
- assertNotNull(script);
- String dbgStr = script.getParsedText();
- assertNotNull(dbgStr);
-
- Object result = script.execute(null, 10);
- Assertions.assertEquals(3, result);
- result = script.execute(null, 20);
- Assertions.assertEquals(4, result);
- result = script.execute(null, 21);
- Assertions.assertEquals(4, result);
- result = script.execute(null, 32);
- Assertions.assertEquals(169, result);
- result = script.execute(null, 38);
- Assertions.assertEquals(42, result);
- }
-
- public enum Scope440 {
- UNDEFINED, UNDECLARED, GLOBAL, LOCAL, THIS, SUPER;
- }
-
- @Test
- void test440c() {
- JexlEngine jexl = new JexlBuilder().loader(getClass().getClassLoader()).imports(this.getClass().getName()).create();
- final String src = "let s = switch (x) { case Scope440.UNDEFINED -> 'undefined'; case Scope440.THIS -> 'this'; default -> 'OTHER'; } s";
- final JexlScript script = jexl.createScript(src, "x");
- assertNotNull(script);
- String dbgStr = script.getParsedText();
- assertNotNull(dbgStr);
-
- Object result = script.execute(null, Scope440.UNDEFINED);
- Assertions.assertEquals("undefined", result);
- result = script.execute(null, Scope440.THIS);
- Assertions.assertEquals("this", result);
- result = script.execute(null, 21);
- Assertions.assertEquals("OTHER", result);
- }
-
@Test
void testIssue441() {
JexlEngine jexl = new JexlBuilder().create();
diff --git a/src/test/java/org/apache/commons/jexl3/JexlTest.java b/src/test/java/org/apache/commons/jexl3/JexlTest.java
index 0b94a95f..35de2b5b 100644
--- a/src/test/java/org/apache/commons/jexl3/JexlTest.java
+++ b/src/test/java/org/apache/commons/jexl3/JexlTest.java
@@ -92,7 +92,7 @@ public final class JexlTest extends JexlTestCase {
* Asserts that the given expression returns the given value when applied to the
* given context
*/
- protected void assertExpression(final JexlContext jc, final String expression, final Object expected) {
+ private void assertExpression(final JexlContext jc, final String expression, final Object expected) {
final JexlExpression e = JEXL.createExpression(expression);
final Object actual = e.evaluate(jc);
assertEquals(expected, actual, expression);
@@ -136,7 +136,6 @@ public final class JexlTest extends JexlTestCase {
/**
* Test assignment.
- * @throws Exception
*/
@Test
void testAssignment() {
@@ -151,14 +150,12 @@ public final class JexlTest extends JexlTestCase {
assertEquals("world", jc.get("hello"), "hello variable not changed");
assertExpression(jc, "result = 1 + 1", Integer.valueOf(2));
assertEquals(Integer.valueOf(2), jc.get("result"), "result variable not changed");
- // todo: make sure properties can be assigned to, fall back to flat var if no property
- // assertExpression(jc, "foo.property1 = '99'", "99");
- // assertEquals("property not set", "99", foo.getProperty1());
+ assertExpression(jc, "foo.property1 = '99'", "99");
+ assertEquals("99", foo.getProperty1(), "property not set");
}
/**
* Make sure bad syntax throws ParseException
- * @throws Exception on errors
*/
@Test
void testBadParse() {
@@ -196,7 +193,6 @@ public final class JexlTest extends JexlTestCase {
/**
* Test that 'and' only evaluates the second item if needed
- * @throws Exception if there are errors
*/
@Test
void testBooleanShortCircuitAnd() {
@@ -218,7 +214,6 @@ public final class JexlTest extends JexlTestCase {
/**
* Test that 'or' only evaluates the second item if needed
- * @throws Exception if there are errors
*/
@Test
void testBooleanShortCircuitOr() {
@@ -255,7 +250,7 @@ public final class JexlTest extends JexlTestCase {
assertExpression(jc, "stringy + 2", "thingy2");
/*
- * test new null coersion
+ * test new null coercion
*/
jc.set("imanull", null);
assertExpression(jc, "imanull + 2", Integer.valueOf(2));
@@ -267,7 +262,7 @@ public final class JexlTest extends JexlTestCase {
}
/**
- * Attempts to recreate bug https://jira.werken.com/ViewIssue.jspa?key=JELLY-8
+ * Attempts to recreate bug <a href="https://jira.werken.com/ViewIssue.jspa?key=JELLY-8">...</a>
*/
@Test
void testCharAtBug() {
@@ -284,7 +279,7 @@ public final class JexlTest extends JexlTestCase {
}
@Test
- void testCoercionWithComparisionOperators() {
+ void testCoercionWithComparisonOperators() {
final JexlContext jc = new MapContext();
assertExpression(jc, "'2' > 1", Boolean.TRUE);
@@ -304,7 +299,6 @@ public final class JexlTest extends JexlTestCase {
/**
* Test the ## comment in a string
- * @throws Exception
*/
@Test
void testComment() {
@@ -414,7 +408,7 @@ public final class JexlTest extends JexlTestCase {
jc.set("longstring", "thingthing");
/*
- * I can't believe anyone thinks this is a syntax.. :)
+ * I can't believe anyone thinks this is a syntax... :)
*/
assertExpression(jc, "empty nullthing", Boolean.TRUE);
assertExpression(jc, "empty string", Boolean.TRUE);
@@ -454,7 +448,7 @@ public final class JexlTest extends JexlTestCase {
jc.set("b", Boolean.FALSE);
jc.set("num", Integer.valueOf(5));
jc.set("now", Calendar.getInstance().getTime());
- final GregorianCalendar gc = new GregorianCalendar(5000, 11, 20);
+ final GregorianCalendar gc = new GregorianCalendar(5000, Calendar.DECEMBER, 20);
jc.set("now2", gc.getTime());
jc.set("bdec", new BigDecimal("7"));
jc.set("bint", new BigInteger("7"));
@@ -513,7 +507,7 @@ public final class JexlTest extends JexlTestCase {
void testIntProperty() {
final Foo foo = new Foo();
- // lets check the square function first.
+ // let's check the square function first.
assertEquals(4, foo.square(2));
assertEquals(4, foo.square(-2));
@@ -574,7 +568,7 @@ public final class JexlTest extends JexlTestCase {
}
/**
- * test the new function e.g constructor invocation
+ * Test the new function e.g. constructor invocation.
*/
@Test
void testNew() {
@@ -762,8 +756,7 @@ public final class JexlTest extends JexlTestCase {
}
/**
- * Simple test of '+' as a string concatenation operator
- * @throws Exception
+ * Simple test of '+' as a string concatenation operator.
*/
@Test
void testStringConcatenation() {
diff --git a/src/test/java/org/apache/commons/jexl3/SwitchTest.java b/src/test/java/org/apache/commons/jexl3/SwitchTest.java
new file mode 100644
index 00000000..986bc477
--- /dev/null
+++ b/src/test/java/org/apache/commons/jexl3/SwitchTest.java
@@ -0,0 +1,174 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * https://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.apache.commons.jexl3;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+/**
+ * Tests switch/case statement and expression.
+ */
+@SuppressWarnings({"AssertEqualsBetweenInconvertibleTypes"})
+public class SwitchTest extends JexlTestCase {
+
+ public SwitchTest() {
+ super("SwitchTest");
+ }
+
+ @Test
+ void testSwitchExpression() {
+ JexlEngine jexl = new JexlBuilder().safe(false).strict(true).create();
+ final JexlScript e = jexl.createScript("var j = switch(i) { case 1 -> 2; case 2 -> 3; default -> 4; }; j", "i");
+ final JexlContext jc = new MapContext();
+ final Object o = e.execute(jc, 1);
+ assertEquals(2, o);
+ }
+
+ @Test
+ void testBrokenSwitchExpression0() {
+ JexlEngine jexl = new JexlBuilder().safe(false).strict(true).create();
+ try {
+ final JexlScript e = jexl.createScript("var j = switch(i) { case 1 -> return 2; }; j", "i");
+ fail("should not be able to create script with return in switch expression");
+ } catch (JexlException.Parsing xparse) {
+ assertTrue(xparse.getMessage().contains("return"));
+ }
+ try {
+ final JexlScript e = jexl.createScript("var j = switch(i) { case 1 -> break; }; j", "i");
+ fail("should not be able to create script with break in switch expression");
+ } catch (JexlException.Parsing xparse) {
+ assertTrue(xparse.getMessage().contains("break"));
+ }
+ }
+
+ @Test
+ void testSwitchStatement() {
+ JexlEngine jexl = new JexlBuilder().safe(false).strict(true).create();
+ final JexlScript e = jexl.createScript("switch(i) { case 1: i += 2; case 2: i += 3; default: i += 4; }; i + 33", "i");
+ final JexlContext jc = new MapContext();
+ final Object o = e.execute(jc, 2);
+ assertEquals(42, o);
+ }
+
+ @Test
+ void test440a() {
+ JexlFeatures f = JexlFeatures.createDefault().ambiguousStatement(true);
+ JexlEngine jexl = new JexlBuilder().features(f).safe(false).strict(true).create();
+ String src =
+ "let y = switch (x) { case 10,11 -> 3 case 20, 21 -> 4\n" + "default -> { let z = 4; z + x } } y";
+ JexlScript script = jexl.createScript(src, "x");
+ assertNotNull(script);
+ String dbgStr = script.getParsedText();
+ assertNotNull(dbgStr);
+
+ Object result = script.execute(null, 10);
+ Assertions.assertEquals(3, result);
+ result = script.execute(null, 11);
+ Assertions.assertEquals(3, result);
+ result = script.execute(null, 20);
+ Assertions.assertEquals(4, result);
+ result = script.execute(null, 21);
+ Assertions.assertEquals(4, result);
+ result = script.execute(null, 38);
+ Assertions.assertEquals(42, result);
+ src = "let y = switch (x) { case 10,11 -> break; case 20, 21 -> 4; } y";
+ try {
+ script = jexl.createScript(src, "x");
+ fail("should not be able to create script with break in switch");
+ } catch (JexlException.Parsing xparse) {
+ assertTrue(xparse.getMessage().contains("break"));
+ }
+ assertNotNull(script);
+ }
+
+ @Test
+ void test440b() {
+ JexlEngine jexl = new JexlBuilder().safe(false).strict(true).create();
+ final String src =
+ "switch (x) { case 10 : return 3; case 20 : case 21 : return 4; case 32: break; default : return x + 4; } 169";
+ final JexlScript script = jexl.createScript(src, "x");
+ assertNotNull(script);
+ String dbgStr = script.getParsedText();
+ assertNotNull(dbgStr);
+
+ Object result = script.execute(null, 10);
+ Assertions.assertEquals(3, result);
+ result = script.execute(null, 20);
+ Assertions.assertEquals(4, result);
+ result = script.execute(null, 21);
+ Assertions.assertEquals(4, result);
+ result = script.execute(null, 32);
+ Assertions.assertEquals(169, result);
+ result = script.execute(null, 38);
+ Assertions.assertEquals(42, result);
+ }
+
+ public enum Scope440 {
+ UNDEFINED, UNDECLARED, GLOBAL, LOCAL, THIS, SUPER;
+ }
+
+ @Test
+ void test440c() {
+ JexlEngine jexl = new JexlBuilder().loader(getClass().getClassLoader()).imports(this.getClass().getName()).create();
+ final String src =
+ "let s = switch (x) { case Scope440.UNDEFINED -> 'undefined'; case Scope440.THIS -> 'this'; default -> 'OTHER'; } s";
+ final JexlScript script = jexl.createScript(src, "x");
+ assertNotNull(script);
+ String dbgStr = script.getParsedText();
+ assertNotNull(dbgStr);
+
+ Object result = script.execute(null, Scope440.UNDEFINED);
+ Assertions.assertEquals("undefined", result);
+ result = script.execute(null, Scope440.THIS);
+ Assertions.assertEquals("this", result);
+ result = script.execute(null, 21);
+ Assertions.assertEquals("OTHER", result);
+ }
+
+ @Test
+ void test440d() {
+ JexlEngine jexl = new JexlBuilder().loader(getClass().getClassLoader()).imports(this.getClass().getName()).create();
+ final String src = "let s = switch (x) { case Scope440.UNDEFINED -> 'undefined'; } s";
+ final JexlScript script = jexl.createScript(src, "x");
+ try {
+ script.execute(null, Scope440.THIS);
+ fail("should not be able to execute script with switch expression with no default");
+ } catch (JexlException xjexl) {
+ assertTrue(xjexl.getMessage().contains("switch"));
+ }
+ }
+
+ @Test
+ void test440e() {
+ JexlEngine jexl = new JexlBuilder().loader(getClass().getClassLoader()).imports(this.getClass().getName()).create();
+ final String src = "function f(x) { switch (x) { case Scope440.UNDEFINED : return 'undefined'; } } f(x)";
+ final JexlScript script = jexl.createScript(src, "x");
+ Object result = script.execute(null, Scope440.UNDEFINED);
+ Assertions.assertEquals("undefined", result);
+ try {
+ script.execute(null, Scope440.THIS);
+ fail("should not be able to execute script with switch expression with no default");
+ } catch (JexlException xjexl) {
+ assertTrue(xjexl.getMessage().contains("switch"));
+ }
+ }
+}