-
Notifications
You must be signed in to change notification settings - Fork 246
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
HSEARCH-3654 Attempt for reusable mapper replacement
- Loading branch information
1 parent
c54d186
commit 4c1bae2
Showing
3 changed files
with
586 additions
and
0 deletions.
There are no files selected for viewing
233 changes: 233 additions & 0 deletions
233
...common/src/main/java/org/hibernate/search/util/impl/test/extension/MultiRunExtension.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,233 @@ | ||
/* | ||
* Hibernate Search, full-text search for your domain model | ||
* | ||
* 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.search.util.impl.test.extension; | ||
|
||
import static java.util.Spliterators.spliteratorUnknownSize; | ||
import static java.util.stream.StreamSupport.stream; | ||
import static org.junit.platform.commons.util.AnnotationUtils.findRepeatableAnnotations; | ||
import static org.junit.platform.commons.util.AnnotationUtils.isAnnotated; | ||
|
||
import java.lang.annotation.ElementType; | ||
import java.lang.annotation.Retention; | ||
import java.lang.annotation.RetentionPolicy; | ||
import java.lang.annotation.Target; | ||
import java.lang.reflect.Method; | ||
import java.util.ArrayList; | ||
import java.util.Iterator; | ||
import java.util.List; | ||
import java.util.Spliterator; | ||
import java.util.stream.Collectors; | ||
import java.util.stream.Stream; | ||
|
||
import org.junit.jupiter.api.TestTemplate; | ||
import org.junit.jupiter.api.extension.AfterEachCallback; | ||
import org.junit.jupiter.api.extension.ExtendWith; | ||
import org.junit.jupiter.api.extension.Extension; | ||
import org.junit.jupiter.api.extension.ExtensionContext; | ||
import org.junit.jupiter.api.extension.ParameterContext; | ||
import org.junit.jupiter.api.extension.ParameterResolutionException; | ||
import org.junit.jupiter.api.extension.ParameterResolver; | ||
import org.junit.jupiter.api.extension.TestTemplateInvocationContext; | ||
import org.junit.jupiter.api.extension.TestTemplateInvocationContextProvider; | ||
import org.junit.jupiter.params.provider.Arguments; | ||
import org.junit.jupiter.params.provider.ArgumentsProvider; | ||
import org.junit.jupiter.params.provider.ArgumentsSource; | ||
import org.junit.jupiter.params.support.AnnotationConsumerInitializer; | ||
import org.junit.platform.commons.util.AnnotationUtils; | ||
import org.junit.platform.commons.util.ReflectionUtils; | ||
|
||
import org.opentest4j.AssertionFailedError; | ||
|
||
public final class MultiRunExtension | ||
implements TestTemplateInvocationContextProvider, AfterEachCallback, ParameterResolver { | ||
private List<Object[]> envArguments; | ||
private int envIndex = 0; | ||
private boolean envInitialized = false; | ||
private Method initMethod; | ||
|
||
@Override | ||
public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) | ||
throws ParameterResolutionException { | ||
return true; | ||
} | ||
|
||
@Override | ||
public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) | ||
throws ParameterResolutionException { | ||
if ( envArguments == null ) { | ||
envArguments = new ArrayList<>(); | ||
Method testMethod = extensionContext.getRequiredTestMethod(); | ||
|
||
for ( ArgumentsSource source : findRepeatableAnnotations( testMethod, ArgumentsSource.class ) ) { | ||
ArgumentsProvider argumentsProvider = | ||
AnnotationConsumerInitializer.initialize( testMethod, ReflectionUtils.newInstance( source.value() ) ); | ||
try { | ||
envArguments.addAll( | ||
argumentsProvider.provideArguments( extensionContext ) | ||
.map( Arguments::get ) | ||
.collect( Collectors.toList() ) ); | ||
} | ||
catch (Exception e) { | ||
throw new IllegalStateException( "unable to read arguments.", e ); | ||
} | ||
} | ||
} | ||
return envArguments.get( envIndex )[parameterContext.getIndex()]; | ||
} | ||
|
||
@Target({ ElementType.METHOD }) | ||
@Retention(RetentionPolicy.RUNTIME) | ||
@TestTemplate | ||
@ExtendWith(MultiRunExtension.class) | ||
public @interface EnvironmentTest { | ||
String init() default "init"; | ||
} | ||
|
||
@Target({ ElementType.METHOD }) | ||
@Retention(RetentionPolicy.RUNTIME) | ||
public @interface TestToExecute { | ||
} | ||
|
||
@Override | ||
public boolean supportsTestTemplate(ExtensionContext context) { | ||
return isAnnotated( context.getTestMethod(), EnvironmentTest.class ); | ||
} | ||
|
||
@Override | ||
public Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(ExtensionContext context) { | ||
EnvironmentTest environment = context.getTestMethod() | ||
.flatMap( method -> AnnotationUtils.findAnnotation( method, EnvironmentTest.class ) ) | ||
.orElseThrow( IllegalStateException::new ); | ||
|
||
String init = environment.init(); | ||
Class<?> testClass = context.getTestClass().orElseThrow(); | ||
try { | ||
initMethod = testClass.getDeclaredMethod( init, context.getRequiredTestMethod().getParameterTypes() ); | ||
initMethod.setAccessible( true ); | ||
} | ||
catch (NoSuchMethodException e) { | ||
throw new IllegalStateException( "Cannot locate init method.", e ); | ||
} | ||
|
||
|
||
// find actual "tests" that we'll invoke via reflection: | ||
List<Method> testMethods = new ArrayList<>(); | ||
for ( Method method : testClass.getDeclaredMethods() ) { | ||
if ( AnnotationUtils.isAnnotated( method, TestToExecute.class ) ) { | ||
testMethods.add( method ); | ||
} | ||
} | ||
|
||
if ( testMethods.isEmpty() ) { | ||
throw new IllegalStateException( "No tests to execute were found." ); | ||
} | ||
|
||
return stream( spliteratorUnknownSize( | ||
new Iterator<TestTemplateInvocationContext>() { | ||
|
||
Iterator<Method> test = testMethods.iterator(); | ||
|
||
@Override | ||
public boolean hasNext() { | ||
if ( Boolean.TRUE.equals( read( context, StoreKey.STOP_RUNNING, Boolean.class ) ) ) { | ||
return false; | ||
} | ||
if ( test.hasNext() ) { | ||
return true; | ||
} | ||
else { | ||
envIndex++; | ||
envInitialized = false; | ||
} | ||
|
||
if ( envIndex < envArguments.size() ) { | ||
test = testMethods.iterator(); | ||
return test.hasNext(); | ||
} | ||
return false; | ||
} | ||
|
||
@Override | ||
public TestTemplateInvocationContext next() { | ||
if ( envInitialized ) { | ||
Method testMethod = test.next(); | ||
write( context, StoreKey.TEST_TO_RUN, testMethod ); | ||
|
||
return new TestTemplateInvocationContext() { | ||
|
||
@Override | ||
public String getDisplayName(int invocationIndex) { | ||
return "Env #" + envIndex + ": " + testMethod.getName(); | ||
} | ||
|
||
@Override | ||
public List<Extension> getAdditionalExtensions() { | ||
return TestTemplateInvocationContext.super.getAdditionalExtensions(); | ||
} | ||
}; | ||
} | ||
else { | ||
return new TestTemplateInvocationContext() { | ||
|
||
@Override | ||
public String getDisplayName(int invocationIndex) { | ||
return "Env #" + envIndex + ": Initializing"; | ||
} | ||
|
||
@Override | ||
public List<Extension> getAdditionalExtensions() { | ||
return TestTemplateInvocationContext.super.getAdditionalExtensions(); | ||
} | ||
}; | ||
} | ||
} | ||
}, Spliterator.NONNULL | ||
), false ); | ||
} | ||
|
||
@Override | ||
public void afterEach(ExtensionContext extensionContext) throws Exception { | ||
// that's where we actually execute the test or init the env | ||
if ( envInitialized ) { | ||
Method testMethod = read( extensionContext, StoreKey.TEST_TO_RUN, Method.class ); | ||
testMethod.setAccessible( true ); | ||
testMethod.invoke( extensionContext.getRequiredTestInstance() ); | ||
} | ||
else { | ||
try { | ||
initMethod.invoke( extensionContext.getRequiredTestInstance(), envArguments.get( envIndex ) ); | ||
} | ||
catch (Exception e) { | ||
envIndex++; | ||
if ( envIndex >= envArguments.size() ) { | ||
write( extensionContext, StoreKey.STOP_RUNNING, Boolean.TRUE ); | ||
} | ||
throw new AssertionFailedError( "Unable to init the env, stopping further execution", e ); | ||
} | ||
envInitialized = true; | ||
} | ||
} | ||
|
||
private void write(ExtensionContext context, StoreKey key, Object value) { | ||
ExtensionContext.Store store = context.getRoot().getStore( | ||
ExtensionContext.Namespace.create( context.getRequiredTestMethod() ) | ||
); | ||
store.put( key, value ); | ||
} | ||
|
||
private <T> T read(ExtensionContext context, StoreKey key, Class<T> clazz) { | ||
ExtensionContext.Store store = context.getRoot().getStore( | ||
ExtensionContext.Namespace.create( context.getRequiredTestMethod() ) | ||
); | ||
return store.get( key, clazz ); | ||
} | ||
|
||
private enum StoreKey { | ||
TEST_TO_RUN, | ||
STOP_RUNNING | ||
} | ||
} |
Oops, something went wrong.