This is an automated email from the ASF dual-hosted git repository.
struberg pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/openwebbeans-meecrowave.git
The following commit(s) were added to refs/heads/main by this push:
new 7eb9fee MEECROWAVE-342 fix junit5 test setup
7eb9fee is described below
commit 7eb9fee055ad4861e8478a7982172813902d80d2
Author: Mark Struberg <[email protected]>
AuthorDate: Mon Oct 21 17:34:21 2024 +0200
MEECROWAVE-342 fix junit5 test setup
Now tests will use a 'fresh' ClassLoader for each non-mono test setup.
That way it's possible to run them in parallel.
I've also added a few tests and did split the surefire runs into 2 parts.
One for running the junit4 related tests, the other one for the junit 5
tests.
---
meecrowave-core/pom.xml | 4 +
meecrowave-junit/pom.xml | 38 ++++++
.../apache/meecrowave/junit5/BaseLifecycle.java | 19 ++-
.../meecrowave/junit5/MeecrowaveExtension.java | 144 +++++++++++++++------
...est.java => PerClass2MeecrowaveConfigTest.java} | 72 ++++-------
.../junit5/PerClassMeecrowaveConfigTest.java | 36 ++++++
.../org/apache/meecrowave/junit5/ScopesTest.java | 2 +-
.../junit5/bean/SomeCommonInterface.java | 24 ++++
meecrowave-specs-api/pom.xml | 3 +-
9 files changed, 255 insertions(+), 87 deletions(-)
diff --git a/meecrowave-core/pom.xml b/meecrowave-core/pom.xml
index 2a6591b..6b7a02d 100644
--- a/meecrowave-core/pom.xml
+++ b/meecrowave-core/pom.xml
@@ -434,8 +434,10 @@
<shadedClassifierName>runner</shadedClassifierName>
<shadedArtifactAttached>true</shadedArtifactAttached>
<createDependencyReducedPom>false</createDependencyReducedPom>
+<!--
<dependencyReducedPomLocation>${project.build.directory}/reduced-pom-bundle.xml
</dependencyReducedPomLocation>
+-->
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>org.apache.meecrowave.runner.Cli</mainClass>
@@ -487,6 +489,7 @@
<configuration>
<shadedClassifierName>runner-light</shadedClassifierName>
<shadedArtifactAttached>true</shadedArtifactAttached>
+ <createDependencyReducedPom>false</createDependencyReducedPom>
<dependencyReducedPomLocation>${project.build.directory}/reduced-pom-bundle-light.xml
</dependencyReducedPomLocation>
<transformers>
@@ -553,6 +556,7 @@
<configuration>
<shadedClassifierName>servlet</shadedClassifierName>
<shadedArtifactAttached>true</shadedArtifactAttached>
+ <createDependencyReducedPom>false</createDependencyReducedPom>
<dependencyReducedPomLocation>${project.build.directory}/reduced-pom-bundle-servlet.xml
</dependencyReducedPomLocation>
<transformers>
diff --git a/meecrowave-junit/pom.xml b/meecrowave-junit/pom.xml
index 758375a..b9caf30 100644
--- a/meecrowave-junit/pom.xml
+++ b/meecrowave-junit/pom.xml
@@ -81,6 +81,44 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <parallel>all</parallel>
+ <threadCount>4</threadCount>
+ <threadCountClasses>3</threadCountClasses>
+ </configuration>
+
+ <executions>
+ <execution>
+ <!-- disable default execution -->
+ <id>default-test</id>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ </execution>
+
+ <execution>
+ <id>mwjunit4</id>
+ <phase>test</phase>
+ <goals><goal>test</goal></goals>
+ <configuration>
+ <includes>
+ <inlude>org/apache/meecrowave/junit/*Test.class</inlude>
+ </includes>
+ </configuration>
+ </execution>
+
+ <execution>
+ <id>mwjunit5</id>
+ <phase>test</phase>
+ <goals><goal>test</goal></goals>
+ <configuration>
+ <includes>
+ <inlude>org/apache/meecrowave/junit5/*Test.class</inlude>
+ </includes>
+ </configuration>
+ </execution>
+ </executions>
+
<dependencies>
<dependency>
<groupId>org.junit.platform</groupId>
diff --git
a/meecrowave-junit/src/main/java/org/apache/meecrowave/junit5/BaseLifecycle.java
b/meecrowave-junit/src/main/java/org/apache/meecrowave/junit5/BaseLifecycle.java
index 0a07140..c9ab009 100644
---
a/meecrowave-junit/src/main/java/org/apache/meecrowave/junit5/BaseLifecycle.java
+++
b/meecrowave-junit/src/main/java/org/apache/meecrowave/junit5/BaseLifecycle.java
@@ -22,10 +22,12 @@ import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.util.stream.Stream;
+import org.apache.meecrowave.internal.ClassLoaderLock;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.extension.ExtensionContext;
abstract class BaseLifecycle {
+
boolean isPerClass(final ExtensionContext context) {
return context.getTestInstanceLifecycle()
.map(it -> it.equals(TestInstance.Lifecycle.PER_CLASS))
@@ -41,7 +43,7 @@ abstract class BaseLifecycle {
.orElse(state);
}
- private static LifecyleState invoke(final Object test, final Class<?
extends Annotation> marker) {
+ private LifecyleState invoke(final Object test, final Class<? extends
Annotation> marker) {
Class<?> type = test.getClass();
while (type != Object.class) {
Stream.of(type.getDeclaredMethods())
@@ -63,7 +65,19 @@ abstract class BaseLifecycle {
return new LifecyleState(true, test);
}
- static class LifecyleState {
+
+ protected void doUnlockContext(final boolean unlocked) {
+ if (!unlocked) {
+ ClassLoaderLock.LOCK.unlock();
+ }
+ }
+
+ protected void doLockContext() {
+ ClassLoaderLock.LOCK.lock();
+ }
+
+
+ class LifecyleState {
private final boolean injected;
private final Object instance;
@@ -76,4 +90,5 @@ abstract class BaseLifecycle {
invoke(instance, AfterLastTest.class);
}
}
+
}
diff --git
a/meecrowave-junit/src/main/java/org/apache/meecrowave/junit5/MeecrowaveExtension.java
b/meecrowave-junit/src/main/java/org/apache/meecrowave/junit5/MeecrowaveExtension.java
index fe4dd87..a5f1be3 100644
---
a/meecrowave-junit/src/main/java/org/apache/meecrowave/junit5/MeecrowaveExtension.java
+++
b/meecrowave-junit/src/main/java/org/apache/meecrowave/junit5/MeecrowaveExtension.java
@@ -20,6 +20,7 @@ package org.apache.meecrowave.junit5;
import org.apache.meecrowave.Meecrowave;
import org.apache.meecrowave.configuration.Configuration;
+import org.apache.meecrowave.internal.ClassLoaderLock;
import org.apache.meecrowave.testing.Injector;
import org.junit.jupiter.api.extension.AfterAllCallback;
import org.junit.jupiter.api.extension.AfterEachCallback;
@@ -42,6 +43,10 @@ public class MeecrowaveExtension extends BaseLifecycle
private static final ExtensionContext.Namespace NAMESPACE =
ExtensionContext.Namespace.create(MeecrowaveExtension.class.getName());
+ private ClassLoader meecrowaveCL;
+ private ClassLoader oldCl;
+
+
private final ScopesExtension scopes = new ScopesExtension() {
@Override
protected Optional<Class<? extends Annotation>[]> getScopes(final
ExtensionContext context) {
@@ -52,6 +57,13 @@ public class MeecrowaveExtension extends BaseLifecycle
}
};
+ protected ClassLoader createMwClassLoader() {
+ if (meecrowaveCL == null) {
+ meecrowaveCL = ClassLoaderLock.getUsableContainerLoader();
+ }
+ return meecrowaveCL;
+ }
+
@Override
public void beforeAll(final ExtensionContext context) {
if (isPerClass(context)) {
@@ -65,7 +77,20 @@ public class MeecrowaveExtension extends BaseLifecycle
ofNullable(store.get(LifecyleState.class, LifecyleState.class))
.ifPresent(s -> s.afterLastTest(context));
if (isPerClass(context)) {
- store.get(Meecrowave.class, Meecrowave.class).close();
+ ClassLoader oldClTmp = null;
+ try {
+ if (meecrowaveCL != null) {
+ oldClTmp = Thread.currentThread().getContextClassLoader();
+ Thread.currentThread().setContextClassLoader(meecrowaveCL);
+ }
+
+ store.get(Meecrowave.class, Meecrowave.class).close();
+ }
+ finally {
+ if (oldClTmp != null) {
+ Thread.currentThread().setContextClassLoader(oldClTmp);
+ }
+ }
}
}
@@ -74,63 +99,104 @@ public class MeecrowaveExtension extends BaseLifecycle
if (!isPerClass(context)) {
doStart(context);
}
+
+ if (meecrowaveCL != null) {
+ oldCl = Thread.currentThread().getContextClassLoader();
+ Thread.currentThread().setContextClassLoader(meecrowaveCL);
+ }
}
@Override
public void afterEach(final ExtensionContext context) {
- if (!isPerClass(context)) {
- doRelease(context);
- context.getStore(NAMESPACE).get(Meecrowave.class,
Meecrowave.class).close();
+ ClassLoader oldClTmp = null;
+ try {
+ if (!isPerClass(context)) {
+ if (meecrowaveCL != null) {
+ oldClTmp = Thread.currentThread().getContextClassLoader();
+ Thread.currentThread().setContextClassLoader(meecrowaveCL);
+ }
+ doRelease(context);
+ context.getStore(NAMESPACE).get(Meecrowave.class,
Meecrowave.class).close();
+ }
+ }
+ finally {
+ if (oldCl != null) {
+ Thread.currentThread().setContextClassLoader(oldCl);
+ }
+ else if (oldClTmp != null) {
+ Thread.currentThread().setContextClassLoader(oldClTmp);
+ }
}
}
private void doStart(final ExtensionContext context) {
- final Meecrowave.Builder builder = new Meecrowave.Builder();
- final MeecrowaveConfig config = findConfig(context);
- final String ctx;
- if (config != null) {
- ctx = config.context();
-
- for (final Method method : MeecrowaveConfig.class.getMethods()) {
- if (MeecrowaveConfig.class != method.getDeclaringClass()) {
- continue;
- }
+ final Thread thread = Thread.currentThread();
+ ClassLoader oldClTmp = thread.getContextClassLoader();
+ boolean unlocked = false;
+ doLockContext();
+
+ try {
+ ClassLoader newCl = createMwClassLoader();
+ if (newCl != null) {
+ thread.setContextClassLoader(newCl);
+ }
- try {
- final Object value = method.invoke(config);
+ final Meecrowave.Builder builder = new Meecrowave.Builder();
+ final MeecrowaveConfig config = findConfig(context);
+ final String ctx;
+ if (config != null) {
+ ctx = config.context();
- final Field configField =
Configuration.class.getDeclaredField(method.getName());
- if (!configField.isAccessible()) {
- configField.setAccessible(true);
+ for (final Method method :
MeecrowaveConfig.class.getMethods()) {
+ if (MeecrowaveConfig.class != method.getDeclaringClass()) {
+ continue;
}
- if (value != null && (!String.class.isInstance(value) ||
!value.toString().isEmpty())) {
+ try {
+ final Object value = method.invoke(config);
+
+ final Field configField =
Configuration.class.getDeclaredField(method.getName());
if (!configField.isAccessible()) {
configField.setAccessible(true);
}
- configField.set(builder, File.class ==
configField.getType() ? /*we use string instead */new File(value.toString()) :
value);
+
+ if (value != null && (!String.class.isInstance(value)
|| !value.toString().isEmpty())) {
+ if (!configField.isAccessible()) {
+ configField.setAccessible(true);
+ }
+ configField.set(builder, File.class ==
configField.getType() ? /*we use string instead */new File(value.toString()) :
value);
+ }
+ }
+ catch (final NoSuchFieldException nsfe) {
+ // ignored
+ }
+ catch (final Exception e) {
+ throw new IllegalStateException(e);
}
- } catch (final NoSuchFieldException nsfe) {
- // ignored
- } catch (final Exception e) {
- throw new IllegalStateException(e);
}
- }
- if (builder.getHttpPort() < 0) {
- builder.randomHttpPort();
+ if (builder.getHttpPort() < 0) {
+ builder.randomHttpPort();
+ }
+ }
+ else {
+ ctx = "";
+ }
+ final ExtensionContext.Store store = context.getStore(NAMESPACE);
+ final Meecrowave meecrowave = new Meecrowave(builder);
+ store.put(Meecrowave.class, meecrowave);
+ store.put(Meecrowave.Builder.class, builder);
+ meecrowave.bake(ctx);
+
+ doInject(context);
+ store.put(LifecyleState.class, onInjection(context, null));
+ }
+ finally {
+ doUnlockContext(unlocked);
+ if (oldClTmp != null) {
+ thread.setContextClassLoader(oldClTmp);
}
- } else {
- ctx = "";
}
- final ExtensionContext.Store store = context.getStore(NAMESPACE);
- final Meecrowave meecrowave = new Meecrowave(builder);
- store.put(Meecrowave.class, meecrowave);
- store.put(Meecrowave.Builder.class, builder);
- meecrowave.bake(ctx);
-
- doInject(context);
- store.put(LifecyleState.class, onInjection(context, null));
}
private MeecrowaveConfig findConfig(final ExtensionContext context) {
@@ -144,7 +210,7 @@ public class MeecrowaveExtension extends BaseLifecycle
private void doRelease(final ExtensionContext context) {
ofNullable(context.getStore(NAMESPACE).get(CreationalContext.class,
CreationalContext.class))
.ifPresent(CreationalContext::release);
- scopes.beforeEach(context);
+ scopes.afterEach(context);
}
private void doInject(final ExtensionContext context) {
diff --git
a/meecrowave-junit/src/test/java/org/apache/meecrowave/junit5/PerClassMeecrowaveConfigTest.java
b/meecrowave-junit/src/test/java/org/apache/meecrowave/junit5/PerClass2MeecrowaveConfigTest.java
similarity index 58%
copy from
meecrowave-junit/src/test/java/org/apache/meecrowave/junit5/PerClassMeecrowaveConfigTest.java
copy to
meecrowave-junit/src/test/java/org/apache/meecrowave/junit5/PerClass2MeecrowaveConfigTest.java
index a2d38ce..610bebb 100644
---
a/meecrowave-junit/src/test/java/org/apache/meecrowave/junit5/PerClassMeecrowaveConfigTest.java
+++
b/meecrowave-junit/src/test/java/org/apache/meecrowave/junit5/PerClass2MeecrowaveConfigTest.java
@@ -18,75 +18,59 @@
*/
package org.apache.meecrowave.junit5;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.fail;
-import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URL;
-
-import jakarta.enterprise.context.ApplicationScoped;
-import jakarta.inject.Inject;
import org.apache.meecrowave.Meecrowave;
-import org.apache.meecrowave.io.IO;
+import org.apache.meecrowave.junit5.bean.SomeCommonInterface;
import org.apache.meecrowave.testing.ConfigurationInject;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.inject.Inject;
+import static org.junit.Assert.assertEquals;
+import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS;
+
@TestInstance(PER_CLASS)
-@MeecrowaveConfig(scanningPackageIncludes =
"org.apache.meecrowave.junit5.PerClassMeecrowaveConfigTest")
-class PerClassMeecrowaveConfigTest {
+@MeecrowaveConfig(scanningPackageIncludes =
"org.apache.meecrowave.junit5.PerClass2MeecrowaveConfigTest")
+class PerClass2MeecrowaveConfigTest {
@ConfigurationInject
private Meecrowave.Builder config;
- @Inject
- private Bean bean;
- private static Bean instance;
+ private @Inject SomeCommonInterface bigOtherOracle;
@Test
- void m1() {
- doTest();
+ public void testBeanPickup() throws Exception {
+ assertEquals(43, bigOtherOracle.meaningOfLife());
+ Thread.sleep(50L);
}
@Test
- void m2() {
- doTest();
+ public void testBeanPickup2() throws Exception {
+ assertEquals(43, bigOtherOracle.meaningOfLife());
+ Thread.sleep(50L);
}
- private void doTest() {
- if (instance == null) {
- first();
- } else {
- second();
- }
+ @Test
+ public void testBeanPickup3() throws Exception {
+ assertEquals(43, bigOtherOracle.meaningOfLife());
+ Thread.sleep(50L);
}
- private void first() {
- assertEquals("ok", bean.get());
- instance = bean;
+ @Test
+ public void testBeanPickup4() throws Exception {
+ assertEquals(43, bigOtherOracle.meaningOfLife());
+ Thread.sleep(50L);
}
- private void second() {
- assertSame(instance, bean);
- }
- private String slurp(final URL url) {
- try (final InputStream is = url.openStream()) {
- return IO.toString(is);
- } catch (final IOException e) {
- fail(e.getMessage());
- }
- return null;
- }
@ApplicationScoped
- public static class Bean {
- String get() {
- return "ok";
+ public static class MyCommonImpl2 implements SomeCommonInterface {
+
+ @Override
+ public int meaningOfLife() {
+ return 43;
}
}
}
diff --git
a/meecrowave-junit/src/test/java/org/apache/meecrowave/junit5/PerClassMeecrowaveConfigTest.java
b/meecrowave-junit/src/test/java/org/apache/meecrowave/junit5/PerClassMeecrowaveConfigTest.java
index a2d38ce..65806ea 100644
---
a/meecrowave-junit/src/test/java/org/apache/meecrowave/junit5/PerClassMeecrowaveConfigTest.java
+++
b/meecrowave-junit/src/test/java/org/apache/meecrowave/junit5/PerClassMeecrowaveConfigTest.java
@@ -32,6 +32,7 @@ import jakarta.inject.Inject;
import org.apache.meecrowave.Meecrowave;
import org.apache.meecrowave.io.IO;
+import org.apache.meecrowave.junit5.bean.SomeCommonInterface;
import org.apache.meecrowave.testing.ConfigurationInject;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
@@ -45,6 +46,8 @@ class PerClassMeecrowaveConfigTest {
@Inject
private Bean bean;
+ private @Inject SomeCommonInterface bigOracle;
+
private static Bean instance;
@Test
@@ -57,6 +60,30 @@ class PerClassMeecrowaveConfigTest {
doTest();
}
+ @Test
+ public void testBeanPickup() throws Exception {
+ assertEquals(42, bigOracle.meaningOfLife());
+ Thread.sleep(500L);
+ }
+
+ @Test
+ public void testBeanPickup2() throws Exception {
+ assertEquals(42, bigOracle.meaningOfLife());
+ Thread.sleep(50L);
+ }
+
+ @Test
+ public void testBeanPickup3() throws Exception {
+ assertEquals(42, bigOracle.meaningOfLife());
+ Thread.sleep(50L);
+ }
+
+ @Test
+ public void testBeanPickup4() throws Exception {
+ assertEquals(42, bigOracle.meaningOfLife());
+ Thread.sleep(50L);
+ }
+
private void doTest() {
if (instance == null) {
first();
@@ -89,4 +116,13 @@ class PerClassMeecrowaveConfigTest {
return "ok";
}
}
+
+ @ApplicationScoped
+ public static class MyCommonImpl1 implements SomeCommonInterface {
+
+ @Override
+ public int meaningOfLife() {
+ return 42;
+ }
+ }
}
diff --git
a/meecrowave-junit/src/test/java/org/apache/meecrowave/junit5/ScopesTest.java
b/meecrowave-junit/src/test/java/org/apache/meecrowave/junit5/ScopesTest.java
index 1b1c9f4..0b84387 100644
---
a/meecrowave-junit/src/test/java/org/apache/meecrowave/junit5/ScopesTest.java
+++
b/meecrowave-junit/src/test/java/org/apache/meecrowave/junit5/ScopesTest.java
@@ -27,7 +27,7 @@ import jakarta.enterprise.context.spi.Context;
import org.apache.webbeans.config.WebBeansContext;
import org.junit.jupiter.api.Test;
-@MeecrowaveConfig
+@MonoMeecrowaveConfig
@Scopes(scopes = RequestScoped.class)
class ScopesTest {
@Test
diff --git
a/meecrowave-junit/src/test/java/org/apache/meecrowave/junit5/bean/SomeCommonInterface.java
b/meecrowave-junit/src/test/java/org/apache/meecrowave/junit5/bean/SomeCommonInterface.java
new file mode 100644
index 0000000..aafe3fa
--- /dev/null
+++
b/meecrowave-junit/src/test/java/org/apache/meecrowave/junit5/bean/SomeCommonInterface.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.meecrowave.junit5.bean;
+
+/**
+ * Common interface to check whether Beans are really only picked up per class
+ */
+public interface SomeCommonInterface {
+ int meaningOfLife();
+}
diff --git a/meecrowave-specs-api/pom.xml b/meecrowave-specs-api/pom.xml
index 473fb49..1874aa5 100644
--- a/meecrowave-specs-api/pom.xml
+++ b/meecrowave-specs-api/pom.xml
@@ -18,12 +18,13 @@
under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="
http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
<parent>
<artifactId>meecrowave</artifactId>
<groupId>org.apache.meecrowave</groupId>
<version>2.0.0-SNAPSHOT</version>
</parent>
- <modelVersion>4.0.0</modelVersion>
<artifactId>meecrowave-specs-api</artifactId>
<name>Meecrowave :: Specs API</name>