/* * Copyright 2016 Nabarun Mondal * 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 com.noga.njexl.lang; import java.util.Map; import java.math.BigDecimal; import java.math.BigInteger; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Set; import com.noga.njexl.lang.junit.Asserter; public class ArithmeticTest extends JexlTestCase { private Asserter asserter; public ArithmeticTest() { super(createThreadedArithmeticEngine(true)); } @Override public void setUp() { asserter = new Asserter(JEXL); } public void testUndefinedVar() throws Exception { asserter.failExpression("objects[1].status", ".* undefined variable : 'objects'.*"); } public void testLeftNullOperand() throws Exception { asserter.setVariable("left", null); asserter.setVariable("right", Integer.valueOf(8)); asserter.failExpression("left + right", ".*null.*"); asserter.failExpression("left - right", ".*null.*"); asserter.failExpression("left * right", ".*null.*"); asserter.failExpression("left / right", ".*null.*"); asserter.failExpression("left % right", ".*null.*"); asserter.failExpression("left & right", ".*null.*"); asserter.failExpression("left | right", ".*null.*"); asserter.failExpression("left ^ right", ".*null.*"); } public void testRightNullOperand() throws Exception { asserter.setVariable("left", Integer.valueOf(9)); asserter.setVariable("right", null); asserter.failExpression("left + right", ".*null.*"); asserter.failExpression("left - right", ".*null.*"); asserter.failExpression("left * right", ".*null.*"); asserter.failExpression("left / right", ".*null.*"); asserter.failExpression("left % right", ".*null.*"); asserter.failExpression("left & right", ".*null.*"); asserter.failExpression("left | right", ".*null.*"); asserter.failExpression("left ^ right", ".*null.*"); } public void testNullOperands() throws Exception { asserter.setVariable("left", null); asserter.setVariable("right", null); asserter.failExpression("left + right", ".*null.*"); asserter.failExpression("left - right", ".*null.*"); asserter.failExpression("left * right", ".*null.*"); asserter.failExpression("left / right", ".*null.*"); asserter.failExpression("left % right", ".*null.*"); asserter.failExpression("left & right", ".*null.*"); asserter.failExpression("left | right", ".*null.*"); asserter.failExpression("left ^ right", ".*null.*"); } public void testNullOperand() throws Exception { asserter.setVariable("right", null); asserter.failExpression("~right", ".*null.*"); asserter.failExpression("-right", ".*arithmetic.*"); } public void testBigDecimal() throws Exception { asserter.setVariable("left", new BigDecimal(2)); asserter.setVariable("right", new BigDecimal(6)); asserter.assertExpression("left + right", new BigDecimal(8)); asserter.assertExpression("right - left", new BigDecimal(4)); asserter.assertExpression("right * left", new BigDecimal(12)); asserter.assertExpression("right / left", new BigDecimal(3)); asserter.assertExpression("right % left", new BigDecimal(0)); } public void testBigInteger() throws Exception { asserter.setVariable("left", new BigInteger("2")); asserter.setVariable("right", new BigInteger("6")); asserter.assertExpression("left + right", new BigInteger("8")); asserter.assertExpression("right - left", new BigInteger("4")); asserter.assertExpression("right * left", new BigInteger("12")); asserter.assertExpression("right / left", new BigInteger("3")); asserter.assertExpression("right % left", new BigInteger("0")); } /** * test some simple mathematical calculations */ public void testUnaryMinus() throws Exception { asserter.setVariable("aByte", new Byte((byte) 1)); asserter.setVariable("aShort", new Short((short) 2)); asserter.setVariable("anInteger", new Integer(3)); asserter.setVariable("aLong", new Long(4)); asserter.setVariable("aFloat", new Float(5.5)); asserter.setVariable("aDouble", new Double(6.6)); asserter.setVariable("aBigInteger", new BigInteger("7")); asserter.setVariable("aBigDecimal", new BigDecimal("8.8")); asserter.assertExpression("-3", new Integer("-3")); asserter.assertExpression("-3.0", new Float("-3.0")); asserter.assertExpression("-aByte", new Byte((byte) -1)); asserter.assertExpression("-aShort", new Short((short) -2)); asserter.assertExpression("-anInteger", new Integer(-3)); asserter.assertExpression("-aLong", new Long(-4)); asserter.assertExpression("-aFloat", new Float(-5.5)); asserter.assertExpression("-aDouble", new Double(-6.6)); asserter.assertExpression("-aBigInteger", new BigInteger("-7")); asserter.assertExpression("-aBigDecimal", new BigDecimal("-8.8")); } /** * test some simple mathematical calculations */ public void testCalculations() throws Exception { asserter.setVariable("foo", new Integer(2)); asserter.assertExpression("foo + 2", new Integer(4)); asserter.assertExpression("3 + 3", new Integer(6)); asserter.assertExpression("3 + 3 + foo", new Integer(8)); asserter.assertExpression("3 * 3", new Integer(9)); asserter.assertExpression("3 * 3 + foo", new Integer(11)); asserter.assertExpression("3 * 3 - foo", new Integer(7)); /* * test parenthesized exprs */ asserter.assertExpression("(4 + 3) * 6", new Integer(42)); asserter.assertExpression("(8 - 2) * 7", new Integer(42)); /* * test some floaty stuff */ asserter.assertExpression("3 * \"3.0\"", new Double(9)); asserter.assertExpression("3 * 3.0", new Double(9)); /* * test / and % */ asserter.assertExpression("6 / 3", new Integer(6 / 3)); asserter.assertExpression("6.4 / 3", new Double(6.4 / 3)); asserter.assertExpression("0 / 3", new Integer(0 / 3)); asserter.assertExpression("4 % 3", new Integer(1)); asserter.assertExpression("4.8 % 3", new Double(4.8 % 3)); } public void testCoercions() throws Exception { asserter.assertExpression("1", new Integer(1)); // numerics default to Integer // asserter.assertExpression("5L", new Long(5)); // TODO when implemented asserter.setVariable("I2", new Integer(2)); asserter.setVariable("L2", new Long(2)); asserter.setVariable("L3", new Long(3)); asserter.setVariable("B10", BigInteger.TEN); // Integer & Integer => Integer asserter.assertExpression("I2 + 2", new Integer(4)); asserter.assertExpression("I2 * 2", new Integer(4)); asserter.assertExpression("I2 - 2", new Integer(0)); asserter.assertExpression("I2 / 2", new Integer(1)); // Integer & Long => Long asserter.assertExpression("I2 * L2", new Long(4)); asserter.assertExpression("I2 / L2", new Long(1)); // Long & Long => Long asserter.assertExpression("L2 + 3", new Long(5)); asserter.assertExpression("L2 + L3", new Long(5)); asserter.assertExpression("L2 / L2", new Long(1)); asserter.assertExpression("L2 / 2", new Long(1)); // BigInteger asserter.assertExpression("B10 / 10", BigInteger.ONE); asserter.assertExpression("B10 / I2", new BigInteger("5")); asserter.assertExpression("B10 / L2", new BigInteger("5")); } public static class MatchingContainer { private final Set<Integer> values; public MatchingContainer(int[] is) { values = new HashSet<Integer>(); for (int value : is) { values.add(value); } } public boolean contains(int value) { return values.contains(value); } } public void testRegexp() throws Exception { asserter.setVariable("str", "abc456"); asserter.assertExpression("str =~ '.*456'", Boolean.TRUE); asserter.assertExpression("str !~ 'ABC.*'", Boolean.TRUE); asserter.setVariable("match", "abc.*"); asserter.setVariable("nomatch", ".*123"); asserter.assertExpression("str =~ match", Boolean.TRUE); asserter.assertExpression("str !~ match", Boolean.FALSE); asserter.assertExpression("str !~ nomatch", Boolean.TRUE); asserter.assertExpression("str =~ nomatch", Boolean.FALSE); asserter.setVariable("match", java.util.regex.Pattern.compile("abc.*")); asserter.setVariable("nomatch", java.util.regex.Pattern.compile(".*123")); asserter.assertExpression("str =~ match", Boolean.TRUE); asserter.assertExpression("str !~ match", Boolean.FALSE); asserter.assertExpression("str !~ nomatch", Boolean.TRUE); asserter.assertExpression("str =~ nomatch", Boolean.FALSE); // check the in/not-in variant asserter.assertExpression("'a' =~ ['a','b','c','d','e','f']", Boolean.TRUE); asserter.assertExpression("'a' !~ ['a','b','c','d','e','f']", Boolean.FALSE); asserter.assertExpression("'z' =~ ['a','b','c','d','e','f']", Boolean.FALSE); asserter.assertExpression("'z' !~ ['a','b','c','d','e','f']", Boolean.TRUE); // check in/not-in on array, list, map, set and duck-type collection int[] ai = {2, 4, 42, 54}; List<Integer> al = new ArrayList<Integer>(); for(int i : ai) { al.add(i); } Map<Integer, String> am = new HashMap<Integer, String>(); am.put(2, "two"); am.put(4, "four"); am.put(42, "forty-two"); am.put(54, "fifty-four"); MatchingContainer ad = new MatchingContainer(ai); Set<Integer> as = ad.values; Object[] vars = { ai, al, am, ad, as }; for(Object var : vars) { asserter.setVariable("container", var); for(int x : ai) { asserter.setVariable("x", x); asserter.assertExpression("x =~ container", Boolean.TRUE); } asserter.setVariable("x", 169); asserter.assertExpression("x !~ container", Boolean.TRUE); } } /** * Our intended change :- * 'true' == true and false == 'false' * '' == false too * anything else throws error * @throws Exception */ public void testBooleanStringConversion() throws Exception{ asserter.setVariable("str", "abc456"); boolean wasExceptionThrown = false ; try{ asserter.assertExpression("str == true", Boolean.FALSE ); } catch (Throwable th){ wasExceptionThrown = true; } if ( wasExceptionThrown ){ asserter.assertExpression("'true' == true " , Boolean.TRUE ); asserter.assertExpression("true != 'false' " , Boolean.TRUE ); asserter.assertExpression("false == 'false' " , Boolean.TRUE ); asserter.assertExpression("'' == false " , Boolean.TRUE ); } else{ asserter.assertExpression("false" , Boolean.TRUE ); } } /** * * if silent, all arith exception return 0.0 * if not silent, all arith exception throw * @throws Exception */ public void testDivideByZero() throws Exception { Map<String, Object> vars = new HashMap<String, Object>(); JexlContext context = new MapContext(vars); vars.put("aByte", new Byte((byte) 1)); vars.put("aShort", new Short((short) 2)); vars.put("aInteger", new Integer(3)); vars.put("aLong", new Long(4)); vars.put("aFloat", new Float(5.5)); vars.put("aDouble", new Double(6.6)); vars.put("aBigInteger", new BigInteger("7")); vars.put("aBigDecimal", new BigDecimal("8.8")); vars.put("zByte", new Byte((byte) 0)); vars.put("zShort", new Short((short) 0)); vars.put("zInteger", new Integer(0)); vars.put("zLong", new Long(0)); vars.put("zFloat", new Float(0)); vars.put("zDouble", new Double(0)); vars.put("zBigInteger", new BigInteger("0")); vars.put("zBigDecimal", new BigDecimal("0")); String[] tnames = { "Byte", "Short", "Integer", "Long", "Float", "Double", "BigInteger", "BigDecimal" }; // number of permutations this will generate final int PERMS = tnames.length * tnames.length; JexlEngine jexl = createThreadedArithmeticEngine(true); jexl.setCache(128); jexl.setSilent(false); // for non-silent, silent... for (int s = 0; s < 2; ++s) { JexlThreadedArithmetic.setLenient(Boolean.valueOf(s == 0)); int zthrow = 0; int zeval = 0; // for vars of all types... for (String vname : tnames) { // for zeros of all types... for (String zname : tnames) { // divide var by zero String expr = "a" + vname + " / " + "z" + zname; try { Expression zexpr = jexl.createExpression(expr); Object nan = zexpr.evaluate(context); // check we have a zero & incremement zero count if (nan instanceof Number) { double zero = ((Number) nan).doubleValue(); if (zero == 0.0) { zeval += 1; } } } catch (Exception any) { // increment the exception count zthrow += 1; } } } if (!jexl.isLenient()) { assertTrue("All expressions should have thrown " + zthrow + "/" + PERMS, zthrow == PERMS); } else { assertTrue("All expressions should have zeroed " + zeval + "/" + PERMS, zeval == PERMS); } } debuggerCheck(jexl); } }