From 1281f03b96a99ee3b6e7086208b187be7baa2094 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Mon, 16 Oct 2023 16:05:09 +0200 Subject: [PATCH] Skipping duplicate test classes during test AOT processing Although it should not happen in theory, sometimes a test class is discovered more than once via the TestClassScanner in our integration tests. When it does happen in our tests, the two Class objects have the same fully-qualified class name but represent different classes which leads to failures due to incorrect associations between test class names and their MergedContextConfiguration. To address this, this commit modifies TestContextAotGenerator so that it skips duplicate test class names. --- .../context/aot/TestContextAotGenerator.java | 30 ++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/spring-test/src/main/java/org/springframework/test/context/aot/TestContextAotGenerator.java b/spring-test/src/main/java/org/springframework/test/context/aot/TestContextAotGenerator.java index d3fa761afd..438b9d7a38 100644 --- a/spring-test/src/main/java/org/springframework/test/context/aot/TestContextAotGenerator.java +++ b/spring-test/src/main/java/org/springframework/test/context/aot/TestContextAotGenerator.java @@ -17,6 +17,7 @@ package org.springframework.test.context.aot; import java.util.Arrays; +import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; @@ -178,18 +179,27 @@ public class TestContextAotGenerator { MultiValueMap> mergedConfigMappings = new LinkedMultiValueMap<>(); ClassLoader classLoader = getClass().getClassLoader(); + Set visitedTestClassNames = new HashSet<>(); testClasses.forEach(testClass -> { - MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass); - mergedConfigMappings.add(mergedConfig, testClass); - collectRuntimeHintsRegistrarClasses(testClass, coreRuntimeHintsRegistrarClasses); - reflectiveRuntimeHintsRegistrar.registerRuntimeHints(this.runtimeHints, testClass); - this.testRuntimeHintsRegistrars.forEach(registrar -> { - if (logger.isTraceEnabled()) { - logger.trace("Processing RuntimeHints contribution from class [%s]" - .formatted(registrar.getClass().getCanonicalName())); + String testClassName = testClass.getName(); + if (visitedTestClassNames.add(testClassName)) { + MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass); + mergedConfigMappings.add(mergedConfig, testClass); + collectRuntimeHintsRegistrarClasses(testClass, coreRuntimeHintsRegistrarClasses); + reflectiveRuntimeHintsRegistrar.registerRuntimeHints(this.runtimeHints, testClass); + this.testRuntimeHintsRegistrars.forEach(registrar -> { + if (logger.isTraceEnabled()) { + logger.trace("Processing RuntimeHints contribution from class [%s]" + .formatted(registrar.getClass().getCanonicalName())); + } + registrar.registerHints(this.runtimeHints, testClass, classLoader); + }); + } + else { + if (logger.isDebugEnabled()) { + logger.debug("Skipping duplicate test class: " + testClassName); } - registrar.registerHints(this.runtimeHints, testClass, classLoader); - }); + } }); coreRuntimeHintsRegistrarClasses.stream()