/*
* Licensed to the author under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 net.java.quickcheck.junit4;
import static java.lang.String.*;
import static net.java.quickcheck.junit.support.GeneratorFactory.*;
import java.lang.reflect.InvocationTargetException;
import junit.framework.Assert;
import net.java.quickcheck.Generator;
import net.java.quickcheck.Runner;
import net.java.quickcheck.RunnerFactory;
import net.java.quickcheck.characteristic.Classification;
import net.java.quickcheck.characteristic.ClassificationExpectation;
import net.java.quickcheck.generator.support.RoundRobinGenerator;
import net.java.quickcheck.junit.ForAll;
import org.junit.internal.runners.MethodRoadie;
import org.junit.runner.Description;
import org.junit.runner.notification.RunNotifier;
class QuickCheckMethodRoadie extends MethodRoadie {
private final Generator<Object> generator;
private final Object test;
private final QuickCheckTestMethod method;
private final ForAll annotation;
public QuickCheckMethodRoadie(Object test, QuickCheckTestMethod method,
RunNotifier notifier, Description description) {
super(test, method, notifier, description);
ForAll annotation = method.getMethod().getAnnotation(ForAll.class);
this.annotation = annotation;
generator = new RoundRobinGenerator<Object>(createGenerator(test,
annotation));
this.test = test;
this.method = method;
}
@Override
public void runTest() {
clearExpectations();
try {
final RunnerCharacteristic runnerCharacteristic = new RunnerCharacteristic();
Runner<Object> runner = new RunnerFactory().createRunner(generator, annotation.runs(),
runnerCharacteristic, annotation.verbose());
runner.forAll();
ClassificationExpectation expectation = ClassificationExpectation
.gathered();
if (expectation != null) {
Classification classification = Classification.gathered();
Assert.assertEquals(format("expected %s but was %s",
expectation, classification), true, expectation
.validate(classification));
}
} finally {
clearExpectations();
}
}
private void clearExpectations() {
ClassificationExpectation.clear();
Classification.clear();
}
private final class RunnerCharacteristic implements
net.java.quickcheck.Characteristic<Object> {
public void specify(Object any) throws Throwable {
// One can not throw exceptions in the
// runBeforesThenTestThenAfters method. So the information that
// the test failed has to be tunneled otherwise.
TestRunnable testRunnable = new TestRunnable(any);
runBeforesThenTestThenAfters(testRunnable);
if (testRunnable.isStopped()) {
throw testRunnable.getCause();
}
Classification.callDone();
}
public String name() {
return method.getMethod().getName();
}
public void setUp() throws Exception {
}
public void tearDown() throws Exception {
}
}
private final class TestRunnable implements Runnable {
private final Object any;
private boolean stop = false;
private Throwable cause;
private TestRunnable(Object any) {
this.any = any;
}
public void run() {
try {
method.getMethod().invoke(test, any);
} catch (InvocationTargetException e) {
// exceptions thrown by characteristic methods may
// not be part of their signatures so there's a
// invocation target exception thrown
stopNow(e.getCause());
} catch (Exception e) {
stopNow(e);
}
}
private void stopNow(Throwable e) {
stop = true;
cause = e;
}
public boolean isStopped() {
return stop;
}
public Throwable getCause() {
return cause;
}
}
}