Created
June 10, 2012 16:26
-
-
Save szpak/2906483 to your computer and use it in GitHub Desktop.
PoC of ConditionFactory for FEST-Assert
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.jayway.awaitility.core; | |
import com.jayway.awaitility.Duration; | |
import javassist.util.proxy.MethodFilter; | |
import javassist.util.proxy.MethodHandler; | |
import javassist.util.proxy.ProxyFactory; | |
import org.fest.assertions.api.BigDecimalAssert; | |
import org.fest.assertions.api.IntegerAssert; | |
import org.fest.assertions.api.ListAssert; | |
import org.fest.assertions.core.Assert; | |
import java.lang.reflect.InvocationTargetException; | |
import java.lang.reflect.Method; | |
import java.lang.reflect.Modifier; | |
import java.math.BigDecimal; | |
import java.util.List; | |
import java.util.concurrent.Callable; | |
import static java.lang.String.format; | |
import static org.fest.assertions.api.Assertions.assertThat; | |
public class FestConditionFactory extends ConditionFactory { | |
public FestConditionFactory(String alias, Duration timeout, Duration pollInterval, Duration pollDelay, | |
boolean catchUncaughtExceptions) { | |
super(alias, timeout, pollInterval, pollDelay, catchUncaughtExceptions); | |
} | |
public FestConditionFactory(Duration timeout, Duration pollInterval, Duration pollDelay, | |
boolean catchUncaughtExceptions) { | |
super(timeout, pollInterval, pollDelay, catchUncaughtExceptions); | |
} | |
public IntegerAssert untilCallFest2(Integer ignored) throws Exception { | |
final MethodCaller<Integer> valueSupplier = new MethodCaller<Integer>(MethodCallRecorder.getLastTarget(), MethodCallRecorder | |
.getLastMethod(), MethodCallRecorder.getLastArgs()); | |
MethodCallRecorder.reset(); | |
return createTypedProxy(new IntegerAssertThatSupplier(valueSupplier), generateConditionSettings(), IntegerAssert.class, Integer.class); | |
} | |
public BigDecimalAssert untilCallFest(BigDecimal ignored) throws Exception { | |
//TODO: MZA: Duplicated, can be moved inside some method | |
final MethodCaller<BigDecimal> valueSupplier = new MethodCaller<BigDecimal>(MethodCallRecorder.getLastTarget(), MethodCallRecorder | |
.getLastMethod(), MethodCallRecorder.getLastArgs()); | |
MethodCallRecorder.reset(); | |
return createTypedProxy(new BigDecimalAssertThatSupplier(valueSupplier), generateConditionSettings(), BigDecimalAssert.class, BigDecimal.class); | |
} | |
public <T> ListAssert<T> untilCallFest(List<T> ignored) throws Exception { | |
final MethodCaller<List<T>> valueSupplier = new MethodCaller<List<T>>(MethodCallRecorder.getLastTarget(), MethodCallRecorder | |
.getLastMethod(), MethodCallRecorder.getLastArgs()); | |
MethodCallRecorder.reset(); | |
return createTypedProxy(new ListAssertThatSupplier<T>(valueSupplier), generateConditionSettings(), ListAssert.class, List.class); | |
} | |
private <T, TA extends Assert<TA, T>> TA createTypedProxy(final Callable<TA> assertThatSupplier, final ConditionSettings settings, Class assertionClass, Class valueClass) { | |
try { | |
ProxyFactory pf = new ProxyFactory(); | |
pf.setSuperclass(assertionClass); | |
pf.setFilter(new MethodFilter() { | |
public boolean isHandled(Method method) { | |
System.out.println(method.toString() + " " + method.getModifiers() + " " + Modifier.isFinal(method.getModifiers())); | |
return true; //TODO: MZA: something to ignore? | |
} | |
}); | |
//TODO: MZA: Only the first "real" method from *Assert will be proxied. Can we ensure that this is | |
// an intended method (not some util method like as())? | |
MethodHandler handler = new MethodHandler() { | |
public Object invoke(final Object self, final Method thisMethod, final Method proceed, final Object[] args) throws Throwable { | |
System.out.println(format("Method %s called: ", thisMethod.getName())); | |
Callable<TA> delegate = new Callable<TA>() { | |
public TA call() throws Exception { | |
TA realAssert = assertThatSupplier.call(); | |
return (TA)thisMethod.invoke(realAssert, args); | |
} | |
}; | |
FestTypedCondition<T, TA> festCondition = new FestTypedCondition<T, TA>(delegate, settings); | |
festCondition.await(); | |
//TODO: MZA: Temporarily assumption that assert methods are always void methods | |
return null; | |
} | |
}; | |
//TODO: MZA: Will null work with every assert? Maybe there is an easier way to create mock on some class? | |
// we don't need the real object below - it is created internally during invocation | |
TA proxy = (TA) pf.create(new Class[]{valueClass}, new Object[]{null}, handler); | |
return proxy; | |
} catch (InstantiationException e) { | |
throw new RuntimeException(e); | |
} catch (IllegalAccessException e) { | |
throw new RuntimeException(e); | |
} catch (NoSuchMethodException e) { | |
throw new RuntimeException(e); | |
} catch (InvocationTargetException e) { | |
throw new RuntimeException(e); | |
} | |
} | |
class FestTypedCondition<T, TA extends Assert<TA, T>> implements Condition { | |
private ConditionAwaiter conditionAwaiter; | |
FestTypedCondition(final Callable<TA> assertionSupplier, ConditionSettings settings) { | |
final Callable<Boolean> callable = new Callable<Boolean>() { | |
public Boolean call() throws Exception { | |
try { | |
assertionSupplier.call(); | |
return true; | |
} catch (InvocationTargetException e) { | |
if (e.getCause() instanceof AssertionError) { | |
System.out.printf("AssertionError: %s, %s\n", e.getCause().getMessage(), e.getCause().getClass()); | |
return false; | |
} else { | |
throw e; | |
} | |
} | |
} | |
}; | |
conditionAwaiter = new ConditionAwaiter(callable, settings) { | |
@Override | |
protected String getTimeoutMessage() { | |
//TODO: MZA: Remember the last error message from AssertionError (as a field) and use it here | |
return String.format("%s expected %s but was <%s>", "Ups"/*getCallableDescription(assertionSupplier)*/, "Ups2"/*HamcrestToStringFilter.filter(matcher)*/, "TODO result"); | |
} | |
}; | |
} | |
public void await() throws Exception { | |
conditionAwaiter.await(); | |
} | |
} | |
} | |
abstract class AbstractAssertThatSupplier<T, AT> implements Callable<AT> { | |
private final ConditionFactory.MethodCaller<T> valueSupplier; | |
protected AbstractAssertThatSupplier(ConditionFactory.MethodCaller<T> valueSupplier) { | |
this.valueSupplier = valueSupplier; | |
} | |
protected T getValueFromSupplier() throws Exception { | |
return valueSupplier.call(); | |
} | |
} | |
class BigDecimalAssertThatSupplier extends AbstractAssertThatSupplier<BigDecimal, BigDecimalAssert> { | |
BigDecimalAssertThatSupplier(ConditionFactory.MethodCaller<BigDecimal> valueSupplier) { | |
super(valueSupplier); | |
} | |
public BigDecimalAssert call() throws Exception { | |
return assertThat(getValueFromSupplier()); | |
} | |
} | |
class IntegerAssertThatSupplier extends AbstractAssertThatSupplier<Integer, IntegerAssert> { | |
IntegerAssertThatSupplier(ConditionFactory.MethodCaller<Integer> valueSupplier) { | |
super(valueSupplier); | |
} | |
public IntegerAssert call() throws Exception { | |
return assertThat(getValueFromSupplier()); | |
} | |
} | |
class ListAssertThatSupplier<T> extends AbstractAssertThatSupplier<List<T>, ListAssert<T>> { | |
ListAssertThatSupplier(ConditionFactory.MethodCaller<List<T>> valueSupplier) { | |
super(valueSupplier); | |
} | |
public ListAssert<T> call() throws Exception { | |
return assertThat(getValueFromSupplier()); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment