From 4da51b3b81663b2bb140b233781ca90ab1b948ab Mon Sep 17 00:00:00 2001 From: Holly Cummins Date: Thu, 9 Jan 2025 16:15:14 +0000 Subject: [PATCH] Add new state-based method for deciding if tests should run in the same classloader --- .../test/common/TestResourceManager.java | 7 + .../common/TestResourceManagerReloadTest.java | 137 ++++++++++++------ .../quarkus/test/junit/TestResourceUtil.java | 22 +++ .../test/junit/TestResourceUtilTest.java | 82 +++++++++++ 4 files changed, 200 insertions(+), 48 deletions(-) create mode 100644 test-framework/junit5/src/test/java/io/quarkus/test/junit/TestResourceUtilTest.java diff --git a/test-framework/common/src/main/java/io/quarkus/test/common/TestResourceManager.java b/test-framework/common/src/main/java/io/quarkus/test/common/TestResourceManager.java index 92439a7a1559a..8b4acf55ff115 100644 --- a/test-framework/common/src/main/java/io/quarkus/test/common/TestResourceManager.java +++ b/test-framework/common/src/main/java/io/quarkus/test/common/TestResourceManager.java @@ -23,6 +23,7 @@ import java.util.Optional; import java.util.ServiceLoader; import java.util.Set; +import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; @@ -569,6 +570,12 @@ public static boolean testResourcesRequireReload(Set return false; } + public static String getReloadGroupIdentifier(Set existing) { + // For now, we reload if it's restricted to class scope, and don't otherwise + String uniquenessModifier = anyResourceRestrictedToClass(existing) ? UUID.randomUUID().toString() : ""; + return existing.stream().map(Object::toString).sorted().collect(Collectors.joining()) + uniquenessModifier; + } + private static boolean anyResourceRestrictedToClass(Set testResources) { for (TestResourceComparisonInfo info : testResources) { if (info.scope == RESTRICTED_TO_CLASS) { diff --git a/test-framework/common/src/test/java/io/quarkus/test/common/TestResourceManagerReloadTest.java b/test-framework/common/src/test/java/io/quarkus/test/common/TestResourceManagerReloadTest.java index 782f0d9ad2076..82a4049f2b820 100644 --- a/test-framework/common/src/test/java/io/quarkus/test/common/TestResourceManagerReloadTest.java +++ b/test-framework/common/src/test/java/io/quarkus/test/common/TestResourceManagerReloadTest.java @@ -1,8 +1,13 @@ package io.quarkus.test.common; +import static io.quarkus.test.common.TestResourceManager.getReloadGroupIdentifier; import static io.quarkus.test.common.TestResourceManager.testResourcesRequireReload; -import static io.quarkus.test.common.TestResourceScope.*; +import static io.quarkus.test.common.TestResourceScope.GLOBAL; +import static io.quarkus.test.common.TestResourceScope.MATCHING_RESOURCES; +import static io.quarkus.test.common.TestResourceScope.RESTRICTED_TO_CLASS; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.Collections; @@ -17,93 +22,129 @@ public class TestResourceManagerReloadTest { @Test public void emptyResources() { - assertFalse(testResourcesRequireReload(Collections.emptySet(), Set.of())); + Set existing = Collections.emptySet(); + Set next = Set.of(); + assertFalse(testResourcesRequireReload(existing, next)); + assertEquals(getReloadGroupIdentifier(existing), getReloadGroupIdentifier(next)); } @Test public void differentCount() { - assertTrue(testResourcesRequireReload(Collections.emptySet(), - Set.of(new TestResourceComparisonInfo("test", RESTRICTED_TO_CLASS, Map.of())))); + Set existing = Collections.emptySet(); + Set next = Set.of(new TestResourceComparisonInfo("test", RESTRICTED_TO_CLASS, Map.of())); - assertTrue(testResourcesRequireReload(Set.of(new TestResourceComparisonInfo("test", RESTRICTED_TO_CLASS, Map.of())), - Collections.emptySet())); + assertTrue(testResourcesRequireReload(existing, next)); + assertTrue(testResourcesRequireReload(next, existing)); + + assertNotEquals(getReloadGroupIdentifier(existing), getReloadGroupIdentifier(next)); } @Test public void sameSingleRestrictedToClassResource() { - assertTrue(testResourcesRequireReload( - Set.of(new TestResourceComparisonInfo("test", RESTRICTED_TO_CLASS, Map.of())), - Set.of(new TestResourceComparisonInfo("test", RESTRICTED_TO_CLASS, Map.of())))); + Set existing = Set + .of(new TestResourceComparisonInfo("test", RESTRICTED_TO_CLASS, Map.of())); + Set next = Set.of(new TestResourceComparisonInfo("test", RESTRICTED_TO_CLASS, Map.of())); + assertTrue(testResourcesRequireReload(existing, next)); + + assertNotEquals(getReloadGroupIdentifier(existing), getReloadGroupIdentifier(next)); } @Test public void sameSingleMatchingResource() { - assertFalse(testResourcesRequireReload( - Set.of(new TestResourceComparisonInfo("test", MATCHING_RESOURCES, Map.of())), - Set.of(new TestResourceComparisonInfo("test", MATCHING_RESOURCES, Map.of())))); + Set existing = Set.of(new TestResourceComparisonInfo("test", MATCHING_RESOURCES, Map.of())); + Set next = Set.of(new TestResourceComparisonInfo("test", MATCHING_RESOURCES, Map.of())); + + assertFalse(testResourcesRequireReload(existing, next)); + + assertEquals(getReloadGroupIdentifier(existing), getReloadGroupIdentifier(next)); } @Test public void sameSingleMatchingResourceWithArgs() { - assertFalse(testResourcesRequireReload( - Set.of(new TestResourceComparisonInfo("test", MATCHING_RESOURCES, Map.of("a", "b"))), - Set.of(new TestResourceComparisonInfo("test", MATCHING_RESOURCES, Map.of("a", "b"))))); + Set existing = Set + .of(new TestResourceComparisonInfo("test", MATCHING_RESOURCES, Map.of("a", "b"))); + Set next = Set + .of(new TestResourceComparisonInfo("test", MATCHING_RESOURCES, Map.of("a", "b"))); + + assertFalse(testResourcesRequireReload(existing, next)); + + assertEquals(getReloadGroupIdentifier(existing), getReloadGroupIdentifier(next)); } @Test public void sameSingleResourceDifferentArgs() { - assertTrue(testResourcesRequireReload( - Set.of(new TestResourceComparisonInfo("test", MATCHING_RESOURCES, Map.of())), - Set.of(new TestResourceComparisonInfo("test", MATCHING_RESOURCES, Map.of("a", "b"))))); + Set existing = Set + .of(new TestResourceComparisonInfo("test", MATCHING_RESOURCES, Map.of("a", "b"))); + Set next = Set.of(new TestResourceComparisonInfo("test", MATCHING_RESOURCES, Map.of())); + + assertTrue(testResourcesRequireReload(existing, next)); + + assertNotEquals(getReloadGroupIdentifier(existing), getReloadGroupIdentifier(next)); } @Test public void sameSingleResourceDifferentArgValues() { - assertTrue(testResourcesRequireReload( - Set.of(new TestResourceComparisonInfo("test", MATCHING_RESOURCES, Map.of("a", "b"))), - Set.of(new TestResourceComparisonInfo("test", MATCHING_RESOURCES, Map.of("a", "c"))))); + Set existing = Set + .of(new TestResourceComparisonInfo("test", MATCHING_RESOURCES, Map.of("a", "b"))); + Set next = Set + .of(new TestResourceComparisonInfo("test", MATCHING_RESOURCES, Map.of("a", "x"))); + + assertTrue(testResourcesRequireReload(existing, next)); + + assertNotEquals(getReloadGroupIdentifier(existing), getReloadGroupIdentifier(next)); } @Test public void differentSingleMatchingResource() { - assertTrue(testResourcesRequireReload( - Set.of(new TestResourceComparisonInfo("test", MATCHING_RESOURCES, Map.of())), - Set.of(new TestResourceComparisonInfo("test2", MATCHING_RESOURCES, Map.of())))); + Set existing = Set.of(new TestResourceComparisonInfo("test", MATCHING_RESOURCES, Map.of())); + Set next = Set.of(new TestResourceComparisonInfo("test2", MATCHING_RESOURCES, Map.of())); + assertTrue(testResourcesRequireReload(existing, next)); + + assertNotEquals(getReloadGroupIdentifier(existing), getReloadGroupIdentifier(next)); } @Test public void sameMultipleMatchingResource() { - assertFalse(testResourcesRequireReload( - Set.of( - new TestResourceComparisonInfo("test", MATCHING_RESOURCES, Map.of()), - new TestResourceComparisonInfo("test2", MATCHING_RESOURCES, Map.of()), - new TestResourceComparisonInfo("test3", GLOBAL, Map.of())), - Set.of(new TestResourceComparisonInfo("test3", GLOBAL, Map.of()), - new TestResourceComparisonInfo("test2", MATCHING_RESOURCES, Map.of()), - new TestResourceComparisonInfo("test", MATCHING_RESOURCES, Map.of())))); + Set existing = Set.of( + new TestResourceComparisonInfo("test", MATCHING_RESOURCES, Map.of()), + new TestResourceComparisonInfo("test2", MATCHING_RESOURCES, Map.of()), + new TestResourceComparisonInfo("test3", GLOBAL, Map.of())); + Set next = Set.of(new TestResourceComparisonInfo("test3", GLOBAL, Map.of()), + new TestResourceComparisonInfo("test2", MATCHING_RESOURCES, Map.of()), + new TestResourceComparisonInfo("test", MATCHING_RESOURCES, Map.of())); + + assertFalse(testResourcesRequireReload(existing, next)); + assertEquals(getReloadGroupIdentifier(existing), getReloadGroupIdentifier(next)); + } @Test public void differentMultipleMatchingResource() { - assertTrue(testResourcesRequireReload( - Set.of( - new TestResourceComparisonInfo("test", MATCHING_RESOURCES, Map.of()), - new TestResourceComparisonInfo("test2", MATCHING_RESOURCES, Map.of()), - new TestResourceComparisonInfo("test3", GLOBAL, Map.of())), - Set.of(new TestResourceComparisonInfo("test3", GLOBAL, Map.of()), - new TestResourceComparisonInfo("test2", MATCHING_RESOURCES, Map.of()), - new TestResourceComparisonInfo("TEST", MATCHING_RESOURCES, Map.of())))); + Set existing = Set.of( + new TestResourceComparisonInfo("test", MATCHING_RESOURCES, Map.of()), + new TestResourceComparisonInfo("test2", MATCHING_RESOURCES, Map.of()), + new TestResourceComparisonInfo("test3", GLOBAL, Map.of())); + Set next = Set.of(new TestResourceComparisonInfo("test3", GLOBAL, Map.of()), + new TestResourceComparisonInfo("test2", MATCHING_RESOURCES, Map.of()), + new TestResourceComparisonInfo("TEST", MATCHING_RESOURCES, Map.of())); + assertTrue(testResourcesRequireReload(existing, next)); + assertNotEquals(getReloadGroupIdentifier(existing), + getReloadGroupIdentifier(next)); } @Test public void differentGlobalMultipleMatchingResource() { - assertTrue(testResourcesRequireReload( - Set.of( - new TestResourceComparisonInfo("test", MATCHING_RESOURCES, Map.of()), - new TestResourceComparisonInfo("test2", MATCHING_RESOURCES, Map.of()), - new TestResourceComparisonInfo("test4", GLOBAL, Map.of())), - Set.of(new TestResourceComparisonInfo("test3", GLOBAL, Map.of()), - new TestResourceComparisonInfo("test2", MATCHING_RESOURCES, Map.of()), - new TestResourceComparisonInfo("TEST", MATCHING_RESOURCES, Map.of())))); + Set existing = Set.of( + new TestResourceComparisonInfo("test", MATCHING_RESOURCES, Map.of()), + new TestResourceComparisonInfo("test2", MATCHING_RESOURCES, Map.of()), + new TestResourceComparisonInfo("test4", GLOBAL, Map.of())); + Set next = Set.of(new TestResourceComparisonInfo("test3", GLOBAL, Map.of()), + new TestResourceComparisonInfo("test2", MATCHING_RESOURCES, Map.of()), + new TestResourceComparisonInfo("TEST", MATCHING_RESOURCES, Map.of())); + + assertTrue(testResourcesRequireReload(existing, next)); + assertNotEquals(getReloadGroupIdentifier(existing), + getReloadGroupIdentifier(next)); + } } diff --git a/test-framework/junit5/src/main/java/io/quarkus/test/junit/TestResourceUtil.java b/test-framework/junit5/src/main/java/io/quarkus/test/junit/TestResourceUtil.java index 56e751b2d307a..cec2cfa0d37cb 100644 --- a/test-framework/junit5/src/main/java/io/quarkus/test/junit/TestResourceUtil.java +++ b/test-framework/junit5/src/main/java/io/quarkus/test/junit/TestResourceUtil.java @@ -80,6 +80,28 @@ static Set nextTestResources(Cla .testResourceComparisonInfo(requiredTestClass, getTestClassesLocation(requiredTestClass), entriesFromProfile); } + public static String getReloadGroupIdentifier(Class requiredTestClass, + Class profileClass) { + return TestResourceManager + .getReloadGroupIdentifier(nextTestResources(requiredTestClass, instantiateProfile(profileClass))); + } + + private static QuarkusTestProfile instantiateProfile(Class nextTestClassProfile) { + if (nextTestClassProfile != null) { + // The class we are given could be in the app classloader, so swap it over + // All this reflective classloading is a bit wasteful, so it would be ideal if the implementation was less picky about classloaders (that's not just moving the reflection further down the line) + try { + if (nextTestClassProfile.getClassLoader() != TestResourceUtil.class.getClassLoader()) { + nextTestClassProfile = (Class) Class.forName(nextTestClassProfile.getName()); + } + return nextTestClassProfile.getConstructor().newInstance(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + return null; + } + /** * Contains a bunch of utilities that are needed for handling {@link TestResourceManager} * via reflection (due to different classloaders) diff --git a/test-framework/junit5/src/test/java/io/quarkus/test/junit/TestResourceUtilTest.java b/test-framework/junit5/src/test/java/io/quarkus/test/junit/TestResourceUtilTest.java new file mode 100644 index 0000000000000..0453c36288439 --- /dev/null +++ b/test-framework/junit5/src/test/java/io/quarkus/test/junit/TestResourceUtilTest.java @@ -0,0 +1,82 @@ +package io.quarkus.test.junit; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.junit.jupiter.api.Test; + +import io.quarkus.test.common.QuarkusTestResourceLifecycleManager; + +public class TestResourceUtilTest { + + // Basic sense check, since most of the heavy lifting is done by TestResourceManager#getReloadGroupIdentifier + @Test + public void testReloadGroupIdentifierIsEqualForTestsWithNoResources() { + String identifier1 = TestResourceUtil.getReloadGroupIdentifier(TestClass.class, ProfileClass.class); + String identifier2 = TestResourceUtil.getReloadGroupIdentifier(TestClass.class, AnotherProfileClass.class); + assertEquals(identifier2, identifier1); + } + + @Test + public void testReloadGroupIdentifierIsEqualForTestsWithIdenticalResources() { + String identifier1 = TestResourceUtil.getReloadGroupIdentifier(TestClass.class, ProfileClassWithResources.class); + String identifier2 = TestResourceUtil.getReloadGroupIdentifier(TestClass.class, AnotherProfileClassWithResources.class); + assertEquals(identifier2, identifier1); + } + + @Test + public void testReloadGroupIdentifierIsEqualForTestsWithDifferentResources() { + String identifier1 = TestResourceUtil.getReloadGroupIdentifier(TestClass.class, ProfileClassWithResources.class); + String identifier2 = TestResourceUtil.getReloadGroupIdentifier(TestClass.class, ProfileClass.class); + assertNotEquals(identifier2, identifier1); + } +} + +class TestClass { + +} + +class ProfileClass implements QuarkusTestProfile { + + public ProfileClass() { + } +} + +class AnotherProfileClass implements QuarkusTestProfile { + + public AnotherProfileClass() { + } +} + +class ProfileClassWithResources implements QuarkusTestProfile { + + public ProfileClassWithResources() { + } + + @Override + public List testResources() { + return Collections.singletonList( + new TestResourceEntry( + Dummy.class, Map.of())); + } +} + +class AnotherProfileClassWithResources implements QuarkusTestProfile { + + public AnotherProfileClassWithResources() { + } + + @Override + public List testResources() { + return Collections.singletonList( + new TestResourceEntry( + Dummy.class, Map.of())); + } +} + +abstract class Dummy implements QuarkusTestResourceLifecycleManager { +} \ No newline at end of file