/*
* LogicalBranchTests.java
*
* Copyright (c) 2012 Mike Strobel
*
* This source code is subject to terms and conditions of the Apache License, Version 2.0.
* A copy of the license can be found in the License.html file at the root of this distribution.
* By using this source code in any fashion, you are agreeing to be bound by the terms of the
* Apache License, Version 2.0.
*
* You must not remove this notice, or any other, from this software.
*/
package com.strobel.expressions;
import com.strobel.core.delegates.Func1;
import com.strobel.reflection.Type;
import com.strobel.reflection.Types;
import com.strobel.util.TypeUtils;
import org.junit.Test;
import java.util.concurrent.Callable;
import static com.strobel.expressions.Expression.*;
import static org.junit.Assert.*;
/**
* @author Mike Strobel
*/
public class LogicalBranchTests extends AbstractExpressionTest {
final static class OrCountdown {
private final int _initialValue;
private int _value;
OrCountdown(final int value) {
_initialValue = value;
_value = value;
}
@SuppressWarnings("UnusedParameters")
public boolean test(final int ignored) {
return --_value == 0;
}
public boolean test() {
return --_value == 0;
}
public int timesCalled() {
return _initialValue - _value;
}
public void reset() {
_value = _initialValue;
}
}
interface BooleanCallback {
boolean call();
}
@Test
public void testThreeOrs() throws Throwable {
final OrCountdown counter = new OrCountdown(3);
final Expression counterConstant = constant(counter);
final LambdaExpression<BooleanCallback> callback = lambda(
Type.of(BooleanCallback.class),
orElse(
call(counterConstant, "test"),
call(counterConstant, "test"),
call(counterConstant, "test")
)
);
final BooleanCallback delegate = callback.compile();
assertTrue(delegate.call());
assertEquals(3, counter.timesCalled());
}
@Test
public void testAndOr() throws Throwable {
final OrCountdown counter = new OrCountdown(2);
final Expression counterConstant = constant(counter);
final LambdaExpression<Callable<String>> callback = lambda(
Types.Callable.makeGenericType(Types.String),
condition(
orElse(
andAlso(
call(counterConstant, "test", constant(1)),
call(counterConstant, "test", constant(2))
),
call(counterConstant, "test", constant(3)),
equal(constant(2), constant(3))
),
constant("true"),
constant("false")
)
);
final Callable<String> delegate = callback.compile();
assertEquals("true", delegate.call());
assertEquals(2, counter.timesCalled());
}
@Test
public void testConditionalNullCheck() throws Throwable {
final ParameterExpression p = parameter(Types.String);
final LambdaExpression<Func1<String, String>> isNullLambda = lambda(
Type.of(Func1.class).makeGenericType(Types.String, Types.String),
condition(isNull(p), constant("true"), constant("false")),
p
);
final LambdaExpression<Func1<String, String>> isNotNullLambda = lambda(
Type.of(Func1.class).makeGenericType(Types.String, Types.String),
condition(isNotNull(p), constant("true"), constant("false")),
p
);
final Func1<String, String> isNullCheck = isNullLambda.compile();
final Func1<String, String> isNullNotCheck = isNotNullLambda.compile();
assertEquals("false", isNullCheck.apply("notNull"));
assertEquals("true", isNullCheck.apply(null));
assertEquals("true", isNullNotCheck.apply("notNull"));
assertEquals("false", isNullNotCheck.apply(null));
}
@Test
public void testRelationalAndAlso() throws Throwable {
runRelationalBranchTest(true, constant(false), constant(true));
runRelationalBranchTest(true, constant((byte)2), constant((byte)3));
runRelationalBranchTest(true, constant('a'), constant('b'));
runRelationalBranchTest(true, constant((short)2), constant((short)3));
runRelationalBranchTest(true, constant(2), constant(3));
runRelationalBranchTest(true, constant(2L), constant(3L));
runRelationalBranchTest(true, constant(2f), constant(3f));
runRelationalBranchTest(true, constant(2d), constant(3d));
final Expression leftString = constant("two");
final Expression rightString = constant("three");
final Expression nullString = constant(null, Types.String);
runRelationalBranchTest(true, leftString, rightString);
runRelationalBranchTest(true, leftString, nullString);
}
@Test
public void testRelationalOrElse() throws Throwable {
runRelationalBranchTest(false, constant(false), constant(true));
runRelationalBranchTest(false, constant((byte)2), constant((byte)3));
runRelationalBranchTest(false, constant('a'), constant('b'));
runRelationalBranchTest(false, constant((short)2), constant((short)3));
runRelationalBranchTest(false, constant(2), constant(3));
runRelationalBranchTest(false, constant(2L), constant(3L));
runRelationalBranchTest(false, constant(2f), constant(3f));
runRelationalBranchTest(false, constant(2d), constant(3d));
final Expression leftString = constant("two");
final Expression rightString = constant("three");
final Expression nullString = constant(null, Types.String);
runRelationalBranchTest(false, leftString, rightString);
runRelationalBranchTest(false, leftString, nullString);
}
private static void runRelationalBranchTest(final boolean isAnd, final Expression smaller, final Expression larger) throws Throwable {
assertTrue(runRelationalBranchTestForOp(isAnd, ExpressionType.Equal, smaller, smaller));
assertTrue(runRelationalBranchTestForOp(isAnd, ExpressionType.Equal, larger, larger));
assertFalse(runRelationalBranchTestForOp(isAnd, ExpressionType.Equal, smaller, larger));
assertTrue(runRelationalBranchTestForOp(isAnd, ExpressionType.NotEqual, smaller, larger));
assertFalse(runRelationalBranchTestForOp(isAnd, ExpressionType.NotEqual, larger, larger));
assertFalse(runRelationalBranchTestForOp(isAnd, ExpressionType.NotEqual, smaller, smaller));
final Type operandType = smaller.getType();
if (!TypeUtils.isArithmetic(operandType)) {
if (TypeUtils.isBoolean(operandType)) {
return;
}
assertTrue(runRelationalBranchTestForOp(isAnd, ExpressionType.ReferenceEqual, smaller, smaller));
assertTrue(runRelationalBranchTestForOp(isAnd, ExpressionType.ReferenceEqual, larger, larger));
assertFalse(runRelationalBranchTestForOp(isAnd, ExpressionType.ReferenceEqual, smaller, larger));
assertTrue(runRelationalBranchTestForOp(isAnd, ExpressionType.ReferenceNotEqual, smaller, larger));
assertFalse(runRelationalBranchTestForOp(isAnd, ExpressionType.ReferenceNotEqual, larger, larger));
assertFalse(runRelationalBranchTestForOp(isAnd, ExpressionType.ReferenceNotEqual, smaller, smaller));
return;
}
assertTrue(runRelationalBranchTestForOp(isAnd, ExpressionType.GreaterThan, larger, smaller));
assertFalse(runRelationalBranchTestForOp(isAnd, ExpressionType.GreaterThan, larger, larger));
assertFalse(runRelationalBranchTestForOp(isAnd, ExpressionType.GreaterThan, smaller, larger));
assertTrue(runRelationalBranchTestForOp(isAnd, ExpressionType.GreaterThanOrEqual, smaller, smaller));
assertTrue(runRelationalBranchTestForOp(isAnd, ExpressionType.GreaterThanOrEqual, larger, larger));
assertFalse(runRelationalBranchTestForOp(isAnd, ExpressionType.GreaterThanOrEqual, smaller, larger));
assertTrue(runRelationalBranchTestForOp(isAnd, ExpressionType.LessThan, smaller, larger));
assertFalse(runRelationalBranchTestForOp(isAnd, ExpressionType.LessThan, larger, larger));
assertFalse(runRelationalBranchTestForOp(isAnd, ExpressionType.LessThan, larger, smaller));
assertTrue(runRelationalBranchTestForOp(isAnd, ExpressionType.LessThanOrEqual, smaller, smaller));
assertTrue(runRelationalBranchTestForOp(isAnd, ExpressionType.LessThanOrEqual, smaller, larger));
assertFalse(runRelationalBranchTestForOp(isAnd, ExpressionType.LessThanOrEqual, larger, smaller));
}
private static boolean runRelationalBranchTestForOp(
final boolean isAnd,
final ExpressionType op,
final Expression left,
final Expression right) throws Throwable {
final OrCountdown counter1 = new OrCountdown(isAnd ? 1 : 2);
final OrCountdown counter2 = new OrCountdown(isAnd ? 1 : 2);
final Expression counterConstant1 = constant(counter1);
final Expression counterConstant2 = constant(counter2);
final LambdaExpression<Callable<String>> callback = lambda(
Types.Callable.makeGenericType(Types.String),
condition(
makeBinary(
isAnd ? ExpressionType.AndAlso : ExpressionType.OrElse,
call(counterConstant1, "test", constant(1)),
makeBinary(
isAnd ? ExpressionType.AndAlso : ExpressionType.OrElse,
makeBinary(op, left, right),
call(counterConstant2, "test", constant(2))
)
),
constant("true"),
constant("false")
)
);
final Callable<String> delegate = callback.compile();
final String stringResult = delegate.call();
final boolean result = "true".equals(stringResult);
assertEquals(1, counter1.timesCalled());
assertEquals(isAnd ? (result ? 1 : 0) : (result ? 0 : 1), counter2.timesCalled());
return result;
}
}