diff --git a/build/src/CommunityRunTestsBuildTarget.kt b/build/src/CommunityRunTestsBuildTarget.kt index 1a379ebadd30..ab99ca70a46d 100644 --- a/build/src/CommunityRunTestsBuildTarget.kt +++ b/build/src/CommunityRunTestsBuildTarget.kt @@ -1,6 +1,7 @@ // Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking +import org.jetbrains.intellij.build.BuildOptions import org.jetbrains.intellij.build.BuildPaths.Companion.COMMUNITY_ROOT import org.jetbrains.intellij.build.TestingOptions import org.jetbrains.intellij.build.TestingTasks @@ -21,7 +22,10 @@ object CommunityRunTestsBuildTarget { runBlocking(Dispatchers.Default) { val context = createCompilationContext( projectHome = COMMUNITY_ROOT.communityRoot, - defaultOutputRoot = COMMUNITY_ROOT.communityRoot.resolve("out/tests") + defaultOutputRoot = COMMUNITY_ROOT.communityRoot.resolve("out/tests"), + options = BuildOptions().also { + it.useTestCompilationOutput = true + }, ) val options = TestingOptions() options.mainModule = options.mainModule ?: "intellij.idea.community.main" diff --git a/platform/bootstrap/dev/src/org/jetbrains/intellij/build/devServer/JUnitDevMainKt.java b/platform/bootstrap/dev/src/org/jetbrains/intellij/build/devServer/JUnitDevMainKt.java index e7148219407f..b353b1213416 100644 --- a/platform/bootstrap/dev/src/org/jetbrains/intellij/build/devServer/JUnitDevMainKt.java +++ b/platform/bootstrap/dev/src/org/jetbrains/intellij/build/devServer/JUnitDevMainKt.java @@ -36,6 +36,7 @@ public final class JUnitDevMainKt { System.setProperty("idea.vendor.name", "JetBrains"); System.setProperty("idea.use.dev.build.server", "true"); + System.setProperty("idea.build.pack.test.source.enabled", "true"); if (jUnitStarterModule != null) System.setProperty("idea.dev.build.unpacked", "true"); // idea.platform.prefix should be set if (System.getProperty("idea.platform.prefix") == null) { diff --git a/platform/build-scripts/api/src/ModuleOutputProvider.kt b/platform/build-scripts/api/src/ModuleOutputProvider.kt index eacdfb716a9e..1158937593ab 100644 --- a/platform/build-scripts/api/src/ModuleOutputProvider.kt +++ b/platform/build-scripts/api/src/ModuleOutputProvider.kt @@ -6,6 +6,8 @@ import org.jetbrains.jps.model.module.JpsModule import java.nio.file.Path interface ModuleOutputProvider { + val useTestCompilationOutput: Boolean + fun readFileContentFromModuleOutput(module: JpsModule, relativePath: String, forTests: Boolean = false): ByteArray? fun findModule(name: String): JpsModule? diff --git a/platform/build-scripts/api/src/moduleContentUtil.kt b/platform/build-scripts/api/src/moduleContentUtil.kt index 57d053385e36..0f5f99dec14f 100644 --- a/platform/build-scripts/api/src/moduleContentUtil.kt +++ b/platform/build-scripts/api/src/moduleContentUtil.kt @@ -20,7 +20,6 @@ import java.nio.file.Path import java.nio.file.attribute.BasicFileAttributes const val PLUGIN_XML_RELATIVE_PATH: String = "META-INF/plugin.xml" -val useTestSourceEnabled: Boolean = System.getProperty("idea.build.pack.test.source.enabled", "true").toBoolean() fun getUnprocessedPluginXmlContent(module: JpsModule, outputProvider: ModuleOutputProvider): ByteArray { return requireNotNull(findUnprocessedDescriptorContent(module = module, path = PLUGIN_XML_RELATIVE_PATH, outputProvider = outputProvider)) { @@ -31,7 +30,7 @@ fun getUnprocessedPluginXmlContent(module: JpsModule, outputProvider: ModuleOutp fun findUnprocessedDescriptorContent(module: JpsModule, path: String, outputProvider: ModuleOutputProvider): ByteArray? { try { val result = outputProvider.readFileContentFromModuleOutput(module = module, relativePath = path, forTests = false) - if (result == null && useTestSourceEnabled) { + if (result == null && outputProvider.useTestCompilationOutput) { return outputProvider.readFileContentFromModuleOutput(module = module, relativePath = path, forTests = true) } return result diff --git a/platform/build-scripts/product-dsl/testSrc/ProductModulesContentSpecTest.kt b/platform/build-scripts/product-dsl/testSrc/ProductModulesContentSpecTest.kt index c675f7c59199..19530111810d 100644 --- a/platform/build-scripts/product-dsl/testSrc/ProductModulesContentSpecTest.kt +++ b/platform/build-scripts/product-dsl/testSrc/ProductModulesContentSpecTest.kt @@ -401,6 +401,9 @@ class ProductModulesContentSpecTest { * Mock ModuleOutputProvider for testing that doesn't load modules. */ private class MockModuleOutputProvider : ModuleOutputProvider { + override val useTestCompilationOutput: Boolean + get() = throw UnsupportedOperationException("Not available in mock") + override fun readFileContentFromModuleOutput(module: JpsModule, relativePath: String, forTests: Boolean): ByteArray? { throw UnsupportedOperationException("Not available in mock") } diff --git a/platform/build-scripts/product-dsl/testSrc/dependency/PluginDependencyGeneratorTest.kt b/platform/build-scripts/product-dsl/testSrc/dependency/PluginDependencyGeneratorTest.kt index 9ccff016be67..3d67f06bff74 100644 --- a/platform/build-scripts/product-dsl/testSrc/dependency/PluginDependencyGeneratorTest.kt +++ b/platform/build-scripts/product-dsl/testSrc/dependency/PluginDependencyGeneratorTest.kt @@ -506,6 +506,9 @@ private fun createTestModuleOutputProvider(project: JpsProject): ModuleOutputPro override fun findRequiredModule(name: String): JpsModule = findModule(name) ?: error("Module not found: $name") + override val useTestCompilationOutput: Boolean + get() = true + override fun readFileContentFromModuleOutput(module: JpsModule, relativePath: String, forTests: Boolean): ByteArray { throw UnsupportedOperationException("Not needed for this test") } diff --git a/platform/build-scripts/src/org/jetbrains/intellij/build/BuildOptions.kt b/platform/build-scripts/src/org/jetbrains/intellij/build/BuildOptions.kt index ac96e021680e..15a5c3203810 100644 --- a/platform/build-scripts/src/org/jetbrains/intellij/build/BuildOptions.kt +++ b/platform/build-scripts/src/org/jetbrains/intellij/build/BuildOptions.kt @@ -44,6 +44,12 @@ data class BuildOptions( @JvmField var isInDevelopmentMode: Boolean = getBooleanProperty("intellij.build.dev.mode", System.getenv("TEAMCITY_VERSION") == null && System.getenv("GITHUB_ACTIONS") == null), @JvmField var useCompiledClassesFromProjectOutput: Boolean = getBooleanProperty(USE_COMPILED_CLASSES_PROPERTY, isInDevelopmentMode), + /** + * In addition to production compilation sources, allow various functions to use and traverse test output. + * It is necessary. e.g., to run tests in a dev-build-provided environment. + */ + var useTestCompilationOutput: Boolean = getBooleanProperty(USE_TEST_COMPILATION_OUTPUT_PROPERTY, defaultValue = USE_TEST_COMPILATION_OUTPUT_DEFAULT_VALUE), + @JvmField val cleanOutDir: Boolean = getBooleanProperty(CLEAN_OUTPUT_DIRECTORY_PROPERTY, true), @JvmField var classOutDir: String? = System.getProperty(PROJECT_CLASSES_OUTPUT_DIRECTORY_PROPERTY), @@ -243,6 +249,13 @@ data class BuildOptions( */ const val CLEAN_OUTPUT_DIRECTORY_PROPERTY: String = "intellij.build.clean.output.root" + /** + * In addition to production compilation sources, allow various functions to use and traverse test output. + * It is necessary. e.g., to run tests in a dev-build-provided environment. + */ + const val USE_TEST_COMPILATION_OUTPUT_PROPERTY: String = "idea.build.pack.test.source.enabled" + const val USE_TEST_COMPILATION_OUTPUT_DEFAULT_VALUE: Boolean = false + /** * If `false` build scripts compile project classes to a special output directory (to not interfere with the default project output if * invoked on a developer machine). diff --git a/platform/build-scripts/src/org/jetbrains/intellij/build/JarPackagerDependencyHelper.kt b/platform/build-scripts/src/org/jetbrains/intellij/build/JarPackagerDependencyHelper.kt index 53ed97a0a22f..6c90ef65ca06 100644 --- a/platform/build-scripts/src/org/jetbrains/intellij/build/JarPackagerDependencyHelper.kt +++ b/platform/build-scripts/src/org/jetbrains/intellij/build/JarPackagerDependencyHelper.kt @@ -34,7 +34,7 @@ internal class JarPackagerDependencyHelper(private val outputProvider: ModuleOut } fun isTestPluginModule(moduleName: String, module: JpsModule?): Boolean { - if (!useTestSourceEnabled) { + if (!outputProvider.useTestCompilationOutput) { return false } diff --git a/platform/build-scripts/src/org/jetbrains/intellij/build/dev/IdeBuilder.kt b/platform/build-scripts/src/org/jetbrains/intellij/build/dev/IdeBuilder.kt index 8b9e33407f83..d08f0fa093c2 100644 --- a/platform/build-scripts/src/org/jetbrains/intellij/build/dev/IdeBuilder.kt +++ b/platform/build-scripts/src/org/jetbrains/intellij/build/dev/IdeBuilder.kt @@ -426,6 +426,7 @@ private suspend fun createBuildContext( logDir = buildDir.resolve("log"), isUnpackedDist = request.isUnpackedDist, + useTestCompilationOutput = buildOptionsTemplate?.useTestCompilationOutput ?: BuildOptions().useTestCompilationOutput, ) options.setTargetOsAndArchToCurrent() options.buildStepsToSkip += listOf( diff --git a/platform/build-scripts/src/org/jetbrains/intellij/build/impl/BazelCompilationContext.kt b/platform/build-scripts/src/org/jetbrains/intellij/build/impl/BazelCompilationContext.kt index 2af9440f78be..c6f80910c9f1 100644 --- a/platform/build-scripts/src/org/jetbrains/intellij/build/impl/BazelCompilationContext.kt +++ b/platform/build-scripts/src/org/jetbrains/intellij/build/impl/BazelCompilationContext.kt @@ -37,7 +37,13 @@ class BazelCompilationContext( private val scope: CoroutineScope?, ) : CompilationContext { override val outputProvider: ModuleOutputProvider by lazy { - BazelModuleOutputProvider(modules = delegate.project.modules, projectHome = delegate.paths.projectHome, bazelOutputRoot = bazelOutputRoot!!, scope = scope) + BazelModuleOutputProvider( + modules = delegate.project.modules, + projectHome = delegate.paths.projectHome, + bazelOutputRoot = bazelOutputRoot!!, + scope = scope, + useTestCompilationOutput = options.useTestCompilationOutput, + ) } override val options: BuildOptions diff --git a/platform/build-scripts/src/org/jetbrains/intellij/build/impl/BazelModuleOutputProvider.kt b/platform/build-scripts/src/org/jetbrains/intellij/build/impl/BazelModuleOutputProvider.kt index 78f9406c0d56..4c98637c4b77 100644 --- a/platform/build-scripts/src/org/jetbrains/intellij/build/impl/BazelModuleOutputProvider.kt +++ b/platform/build-scripts/src/org/jetbrains/intellij/build/impl/BazelModuleOutputProvider.kt @@ -5,11 +5,13 @@ package org.jetbrains.intellij.build.impl import com.intellij.util.io.toByteArray import kotlinx.coroutines.CoroutineScope +import org.jetbrains.intellij.bazelEnvironment.BazelLabel +import org.jetbrains.intellij.bazelEnvironment.BazelRunfiles +import org.jetbrains.intellij.build.BuildOptions.Companion.USE_TEST_COMPILATION_OUTPUT_DEFAULT_VALUE +import org.jetbrains.intellij.build.BuildOptions.Companion.USE_TEST_COMPILATION_OUTPUT_PROPERTY import org.jetbrains.intellij.build.ModuleOutputProvider import org.jetbrains.intellij.build.io.ZipEntryProcessorResult import org.jetbrains.intellij.build.io.readZipFile -import org.jetbrains.intellij.bazelEnvironment.BazelLabel -import org.jetbrains.intellij.bazelEnvironment.BazelRunfiles import org.jetbrains.jps.model.module.JpsModule import org.jetbrains.jps.model.serialization.JpsModelSerializationDataService import java.nio.file.Files @@ -21,6 +23,7 @@ internal class BazelModuleOutputProvider( private val projectHome: Path, val bazelOutputRoot: Path, scope: CoroutineScope?, + override val useTestCompilationOutput: Boolean, ) : ModuleOutputProvider { private val nameToModule = modules.associateByTo(HashMap(modules.size)) { it.name } @@ -119,6 +122,15 @@ internal class BazelModuleOutputProvider( private fun getModuleOutputRootsImpl(module: JpsModule, forTests: Boolean): List { val moduleDescription = bazelTargetsMap.modules[module.name] ?: error("Cannot find module '${module.name}' in the project") + if (forTests && !useTestCompilationOutput) { + error( + "Cannot find test sources for module '${module.name}' because 'useTestSourceEnabled' is false.\n" + + "System property '${USE_TEST_COMPILATION_OUTPUT_PROPERTY}' value: ${System.getProperty(USE_TEST_COMPILATION_OUTPUT_PROPERTY)}, " + + "BazelModuleOutputProvider.useTestCompilationOutput (from BuildOptions.useTestCompilationOutput) value: $useTestCompilationOutput, " + + "default value: $USE_TEST_COMPILATION_OUTPUT_DEFAULT_VALUE" + ) + } + return if (BazelRunfiles.isRunningFromBazel) { val targets = if (forTests) moduleDescription.testTargets else moduleDescription.productionTargets targets.map { BazelRunfiles.getFileByLabel(BazelLabel.fromString(it)) } diff --git a/platform/build-scripts/src/org/jetbrains/intellij/build/impl/CompilationContextImpl.kt b/platform/build-scripts/src/org/jetbrains/intellij/build/impl/CompilationContextImpl.kt index 314353d45b44..d383179eb994 100644 --- a/platform/build-scripts/src/org/jetbrains/intellij/build/impl/CompilationContextImpl.kt +++ b/platform/build-scripts/src/org/jetbrains/intellij/build/impl/CompilationContextImpl.kt @@ -170,7 +170,7 @@ class CompilationContextImpl internal constructor( val global: JpsGlobal get() = model.global - override val outputProvider: ModuleOutputProvider = JpsModuleOutputProvider(project) + override val outputProvider: ModuleOutputProvider = JpsModuleOutputProvider(project, useTestCompilationOutput = options.useTestCompilationOutput) override var classesOutputDirectory: Path get() = Path.of(JpsPathUtil.urlToPath(JpsJavaExtensionService.getInstance().getOrCreateProjectExtension(project).outputUrl)) diff --git a/platform/build-scripts/src/org/jetbrains/intellij/build/impl/JpsModuleOutputProvider.kt b/platform/build-scripts/src/org/jetbrains/intellij/build/impl/JpsModuleOutputProvider.kt index 40394a2c1f76..ba6db4bd2c25 100644 --- a/platform/build-scripts/src/org/jetbrains/intellij/build/impl/JpsModuleOutputProvider.kt +++ b/platform/build-scripts/src/org/jetbrains/intellij/build/impl/JpsModuleOutputProvider.kt @@ -14,7 +14,7 @@ import java.nio.file.NoSuchFileException import java.nio.file.Path import kotlin.io.path.isRegularFile -internal class JpsModuleOutputProvider(private val project: JpsProject) : ModuleOutputProvider { +internal class JpsModuleOutputProvider(private val project: JpsProject, override val useTestCompilationOutput: Boolean) : ModuleOutputProvider { private val modules = project.modules private val nameToModule = modules.associateByTo(HashMap(modules.size)) { it.name } diff --git a/platform/build-scripts/src/org/jetbrains/intellij/build/productLayout/ModuleSetRunner.kt b/platform/build-scripts/src/org/jetbrains/intellij/build/productLayout/ModuleSetRunner.kt index f564fbf24c6d..688da46ea908 100644 --- a/platform/build-scripts/src/org/jetbrains/intellij/build/productLayout/ModuleSetRunner.kt +++ b/platform/build-scripts/src/org/jetbrains/intellij/build/productLayout/ModuleSetRunner.kt @@ -195,16 +195,18 @@ private suspend fun jsonResponse( } private fun createModuleOutputProvider(projectRoot: Path, scope: CoroutineScope): ModuleOutputProvider { + val useTestCompilationOutput = true val project = JpsSerializationManager.getInstance().loadProject( projectRoot.toString(), mapOf("MAVEN_REPOSITORY" to JpsMavenSettings.getMavenRepositoryPath()), false ) - val bazelOutputRoot = bazelOutputRoot ?: return JpsModuleOutputProvider(project) + val bazelOutputRoot = bazelOutputRoot ?: return JpsModuleOutputProvider(project, useTestCompilationOutput = useTestCompilationOutput) return BazelModuleOutputProvider( modules = project.modules, projectHome = projectRoot, bazelOutputRoot = bazelOutputRoot, scope = scope, + useTestCompilationOutput = useTestCompilationOutput, ) } diff --git a/tools/intellij.lambda.testFramework/src/com/intellij/lambda/testFramework/starter/IdeInstance.kt b/tools/intellij.lambda.testFramework/src/com/intellij/lambda/testFramework/starter/IdeInstance.kt index 200d2e513bb8..711262cf62f5 100644 --- a/tools/intellij.lambda.testFramework/src/com/intellij/lambda/testFramework/starter/IdeInstance.kt +++ b/tools/intellij.lambda.testFramework/src/com/intellij/lambda/testFramework/starter/IdeInstance.kt @@ -33,6 +33,9 @@ object IdeInstance { fun isStarted(): Boolean = _ide != null fun startIde(runMode: IdeRunMode): IdeWithLambda = synchronized(this) { + // Allow IDE building to access test modules + System.setProperty("idea.build.pack.test.source.enabled", "true") + try { if (isStarted() && currentIdeMode == runMode && IdeStartConfig.current == currentIdeConfig) { LOG.info("IDE is already running in mode: $runMode and there were no requests to change it's config. Reusing the current instance of IDE.")