package edu.washington.escience.myria.operator.apply; import static org.junit.Assert.assertEquals; import java.lang.reflect.InvocationTargetException; import org.junit.Test; import edu.washington.escience.myria.DbException; import edu.washington.escience.myria.Schema; import edu.washington.escience.myria.Type; import edu.washington.escience.myria.expression.ConstantExpression; import edu.washington.escience.myria.expression.Expression; import edu.washington.escience.myria.expression.ExpressionOperator; import edu.washington.escience.myria.expression.IntDivideExpression; import edu.washington.escience.myria.expression.MinusExpression; import edu.washington.escience.myria.expression.ModuloExpression; import edu.washington.escience.myria.expression.PlusExpression; import edu.washington.escience.myria.expression.TimesExpression; import edu.washington.escience.myria.expression.evaluate.ConstantEvaluator; import edu.washington.escience.myria.expression.evaluate.ExpressionOperatorParameter; public class ExpressionTest { /** * Given a constant expression, evaluate it and return its value. * * @param op the expression * @return the constant value * @throws DbException if there is an error evaluating the expression */ private Object evaluateConstantExpression(final ExpressionOperator op) throws DbException { Expression expr = new Expression("op", op); ConstantEvaluator eval = new ConstantEvaluator(expr, new ExpressionOperatorParameter(Schema.EMPTY_SCHEMA)); return eval.eval(); } /** * Given a constant expression, try and evaluate it. If successful, return it. If not, unwrap the expression to the * root cause and return that. * * @param op the expression * @return the constant value * @throws Throwable the root cause of a failed Janino compilation. */ private Object evaluateConstantAndUnrollException(final ExpressionOperator op) throws Throwable { try { return evaluateConstantExpression(op); } catch (DbException e) { Throwable e1 = e.getCause(); if (e1 == null || !(e1 instanceof InvocationTargetException)) { throw e; } Throwable e2 = e1.getCause(); if (e2 == null) { throw e; } throw e2; } } @Test(expected = ArithmeticException.class) public void testOverflowIntSum() throws Throwable { ConstantExpression val1 = new ConstantExpression(Integer.MAX_VALUE); ConstantExpression val2 = new ConstantExpression(1); ExpressionOperator sum = new PlusExpression(val1, val2); evaluateConstantAndUnrollException(sum); } @Test(expected = ArithmeticException.class) public void testUnderflowIntSum() throws Throwable { ConstantExpression val1 = new ConstantExpression(Integer.MIN_VALUE); ConstantExpression val2 = new ConstantExpression(-1); ExpressionOperator ans = new PlusExpression(val1, val2); evaluateConstantAndUnrollException(ans); } @Test(expected = ArithmeticException.class) public void testOverflowIntDiff() throws Throwable { ConstantExpression val1 = new ConstantExpression(Integer.MAX_VALUE); ConstantExpression val2 = new ConstantExpression(-1); ExpressionOperator sum = new MinusExpression(val1, val2); evaluateConstantAndUnrollException(sum); } @Test(expected = ArithmeticException.class) public void testUnderflowIntDiff() throws Throwable { ConstantExpression val1 = new ConstantExpression(Integer.MIN_VALUE); ConstantExpression val2 = new ConstantExpression(1); ExpressionOperator ans = new MinusExpression(val1, val2); evaluateConstantAndUnrollException(ans); } @Test(expected = ArithmeticException.class) public void testOverflowLongSum() throws Throwable { ConstantExpression val1 = new ConstantExpression(Long.MAX_VALUE); ConstantExpression val2 = new ConstantExpression(1L); ExpressionOperator ans = new PlusExpression(val1, val2); evaluateConstantAndUnrollException(ans); } @Test(expected = ArithmeticException.class) public void testUnderflowLongSum() throws Throwable { ConstantExpression val1 = new ConstantExpression(Long.MIN_VALUE); ConstantExpression val2 = new ConstantExpression(-1L); ExpressionOperator ans = new PlusExpression(val1, val2); evaluateConstantAndUnrollException(ans); } @Test(expected = ArithmeticException.class) public void testOverflowLongDiff() throws Throwable { ConstantExpression val1 = new ConstantExpression(Long.MAX_VALUE); ConstantExpression val2 = new ConstantExpression(-1L); ExpressionOperator ans = new MinusExpression(val1, val2); evaluateConstantAndUnrollException(ans); } @Test(expected = ArithmeticException.class) public void testUnderflowLongDiff() throws Throwable { ConstantExpression val1 = new ConstantExpression(Long.MIN_VALUE); ConstantExpression val2 = new ConstantExpression(1L); ExpressionOperator ans = new MinusExpression(val1, val2); evaluateConstantAndUnrollException(ans); } @Test(expected = ArithmeticException.class) public void testOverflowIntProd() throws Throwable { ConstantExpression val1 = new ConstantExpression(Integer.MAX_VALUE); ConstantExpression val2 = new ConstantExpression(Integer.MAX_VALUE); ExpressionOperator ans = new TimesExpression(val1, val2); evaluateConstantAndUnrollException(ans); } @Test(expected = ArithmeticException.class) public void testOverflowIntProd2() throws Throwable { ConstantExpression val1 = new ConstantExpression(Integer.MAX_VALUE); ConstantExpression val2 = new ConstantExpression(Integer.MIN_VALUE); ExpressionOperator ans = new TimesExpression(val1, val2); evaluateConstantAndUnrollException(ans); } @Test(expected = ArithmeticException.class) public void testOverflowLongProd() throws Throwable { ConstantExpression val1 = new ConstantExpression(Long.MAX_VALUE); ConstantExpression val2 = new ConstantExpression(2L); ExpressionOperator ans = new TimesExpression(val1, val2); evaluateConstantAndUnrollException(ans); } @Test(expected = ArithmeticException.class) public void testOverflowLongProd2() throws Throwable { ConstantExpression val1 = new ConstantExpression(Long.MAX_VALUE); ConstantExpression val2 = new ConstantExpression(Long.MIN_VALUE); ExpressionOperator ans = new TimesExpression(val1, val2); evaluateConstantAndUnrollException(ans); } @Test public void testIntDivideWithLongs() throws Throwable { ConstantExpression val1 = new ConstantExpression(5L); ConstantExpression val2 = new ConstantExpression(6L); ExpressionOperator expr = new IntDivideExpression(val1, val2); Object ans = evaluateConstantAndUnrollException(expr); assertEquals(Long.class, ans.getClass()); assertEquals(0L, ans); expr = new IntDivideExpression(val2, val1); ans = evaluateConstantAndUnrollException(expr); assertEquals(Long.class, ans.getClass()); assertEquals(1L, ans); ConstantExpression val3 = new ConstantExpression(-5L); expr = new IntDivideExpression(val3, val1); ans = evaluateConstantAndUnrollException(expr); assertEquals(Long.class, ans.getClass()); assertEquals(-1L, ans); expr = new IntDivideExpression(val1, val3); ans = evaluateConstantAndUnrollException(expr); assertEquals(Long.class, ans.getClass()); assertEquals(-1L, ans); } @Test public void testIntDivideWithFloats() throws Throwable { ConstantExpression val1 = new ConstantExpression(5f); ConstantExpression val2 = new ConstantExpression(6f); ExpressionOperator expr = new IntDivideExpression(val1, val2); Object ans = evaluateConstantAndUnrollException(expr); assertEquals(Long.class, ans.getClass()); assertEquals(0L, ans); expr = new IntDivideExpression(val2, val1); ans = evaluateConstantAndUnrollException(expr); assertEquals(Long.class, ans.getClass()); assertEquals(1L, ans); ConstantExpression val3 = new ConstantExpression(-5f); expr = new IntDivideExpression(val3, val1); ans = evaluateConstantAndUnrollException(expr); assertEquals(Long.class, ans.getClass()); assertEquals(-1L, ans); expr = new IntDivideExpression(val1, val3); ans = evaluateConstantAndUnrollException(expr); assertEquals(Long.class, ans.getClass()); assertEquals(-1L, ans); } @Test public void testIntDivideWithInts() throws Throwable { ConstantExpression val1 = new ConstantExpression(5); ConstantExpression val2 = new ConstantExpression(6); ExpressionOperator expr = new IntDivideExpression(val1, val2); Object ans = evaluateConstantAndUnrollException(expr); assertEquals(Integer.class, ans.getClass()); assertEquals(0, ans); expr = new IntDivideExpression(val2, val1); ans = evaluateConstantAndUnrollException(expr); assertEquals(Integer.class, ans.getClass()); assertEquals(1, ans); ConstantExpression val3 = new ConstantExpression(-5); expr = new IntDivideExpression(val3, val1); ans = evaluateConstantAndUnrollException(expr); assertEquals(Integer.class, ans.getClass()); assertEquals(-1, ans); expr = new IntDivideExpression(val1, val3); ans = evaluateConstantAndUnrollException(expr); assertEquals(Integer.class, ans.getClass()); assertEquals(-1, ans); } @Test public void testIntDivideWithDoubles() throws Throwable { ConstantExpression val1 = new ConstantExpression(5.0); ConstantExpression val2 = new ConstantExpression(6.0); ExpressionOperator expr = new IntDivideExpression(val1, val2); Object ans = evaluateConstantAndUnrollException(expr); assertEquals(Long.class, ans.getClass()); assertEquals(0L, ans); expr = new IntDivideExpression(val2, val1); ans = evaluateConstantAndUnrollException(expr); assertEquals(Long.class, ans.getClass()); assertEquals(1L, ans); ConstantExpression val3 = new ConstantExpression(-5.0); expr = new IntDivideExpression(val3, val1); ans = evaluateConstantAndUnrollException(expr); assertEquals(Long.class, ans.getClass()); assertEquals(-1L, ans); expr = new IntDivideExpression(val1, val3); ans = evaluateConstantAndUnrollException(expr); assertEquals(Long.class, ans.getClass()); assertEquals(-1L, ans); } @Test public void testIntDivideWithDoublesNotOverflow() throws Throwable { ConstantExpression val1 = new ConstantExpression(-Math.pow(2, 64)); ConstantExpression val2 = new ConstantExpression(2.0); ExpressionOperator expr = new IntDivideExpression(val1, val2); Object ans = evaluateConstantAndUnrollException(expr); assertEquals(Long.MIN_VALUE, ans); } @Test(expected = ArithmeticException.class) public void testIntDivideWithDoublesOverflow() throws Throwable { ConstantExpression val1 = new ConstantExpression(Math.pow(2, 63)); /* Long.MAX_VALUE + 1 */ ConstantExpression val2 = new ConstantExpression(1.0); ExpressionOperator expr = new IntDivideExpression(val1, val2); evaluateConstantAndUnrollException(expr); } @Test(expected = ArithmeticException.class) public void testIntDivideWithDoublesOverflow2() throws Throwable { ConstantExpression val1 = new ConstantExpression(Math.pow(2, 62)); /* (Long.MAX_VALUE + 1) >> 1 */ ConstantExpression val2 = new ConstantExpression(0.5); ExpressionOperator expr = new IntDivideExpression(val1, val2); evaluateConstantAndUnrollException(expr); } @Test public void testModuloWithLongs() throws Throwable { ConstantExpression val1 = new ConstantExpression(5L); ConstantExpression val2 = new ConstantExpression(6L); ExpressionOperator expr = new ModuloExpression(val1, val2); Object ans = evaluateConstantAndUnrollException(expr); assertEquals(Long.class, ans.getClass()); assertEquals(5L, ans); expr = new ModuloExpression(val2, val1); ans = evaluateConstantAndUnrollException(expr); assertEquals(Long.class, ans.getClass()); assertEquals(1L, ans); ConstantExpression val3 = new ConstantExpression(-5L); expr = new ModuloExpression(val3, val1); ans = evaluateConstantAndUnrollException(expr); assertEquals(Long.class, ans.getClass()); assertEquals(0L, ans); expr = new ModuloExpression(val1, val3); ans = evaluateConstantAndUnrollException(expr); assertEquals(Long.class, ans.getClass()); assertEquals(0L, ans); } @Test public void testModuloWithInts() throws Throwable { ConstantExpression val1 = new ConstantExpression(5); ConstantExpression val2 = new ConstantExpression(6); ExpressionOperator expr = new ModuloExpression(val1, val2); Object ans = evaluateConstantAndUnrollException(expr); assertEquals(Integer.class, ans.getClass()); assertEquals(5, ans); expr = new ModuloExpression(val2, val1); ans = evaluateConstantAndUnrollException(expr); assertEquals(Integer.class, ans.getClass()); assertEquals(1, ans); ConstantExpression val3 = new ConstantExpression(-5); expr = new ModuloExpression(val3, val1); ans = evaluateConstantAndUnrollException(expr); assertEquals(Integer.class, ans.getClass()); assertEquals(0, ans); expr = new ModuloExpression(val1, val3); ans = evaluateConstantAndUnrollException(expr); assertEquals(Integer.class, ans.getClass()); assertEquals(0, ans); } @Test(expected = IllegalArgumentException.class) public void testModuloWithFloats() throws Throwable { ConstantExpression val1 = new ConstantExpression(5.0F); ConstantExpression val2 = new ConstantExpression(6.0F); ExpressionOperator expr = new ModuloExpression(val1, val2); evaluateConstantAndUnrollException(expr); } @Test(expected = IllegalArgumentException.class) public void testModuloWithDoubles() throws Throwable { ConstantExpression val1 = new ConstantExpression(5.0); ConstantExpression val2 = new ConstantExpression(6.0); ExpressionOperator expr = new ModuloExpression(val1, val2); evaluateConstantAndUnrollException(expr); } @Test public void testLongString() throws Throwable { ConstantExpression tenBillion = new ConstantExpression(Type.LONG_TYPE, "10000000000"); assertEquals(10000000000L, evaluateConstantAndUnrollException(tenBillion)); tenBillion = new ConstantExpression(Type.LONG_TYPE, "10000000000l"); assertEquals(10000000000L, evaluateConstantAndUnrollException(tenBillion)); tenBillion = new ConstantExpression(Type.LONG_TYPE, "10000000000L"); assertEquals(10000000000L, evaluateConstantAndUnrollException(tenBillion)); } @Test public void testFloatString() throws Throwable { ConstantExpression piApprox = new ConstantExpression(Type.FLOAT_TYPE, "3.1415926"); assertEquals((float) Math.PI, (Float) evaluateConstantAndUnrollException(piApprox), 0.00001); piApprox = new ConstantExpression(Type.FLOAT_TYPE, "3.1415926f"); assertEquals((float) Math.PI, (Float) evaluateConstantAndUnrollException(piApprox), 0.00001); piApprox = new ConstantExpression(Type.FLOAT_TYPE, "3.1415926F"); assertEquals((float) Math.PI, (Float) evaluateConstantAndUnrollException(piApprox), 0.00001); } @Test public void testIntString() throws Throwable { ConstantExpression oneBillion = new ConstantExpression(Type.INT_TYPE, "1000000000"); assertEquals(1000000000, evaluateConstantAndUnrollException(oneBillion)); } @Test public void testDoubleString() throws Throwable { ConstantExpression piApprox = new ConstantExpression(Type.DOUBLE_TYPE, String.valueOf(Math.PI)); assertEquals(Math.PI, (Double) evaluateConstantAndUnrollException(piApprox), 0.00000001); } }