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>

Reply via email to