blob: f72b18911a7604099a9e5a7a9cb66b733136333f [file] [log] [blame]
/*
* 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
*
* 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.apache.commons.jexl3;
import org.apache.commons.jexl3.internal.Engine;
import org.apache.commons.jexl3.internal.introspection.Uberspect;
import java.util.HashMap;
import java.util.Map;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
//import org.apache.commons.beanutils.LazyDynaMap;
/**
* Test cases for reported issue between JEXL-1 and JEXL-100.
*/
@SuppressWarnings({"boxing", "UnnecessaryBoxing", "AssertEqualsBetweenInconvertibleTypes"})
public class IssuesTest extends JexlTestCase {
public IssuesTest() {
super("IssuesTest", null);
}
@Before
@Override
public void setUp() throws Exception {
// ensure jul logging is only error to avoid warning in silent mode
java.util.logging.Logger.getLogger(JexlEngine.class.getName()).setLevel(java.util.logging.Level.SEVERE);
}
// JEXL-49: blocks not parsed (fixed)
@Test
public void test49() throws Exception {
final JexlEngine jexl = new Engine();
final Map<String, Object> vars = new HashMap<String, Object>();
final JexlContext ctxt = new MapContext(vars);
final String stmt = "a = 'b'; c = 'd';";
final JexlScript expr = jexl.createScript(stmt);
/* Object value = */ expr.execute(ctxt);
Assert.assertTrue("JEXL-49 is not fixed", vars.get("a").equals("b") && vars.get("c").equals("d"));
}
// JEXL-48: bad assignment detection
public static class Another {
public String name = "whatever";
private final Boolean foo = Boolean.TRUE;
public Boolean foo() {
return foo;
}
public int goo() {
return 100;
}
}
public static class Foo {
private final Another inner;
Foo() {
inner = new Another();
}
public Another getInner() {
return inner;
}
}
@Test
public void test48() throws Exception {
final JexlEngine jexl = new Engine();
final JexlEvalContext jc = new JexlEvalContext();
final JexlOptions options = jc.getEngineOptions();
// ensure errors will throw
options.setStrict(true);
options.setSilent(false);
try {
final String jexlExp = "(foo.getInner().foo() eq true) and (foo.getInner().goo() = (foo.getInner().goo()+1-1))";
final JexlExpression e = jexl.createExpression(jexlExp);
jc.set("foo", new Foo());
/* Object o = */ e.evaluate(jc);
Assert.fail("Should have failed due to invalid assignment");
} catch (final JexlException.Assignment xparse) {
final String dbg = xparse.toString();
} catch (final JexlException xjexl) {
Assert.fail("Should have thrown a parse exception");
}
}
// JEXL-47: C style comments (single & multi line) (fixed in Parser.jjt)
// JEXL-44: comments dont allow double quotes (fixed in Parser.jjt)
@Test
public void test47() throws Exception {
final JexlEngine jexl = new Engine();
final JexlEvalContext ctxt = new JexlEvalContext();
final JexlOptions options = ctxt.getEngineOptions();
// ensure errors will throw
options.setSilent(false);
JexlExpression expr = jexl.createExpression("true//false\n");
Object value = expr.evaluate(ctxt);
Assert.assertTrue("should be true", (Boolean) value);
expr = jexl.createExpression("/*true*/false");
value = expr.evaluate(ctxt);
Assert.assertFalse("should be false", (Boolean) value);
expr = jexl.createExpression("/*\"true\"*/false");
value = expr.evaluate(ctxt);
Assert.assertFalse("should be false", (Boolean) value);
}
// JEXL-42: NullPointerException evaluating an expression
// fixed in JexlArithmetic by allowing add operator to deal with string, null
@Test
public void test42() throws Exception {
final JexlEngine jexl = new JexlBuilder().create();
final JxltEngine uel = jexl.createJxltEngine();
// ensure errors will throw
//jexl.setSilent(false);
final JexlEvalContext ctxt = new JexlEvalContext();
final JexlOptions options = ctxt.getEngineOptions();
options.set(jexl);
options.setStrict(false);
options.setStrictArithmetic(false);
ctxt.set("ax", "ok");
final JxltEngine.Expression expr = uel.createExpression("${ax+(bx)}");
final Object value = expr.evaluate(ctxt);
Assert.assertEquals("should be ok", "ok", value);
}
// JEXL-40: failed to discover all methods (non public class implements public method)
// fixed in ClassMap by taking newer version of populateCache from Velocity
public static abstract class Base {
public abstract boolean foo();
}
static class Derived extends Base {
@Override
public boolean foo() {
return true;
}
}
@Test
public void test40() throws Exception {
final JexlEngine jexl = new Engine();
final JexlEvalContext ctxt = new JexlEvalContext();
final JexlOptions options = ctxt.getEngineOptions();
options.set(jexl);
// ensure errors will throw
options.setSilent(false);
ctxt.set("derived", new Derived());
final JexlExpression expr = jexl.createExpression("derived.foo()");
final Object value = expr.evaluate(ctxt);
Assert.assertTrue("should be true", (Boolean) value);
}
// JEXL-52: can be implemented by deriving Interpreter.{g,s}etAttribute; later
@Test
public void test52base() throws Exception {
final Engine jexl = (Engine) createEngine(false);
final Uberspect uber = (Uberspect) jexl.getUberspect();
// most likely, call will be in an Interpreter, getUberspect
String[] names = uber.getMethodNames(Another.class);
Assert.assertTrue("should find methods", names.length > 0);
int found = 0;
for (final String name : names) {
if ("foo".equals(name) || "goo".equals(name)) {
found += 1;
}
}
Assert.assertEquals("should have foo & goo", 2, found);
names = uber.getFieldNames(Another.class);
Assert.assertTrue("should find fields", names.length > 0);
found = 0;
for (final String name : names) {
if ("name".equals(name)) {
found += 1;
}
}
Assert.assertEquals("should have name", 1, found);
}
// JEXL-10/JEXL-11: variable checking, null operand is error
@Test
public void test11() throws Exception {
final JexlEngine jexl = createEngine(false);
final JexlEvalContext ctxt = new JexlEvalContext();
final JexlOptions options = ctxt.getEngineOptions();
// ensure errors will throw
options.setSilent(false);
options.setStrict(true);
ctxt.set("a", null);
final String[] exprs = {
//"10 + null",
//"a - 10",
//"b * 10",
"a % b"//,
//"1000 / a"
};
for (final String s : exprs) {
try {
final JexlExpression expr = jexl.createExpression(s);
/* Object value = */
expr.evaluate(ctxt);
Assert.fail(s + " : should have failed due to null argument");
} catch (final JexlException xjexl) {
// expected
}
}
}
// JEXL-62
@Test
public void test62() throws Exception {
final JexlEngine jexl = createEngine(false);
final MapContext vars = new MapContext();
final JexlEvalContext ctxt = new JexlEvalContext(vars);
final JexlOptions options = ctxt.getEngineOptions();
options.setStrict(true);
options.setSilent(true);// to avoid throwing JexlException on null method call
JexlScript jscript;
jscript = jexl.createScript("dummy.hashCode()");
Assert.assertNull(jscript.getSourceText(), jscript.execute(ctxt)); // OK
ctxt.set("dummy", "abcd");
Assert.assertEquals(jscript.getSourceText(), Integer.valueOf("abcd".hashCode()), jscript.execute(ctxt)); // OK
jscript = jexl.createScript("dummy.hashCode");
Assert.assertNull(jscript.getSourceText(), jscript.execute(ctxt)); // OK
JexlExpression jexpr;
vars.clear();
jexpr = jexl.createExpression("dummy.hashCode()");
Assert.assertNull(jexpr.toString(), jexpr.evaluate(ctxt)); // OK
ctxt.set("dummy", "abcd");
Assert.assertEquals(jexpr.toString(), Integer.valueOf("abcd".hashCode()), jexpr.evaluate(ctxt)); // OK
jexpr = jexl.createExpression("dummy.hashCode");
Assert.assertNull(jexpr.toString(), jexpr.evaluate(ctxt)); // OK
}
// JEXL-87
@Test
public void test87() throws Exception {
final JexlEngine jexl = createEngine(false);
final JexlEvalContext ctxt = new JexlEvalContext();
final JexlOptions options = ctxt.getEngineOptions();
// ensure errors will throw
options.setSilent(false);
final JexlExpression divide = jexl.createExpression("l / r");
final JexlExpression modulo = jexl.createExpression("l % r");
ctxt.set("l", java.math.BigInteger.valueOf(7));
ctxt.set("r", java.math.BigInteger.valueOf(2));
Assert.assertEquals(java.math.BigInteger.valueOf(3), divide.evaluate(ctxt));
Assert.assertTrue(jexl.getArithmetic().equals(1, modulo.evaluate(ctxt)));
ctxt.set("l", java.math.BigDecimal.valueOf(7));
ctxt.set("r", java.math.BigDecimal.valueOf(2));
Assert.assertEquals(java.math.BigDecimal.valueOf(3.5), divide.evaluate(ctxt));
Assert.assertTrue(jexl.getArithmetic().equals(1, modulo.evaluate(ctxt)));
}
// JEXL-90
@Test
public void test90() throws Exception {
final JexlEngine jexl = createEngine(false);
final JexlEvalContext ctxt = new JexlEvalContext();
final JexlOptions options = ctxt.getEngineOptions();
// ensure errors will throw
options.setSilent(false);
// ';' is necessary between expressions
final String[] fexprs = {
"a=3 b=4",
"while(a) while(a)",
"1 2",
"if (true) 2; 3 {}",
"while (x) 1 if (y) 2 3"
};
for (final String fexpr : fexprs) {
try {
jexl.createScript(fexpr);
Assert.fail(fexpr + ": Should have failed in parse");
} catch (final JexlException xany) {
// expected to fail in createExpression
}
}
// ';' is necessary between expressions and only expressions
final String[] exprs = {
"if (x) {1} if (y) {2}",
"if (x) 1 if (y) 2",
"while (x) 1 if (y) 2 else 3",
"for(z : [3, 4, 5]) { z } y ? 2 : 1",
"for(z : [3, 4, 5]) { z } if (y) 2 else 1"
};
ctxt.set("x", Boolean.FALSE);
ctxt.set("y", Boolean.TRUE);
for (final String expr : exprs) {
final JexlScript s = jexl.createScript(expr);
Assert.assertEquals(Integer.valueOf(2), s.execute(ctxt));
}
debuggerCheck(jexl);
}
// JEXL-44
@Test
public void test44() throws Exception {
final JexlEngine jexl = createEngine(false);
final JexlEvalContext ctxt = new JexlEvalContext();
final JexlOptions options = ctxt.getEngineOptions();
// ensure errors will throw
options.setSilent(false);
JexlScript script;
script = jexl.createScript("'hello world!'//commented");
Assert.assertEquals("hello world!", script.execute(ctxt));
script = jexl.createScript("'hello world!';//commented\n'bye...'");
Assert.assertEquals("bye...", script.execute(ctxt));
script = jexl.createScript("'hello world!'## commented");
Assert.assertEquals("hello world!", script.execute(ctxt));
script = jexl.createScript("'hello world!';## commented\n'bye...'");
Assert.assertEquals("bye...", script.execute(ctxt));
}
@Test
public void test97() throws Exception {
final JexlEngine jexl = createEngine(false);
final JexlEvalContext ctxt = new JexlEvalContext();
final JexlOptions options = ctxt.getEngineOptions();
// ensure errors will throw
options.setSilent(false);
for (char v = 'a'; v <= 'z'; ++v) {
ctxt.set(Character.toString(v), 10);
}
final String input
= "(((((((((((((((((((((((((z+y)/x)*w)-v)*u)/t)-s)*r)/q)+p)-o)*n)-m)+l)*k)+j)/i)+h)*g)+f)/e)+d)-c)/b)+a)";
JexlExpression script;
// Make sure everything is loaded...
final long start = System.nanoTime();
script = jexl.createExpression(input);
final Object value = script.evaluate(ctxt);
Assert.assertEquals(Integer.valueOf(11), value);
final long end = System.nanoTime();
final double millisec = (end - start) / 1e6;
final double limit = 200.0; // Allow plenty of slack
Assert.assertTrue("Expected parse to take less than " + limit + "ms, actual " + millisec, millisec < limit);
}
public static class fn98 {
public String replace(final String str, final String target, final String replacement) {
return str.replace(target, replacement);
}
}
@Test
public void test98() throws Exception {
final String[] exprs = {
"fn:replace('DOMAIN\\somename', '\\\\', '\\\\\\\\')",
"fn:replace(\"DOMAIN\\somename\", \"\\\\\", \"\\\\\\\\\")",
"fn:replace('DOMAIN\\somename', '\\u005c', '\\u005c\\u005c')"
};
final Map<String, Object> funcs = new HashMap<String, Object>();
funcs.put("fn", new fn98());
final JexlEngine jexl = new JexlBuilder().namespaces(funcs).create();
for (final String expr : exprs) {
final Object value = jexl.createExpression(expr).evaluate(null);
Assert.assertEquals(expr, "DOMAIN\\\\somename", value);
}
}
}