From 56854f775e190d95e85ba5429eecd16605398cdd Mon Sep 17 00:00:00 2001 From: Eugene Zhuravlev Date: Mon, 20 Oct 2025 20:26:56 +0200 Subject: [PATCH] support 'productionOnTests' dependencies in ModuleCompileScope (IDEA-377309) (cherry picked from commit a8be6439d3e0d1f08714105eb3accc89a243149e) IJ-CR-179279 GitOrigin-RevId: bca838021c775000db3bf88212ab5ac829953f73 --- .../compiler/impl/ModuleCompileScope.java | 11 ++- .../compiler/ModuleCompileScopeTest.java | 79 +++++++++++++++++++ 2 files changed, 86 insertions(+), 4 deletions(-) diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/ModuleCompileScope.java b/java/compiler/impl/src/com/intellij/compiler/impl/ModuleCompileScope.java index bba31b8be054..c590b5ceab22 100644 --- a/java/compiler/impl/src/com/intellij/compiler/impl/ModuleCompileScope.java +++ b/java/compiler/impl/src/com/intellij/compiler/impl/ModuleCompileScope.java @@ -1,8 +1,5 @@ // Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -/* - * @author Eugene Zhuravlev - */ package com.intellij.compiler.impl; import com.intellij.compiler.ModuleSourceSet; @@ -71,6 +68,12 @@ public class ModuleCompileScope extends FileIndexCompileScope { } return true; }); + enumerator.forEach(orderEntry -> { + if (orderEntry instanceof ModuleOrderEntry moduleEntry && moduleEntry.isProductionOnTestDependency() && myScopeModules.contains(moduleEntry.getModule())) { + myTestSourcesModules.add(moduleEntry.getModule()); + } + return true; + }); } else { myScopeModules.add(module); @@ -90,7 +93,7 @@ public class ModuleCompileScope extends FileIndexCompileScope { if (myIncludeTests) { return result.stream().filter(set -> !set.getType().isTest() || myTestSourcesModules.contains(set.getModule())).collect(Collectors.toList()); } - return result.stream().filter(set -> !set.getType().isTest()).collect(Collectors.toList()); + return result.stream().filter(set -> !set.getType().isTest() || myTestSourcesModules.contains(set.getModule())).collect(Collectors.toList()); } public static boolean shouldIncludeTestsFromDependentModulesToTestClasspath(@NotNull Module module) { diff --git a/java/compiler/tests/com/intellij/compiler/ModuleCompileScopeTest.java b/java/compiler/tests/com/intellij/compiler/ModuleCompileScopeTest.java index fcffe312e27a..a1cc3a3a2920 100644 --- a/java/compiler/tests/com/intellij/compiler/ModuleCompileScopeTest.java +++ b/java/compiler/tests/com/intellij/compiler/ModuleCompileScopeTest.java @@ -229,6 +229,85 @@ public class ModuleCompileScopeTest extends BaseCompilerTestCase { } } + public void testCompileProductionOnTestsDependency() { + Disposable extDisposable = Disposer.newDisposable(); + + // emulate behavior for maven-imported projects + OrderEnumerationHandler.EP_NAME.getPoint().registerExtension(new OrderEnumerationHandler.Factory() { + private static final OrderEnumerationHandler HANDLER = new OrderEnumerationHandler() { + @Override + public boolean shouldIncludeTestsFromDependentModulesToTestClasspath() { + return false; + } + + @Override + public boolean shouldProcessDependenciesRecursively() { + return false; + } + }; + + @Override + public boolean isApplicable(@NotNull Module module) { + return true; + } + + @Override + public @NotNull OrderEnumerationHandler createHandler(@NotNull Module module) { + return HANDLER; + } + }, extDisposable); + + try { + //String aText = "class A{ String msg = TestBM2.message; public static void foo(int param) {} }"; + String aText = "class A{ public static void foo(int param) {} }"; + VirtualFile a = createFile("m1/src/A.java", aText); + String bText = "class B { void bar() {A.foo(10);}}"; + VirtualFile b = createFile("m1/testSrc/B.java", bText); + Module m1 = addModule("m1", a.getParent(), b.getParent(), null); + + String testBM2Text = "class TestBM2{public static final String message =\"hello\";}"; + VirtualFile fileTestBM2 = createFile("m2/testSrc/TestBM2.java", testBM2Text); + Module m2 = addModule("m2", null, fileTestBM2.getParent(), null); + + ModuleRootModificationUtil.addDependency(m1, m2, DependencyScope.COMPILE, false /* exported */, true /* productionOnTests */); + make(m1, m2); + + assertOutput(m1, fs().file("A.class"), false); + assertOutput(m1, fs().file("B.class"), true); + assertOutput(m2, fs().file("TestBM2.class"), true); + + VirtualFile outputM1 = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(getOutputDir(m1, false)); + assertNotNull(outputM1); + final VirtualFile testOutputM1 = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(getOutputDir(m1, true)); + assertNotNull(testOutputM1); + VirtualFile classFileM1 = outputM1.findChild("A.class"); + assertNotNull(classFileM1); + VirtualFile testClassFileM1 = testOutputM1.findChild("B.class"); + assertNotNull(testClassFileM1); + deleteFile(classFileM1); + deleteFile(testClassFileM1); + changeFile(a, aText + " "); // touch a + changeFile(b, bText + " some error"); // touch b so it won't compile + + VirtualFile testOutputM2 = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(getOutputDir(m2, true)); + assertNotNull(testOutputM2); + VirtualFile testClassFileM2 = testOutputM2.findChild("TestBM2.class"); + assertNotNull(testClassFileM2); + deleteFile(testClassFileM2); + changeFile(fileTestBM2, testBM2Text + " "); // touch fileTestBM2 + + // The module scope should not contain tests from m1, because includeTests = false. + // Despite on that, it should contain tests from m2, because productionOnTests = true for the module dependency m1 --> m2 + make(getCompilerManager().createModulesCompileScope(new Module[] {m1}, true /*includeDeps*/, true, false /* include tests */)); + assertOutput(m1, fs().file("A.class"), false); + assertOutput(m1, fs(), true); + assertOutput(m2, fs().file("TestBM2.class"), true); // make sure TestBM2 from dependent module is compiled, because it has been included in the compile scope + } + finally { + Disposer.dispose(extDisposable); + } + } + public void testMakeTwoModules() { VirtualFile file1 = createFile("m1/src/A.java", "class A{}"); Module m1 = addModule("m1", file1.getParent());