-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
318 additions
and
0 deletions.
There are no files selected for viewing
227 changes: 227 additions & 0 deletions
227
src/main/java/org/embulk/gradle/embulk_plugins/EmbulkJUnitPlatformTestClassProcessor.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,227 @@ | ||
/* | ||
* Copyright 2018 the original author or authors. | ||
* | ||
* 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.embulk.gradle.embulk_plugins; | ||
|
||
import org.gradle.api.Action; | ||
import org.gradle.api.internal.tasks.testing.TestResultProcessor; | ||
import org.gradle.api.internal.tasks.testing.filter.TestSelectionMatcher; | ||
import org.gradle.api.internal.tasks.testing.junit.AbstractJUnitTestClassProcessor; | ||
import org.gradle.internal.UncheckedException; | ||
import org.gradle.internal.actor.Actor; | ||
import org.gradle.internal.actor.ActorFactory; | ||
import org.gradle.internal.id.IdGenerator; | ||
import org.gradle.internal.time.Clock; | ||
import org.junit.platform.engine.DiscoverySelector; | ||
import org.junit.platform.engine.FilterResult; | ||
import org.junit.platform.engine.TestDescriptor; | ||
import org.junit.platform.engine.TestSource; | ||
import org.junit.platform.engine.discovery.DiscoverySelectors; | ||
import org.junit.platform.engine.support.descriptor.ClassSource; | ||
import org.junit.platform.engine.support.descriptor.MethodSource; | ||
import org.junit.platform.launcher.Launcher; | ||
import org.junit.platform.launcher.LauncherDiscoveryRequest; | ||
import org.junit.platform.launcher.PostDiscoveryFilter; | ||
import org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder; | ||
import org.junit.platform.launcher.core.LauncherFactory; | ||
|
||
import javax.annotation.Nonnull; | ||
import java.lang.reflect.Modifier; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.Optional; | ||
import java.util.stream.Collectors; | ||
|
||
import static org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.isNestedClassInsideEnclosedRunner; | ||
import static org.junit.platform.launcher.EngineFilter.excludeEngines; | ||
import static org.junit.platform.launcher.EngineFilter.includeEngines; | ||
import static org.junit.platform.launcher.TagFilter.excludeTags; | ||
import static org.junit.platform.launcher.TagFilter.includeTags; | ||
|
||
public class EmbulkJUnitPlatformTestClassProcessor extends AbstractJUnitTestClassProcessor<JUnitPlatformSpec> { | ||
private CollectAllTestClassesExecutor testClassExecutor; | ||
|
||
public EmbulkJUnitPlatformTestClassProcessor(JUnitPlatformSpec spec, IdGenerator<?> idGenerator, ActorFactory actorFactory, Clock clock) { | ||
super(spec, idGenerator, actorFactory, clock); | ||
} | ||
|
||
@Override | ||
protected TestResultProcessor createResultProcessorChain(TestResultProcessor resultProcessor) { | ||
return resultProcessor; | ||
} | ||
|
||
@Override | ||
protected Action<String> createTestExecutor(Actor resultProcessorActor) { | ||
TestResultProcessor threadSafeResultProcessor = resultProcessorActor.getProxy(TestResultProcessor.class); | ||
testClassExecutor = new CollectAllTestClassesExecutor(threadSafeResultProcessor); | ||
return testClassExecutor; | ||
} | ||
|
||
@Override | ||
public void stop() { | ||
testClassExecutor.processAllTestClasses(); | ||
super.stop(); | ||
} | ||
|
||
private class CollectAllTestClassesExecutor implements Action<String> { | ||
private final List<Class<?>> testClasses = new ArrayList<>(); | ||
private final TestResultProcessor resultProcessor; | ||
|
||
CollectAllTestClassesExecutor(TestResultProcessor resultProcessor) { | ||
this.resultProcessor = resultProcessor; | ||
} | ||
|
||
@Override | ||
public void execute(@Nonnull String testClassName) { | ||
Class<?> klass = loadClass(testClassName); | ||
if (isInnerClass(klass) || isNestedClassInsideEnclosedRunner(klass)) { | ||
return; | ||
} | ||
testClasses.add(klass); | ||
} | ||
|
||
private void processAllTestClasses() { | ||
Launcher launcher = LauncherFactory.create(); | ||
launcher.registerTestExecutionListeners(new JUnitPlatformTestExecutionListener(resultProcessor, clock, idGenerator)); | ||
launcher.execute(createLauncherDiscoveryRequest(testClasses)); | ||
} | ||
} | ||
|
||
private boolean isInnerClass(Class<?> klass) { | ||
return klass.getEnclosingClass() != null && !Modifier.isStatic(klass.getModifiers()); | ||
} | ||
|
||
private Class<?> loadClass(String className) { | ||
try { | ||
ClassLoader applicationClassloader = Thread.currentThread().getContextClassLoader(); | ||
return Class.forName(className, false, applicationClassloader); | ||
} catch (ClassNotFoundException e) { | ||
throw UncheckedException.throwAsUncheckedException(e); | ||
} | ||
} | ||
|
||
private LauncherDiscoveryRequest createLauncherDiscoveryRequest(List<Class<?>> testClasses) { | ||
List<DiscoverySelector> classSelectors = testClasses.stream() | ||
.map(DiscoverySelectors::selectClass) | ||
.collect(Collectors.toList()); | ||
|
||
LauncherDiscoveryRequestBuilder requestBuilder = LauncherDiscoveryRequestBuilder.request().selectors(classSelectors); | ||
|
||
addTestNameFilters(requestBuilder); | ||
addEnginesFilter(requestBuilder); | ||
addTagsFilter(requestBuilder); | ||
|
||
return requestBuilder.build(); | ||
} | ||
|
||
private void addEnginesFilter(LauncherDiscoveryRequestBuilder requestBuilder) { | ||
if (!spec.getIncludeEngines().isEmpty()) { | ||
requestBuilder.filters(includeEngines(spec.getIncludeEngines())); | ||
} | ||
if (!spec.getExcludeEngines().isEmpty()) { | ||
requestBuilder.filters(excludeEngines(spec.getExcludeEngines())); | ||
} | ||
} | ||
|
||
private void addTagsFilter(LauncherDiscoveryRequestBuilder requestBuilder) { | ||
if (!spec.getIncludeTags().isEmpty()) { | ||
requestBuilder.filters(includeTags(spec.getIncludeTags())); | ||
} | ||
if (!spec.getExcludeTags().isEmpty()) { | ||
requestBuilder.filters(excludeTags(spec.getExcludeTags())); | ||
} | ||
} | ||
|
||
private void addTestNameFilters(LauncherDiscoveryRequestBuilder requestBuilder) { | ||
if (!spec.getIncludedTests().isEmpty() || !spec.getIncludedTestsCommandLine().isEmpty() || !spec.getExcludedTests().isEmpty()) { | ||
TestSelectionMatcher matcher = new TestSelectionMatcher(spec.getIncludedTests(), | ||
spec.getExcludedTests(), spec.getIncludedTestsCommandLine()); | ||
requestBuilder.filters(new ClassMethodNameFilter(matcher)); | ||
} | ||
} | ||
|
||
private static class ClassMethodNameFilter implements PostDiscoveryFilter { | ||
private final TestSelectionMatcher matcher; | ||
|
||
private ClassMethodNameFilter(TestSelectionMatcher matcher) { | ||
this.matcher = matcher; | ||
} | ||
|
||
@Override | ||
public FilterResult apply(TestDescriptor descriptor) { | ||
if (classMatch(descriptor)) { | ||
return FilterResult.included("Class match"); | ||
} | ||
return FilterResult.includedIf(shouldRun(descriptor), () -> "Method or class match", () -> "Method or class mismatch"); | ||
} | ||
|
||
private boolean shouldRun(TestDescriptor descriptor) { | ||
return shouldRun(descriptor, false); | ||
} | ||
|
||
private boolean shouldRun(TestDescriptor descriptor, boolean checkingParent) { | ||
Optional<TestSource> source = descriptor.getSource(); | ||
if (!source.isPresent()) { | ||
return true; | ||
} | ||
|
||
if (source.get() instanceof MethodSource) { | ||
MethodSource methodSource = (MethodSource) source.get(); | ||
return matcher.matchesTest(methodSource.getClassName(), methodSource.getMethodName()) | ||
|| matchesParentMethod(descriptor, methodSource.getMethodName()); | ||
} else if (source.get() instanceof ClassSource) { | ||
if (!checkingParent) { | ||
for (TestDescriptor child : descriptor.getChildren()) { | ||
if (shouldRun(child)) { | ||
return true; | ||
} | ||
} | ||
} | ||
if (descriptor.getChildren().isEmpty()) { | ||
String className = ((ClassSource) source.get()).getClassName(); | ||
return matcher.matchesTest(className, null) | ||
|| matcher.matchesTest(className, descriptor.getLegacyReportingName()); | ||
} else { | ||
return true; | ||
} | ||
} else { | ||
return descriptor.getParent().isPresent() && shouldRun(descriptor.getParent().get(), true); | ||
} | ||
} | ||
|
||
private boolean matchesParentMethod(TestDescriptor descriptor, String methodName) { | ||
return descriptor.getParent().flatMap(this::className).filter(className -> matcher.matchesTest(className, methodName)).isPresent(); | ||
} | ||
|
||
private boolean classMatch(TestDescriptor descriptor) { | ||
while (descriptor.getParent().isPresent()) { | ||
if (className(descriptor).filter(className -> matcher.matchesTest(className, null)).isPresent()) { | ||
return true; | ||
} | ||
descriptor = descriptor.getParent().get(); | ||
} | ||
return false; | ||
} | ||
|
||
private Optional<String> className(TestDescriptor descriptor) { | ||
return descriptor.getSource() | ||
.filter(ClassSource.class::isInstance) | ||
.map(ClassSource.class::cast) | ||
.map(ClassSource::getClassName); | ||
} | ||
} | ||
|
||
} |
91 changes: 91 additions & 0 deletions
91
src/main/java/org/embulk/gradle/embulk_plugins/EmbulkJUnitPlatformTestFramework.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,91 @@ | ||
/* | ||
* Copyright 2023 The Embulk project | ||
* | ||
* 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.embulk.gradle.embulk_plugins; | ||
|
||
import java.lang.reflect.InvocationTargetException; | ||
import java.lang.reflect.Method; | ||
import org.gradle.api.GradleException; | ||
import org.gradle.api.tasks.testing.Test; | ||
import org.gradle.api.internal.tasks.testing.TestFramework; | ||
import org.gradle.api.internal.tasks.testing.WorkerTestClassProcessorFactory; | ||
import org.gradle.api.internal.tasks.testing.filter.DefaultTestFilter; | ||
import org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestFramework; | ||
import org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformSpec; | ||
|
||
import org.gradle.api.Action; | ||
import org.gradle.api.JavaVersion; | ||
import org.gradle.api.internal.tasks.testing.TestClassProcessor; | ||
import org.gradle.api.internal.tasks.testing.TestFramework; | ||
import org.gradle.api.internal.tasks.testing.WorkerTestClassProcessorFactory; | ||
import org.gradle.api.internal.tasks.testing.detection.TestFrameworkDetector; | ||
import org.gradle.api.internal.tasks.testing.filter.DefaultTestFilter; | ||
import org.gradle.api.tasks.testing.junitplatform.JUnitPlatformOptions; | ||
import org.gradle.internal.UncheckedException; | ||
import org.gradle.internal.actor.ActorFactory; | ||
import org.gradle.internal.id.IdGenerator; | ||
import org.gradle.internal.jvm.UnsupportedJavaRuntimeException; | ||
import org.gradle.internal.scan.UsedByScanPlugin; | ||
import org.gradle.internal.service.ServiceRegistry; | ||
import org.gradle.internal.time.Clock; | ||
import org.gradle.process.internal.worker.WorkerProcessBuilder; | ||
|
||
import javax.annotation.Nonnull; | ||
import java.io.IOException; | ||
import java.io.Serializable; | ||
import java.lang.reflect.Constructor; | ||
import java.util.List; | ||
|
||
public class EmbulkJUnitPlatformTestFramework extends JUnitPlatformTestFramework { | ||
public EmbulkJUnitPlatformTestFramework(final DefaultTestFilter filter) { | ||
super(filter); | ||
} | ||
|
||
@Override | ||
public WorkerTestClassProcessorFactory getProcessorFactory() { | ||
return super.getProcessorFactory(); | ||
/* | ||
if (!JavaVersion.current().isJava8Compatible()) { | ||
throw new UnsupportedJavaRuntimeException("Running JUnit Platform requires Java 8+, please configure your test java executable with Java 8 or higher."); | ||
} | ||
return new JUnitPlatformTestClassProcessorFactory(new JUnitPlatformSpec(options, | ||
filter.getIncludePatterns(), filter.getExcludePatterns(), | ||
filter.getCommandLineIncludePatterns())); | ||
*/ | ||
} | ||
|
||
static class JUnitPlatformTestClassProcessorFactory implements WorkerTestClassProcessorFactory, Serializable { | ||
private final JUnitPlatformSpec spec; | ||
|
||
JUnitPlatformTestClassProcessorFactory(JUnitPlatformSpec spec) { | ||
this.spec = spec; | ||
} | ||
|
||
@Override | ||
public TestClassProcessor create(ServiceRegistry serviceRegistry) { | ||
try { | ||
IdGenerator<?> idGenerator = serviceRegistry.get(IdGenerator.class); | ||
Clock clock = serviceRegistry.get(Clock.class); | ||
ActorFactory actorFactory = serviceRegistry.get(ActorFactory.class); | ||
Class<?> clazz = getClass().getClassLoader().loadClass("org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor"); | ||
Constructor<?> constructor = clazz.getConstructor(JUnitPlatformSpec.class, IdGenerator.class, ActorFactory.class, Clock.class); | ||
return (TestClassProcessor) constructor.newInstance(spec, idGenerator, actorFactory, clock); | ||
} catch (Exception e) { | ||
throw UncheckedException.throwAsUncheckedException(e); | ||
} | ||
} | ||
} | ||
} |