Skip to content

Instantly share code, notes, and snippets.

@szpak
Created June 10, 2012 16:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save szpak/2906483 to your computer and use it in GitHub Desktop.
Save szpak/2906483 to your computer and use it in GitHub Desktop.
PoC of ConditionFactory for FEST-Assert
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