From 87eb67fa1bd5c45dffaac818bbd29853614fa6cc Mon Sep 17 00:00:00 2001 From: Alexey Loubyansky Date: Fri, 9 Dec 2022 18:31:05 +0100 Subject: [PATCH] Look for a test class in the model loaded from the Gradle tooling --- .../GradleApplicationModelBuilder.java | 83 ++++++---- .../bootstrap/workspace/ArtifactSources.java | 3 +- .../bootstrap/utils/BuildToolHelper.java | 3 +- .../resolver/QuarkusGradleModelFactory.java | 3 +- .../src/test/java/Dummy.java | 2 + .../builder/QuarkusModelBuilderTest.java | 5 +- .../quarkus/test/common/TestClassIndexer.java | 23 ++- .../test/common/TestResourceManager.java | 16 +- .../AbstractJvmQuarkusTestExtension.java | 150 +++++++++++------- .../test/junit/QuarkusTestExtension.java | 6 +- 10 files changed, 192 insertions(+), 102 deletions(-) create mode 100644 integration-tests/gradle/src/main/resources/builder/simple-module-project/src/test/java/Dummy.java diff --git a/devtools/gradle/gradle-model/src/main/java/io/quarkus/gradle/tooling/GradleApplicationModelBuilder.java b/devtools/gradle/gradle-model/src/main/java/io/quarkus/gradle/tooling/GradleApplicationModelBuilder.java index d9788a7117920..fea4982a42529 100644 --- a/devtools/gradle/gradle-model/src/main/java/io/quarkus/gradle/tooling/GradleApplicationModelBuilder.java +++ b/devtools/gradle/gradle-model/src/main/java/io/quarkus/gradle/tooling/GradleApplicationModelBuilder.java @@ -9,9 +9,11 @@ import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Properties; import java.util.Set; @@ -27,7 +29,9 @@ import org.gradle.api.initialization.IncludedBuild; import org.gradle.api.tasks.SourceSet; import org.gradle.api.tasks.SourceSetContainer; +import org.gradle.api.tasks.TaskCollection; import org.gradle.api.tasks.compile.AbstractCompile; +import org.gradle.api.tasks.testing.Test; import org.gradle.internal.composite.IncludedBuildInternal; import org.gradle.language.jvm.tasks.ProcessResources; import org.gradle.tooling.provider.model.ParameterizedToolingModelBuilder; @@ -94,7 +98,8 @@ public Object buildAll(String modelName, Project project) { public Object buildAll(String modelName, ModelParameter parameter, Project project) { final LaunchMode mode = LaunchMode.valueOf(parameter.getMode()); - final ApplicationDeploymentClasspathBuilder classpathBuilder = new ApplicationDeploymentClasspathBuilder(project, mode); + final ApplicationDeploymentClasspathBuilder classpathBuilder = new ApplicationDeploymentClasspathBuilder(project, + mode); final Configuration classpathConfig = classpathBuilder.getRuntimeConfiguration(); final Configuration deploymentConfig = classpathBuilder.getDeploymentConfiguration(); final PlatformImports platformImports = classpathBuilder.getPlatformImports(); @@ -127,18 +132,41 @@ public static ResolvedDependency getProjectArtifact(Project project, boolean wor .setArtifactId(project.getName()) .setVersion(project.getVersion().toString()); - SourceSetContainer sourceSets = project.getExtensions().getByType(SourceSetContainer.class); + final SourceSetContainer sourceSets = project.getExtensions().getByType(SourceSetContainer.class); final WorkspaceModule.Mutable mainModule = WorkspaceModule.builder() .setModuleId(new GAV(appArtifact.getGroupId(), appArtifact.getArtifactId(), appArtifact.getVersion())) .setModuleDir(project.getProjectDir().toPath()) .setBuildDir(project.getBuildDir().toPath()) .setBuildFile(project.getBuildFile().toPath()); - initProjectModule(project, mainModule, sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME), - SourceSet.MAIN_SOURCE_SET_NAME, ArtifactSources.MAIN); + initProjectModule(project, mainModule, sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME), ArtifactSources.MAIN); if (workspaceDiscovery) { - initProjectModule(project, mainModule, sourceSets.getByName(SourceSet.TEST_SOURCE_SET_NAME), - SourceSet.TEST_SOURCE_SET_NAME, ArtifactSources.TEST); + final TaskCollection testTasks = project.getTasks().withType(Test.class); + if (!testTasks.isEmpty()) { + final Map sourceSetsByClassesDir = new HashMap<>(); + sourceSets.forEach(s -> { + s.getOutput().getClassesDirs().forEach(d -> { + if (d.exists()) { + sourceSetsByClassesDir.put(d, s); + } + }); + }); + testTasks.forEach(t -> { + if (t.getEnabled()) { + t.getTestClassesDirs().forEach(d -> { + if (d.exists()) { + final SourceSet sourceSet = sourceSetsByClassesDir.remove(d); + if (sourceSet != null) { + initProjectModule(project, mainModule, sourceSet, + sourceSet.getName().equals(SourceSet.TEST_SOURCE_SET_NAME) + ? ArtifactSources.TEST + : sourceSet.getName()); + } + } + }); + } + }); + } } final PathList.Builder paths = PathList.builder(); @@ -150,12 +178,8 @@ public static ResolvedDependency getProjectArtifact(Project project, boolean wor private static void collectDestinationDirs(Collection sources, final PathList.Builder paths) { for (SourceDir src : sources) { - if (!Files.exists(src.getOutputDir())) { - continue; - } - final Path path = src.getOutputDir(); - if (paths.contains(path)) { + if (paths.contains(path) || !Files.exists(path)) { continue; } paths.add(path); @@ -314,7 +338,7 @@ private void collectDependencies(org.gradle.api.artifacts.ResolvedDependency res paths = pathBuilder.build(); } } else if (sourceSets != null) { - if ("test".equals(classifier)) { + if (SourceSet.TEST_SOURCE_SET_NAME.equals(classifier)) { final PathList.Builder pathBuilder = PathList.builder(); projectModule = initProjectModuleAndBuildPaths(projectDep, a, modelBuilder, depBuilder, pathBuilder, SourceSet.TEST_SOURCE_SET_NAME, true); @@ -347,7 +371,7 @@ private void collectDependencies(org.gradle.api.artifacts.ResolvedDependency res } } - processedModules.add(new GACT(resolvedDep.getModuleGroup(), resolvedDep.getModuleName())); + processedModules.add(ArtifactKey.ga(resolvedDep.getModuleGroup(), resolvedDep.getModuleName())); for (org.gradle.api.artifacts.ResolvedDependency child : resolvedDep.getChildren()) { if (!processedModules.contains(new GACT(child.getModuleGroup(), child.getModuleName()))) { collectDependencies(child, workspaceDiscovery, project, artifactFiles, processedModules, @@ -357,7 +381,7 @@ private void collectDependencies(org.gradle.api.artifacts.ResolvedDependency res } private static String toNonNullClassifier(String resolvedClassifier) { - return resolvedClassifier == null ? "" : resolvedClassifier; + return resolvedClassifier == null ? ArtifactCoords.DEFAULT_CLASSIFIER : resolvedClassifier; } private WorkspaceModule.Mutable initProjectModuleAndBuildPaths(final Project project, @@ -375,13 +399,13 @@ private WorkspaceModule.Mutable initProjectModuleAndBuildPaths(final Project pro final String classifier = toNonNullClassifier(resolvedArtifact.getClassifier()); SourceSetContainer sourceSets = project.getExtensions().getByType(SourceSetContainer.class); - initProjectModule(project, projectModule, sourceSets.findByName(sourceName), sourceName, classifier); + initProjectModule(project, projectModule, sourceSets.findByName(sourceName), classifier); collectDestinationDirs(projectModule.getSources(classifier).getSourceDirs(), buildPaths); collectDestinationDirs(projectModule.getSources(classifier).getResourceDirs(), buildPaths); appModel.addReloadableWorkspaceModule( - new GACT(resolvedArtifact.getModuleVersion().getId().getGroup(), resolvedArtifact.getName(), classifier, + ArtifactKey.of(resolvedArtifact.getModuleVersion().getId().getGroup(), resolvedArtifact.getName(), classifier, ArtifactCoords.TYPE_JAR)); return projectModule; } @@ -445,7 +469,7 @@ private static Properties readDescriptor(final Path path) { } private static void initProjectModule(Project project, WorkspaceModule.Mutable module, SourceSet sourceSet, - String sourceName, String classifier) { + String classifier) { if (sourceSet == null) { return; @@ -456,8 +480,6 @@ private static void initProjectModule(Project project, WorkspaceModule.Mutable m // see https://github.com/quarkusio/quarkus/issues/20755 final List sourceDirs = new ArrayList<>(1); - final List resourceDirs = new ArrayList<>(1); - project.getTasks().withType(AbstractCompile.class, t -> { if (!t.getEnabled()) { return; @@ -474,9 +496,7 @@ private static void initProjectModule(Project project, WorkspaceModule.Mutable m // we are looking for the root dirs containing sources if (a.getRelativePath().getSegments().length == 1) { final File srcDir = a.getFile().getParentFile(); - DefaultSourceDir sources = new DefaultSourceDir(srcDir.toPath(), destDir.toPath(), - Collections.singletonMap("compiler", t.getName())); - sourceDirs.add(sources); + sourceDirs.add(new DefaultSourceDir(srcDir.toPath(), destDir.toPath(), Map.of("compiler", t.getName()))); } }); }); @@ -500,9 +520,8 @@ private static void initProjectModule(Project project, WorkspaceModule.Mutable m // we are looking for the root dirs containing sources if (a.getRelativePath().getSegments().length == 1) { final File srcDir = a.getFile().getParentFile(); - DefaultSourceDir sources = new DefaultSourceDir(srcDir.toPath(), destDir.toPath(), - Collections.singletonMap("compiler", t.getName())); - sourceDirs.add(sources); + sourceDirs + .add(new DefaultSourceDir(srcDir.toPath(), destDir.toPath(), Map.of("compiler", t.getName()))); } }); }); @@ -510,8 +529,8 @@ private static void initProjectModule(Project project, WorkspaceModule.Mutable m // ignore } + final LinkedHashMap resourceDirs = new LinkedHashMap<>(1); final File resourcesOutputDir = sourceSet.getOutput().getResourcesDir(); - project.getTasks().withType(ProcessResources.class, t -> { if (!t.getEnabled()) { return; @@ -528,16 +547,20 @@ private static void initProjectModule(Project project, WorkspaceModule.Mutable m // we are looking for the root dirs containing sources if (a.getRelativePath().getSegments().length == 1) { final File srcDir = a.getFile().getParentFile(); - resourceDirs.add(new DefaultSourceDir(srcDir.toPath(), destDir)); + resourceDirs.put(srcDir, destDir); } }); }); // there could be a task generating resources if (resourcesOutputDir.exists() && resourceDirs.isEmpty()) { sourceSet.getResources().getSrcDirs() - .forEach(srcDir -> resourceDirs.add(new DefaultSourceDir(srcDir.toPath(), resourcesOutputDir.toPath()))); + .forEach(srcDir -> resourceDirs.put(srcDir, resourcesOutputDir.toPath())); + } + final List resources = new ArrayList<>(resourceDirs.size()); + for (Map.Entry e : resourceDirs.entrySet()) { + resources.add(new DefaultSourceDir(e.getKey().toPath(), e.getValue())); } - module.addArtifactSources(new DefaultArtifactSources(classifier, sourceDirs, resourceDirs)); + module.addArtifactSources(new DefaultArtifactSources(classifier, sourceDirs, resources)); } private void addSubstitutedProject(PathList.Builder paths, File projectFile) { diff --git a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/workspace/ArtifactSources.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/workspace/ArtifactSources.java index 5b8e385f08b00..13569de58cf3e 100644 --- a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/workspace/ArtifactSources.java +++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/bootstrap/workspace/ArtifactSources.java @@ -4,13 +4,14 @@ import java.util.Collection; import java.util.List; +import io.quarkus.maven.dependency.ArtifactCoords; import io.quarkus.paths.EmptyPathTree; import io.quarkus.paths.MultiRootPathTree; import io.quarkus.paths.PathTree; public interface ArtifactSources { - String MAIN = ""; + String MAIN = ArtifactCoords.DEFAULT_CLASSIFIER; String TEST = "tests"; static ArtifactSources main(SourceDir sources, SourceDir resources) { diff --git a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/utils/BuildToolHelper.java b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/utils/BuildToolHelper.java index fe9fd8358f9ae..637db4e7c9bd0 100644 --- a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/utils/BuildToolHelper.java +++ b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/utils/BuildToolHelper.java @@ -21,7 +21,7 @@ public class BuildToolHelper { private static final Logger log = Logger.getLogger(BuildToolHelper.class); private final static String[] DEVMODE_REQUIRED_TASKS = new String[] { "classes" }; - private final static String[] TEST_REQUIRED_TASKS = new String[] { "classes", "testClasses" }; + private final static String[] TEST_REQUIRED_TASKS = new String[] { "classes", "testClasses", "integrationTestClasses" }; private final static List ENABLE_JAR_PACKAGING = Collections .singletonList("-Dorg.gradle.java.compile-classpath-packaging=true"); @@ -108,6 +108,7 @@ public static ApplicationModel enableGradleAppModelForTest(Path projectRoot) public static ApplicationModel enableGradleAppModel(Path projectRoot, String mode, List jvmArgs, String... tasks) throws IOException, AppModelResolverException { if (isGradleProject(projectRoot)) { + log.infof("Loading Quarkus Gradle application model for %s", projectRoot); final ApplicationModel model = QuarkusGradleModelFactory.create( getBuildFile(projectRoot, BuildTool.GRADLE).toFile(), mode, jvmArgs, tasks); diff --git a/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/QuarkusGradleModelFactory.java b/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/QuarkusGradleModelFactory.java index 6177ef610942f..1a0fce2b7f81b 100644 --- a/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/QuarkusGradleModelFactory.java +++ b/independent-projects/bootstrap/gradle-resolver/src/main/java/io/quarkus/bootstrap/resolver/QuarkusGradleModelFactory.java @@ -1,7 +1,6 @@ package io.quarkus.bootstrap.resolver; import java.io.File; -import java.util.Collections; import java.util.List; import org.gradle.tooling.GradleConnector; @@ -14,7 +13,7 @@ public class QuarkusGradleModelFactory { public static ApplicationModel create(File projectDir, String mode, String... tasks) { - return create(projectDir, mode, Collections.emptyList(), tasks); + return create(projectDir, mode, List.of(), tasks); } public static ApplicationModel create(File projectDir, String mode, List jvmArgs, String... tasks) { diff --git a/integration-tests/gradle/src/main/resources/builder/simple-module-project/src/test/java/Dummy.java b/integration-tests/gradle/src/main/resources/builder/simple-module-project/src/test/java/Dummy.java new file mode 100644 index 0000000000000..6662fdfe2a5e0 --- /dev/null +++ b/integration-tests/gradle/src/main/resources/builder/simple-module-project/src/test/java/Dummy.java @@ -0,0 +1,2 @@ +public class Dummy { +} \ No newline at end of file diff --git a/integration-tests/gradle/src/test/java/io/quarkus/gradle/builder/QuarkusModelBuilderTest.java b/integration-tests/gradle/src/test/java/io/quarkus/gradle/builder/QuarkusModelBuilderTest.java index ccb64db50f69c..213ea5df6a14c 100644 --- a/integration-tests/gradle/src/test/java/io/quarkus/gradle/builder/QuarkusModelBuilderTest.java +++ b/integration-tests/gradle/src/test/java/io/quarkus/gradle/builder/QuarkusModelBuilderTest.java @@ -46,7 +46,7 @@ public void shouldLoadSimpleModuleTestModel() throws URISyntaxException, IOExcep @Test public void shouldLoadSimpleModuleDevModel() throws URISyntaxException, IOException { File projectDir = getResourcesProject("builder/simple-module-project"); - final ApplicationModel quarkusModel = QuarkusGradleModelFactory.create(projectDir, "DEVELOPMENT"); + final ApplicationModel quarkusModel = QuarkusGradleModelFactory.create(projectDir, "DEVELOPMENT", "testClasses"); assertNotNull(quarkusModel); assertNotNull(quarkusModel.getApplicationModule()); @@ -63,7 +63,8 @@ public void shouldLoadSimpleModuleDevModel() throws URISyntaxException, IOExcept public void shouldLoadMultiModuleTestModel() throws URISyntaxException, IOException { File projectDir = getResourcesProject("builder/multi-module-project"); - final ApplicationModel quarkusModel = QuarkusGradleModelFactory.create(new File(projectDir, "application"), "TEST"); + final ApplicationModel quarkusModel = QuarkusGradleModelFactory.create(new File(projectDir, "application"), "TEST", + "testClasses"); assertNotNull(quarkusModel); diff --git a/test-framework/common/src/main/java/io/quarkus/test/common/TestClassIndexer.java b/test-framework/common/src/main/java/io/quarkus/test/common/TestClassIndexer.java index ec86da4fad87c..59eaac4c89322 100644 --- a/test-framework/common/src/main/java/io/quarkus/test/common/TestClassIndexer.java +++ b/test-framework/common/src/main/java/io/quarkus/test/common/TestClassIndexer.java @@ -29,8 +29,11 @@ private TestClassIndexer() { } public static Index indexTestClasses(Class testClass) { + return indexTestClasses(getTestClassesLocation(testClass)); + } + + public static Index indexTestClasses(final Path testClassesLocation) { final Indexer indexer = new Indexer(); - final Path testClassesLocation = getTestClassesLocation(testClass); try { if (Files.isDirectory(testClassesLocation)) { indexTestClassesDir(indexer, testClassesLocation); @@ -48,7 +51,11 @@ public static Index indexTestClasses(Class testClass) { } public static void writeIndex(Index index, Class testClass) { - try (FileOutputStream fos = new FileOutputStream(indexPath(testClass).toFile(), false)) { + writeIndex(index, getTestClassesLocation(testClass), testClass); + } + + public static void writeIndex(Index index, Path testClassLocation, Class testClass) { + try (FileOutputStream fos = new FileOutputStream(indexPath(testClassLocation, testClass).toFile(), false)) { IndexWriter indexWriter = new IndexWriter(fos); indexWriter.write(index); } catch (IOException ignored) { @@ -58,7 +65,11 @@ public static void writeIndex(Index index, Class testClass) { } public static Index readIndex(Class testClass) { - Path path = indexPath(testClass); + return readIndex(getTestClassesLocation(testClass), testClass); + } + + public static Index readIndex(Path testClassLocation, Class testClass) { + Path path = indexPath(testClassLocation, testClass); if (path.toFile().exists()) { try (FileInputStream fis = new FileInputStream(path.toFile())) { return new IndexReader(fis).read(); @@ -75,7 +86,11 @@ public static Index readIndex(Class testClass) { } private static Path indexPath(Class testClass) { - return PathTestHelper.getTestClassesLocation(testClass).resolve(testClass.getSimpleName() + ".idx"); + return indexPath(PathTestHelper.getTestClassesLocation(testClass), testClass); + } + + private static Path indexPath(Path testClassLocation, Class testClass) { + return testClassLocation.resolve(testClass.getSimpleName() + ".idx"); } private static void indexTestClassesDir(Indexer indexer, final Path testClassesLocation) throws IOException { 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 b75f18a68a79c..05fcffc736e64 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 @@ -4,6 +4,7 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -56,6 +57,13 @@ public TestResourceManager(Class testClass, Class profileClass, List testClass, Class profileClass, List additionalTestResources, boolean disableGlobalTestResources, Map devServicesProperties, Optional containerNetworkId) { + this(testClass, profileClass, additionalTestResources, disableGlobalTestResources, devServicesProperties, + containerNetworkId, PathTestHelper.getTestClassesLocation(testClass)); + } + + public TestResourceManager(Class testClass, Class profileClass, List additionalTestResources, + boolean disableGlobalTestResources, Map devServicesProperties, + Optional containerNetworkId, Path testClassLocation) { this.parallelTestResourceEntries = new ArrayList<>(); this.sequentialTestResourceEntries = new ArrayList<>(); @@ -65,7 +73,8 @@ public TestResourceManager(Class testClass, Class profileClass, List(additionalTestResources); } else { - uniqueEntries = getUniqueTestResourceClassEntries(testClass, profileClass, additionalTestResources); + uniqueEntries = getUniqueTestResourceClassEntries(testClassLocation, testClass, profileClass, + additionalTestResources); } Set remainingUniqueEntries = initParallelTestResources(uniqueEntries); initSequentialTestResources(remainingUniqueEntries); @@ -247,9 +256,10 @@ private TestResourceManager.TestResourceEntry buildTestResourceEntry(TestResourc } } - private Set getUniqueTestResourceClassEntries(Class testClass, Class profileClass, + private Set getUniqueTestResourceClassEntries(Path testClassLocation, Class testClass, + Class profileClass, List additionalTestResources) { - IndexView index = TestClassIndexer.readIndex(testClass); + IndexView index = TestClassIndexer.readIndex(testClassLocation, testClass); Set uniqueEntries = new LinkedHashSet<>(); // reload the test and profile classes in the right CL Class testClassFromTCCL; diff --git a/test-framework/junit5/src/main/java/io/quarkus/test/junit/AbstractJvmQuarkusTestExtension.java b/test-framework/junit5/src/main/java/io/quarkus/test/junit/AbstractJvmQuarkusTestExtension.java index 629dd7c3df92f..5c73ff7f00568 100644 --- a/test-framework/junit5/src/main/java/io/quarkus/test/junit/AbstractJvmQuarkusTestExtension.java +++ b/test-framework/junit5/src/main/java/io/quarkus/test/junit/AbstractJvmQuarkusTestExtension.java @@ -3,6 +3,7 @@ import static io.quarkus.test.common.PathTestHelper.getAppClassLocationForTestLocation; import static io.quarkus.test.common.PathTestHelper.getTestClassesLocation; +import java.io.IOException; import java.lang.annotation.Annotation; import java.nio.file.Files; import java.nio.file.Path; @@ -25,10 +26,12 @@ import io.quarkus.bootstrap.app.CuratedApplication; import io.quarkus.bootstrap.app.QuarkusBootstrap; import io.quarkus.bootstrap.model.ApplicationModel; +import io.quarkus.bootstrap.resolver.AppModelResolverException; import io.quarkus.bootstrap.runner.Timing; import io.quarkus.bootstrap.utils.BuildToolHelper; import io.quarkus.bootstrap.workspace.ArtifactSources; import io.quarkus.bootstrap.workspace.SourceDir; +import io.quarkus.bootstrap.workspace.WorkspaceModule; import io.quarkus.deployment.dev.testing.CurrentTestApplication; import io.quarkus.paths.PathList; import io.quarkus.runtime.configuration.ProfileManager; @@ -53,25 +56,86 @@ public class AbstractJvmQuarkusTestExtension extends AbstractQuarkusTestWithCont protected PrepareResult createAugmentor(ExtensionContext context, Class profile, Collection shutdownTasks) throws Exception { - Class requiredTestClass = context.getRequiredTestClass(); - currentJUnitTestClass = requiredTestClass; - Path testClassLocation = getTestClassesLocation(requiredTestClass); - final Path appClassLocation = getAppClassLocationForTestLocation(testClassLocation.toString()); - final PathList.Builder rootBuilder = PathList.builder(); - Consumer addToBuilderIfConditionMet = path -> { if (path != null && Files.exists(path) && !rootBuilder.contains(path)) { rootBuilder.add(path); } }; - if (!appClassLocation.equals(testClassLocation)) { - addToBuilderIfConditionMet.accept(testClassLocation); - // if test classes is a dir, we should also check whether test resources dir exists as a separate dir (gradle) - // TODO: this whole app/test path resolution logic is pretty dumb, it needs be re-worked using proper workspace discovery - final Path testResourcesLocation = PathTestHelper.getResourcesForClassesDirOrNull(testClassLocation, "test"); - addToBuilderIfConditionMet.accept(testResourcesLocation); + final Class requiredTestClass = context.getRequiredTestClass(); + currentJUnitTestClass = requiredTestClass; + + final Path testClassLocation; + final Path appClassLocation; + final Path projectRoot = Paths.get("").normalize().toAbsolutePath(); + + final ApplicationModel gradleAppModel = getGradleAppModelForIDE(projectRoot); + // If gradle project running directly with IDE + if (gradleAppModel != null && gradleAppModel.getApplicationModule() != null) { + final WorkspaceModule module = gradleAppModel.getApplicationModule(); + final String testClassFileName = requiredTestClass.getName().replace('.', '/') + ".class"; + Path testClassesDir = null; + for (String classifier : module.getSourceClassifiers()) { + final ArtifactSources sources = module.getSources(classifier); + if (sources.isOutputAvailable() && sources.getOutputTree().contains(testClassFileName)) { + for (SourceDir src : sources.getSourceDirs()) { + addToBuilderIfConditionMet.accept(src.getOutputDir()); + if (Files.exists(src.getOutputDir().resolve(testClassFileName))) { + testClassesDir = src.getOutputDir(); + } + } + for (SourceDir src : sources.getResourceDirs()) { + addToBuilderIfConditionMet.accept(src.getOutputDir()); + } + for (SourceDir src : module.getMainSources().getSourceDirs()) { + addToBuilderIfConditionMet.accept(src.getOutputDir()); + } + for (SourceDir src : module.getMainSources().getResourceDirs()) { + addToBuilderIfConditionMet.accept(src.getOutputDir()); + } + break; + } + } + if (testClassesDir == null) { + final StringBuilder sb = new StringBuilder(); + sb.append("Failed to locate ").append(requiredTestClass.getName()).append(" in "); + for (String classifier : module.getSourceClassifiers()) { + final ArtifactSources sources = module.getSources(classifier); + if (sources.isOutputAvailable()) { + for (SourceDir d : sources.getSourceDirs()) { + if (Files.exists(d.getOutputDir())) { + sb.append(System.lineSeparator()).append(d.getOutputDir()); + } + } + } + } + throw new RuntimeException(sb.toString()); + } + testClassLocation = testClassesDir; + + } else { + if (System.getProperty(BootstrapConstants.OUTPUT_SOURCES_DIR) != null) { + final String[] sourceDirectories = System.getProperty(BootstrapConstants.OUTPUT_SOURCES_DIR).split(","); + for (String sourceDirectory : sourceDirectories) { + final Path directory = Paths.get(sourceDirectory); + addToBuilderIfConditionMet.accept(directory); + } + } + + testClassLocation = getTestClassesLocation(requiredTestClass); + appClassLocation = getAppClassLocationForTestLocation(testClassLocation.toString()); + if (!appClassLocation.equals(testClassLocation)) { + addToBuilderIfConditionMet.accept(testClassLocation); + // if test classes is a dir, we should also check whether test resources dir exists as a separate dir (gradle) + // TODO: this whole app/test path resolution logic is pretty dumb, it needs be re-worked using proper workspace discovery + final Path testResourcesLocation = PathTestHelper.getResourcesForClassesDirOrNull(testClassLocation, "test"); + addToBuilderIfConditionMet.accept(testResourcesLocation); + } + + addToBuilderIfConditionMet.accept(appClassLocation); + final Path appResourcesLocation = PathTestHelper.getResourcesForClassesDirOrNull(appClassLocation, "main"); + addToBuilderIfConditionMet.accept(appResourcesLocation); } originalCl = Thread.currentThread().getContextClassLoader(); @@ -105,52 +169,18 @@ protected PrepareResult createAugmentor(ExtensionContext context, Class getQuarkusTestProfile(ExtensionContext extensionContext) { @@ -222,12 +258,14 @@ protected static class PrepareResult { protected final AugmentAction augmentAction; protected final QuarkusTestProfile profileInstance; protected final CuratedApplication curatedApplication; + protected final Path testClassLocation; public PrepareResult(AugmentAction augmentAction, QuarkusTestProfile profileInstance, - CuratedApplication curatedApplication) { + CuratedApplication curatedApplication, Path testClassLocation) { this.augmentAction = augmentAction; this.profileInstance = profileInstance; this.curatedApplication = curatedApplication; + this.testClassLocation = testClassLocation; } } } 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 4c49143c21fcf..fd9153d2010fc 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 @@ -225,12 +225,12 @@ public Thread newThread(Runnable r) { //must be done after the TCCL has been set testResourceManager = (Closeable) startupAction.getClassLoader().loadClass(TestResourceManager.class.getName()) - .getConstructor(Class.class, Class.class, List.class, boolean.class, Map.class, Optional.class) + .getConstructor(Class.class, Class.class, List.class, boolean.class, Map.class, Optional.class, Path.class) .newInstance(requiredTestClass, profile != null ? profile : null, getAdditionalTestResources(profileInstance, startupAction.getClassLoader()), profileInstance != null && profileInstance.disableGlobalTestResources(), - startupAction.getDevServicesProperties(), Optional.empty()); + startupAction.getDevServicesProperties(), Optional.empty(), result.testClassLocation); testResourceManager.getClass().getMethod("init", String.class).invoke(testResourceManager, profile != null ? profile.getName() : null); Map properties = (Map) testResourceManager.getClass().getMethod("start") @@ -1251,7 +1251,7 @@ public static class TestBuildChainFunction implements Function> apply(Map stringObjectMap) { Path testLocation = (Path) stringObjectMap.get(TEST_LOCATION); // the index was written by the extension - Index testClassesIndex = TestClassIndexer.readIndex((Class) stringObjectMap.get(TEST_CLASS)); + Index testClassesIndex = TestClassIndexer.readIndex(testLocation, (Class) stringObjectMap.get(TEST_CLASS)); List> allCustomizers = new ArrayList<>(1); Consumer defaultCustomizer = new Consumer() {