diff --git a/build/tests-options.bzl b/build/tests-options.bzl index 2a1860be5c86..f7c160737962 100644 --- a/build/tests-options.bzl +++ b/build/tests-options.bzl @@ -112,6 +112,8 @@ def jps_test(name, jvm_flags = [], runtime_deps = [], args = [], **kwargs): data = [ # so com.intellij.tests.JUnit5BazelRunner.guessBazelWorkspaceDir will find a real workspace root "@community//:intellij.idea.community.main.iml", + # required for com.intellij.openapi.projectRoots.impl.JavaSdkImpl.internalJdkAnnotationsPath + "@community//java:mockJDK", ], use_testrunner = False, **kwargs diff --git a/java/BUILD.bazel b/java/BUILD.bazel new file mode 100644 index 000000000000..a32a996db0a4 --- /dev/null +++ b/java/BUILD.bazel @@ -0,0 +1,11 @@ +filegroup( + name = "mockJDK", + srcs = glob([ + "mockJDK-1.4/**", + "mockJDK-1.7/**", + "mockJDK-1.8/**", + "mockJDK-1.9/**", + "jdkAnnotations/**", + ], allow_empty = False), + visibility = ["//visibility:public"], +) diff --git a/java/testFramework/shared/src/com/intellij/testFramework/IdeaTestUtil.java b/java/testFramework/shared/src/com/intellij/testFramework/IdeaTestUtil.java index e3b8c2f04460..99895ba16932 100644 --- a/java/testFramework/shared/src/com/intellij/testFramework/IdeaTestUtil.java +++ b/java/testFramework/shared/src/com/intellij/testFramework/IdeaTestUtil.java @@ -18,6 +18,7 @@ import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.pom.java.LanguageLevel; +import com.intellij.testFramework.common.BazelTestUtil; import com.intellij.testFramework.fixtures.MavenDependencyUtil; import com.intellij.util.ArrayUtilRt; import com.intellij.util.PathUtil; @@ -266,7 +267,16 @@ public final class IdeaTestUtil { } private static @NotNull File getPathForJdkNamed(@NotNull String name) { - return new File(PlatformTestUtil.getCommunityPath(), "java/" + name); + // Bazel-provided test dependencies, from runfiles tree + if (BazelTestUtil.isUnderBazelTest()) { + return BazelTestUtil.findRunfilesDirectoryUnderCommunityOrUltimate("java/" + name).toFile(); + } + + File file = new File(PlatformTestUtil.getCommunityPath(), "java/" + name); + if (!file.isDirectory()) { + throw new RuntimeException("Can't find JDK directory for " + name + " at " + file); + } + return file; } public static void addWebJarsToModule(@NotNull Module module) { diff --git a/platform/testFramework/common/src/common/BazelTestUtil.kt b/platform/testFramework/common/src/common/BazelTestUtil.kt new file mode 100644 index 000000000000..a3fca43d3d73 --- /dev/null +++ b/platform/testFramework/common/src/common/BazelTestUtil.kt @@ -0,0 +1,85 @@ +// 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 org.jetbrains.annotations.ApiStatus +import java.nio.file.Path +import kotlin.io.path.absolute +import kotlin.io.path.isDirectory + +@ApiStatus.Experimental +object BazelTestUtil { + // see https://bazel.build/reference/test-encyclopedia#initial-conditions + // also https://leimao.github.io/blog/Bazel-Test-Outputs/ + private const val TEST_SRCDIR_ENV_NAME = "TEST_SRCDIR" + private const val TEST_UNDECLARED_OUTPUTS_DIR_ENV_NAME = "TEST_UNDECLARED_OUTPUTS_DIR" + + @JvmStatic + val isUnderBazelTest: Boolean = + System.getenv(TEST_SRCDIR_ENV_NAME) != null && + System.getenv(TEST_UNDECLARED_OUTPUTS_DIR_ENV_NAME) != null + + /** + * Absolute path to the base of the runfiles tree (your test dependencies too), + * see [Test encyclopedia](https://bazel.build/reference/test-encyclopedia#initial-conditions) + */ + @JvmStatic + val bazelTestRunfilesPath: Path by lazy { + val value = System.getenv(TEST_SRCDIR_ENV_NAME) + if (value == null) { + error("Not running under `bazel test` because $TEST_SRCDIR_ENV_NAME env is not set. Check isUnderBazelTest first.") + } + val path = Path.of(value).absolute() + if (!path.isDirectory()) { + error("Bazel test env '$TEST_SRCDIR_ENV_NAME' points to non-directory: $path") + } + path + } + + @JvmStatic + val bazelUndeclaredTestOutputsPath: Path by lazy { + val value = System.getenv(TEST_UNDECLARED_OUTPUTS_DIR_ENV_NAME) + if (value == null) { + error("Not running under `bazel test` because $TEST_UNDECLARED_OUTPUTS_DIR_ENV_NAME env is not set. Check isUnderBazelTest first.") + } + val path = Path.of(value).absolute() + if (!path.isDirectory()) { + error("Bazel test env '$TEST_UNDECLARED_OUTPUTS_DIR_ENV_NAME' points to non-directory: $path") + } + path + } + + /** + * Tests under community root may run in community (OSS) or in the ultimate monorepo context. + * + * Under ultimate monorepo Bazel project, workspace for test dependencies is named `community+`, + * while when run under community Bazel project, it's named `_main`. + * + * This function finds `relativePath` under one of them, depending on current project. + * It fails when the directory can't be found or there is an ambiguity. + * + * see https://bazel.build/reference/be/common-definitions#typical-attributes (check `data`) + * + * see https://bazel.build/reference/test-encyclopedia#initial-conditions + */ + @JvmStatic + fun findRunfilesDirectoryUnderCommunityOrUltimate(relativePath: String): Path { + val root1 = bazelTestRunfilesPath.resolve("community+").resolve(relativePath) + val root2 = bazelTestRunfilesPath.resolve("_main").resolve(relativePath) + + val root1exists = root1.isDirectory() + val root2exists = root2.isDirectory() + if (!root1exists && !root2exists) { + error("Cannot find runfiles directory $relativePath under community+ or _main. " + + "TEST_SRCDIR (runfiles root) = ${bazelTestRunfilesPath}. " + + "Tried $root1 and $root2. " + + "Please check that you passed this directory via data attribute of test rule") + } + if (root1exists && root2exists) { + error("Both $root1 and $root2 exist. " + + "Meaning $relativePath is available both under community and ultimate roots. " + + "This ambitious setup might cause problems. " + + "Please remove $root1 or $root2 or use a different relative path for test rule") + } + return if (root1exists) root1 else root2 + } +}