/* * Hibernate OGM, Domain model persistence for NoSQL datastores * * License: GNU Lesser General Public License (LGPL), version 2.1 or later * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>. */ package org.hibernate.ogm.utils; import java.net.URL; import java.nio.file.Files; import java.nio.file.Paths; import java.util.function.Predicate; import java.util.stream.Stream; import org.hibernate.ogm.datastore.impl.DatastoreProviderType; import org.hibernate.ogm.dialect.spi.GridDialect; import org.jboss.byteman.contrib.bmunit.BMUnitRunner; import org.junit.runner.Description; import org.junit.runner.notification.RunNotifier; import org.junit.runners.model.FrameworkMethod; import org.junit.runners.model.InitializationError; /** * A JUnit 4 test runner for which allows to skip tests for single grid dialects or datastore providers by annotating * them with {@link SkipByGridDialect} or {@link SkipByDatastoreProvider}, respectively. * * @author Gunnar Morling */ public class SkippableTestRunner extends BMUnitRunner { public SkippableTestRunner(Class<?> klass) throws InitializationError { super( klass ); } @Override public void run(RunNotifier notifier) { if ( isTestClassSkipped() || areAllTestMethodsSkipped() ) { skipTest( notifier ); } else { super.run( notifier ); } } @Override protected void runChild(final FrameworkMethod method, RunNotifier notifier) { if ( isTestMethodSkipped( method ) ) { skipTest( method, notifier ); } else { super.runChild( method, notifier ); } } /** * Skip a whole test class. */ protected void skipTest(RunNotifier notifier) { notifier.fireTestIgnored( Description.createSuiteDescription( super.getTestClass().getJavaClass() ) ); } /** * Skip a test method. */ protected void skipTest(FrameworkMethod method, RunNotifier notifier) { notifier.fireTestIgnored( describeChild( method ) ); } /** * Whether the test class is skipped by {@link SkipByDatastoreProvider}. */ protected boolean isTestClassSkipped() { return isTestClassSkippedByDatastoreProvider() || isTestClassSkippedByGridDialect() || isTestClassSkippedByFiles(); } private boolean isTestClassSkippedByDatastoreProvider() { SkipByDatastoreProvider skipByDatastoreProvider = getTestClass().getJavaClass().getAnnotation( SkipByDatastoreProvider.class ); return skipByDatastoreProvider != null ? isSkipped( skipByDatastoreProvider ) : false; } private boolean isTestClassSkippedByGridDialect() { SkipByGridDialect skipByGridDialect = getTestClass().getJavaClass().getAnnotation( SkipByGridDialect.class ); return skipByGridDialect != null ? isSkipped( skipByGridDialect ) : false; } private boolean isTestClassSkippedByFiles() { Class<?> testClass = getTestClass().getJavaClass(); final Predicate<String> predicate = new ClassNameIsSkippable( testClass ); return isSkippable( predicate ); } private boolean isSkippable(final Predicate<String> predicate) { URL resourceAsStream = Thread.currentThread().getContextClassLoader().getResource( "skipTests" ); if ( resourceAsStream != null ) { try ( Stream<String> stream = Files.lines( Paths.get( resourceAsStream.toURI() ) ) ) { return stream.anyMatch( predicate ); } catch (Exception e) { throw new RuntimeException( e ); } } return false; } private static class ClassNameIsSkippable implements Predicate<String> { private final Class<?> testClass; public ClassNameIsSkippable(Class<?> testClass) { this.testClass = testClass; } @Override public boolean test(String classNameToSkip) { return test( testClass, classNameToSkip ); } private static boolean test(Class<?> testClass, String classNameToSkip) { if ( testClass.getName().equals( classNameToSkip ) ) { return true; } Class<?> superclass = testClass.getSuperclass(); if ( superclass != null && superclass != Object.class ) { return test( superclass, classNameToSkip ); } return false; } } /** * Whether the given method is to be skipped or not, by means of the {@link SkipByDatastoreProvider} or {@link SkipByGridDialect} either given on the * method itself or on the class of the test. */ protected boolean isTestMethodSkipped(final FrameworkMethod method) { if ( isTestMethodSkippedByDatastoreProvider( method ) || isTestMethodSkippedByGridDialect( method ) ) { return true; } if ( isTestMethodSkippedByFiles( method ) ) { return true; } return isTestClassSkipped(); } private boolean isTestMethodSkippedByDatastoreProvider(FrameworkMethod method) { SkipByDatastoreProvider skipByDatastoreProvider = method.getAnnotation( SkipByDatastoreProvider.class ); return skipByDatastoreProvider != null ? isSkipped( skipByDatastoreProvider ) : false; } private boolean isTestMethodSkippedByGridDialect(FrameworkMethod method) { SkipByGridDialect skipByGridDialect = method.getAnnotation( SkipByGridDialect.class ); return skipByGridDialect != null ? isSkipped( skipByGridDialect ) : false; } private boolean isTestMethodSkippedByFiles(FrameworkMethod method) { final Predicate<String> predicate = new MethodIsSkippable( method ); return isSkippable( predicate ); } private static class MethodIsSkippable implements Predicate<String> { private final Class<?> testClass; private final String methodName; private final Predicate<String> classNameIsSkippable; public MethodIsSkippable(FrameworkMethod method) { this.testClass = method.getDeclaringClass(); this.methodName = method.getName(); this.classNameIsSkippable = new ClassNameIsSkippable( testClass ); } @Override public boolean test(String fullMethodName) { int index = fullMethodName.indexOf( '#' ); if ( index > 0 ) { String classToSkip = fullMethodName.substring( 0, index ); if ( classNameIsSkippable.test( classToSkip ) ) { String skippableMethodName = fullMethodName.substring( index + 1 ); if ( methodName.equals( skippableMethodName ) ) { return true; } } } return false; } } protected boolean areAllTestMethodsSkipped() { for ( FrameworkMethod method : getChildren() ) { if ( !isTestMethodSkipped( method ) ) { return false; } } return true; } private boolean isSkipped(SkipByDatastoreProvider skipByDatastoreProvider) { for ( DatastoreProviderType datastoreProvider : skipByDatastoreProvider.value() ) { if ( datastoreProvider == TestHelper.getCurrentDatastoreProviderType() ) { return true; } } return false; } private boolean isSkipped(SkipByGridDialect skipByGridDialect) { Class<? extends GridDialect> actualGridDialectClass = TestHelper.getCurrentGridDialectClass(); for ( GridDialectType gridDialectType : skipByGridDialect.value() ) { Class<? extends GridDialect> gridDialectClass = gridDialectType.loadGridDialectClass(); if ( isSkipped( actualGridDialectClass, gridDialectClass ) ) { return true; } } for ( Class<? extends GridDialect> gridDialectClass : skipByGridDialect.dialects() ) { if ( isSkipped( actualGridDialectClass, gridDialectClass ) ) { return true; } } return false; } private boolean isSkipped(Class<? extends GridDialect> actualGridDialectClass, Class<? extends GridDialect> gridDialectClass) { boolean skipTest = gridDialectClass != null && gridDialectClass.isAssignableFrom( actualGridDialectClass ); return skipTest; } }