+ * In production and regular dev runs, the agent is resolved via standard mechanisms (e.g., BuildDependenciesJps). + * But Bazel tests execute in a hermetic sandbox where downloads and arbitrary file access are restricted and all + * inputs must be declared as dependencies (runfiles). To make the agent available in that environment, Bazel wires + * the artifact into the test's classpath/runfiles. This service, implemented in test sources, knows how to locate + * that artifact and provide its path to the runtime code. + *
+ * The implementation must be present on the tests classpath and is discovered via {@link java.util.ServiceLoader}. + */ +public interface TestDebuggerAgentArtifactsProvider { + + Path getDebuggerAgentJar(); + +} diff --git a/java/debugger/impl/test/com/intellij/debugger/artifacts/TestDebuggerAgentArtifactsProviderImpl.java b/java/debugger/impl/test/com/intellij/debugger/artifacts/TestDebuggerAgentArtifactsProviderImpl.java new file mode 100644 index 000000000000..bb0cef1171d3 --- /dev/null +++ b/java/debugger/impl/test/com/intellij/debugger/artifacts/TestDebuggerAgentArtifactsProviderImpl.java @@ -0,0 +1,23 @@ +// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.debugger.artifacts; + +import com.intellij.debugger.testFramework.TestDebuggerAgentArtifactsProvider; +import com.intellij.testFramework.common.bazel.BazelLabel; +import com.intellij.testFramework.common.BazelTestUtil; + +import java.nio.file.Path; + +public class TestDebuggerAgentArtifactsProviderImpl implements TestDebuggerAgentArtifactsProvider { + + @Override + public Path getDebuggerAgentJar() { + String debuggerAgentLabel = "@debugger_test_deps_debugger_agent//file:debugger-agent.jar"; + BazelLabel label = BazelLabel.Companion.fromString(debuggerAgentLabel); + + if (BazelTestUtil.isUnderBazelTest()) { + return BazelTestUtil.getFileFromBazelRuntime(label); + } else { + throw new IllegalStateException("Expected to be used only in bazel-test environment"); + } + } +} diff --git a/java/debugger/impl/testResources/META-INF/services/com.intellij.debugger.testFramework.TestDebuggerAgentArtifactsProvider b/java/debugger/impl/testResources/META-INF/services/com.intellij.debugger.testFramework.TestDebuggerAgentArtifactsProvider new file mode 100644 index 000000000000..e7dab8b4db3c --- /dev/null +++ b/java/debugger/impl/testResources/META-INF/services/com.intellij.debugger.testFramework.TestDebuggerAgentArtifactsProvider @@ -0,0 +1 @@ +com.intellij.debugger.artifacts.TestDebuggerAgentArtifactsProviderImpl \ No newline at end of file diff --git a/platform/testFramework/common/src/common/BazelTestUtil.kt b/platform/testFramework/common/src/common/BazelTestUtil.kt index f74ac2a34c98..9aa50e515af4 100644 --- a/platform/testFramework/common/src/common/BazelTestUtil.kt +++ b/platform/testFramework/common/src/common/BazelTestUtil.kt @@ -1,10 +1,12 @@ // Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.testFramework.common +import com.intellij.testFramework.common.bazel.BazelLabel import org.jetbrains.annotations.ApiStatus import java.nio.file.Path import kotlin.io.path.absolute import kotlin.io.path.isDirectory +import kotlin.io.path.isRegularFile import kotlin.io.path.useLines @ApiStatus.Experimental @@ -64,6 +66,22 @@ object BazelTestUtil { path } + @JvmStatic + fun getFileFromBazelRuntime(label: BazelLabel): Path { + val repoEntry = bazelTestRepoMapping.getOrElse(label.repo) { + error("Unable to determine dependency path '${label.asLabel}'") + } + val file = bazelTestRunfilesPath.resolve( + repoEntry.runfilesRelativePath + "/${label.packageName}/${label.file}" + ) + return when { + file.isRegularFile() || file.isDirectory() -> file.toAbsolutePath() + else -> { + error("Unable to find test dependency '${label.asLabel}' at $file") + } + } + } + /** * Tests under community root may run in community (OSS) or in the ultimate monorepo context. * diff --git a/platform/testFramework/common/src/common/bazel/BazelLabel.kt b/platform/testFramework/common/src/common/bazel/BazelLabel.kt new file mode 100644 index 000000000000..067a10c953f6 --- /dev/null +++ b/platform/testFramework/common/src/common/bazel/BazelLabel.kt @@ -0,0 +1,27 @@ +// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.testFramework.common.bazel + +import org.jetbrains.annotations.ApiStatus + +// https://bazel.build/rules/lib/builtins/Label.html +@ApiStatus.Experimental +data class BazelLabel( + val repo: String, + val packageName: String, + val file: String, +) { + companion object { + private val regex = Regex("@([a-zA-Z0-9_-]+)//([a-z0-9_/-]+):([a-zA-Z0-9._-]+)") + fun fromString(label: String): BazelLabel { + val match = regex.matchEntire(label) ?: error("Bazel label must match '${regex.pattern}': $label") + return BazelLabel( + repo = match.groupValues[1], + packageName = match.groupValues[2], + file = match.groupValues[3], + ) + } + } + + val asLabel: String + get() = "@$repo//$packageName:$file" +} diff --git a/plugins/kotlin/base/plugin/test/org/jetbrains/kotlin/idea/artifacts/TestKotlinArtifacts.kt b/plugins/kotlin/base/plugin/test/org/jetbrains/kotlin/idea/artifacts/TestKotlinArtifacts.kt index 205361be2cf1..c02ce8112a0f 100644 --- a/plugins/kotlin/base/plugin/test/org/jetbrains/kotlin/idea/artifacts/TestKotlinArtifacts.kt +++ b/plugins/kotlin/base/plugin/test/org/jetbrains/kotlin/idea/artifacts/TestKotlinArtifacts.kt @@ -3,6 +3,7 @@ package org.jetbrains.kotlin.idea.base.plugin.artifacts import com.intellij.openapi.application.PathManager import com.intellij.testFramework.common.BazelTestUtil +import com.intellij.testFramework.common.bazel.BazelLabel import com.intellij.util.io.createParentDirectories import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking @@ -81,43 +82,6 @@ object TestKotlinArtifacts { listOf(getKotlinDepsByLabel("@kotlin_test_deps_kotlin-jps-plugin-classpath//file:kotlin-jps-plugin-classpath.jar").toPath()) } - // https://bazel.build/rules/lib/builtins/Label.html - data class BazelLabel( - val repo: String, - val packageName: String, - val file: String, - ) { - companion object { - private val regex = Regex("@([a-zA-Z0-9_-]+)//([a-z0-9_/-]+):([a-zA-Z0-9._-]+)") - fun fromString(label: String): BazelLabel { - val match = regex.matchEntire(label) ?: error("Bazel label must match '${regex.pattern}': $label") - return BazelLabel( - repo = match.groupValues[1], - packageName = match.groupValues[2], - file = match.groupValues[3], - ) - } - } - - val asLabel: String - get() = "@$repo//$packageName:$file" - } - - private fun getFileFromBazelRuntime(label: BazelLabel): Path { - val repoEntry = BazelTestUtil.bazelTestRepoMapping.getOrElse(label.repo) { - error("Unable to determine dependency path '${label.asLabel}'") - } - val file = BazelTestUtil.bazelTestRunfilesPath.resolve( - repoEntry.runfilesRelativePath + "/${label.packageName}/${label.file}" - ) - return when { - file.exists() -> file.toAbsolutePath() - else -> { - error("Unable to find test dependency '${label.asLabel}' at $file") - } - } - } - private data class HttpFile(val downloadFilePath: String, val name: String, val url: String) private val kotlinTestDependenciesHttpFiles by lazy { @@ -183,7 +147,7 @@ object TestKotlinArtifacts { private fun getKotlinDepsByLabel(label: BazelLabel): File { // Why it is different val dependency = if (BazelTestUtil.isUnderBazelTest) { - getFileFromBazelRuntime(label) + BazelTestUtil.getFileFromBazelRuntime(label) } else { downloadFile(label) } diff --git a/plugins/kotlin/jvm-debugger/test/k2/BUILD.bazel b/plugins/kotlin/jvm-debugger/test/k2/BUILD.bazel index d3216b0cad52..1c682a527366 100644 --- a/plugins/kotlin/jvm-debugger/test/k2/BUILD.bazel +++ b/plugins/kotlin/jvm-debugger/test/k2/BUILD.bazel @@ -5,12 +5,12 @@ jps_test( runtime_deps = [ ":k2_test_lib", "//:main_test_lib", - "@lib//:java-debugger-agent-holder-debugger-agent", ], data = [ "@kotlin_test_deps_org_jetbrains_kotlinx_kotlinx_coroutines_core_1_10_2//file:kotlinx_coroutines_core_1_10_2.jar", "@kotlin_test_deps_org_jetbrains_kotlinx_kotlinx_coroutines_core_jvm_1_10_2//file:kotlinx_coroutines_core_jvm_1_10_2.jar", + "@debugger_test_deps_debugger_agent//file:debugger-agent.jar", "@community//plugins/kotlin/jvm-debugger/test:testData", "@kotlin_test_deps_annotations//file:annotations.jar", "@kotlin_test_deps_junit//file:junit.jar", @@ -54,7 +54,7 @@ jvm_library( name = "k2_test_lib", module_name = "kotlin.jvm-debugger.test.k2", visibility = ["//visibility:public"], - srcs = glob(["test/**/*.kt", "test/**/*.java", "test/**/*.form"], allow_empty = True), + srcs = glob(["test/**/*.kt", "test/**/*.java"], allow_empty = True), deps = [ "//platform/core-ui", "//plugins/kotlin/plugin/k2",