/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2011, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.testing.junit4;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.List;
import org.jboss.logging.Logger;
import org.hibernate.dialect.Dialect;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.testing.DialectCheck;
import org.hibernate.testing.FailureExpected;
import org.hibernate.testing.RequiresDialect;
import org.hibernate.testing.RequiresDialectFeature;
import org.hibernate.testing.Skip;
import org.hibernate.testing.SkipForDialect;
import org.junit.Ignore;
import org.junit.runner.manipulation.NoTestsRemainException;
import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.InitializationError;
import org.junit.runners.model.Statement;
/**
* The Hibernate-specific {@link org.junit.runner.Runner} implementation which layers {@link ExtendedFrameworkMethod}
* support on top of the standard JUnit {@link FrameworkMethod} for extra information after checking to make sure the
* test should be run.
*
* @author Steve Ebersole
*/
public class CustomRunner extends BlockJUnit4ClassRunner {
private static final Logger log = Logger.getLogger( CustomRunner.class );
private TestClassMetadata testClassMetadata;
public CustomRunner(Class<?> clazz) throws InitializationError, NoTestsRemainException {
super( clazz );
}
@Override
protected void collectInitializationErrors(List<Throwable> errors) {
super.collectInitializationErrors( errors );
this.testClassMetadata = new TestClassMetadata( getTestClass().getJavaClass() );
testClassMetadata.validate( errors );
}
public TestClassMetadata getTestClassMetadata() {
return testClassMetadata;
}
private Boolean isAllTestsIgnored = null;
private boolean isAllTestsIgnored() {
if ( isAllTestsIgnored == null ) {
if ( computeTestMethods().isEmpty() ) {
isAllTestsIgnored = true;
}
else {
isAllTestsIgnored = true;
for ( FrameworkMethod method : computeTestMethods() ) {
Ignore ignore = method.getAnnotation( Ignore.class );
if ( ignore == null ) {
isAllTestsIgnored = false;
break;
}
}
}
}
return isAllTestsIgnored;
}
@Override
protected Statement withBeforeClasses(Statement statement) {
if ( isAllTestsIgnored() ) {
return super.withBeforeClasses( statement );
}
return new BeforeClassCallbackHandler(
this,
super.withBeforeClasses( statement )
);
}
@Override
protected Statement withAfterClasses(Statement statement) {
if ( isAllTestsIgnored() ) {
return super.withAfterClasses( statement );
}
return new AfterClassCallbackHandler(
this,
super.withAfterClasses( statement )
);
}
@Override
protected Statement methodBlock(FrameworkMethod method) {
final Statement originalMethodBlock = super.methodBlock( method );
final ExtendedFrameworkMethod extendedFrameworkMethod = (ExtendedFrameworkMethod) method;
return new FailureExpectedHandler( originalMethodBlock, testClassMetadata, extendedFrameworkMethod, testInstance );
}
private Object testInstance;
protected Object getTestInstance() throws Exception {
if ( testInstance == null ) {
testInstance = super.createTest();
}
return testInstance;
}
@Override
protected Object createTest() throws Exception {
return getTestInstance();
}
private List<FrameworkMethod> computedTestMethods;
@Override
protected List<FrameworkMethod> computeTestMethods() {
if ( computedTestMethods == null ) {
computedTestMethods = doComputation();
}
return computedTestMethods;
}
protected List<FrameworkMethod> doComputation() {
// Next, get all the test methods as understood by JUnit
final List<FrameworkMethod> methods = super.computeTestMethods();
// Now process that full list of test methods and build our custom result
final List<FrameworkMethod> result = new ArrayList<FrameworkMethod>();
final boolean doValidation = Boolean.getBoolean( Helper.VALIDATE_FAILURE_EXPECTED );
int testCount = 0;
Ignore virtualIgnore;
for ( FrameworkMethod frameworkMethod : methods ) {
// potentially ignore based on expected failure
final FailureExpected failureExpected = Helper.locateAnnotation( FailureExpected.class, frameworkMethod, getTestClass() );
if ( failureExpected != null && !doValidation ) {
virtualIgnore = new IgnoreImpl( Helper.extractIgnoreMessage( failureExpected, frameworkMethod ) );
}
else {
virtualIgnore = convertSkipToIgnore( frameworkMethod );
}
testCount++;
log.trace( "adding test " + Helper.extractTestName( frameworkMethod ) + " [#" + testCount + "]" );
result.add( new ExtendedFrameworkMethod( frameworkMethod, virtualIgnore, failureExpected ) );
}
return result;
}
@SuppressWarnings( {"ClassExplicitlyAnnotation"})
public static class IgnoreImpl implements Ignore {
private final String value;
public IgnoreImpl(String value) {
this.value = value;
}
@Override
public String value() {
return value;
}
@Override
public Class<? extends Annotation> annotationType() {
return Ignore.class;
}
}
private static Dialect dialect = determineDialect();
private static Dialect determineDialect() {
try {
return Dialect.getDialect();
}
catch( Exception e ) {
return new Dialect() {
};
}
}
protected Ignore convertSkipToIgnore(FrameworkMethod frameworkMethod) {
// @Skip
Skip skip = Helper.locateAnnotation( Skip.class, frameworkMethod, getTestClass() );
if ( skip != null ) {
if ( isMatch( skip.condition() ) ) {
return buildIgnore( skip );
}
}
// @SkipForDialect
SkipForDialect skipForDialectAnn = Helper.locateAnnotation( SkipForDialect.class, frameworkMethod, getTestClass() );
if ( skipForDialectAnn != null ) {
for ( Class<? extends Dialect> dialectClass : skipForDialectAnn.value() ) {
if ( skipForDialectAnn.strictMatching() ) {
if ( dialectClass.equals( dialect.getClass() ) ) {
return buildIgnore( skipForDialectAnn );
}
}
else {
if ( dialectClass.isInstance( dialect ) ) {
return buildIgnore( skipForDialectAnn );
}
}
}
}
// @RequiresDialect
RequiresDialect requiresDialectAnn = Helper.locateAnnotation( RequiresDialect.class, frameworkMethod, getTestClass() );
if ( requiresDialectAnn != null ) {
boolean foundMatch = false;
for ( Class<? extends Dialect> dialectClass : requiresDialectAnn.value() ) {
foundMatch = requiresDialectAnn.strictMatching()
? dialectClass.equals( dialect.getClass() )
: dialectClass.isInstance( dialect );
if ( foundMatch ) {
break;
}
}
if ( !foundMatch ) {
return buildIgnore( requiresDialectAnn );
}
}
// @RequiresDialectFeature
RequiresDialectFeature requiresDialectFeatureAnn = Helper.locateAnnotation( RequiresDialectFeature.class, frameworkMethod, getTestClass() );
if ( requiresDialectFeatureAnn != null ) {
Class<? extends DialectCheck> checkClass = requiresDialectFeatureAnn.value();
try {
DialectCheck check = checkClass.newInstance();
if ( !check.isMatch( dialect ) ) {
return buildIgnore( requiresDialectFeatureAnn );
}
}
catch (RuntimeException e) {
throw e;
}
catch (Exception e) {
throw new RuntimeException( "Unable to instantiate DialectCheck", e );
}
}
return null;
}
private Ignore buildIgnore(Skip skip) {
return new IgnoreImpl( "@Skip : " + skip.message() );
}
private Ignore buildIgnore(SkipForDialect skip) {
return buildIgnore( "@SkipForDialect match", skip.comment(), skip.jiraKey() );
}
private Ignore buildIgnore(String reason, String comment, String jiraKey) {
StringBuilder buffer = new StringBuilder( reason );
if ( StringHelper.isNotEmpty( comment ) ) {
buffer.append( "; " ).append( comment );
}
if ( StringHelper.isNotEmpty( jiraKey ) ) {
buffer.append( " (" ).append( jiraKey ).append( ')' );
}
return new IgnoreImpl( buffer.toString() );
}
private Ignore buildIgnore(RequiresDialect requiresDialect) {
return buildIgnore( "@RequiresDialect non-match", requiresDialect.comment(), requiresDialect.jiraKey() );
}
private Ignore buildIgnore(RequiresDialectFeature requiresDialectFeature) {
return buildIgnore( "@RequiresDialectFeature non-match", requiresDialectFeature.comment(), requiresDialectFeature.jiraKey() );
}
private boolean isMatch(Class<? extends Skip.Matcher> condition) {
try {
Skip.Matcher matcher = condition.newInstance();
return matcher.isMatch();
}
catch (Exception e) {
throw new MatcherInstantiationException( condition, e );
}
}
private static class MatcherInstantiationException extends RuntimeException {
private MatcherInstantiationException(Class<? extends Skip.Matcher> matcherClass, Throwable cause) {
super( "Unable to instantiate specified Matcher [" + matcherClass.getName(), cause );
}
}
}