Skip to content

Commit

Permalink
Add new state-based method for deciding if tests should run in the sa…
Browse files Browse the repository at this point in the history
…me classloader
  • Loading branch information
holly-cummins committed Jan 9, 2025
1 parent 0d6a99e commit 4da51b3
Show file tree
Hide file tree
Showing 4 changed files with 200 additions and 48 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -569,6 +570,12 @@ public static boolean testResourcesRequireReload(Set<TestResourceComparisonInfo>
return false;
}

public static String getReloadGroupIdentifier(Set<TestResourceComparisonInfo> 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<TestResourceComparisonInfo> testResources) {
for (TestResourceComparisonInfo info : testResources) {
if (info.scope == RESTRICTED_TO_CLASS) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -17,93 +22,129 @@ public class TestResourceManagerReloadTest {

@Test
public void emptyResources() {
assertFalse(testResourcesRequireReload(Collections.emptySet(), Set.of()));
Set<TestResourceComparisonInfo> existing = Collections.emptySet();
Set<TestResourceComparisonInfo> 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<TestResourceComparisonInfo> existing = Collections.emptySet();
Set<TestResourceComparisonInfo> 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<TestResourceComparisonInfo> existing = Set
.of(new TestResourceComparisonInfo("test", RESTRICTED_TO_CLASS, Map.of()));
Set<TestResourceComparisonInfo> 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<TestResourceComparisonInfo> existing = Set.of(new TestResourceComparisonInfo("test", MATCHING_RESOURCES, Map.of()));
Set<TestResourceComparisonInfo> 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<TestResourceComparisonInfo> existing = Set
.of(new TestResourceComparisonInfo("test", MATCHING_RESOURCES, Map.of("a", "b")));
Set<TestResourceComparisonInfo> 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<TestResourceComparisonInfo> existing = Set
.of(new TestResourceComparisonInfo("test", MATCHING_RESOURCES, Map.of("a", "b")));
Set<TestResourceComparisonInfo> 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<TestResourceComparisonInfo> existing = Set
.of(new TestResourceComparisonInfo("test", MATCHING_RESOURCES, Map.of("a", "b")));
Set<TestResourceComparisonInfo> 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<TestResourceComparisonInfo> existing = Set.of(new TestResourceComparisonInfo("test", MATCHING_RESOURCES, Map.of()));
Set<TestResourceComparisonInfo> 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<TestResourceComparisonInfo> existing = Set.of(
new TestResourceComparisonInfo("test", MATCHING_RESOURCES, Map.of()),
new TestResourceComparisonInfo("test2", MATCHING_RESOURCES, Map.of()),
new TestResourceComparisonInfo("test3", GLOBAL, Map.of()));
Set<TestResourceComparisonInfo> 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<TestResourceComparisonInfo> existing = Set.of(
new TestResourceComparisonInfo("test", MATCHING_RESOURCES, Map.of()),
new TestResourceComparisonInfo("test2", MATCHING_RESOURCES, Map.of()),
new TestResourceComparisonInfo("test3", GLOBAL, Map.of()));
Set<TestResourceComparisonInfo> 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<TestResourceComparisonInfo> existing = Set.of(
new TestResourceComparisonInfo("test", MATCHING_RESOURCES, Map.of()),
new TestResourceComparisonInfo("test2", MATCHING_RESOURCES, Map.of()),
new TestResourceComparisonInfo("test4", GLOBAL, Map.of()));
Set<TestResourceComparisonInfo> 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));

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,28 @@ static Set<TestResourceManager.TestResourceComparisonInfo> nextTestResources(Cla
.testResourceComparisonInfo(requiredTestClass, getTestClassesLocation(requiredTestClass), entriesFromProfile);
}

public static String getReloadGroupIdentifier(Class<?> requiredTestClass,
Class<? extends QuarkusTestProfile> profileClass) {
return TestResourceManager
.getReloadGroupIdentifier(nextTestResources(requiredTestClass, instantiateProfile(profileClass)));
}

private static QuarkusTestProfile instantiateProfile(Class<? extends QuarkusTestProfile> 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<? extends QuarkusTestProfile>) 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)
Expand Down
Original file line number Diff line number Diff line change
@@ -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<TestResourceEntry> testResources() {
return Collections.singletonList(
new TestResourceEntry(
Dummy.class, Map.of()));
}
}

class AnotherProfileClassWithResources implements QuarkusTestProfile {

public AnotherProfileClassWithResources() {
}

@Override
public List<TestResourceEntry> testResources() {
return Collections.singletonList(
new TestResourceEntry(
Dummy.class, Map.of()));
}
}

abstract class Dummy implements QuarkusTestResourceLifecycleManager {
}

0 comments on commit 4da51b3

Please sign in to comment.