From 5db60deb47613729b97453025cd4af7a0cbfe0c9 Mon Sep 17 00:00:00 2001 From: Falko Modler Date: Sat, 28 Aug 2021 23:06:51 +0200 Subject: [PATCH] Fix wrong TestInfo test method in @Before/AfterEach Fixes #19747 --- .../it/main/QuarkusTestCallbacksTestCase.java | 35 ++++++++++++++++--- ...leAnnotationCheckerBeforeEachCallback.java | 3 ++ .../test/junit/QuarkusTestExtension.java | 25 ++++++++----- 3 files changed, 50 insertions(+), 13 deletions(-) diff --git a/integration-tests/main/src/test/java/io/quarkus/it/main/QuarkusTestCallbacksTestCase.java b/integration-tests/main/src/test/java/io/quarkus/it/main/QuarkusTestCallbacksTestCase.java index 404630cb8cec5..af121205b21c2 100644 --- a/integration-tests/main/src/test/java/io/quarkus/it/main/QuarkusTestCallbacksTestCase.java +++ b/integration-tests/main/src/test/java/io/quarkus/it/main/QuarkusTestCallbacksTestCase.java @@ -3,11 +3,16 @@ import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.RetentionPolicy.RUNTIME; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.lang.annotation.Retention; import java.lang.annotation.Target; +import java.lang.reflect.Method; -import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInfo; @@ -22,11 +27,29 @@ @QuarkusTest public class QuarkusTestCallbacksTestCase { + @BeforeEach + void beforeEachWithTestInfo(TestInfo testInfo) throws NoSuchMethodException { + checkBeforeOrAfterEachTestInfo(testInfo, "beforeEachWithTestInfo"); + } + + @AfterEach + void afterEachWithTestInfo(TestInfo testInfo) throws NoSuchMethodException { + checkBeforeOrAfterEachTestInfo(testInfo, "afterEachWithTestInfo"); + } + + private void checkBeforeOrAfterEachTestInfo(TestInfo testInfo, String unexpectedMethodName) throws NoSuchMethodException { + assertNotNull(testInfo); + String testMethodName = testInfo.getTestMethod().get().getName(); + assertNotEquals(testMethodName, + QuarkusTestCallbacksTestCase.class.getDeclaredMethod(unexpectedMethodName, TestInfo.class)); + assertTrue(testMethodName.startsWith("test")); + } + @Test @TestAnnotation @Order(1) public void testTestMethodHasAnnotation() { - + assertTrue(SimpleAnnotationCheckerBeforeEachCallback.testAnnotationChecked); } @Test @@ -36,11 +59,13 @@ public void testBeforeClass() { } @Test + @TestAnnotation @Order(3) public void testInfoTestCase(TestInfo testInfo) throws NoSuchMethodException { - Assertions.assertEquals(testInfo.getTestClass().get(), QuarkusTestCallbacksTestCase.class); - Assertions.assertEquals(testInfo.getTestMethod().get(), - QuarkusTestCallbacksTestCase.class.getDeclaredMethod("testInfoTestCase", TestInfo.class)); + assertEquals(testInfo.getTestClass().get(), QuarkusTestCallbacksTestCase.class); + Method testMethod = testInfo.getTestMethod().get(); + assertEquals(testMethod, QuarkusTestCallbacksTestCase.class.getDeclaredMethod("testInfoTestCase", TestInfo.class)); + assertEquals(1, testMethod.getAnnotationsByType(TestAnnotation.class).length); } @Target({ METHOD }) diff --git a/integration-tests/main/src/test/java/io/quarkus/it/main/SimpleAnnotationCheckerBeforeEachCallback.java b/integration-tests/main/src/test/java/io/quarkus/it/main/SimpleAnnotationCheckerBeforeEachCallback.java index 85927763f1085..8e9447609046b 100644 --- a/integration-tests/main/src/test/java/io/quarkus/it/main/SimpleAnnotationCheckerBeforeEachCallback.java +++ b/integration-tests/main/src/test/java/io/quarkus/it/main/SimpleAnnotationCheckerBeforeEachCallback.java @@ -7,6 +7,8 @@ public class SimpleAnnotationCheckerBeforeEachCallback implements QuarkusTestBeforeEachCallback { + static boolean testAnnotationChecked; + @Override public void beforeEach(QuarkusTestMethodContext context) { // make sure that this comes into play only for the test we care about @@ -26,5 +28,6 @@ public void beforeEach(QuarkusTestMethodContext context) { throw new IllegalStateException( "Expected to find annotation @TestAnnotation on method test method testTestMethodHasAnnotation"); } + testAnnotationChecked = true; } } diff --git a/test-framework/junit5/src/main/java/io/quarkus/test/junit/QuarkusTestExtension.java b/test-framework/junit5/src/main/java/io/quarkus/test/junit/QuarkusTestExtension.java index 6a4574d813a6d..deb9fc5f55540 100644 --- a/test-framework/junit5/src/main/java/io/quarkus/test/junit/QuarkusTestExtension.java +++ b/test-framework/junit5/src/main/java/io/quarkus/test/junit/QuarkusTestExtension.java @@ -22,6 +22,7 @@ import java.util.ArrayList; import java.util.Deque; import java.util.HashMap; +import java.util.IdentityHashMap; import java.util.List; import java.util.Locale; import java.util.Map; @@ -196,6 +197,8 @@ public void run() { } }; + private final IdentityHashMap tcclMethodCache = new IdentityHashMap<>(); + static { ClassLoader classLoader = QuarkusTestExtension.class.getClassLoader(); if (classLoader instanceof QuarkusClassLoader) { @@ -430,6 +433,7 @@ public void close() throws IOException { } tm.close(); } finally { + tcclMethodCache.clear(); GroovyCacheCleaner.clearGroovyCache(); if (hangTaskKey != null) { hangTaskKey.cancel(true); @@ -988,13 +992,13 @@ private Object runExtensionMethod(ReflectiveInvocationContext invocation try { Class testClassFromTCCL = Class.forName(extensionContext.getRequiredTestClass().getName(), true, Thread.currentThread().getContextClassLoader()); - Method newMethod = determineTCCLExtensionMethod(invocationContext, testClassFromTCCL); + Method newMethod = determineTCCLExtensionMethod(invocationContext.getExecutable(), testClassFromTCCL); boolean methodFromEnclosing = false; // this is needed to support before*** and after*** methods that are part of class that encloses the test class // (the test class is in this case a @Nested test) if ((newMethod == null) && (testClassFromTCCL.getEnclosingClass() != null)) { testClassFromTCCL = testClassFromTCCL.getEnclosingClass(); - newMethod = determineTCCLExtensionMethod(invocationContext, testClassFromTCCL); + newMethod = determineTCCLExtensionMethod(invocationContext.getExecutable(), testClassFromTCCL); methodFromEnclosing = true; } if (newMethod == null) { @@ -1035,8 +1039,9 @@ private Object runExtensionMethod(ReflectiveInvocationContext invocation cloneRequired = false; } else if (TestInfo.class.isAssignableFrom(theclass)) { TestInfo info = (TestInfo) arg; + Method newTestMethod = determineTCCLExtensionMethod(info.getTestMethod().get(), testClassFromTCCL); replacement = new TestInfoImpl(info.getDisplayName(), info.getTags(), Optional.of(testClassFromTCCL), - Optional.of(newMethod)); + Optional.of(newTestMethod)); } else if (clonePattern.matcher(className).matches()) { cloneRequired = true; } else { @@ -1089,13 +1094,16 @@ private Object runExtensionMethod(ReflectiveInvocationContext invocation } } - private Method determineTCCLExtensionMethod(ReflectiveInvocationContext invocationContext, Class c) + private Method determineTCCLExtensionMethod(Method originalMethod, Class c) throws ClassNotFoundException { - Method newMethod = null; + Method newMethod = tcclMethodCache.get(originalMethod); + if (newMethod != null) { + return newMethod; + } while (c != Object.class) { - if (c.getName().equals(invocationContext.getExecutable().getDeclaringClass().getName())) { + if (c.getName().equals(originalMethod.getDeclaringClass().getName())) { try { - Class[] originalParameterTypes = invocationContext.getExecutable().getParameterTypes(); + Class[] originalParameterTypes = originalMethod.getParameterTypes(); List> parameterTypesFromTccl = new ArrayList<>(originalParameterTypes.length); for (Class type : originalParameterTypes) { if (type.isPrimitive()) { @@ -1106,7 +1114,7 @@ private Method determineTCCLExtensionMethod(ReflectiveInvocationContext Thread.currentThread().getContextClassLoader())); } } - newMethod = c.getDeclaredMethod(invocationContext.getExecutable().getName(), + newMethod = c.getDeclaredMethod(originalMethod.getName(), parameterTypesFromTccl.toArray(new Class[0])); break; } catch (NoSuchMethodException ignored) { @@ -1115,6 +1123,7 @@ private Method determineTCCLExtensionMethod(ReflectiveInvocationContext } c = c.getSuperclass(); } + tcclMethodCache.put(originalMethod, newMethod); return newMethod; }