/*
* 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);
}
}