/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed 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 org.springsource.loaded.testgen;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.junit.Assert;
import org.junit.runner.Description;
import org.junit.runner.notification.Failure;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.ParentRunner;
import org.junit.runners.model.InitializationError;
import org.springsource.loaded.test.infra.IResult;
import org.springsource.loaded.test.infra.ResultException;
/**
* This test runner in injects an IChoiceGenerator into the test class it is running. The choice generator is injected by setting
* choiceGenerator field in the test instance.
* <p>
* For now this test runner assumes the test class is using JUnit 3 style method conventions for setup and teardown, and should
* provide only a single 'test' method to run the tests.
*
* @author kdvolder
*/
public class ExploreAllChoicesRunner extends ParentRunner<GeneratedTest> {
private static boolean generatedTestsOn = Boolean.parseBoolean(System.getProperty("springloaded.tests.generatedTests", "true"));
private Class<? extends GenerativeSpringLoadedTest> testClass;
private List<GeneratedTest> children = null;
private boolean predictResults;
public ExploreAllChoicesRunner(Class<? extends GenerativeSpringLoadedTest> testClass) throws InitializationError {
super(testClass);
this.testClass = testClass;
this.predictResults = testClass.isAnnotationPresent(PredictResult.class);
}
@Override
protected List<GeneratedTest> getChildren() {
if (!generatedTestsOn)
return Collections.emptyList();
if (children != null)
return children;
List<GeneratedTest> newChildren = new ArrayList<GeneratedTest>();
try {
SystematicChoiceGenerator choiceGenerator = new SystematicChoiceGenerator();
do {
GenerativeTest test = testClass.newInstance();
//Inject a choice generator into the test!
test.choiceGenerator = choiceGenerator;
test.generative = true;
try {
test.setup();
IResult r = null;
if (predictResults) {
try {
r = test.test(); //run test in generative mode
} catch (ResultException e) {
r = e;
} catch (RejectedChoice e) {
//Choices shouldn't be rejected during test run only during setup!
throw new IllegalStateException(e);
}
}
addTest(newChildren, new GeneratedTest(choiceGenerator.choices, r, test.getConfigDescription()));
} catch (RejectedChoice e) {
//Ignore this test
} finally {
test.teardown();
}
} while (choiceGenerator.backtrack());
children = newChildren;
return newChildren;
} catch (Exception e) {
//TODO: [...] use JUnit framework to handle this more gracefully
// This probably means overriding one of the validation methods and, moving most of this code into validation
// and storing the children during validation so that this method here will only have to return the stored
// children and should never raise any errors.
throw new Error(e);
}
}
protected void addTest(List<GeneratedTest> newChildren, GeneratedTest test) {
// System.out.println("Adding "+test.getDisplayName());
newChildren.add(test);
}
@Override
protected Description describeChild(GeneratedTest child) {
return Description.createTestDescription(testClass, sanitise(child.getDisplayName()));
}
/**
* Certain characters confuse the Eclipse JUnit view... replace those with harmless ones.
*/
private String sanitise(String displayName) {
return displayName.replace('(', '[').replace(')', ']').replace('\n', ' ').replace('\r', ' ');
}
@Override
protected void runChild(GeneratedTest testPredicted, RunNotifier notifier) {
notifier.fireTestStarted(describeChild(testPredicted));
try {
//Determine expected test result first:
IResult expectedResult = testPredicted.getExpectedResult();
if (expectedResult == null) {
//Suite was not created with @PredictResult must predict it now
GenerativeTest test = testClass.newInstance();
//Inject a choice generator into the test!
test.choiceGenerator = new SystematicChoiceGenerator(testPredicted.getChoices());
test.generative = true;
try {
test.setup();
expectedResult = test.test();
} catch (ResultException e) {
expectedResult = e;
} finally {
test.teardown();
}
}
//Run the test again and verify
GenerativeTest test = testClass.newInstance();
//Inject a choice generator into the test!
test.choiceGenerator = new SystematicChoiceGenerator(testPredicted.getChoices());
test.generative = false;
try {
test.setup();
Assert.assertEquals(testPredicted.getConfigDescription(), test.getConfigDescription());
IResult actual;
try {
actual = test.test(); //run test in verify mode
} catch (ResultException e) {
actual = e;
}
test.assertEqualIResults(expectedResult, actual);
} finally {
test.teardown();
}
} catch (Throwable e) {
notifier.fireTestFailure(new Failure(describeChild(testPredicted), e));
} finally {
notifier.fireTestFinished(describeChild(testPredicted));
}
}
}