From ee4043a7271d9c12f45b779907d16007f108b9d9 Mon Sep 17 00:00:00 2001 From: "Alexander.Glukhov" Date: Thu, 21 Aug 2025 13:57:06 +0200 Subject: [PATCH] [gradle][tests][IDEA-375395] GradleImportingTestCase refactoring GitOrigin-RevId: 15dd722dd9bf0b886c561c8c21302392099e9d08 --- plugins/gradle/BUILD.bazel | 2 + plugins/gradle/intellij.gradle.tests.iml | 2 + .../importing/GradleFindUsagesTest.java | 18 +- .../importing/GradleImportingTestCase.kt | 560 ++++++++---------- .../plugins/gradle/util/testWrapperUtil.kt | 55 ++ .../AbstractKotlinMppGradleImportingTest.kt | 8 +- ...plePluginVersionGradleImportingTestCase.kt | 4 + 7 files changed, 313 insertions(+), 336 deletions(-) create mode 100644 plugins/gradle/testSources/org/jetbrains/plugins/gradle/util/testWrapperUtil.kt diff --git a/plugins/gradle/BUILD.bazel b/plugins/gradle/BUILD.bazel index 31dd19a677ef..91252431fa01 100644 --- a/plugins/gradle/BUILD.bazel +++ b/plugins/gradle/BUILD.bazel @@ -168,6 +168,8 @@ jvm_library( "//platform/smRunner:smRunner_test_lib", "@lib//:gson", "@lib//:guava", + "//platform/eel", + "//platform/eel-provider", ], exports = ["//platform/external-system-api/testFramework"], runtime_deps = [ diff --git a/plugins/gradle/intellij.gradle.tests.iml b/plugins/gradle/intellij.gradle.tests.iml index d5cd1cacf157..b8b7ca563f1f 100644 --- a/plugins/gradle/intellij.gradle.tests.iml +++ b/plugins/gradle/intellij.gradle.tests.iml @@ -63,5 +63,7 @@ + + \ No newline at end of file diff --git a/plugins/gradle/java/testSources/importing/GradleFindUsagesTest.java b/plugins/gradle/java/testSources/importing/GradleFindUsagesTest.java index eb39a56d72e1..287b04195c37 100644 --- a/plugins/gradle/java/testSources/importing/GradleFindUsagesTest.java +++ b/plugins/gradle/java/testSources/importing/GradleFindUsagesTest.java @@ -6,7 +6,10 @@ import com.intellij.openapi.module.Module; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.Segment; import com.intellij.openapi.util.Trinity; -import com.intellij.psi.*; +import com.intellij.psi.JavaPsiFacade; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiMethod; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.search.LocalSearchScope; import com.intellij.psi.search.PsiSearchHelper; @@ -14,10 +17,8 @@ import com.intellij.psi.search.SearchScope; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.usageView.UsageInfo; import com.intellij.util.containers.ContainerUtil; -import org.gradle.wrapper.PathAssembler; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.plugins.gradle.service.execution.GradleUserHomeUtil; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariableDeclaration; @@ -26,7 +27,6 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMe import org.junit.Test; import org.junit.runners.Parameterized; -import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; @@ -50,16 +50,6 @@ public class GradleFindUsagesTest extends GradleImportingTestCase { return Arrays.asList(new Object[][]{{BASE_GRADLE_VERSION}}); } - @Override - protected void collectAllowedRoots(List roots, PathAssembler.LocalDistribution distribution) { - super.collectAllowedRoots(roots, distribution); - File gradleUserHomeDir = GradleUserHomeUtil.gradleUserHomeDir(); - File generatedGradleJarsDir = new File(gradleUserHomeDir, "caches/" + getGradleVersion() + "/generated-gradle-jars"); - roots.add(generatedGradleJarsDir.getPath()); - File gradleDistLibDir = new File(distribution.getDistributionDir(), "gradle-" + getGradleVersion() + "/lib"); - roots.add(gradleDistLibDir.getPath()); - } - @Test public void testBuildSrcClassesUsages() throws Exception { createProjectSubFile("settings.gradle", "rootProject.name = 'multiproject'\n" + diff --git a/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleImportingTestCase.kt b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleImportingTestCase.kt index 77be4a4129dd..96a9a702a196 100644 --- a/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleImportingTestCase.kt +++ b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleImportingTestCase.kt @@ -31,12 +31,11 @@ import com.intellij.openapi.ui.TestDialogManager import com.intellij.openapi.util.Couple import com.intellij.openapi.util.Disposer import com.intellij.openapi.util.Ref -import com.intellij.openapi.util.SystemInfo import com.intellij.openapi.util.io.FileUtil -import com.intellij.openapi.util.io.NioFiles import com.intellij.openapi.util.text.Strings import com.intellij.openapi.vfs.LocalFileSystem import com.intellij.openapi.vfs.VirtualFile +import com.intellij.platform.eel.provider.getEelDescriptor import com.intellij.platform.testFramework.io.ExternalResourcesChecker.reportUnavailability import com.intellij.testFramework.ExtensionTestUtil.maskExtensions import com.intellij.testFramework.IdeaTestUtil @@ -45,9 +44,9 @@ import com.intellij.util.SmartList import com.intellij.util.ThrowableRunnable import com.intellij.util.currentJavaVersion import com.intellij.util.io.copyRecursively -import org.gradle.StartParameter +import com.intellij.util.io.createParentDirectories +import com.intellij.util.io.delete import org.gradle.util.GradleVersion -import org.gradle.wrapper.PathAssembler import org.jetbrains.annotations.NonNls import org.jetbrains.plugins.gradle.frameworkSupport.GradleDsl import org.jetbrains.plugins.gradle.frameworkSupport.settingsScript.GradleSettingScriptBuilder @@ -65,21 +64,16 @@ import org.jetbrains.plugins.gradle.tooling.GradleJvmResolver.Companion.resolveG import org.jetbrains.plugins.gradle.tooling.JavaVersionRestriction import org.jetbrains.plugins.gradle.tooling.TargetJavaVersionWatcher import org.jetbrains.plugins.gradle.tooling.VersionMatcherRule -import org.jetbrains.plugins.gradle.util.GradleConstants -import org.jetbrains.plugins.gradle.util.GradleUtil +import org.jetbrains.plugins.gradle.util.* import org.junit.Assume import org.junit.Rule import org.junit.rules.TestName import org.junit.runner.RunWith import org.junit.runners.Parameterized -import java.io.File import java.io.IOException -import java.io.UncheckedIOException -import java.nio.file.Files import java.nio.file.Path import java.util.function.Consumer -import java.util.zip.ZipException -import java.util.zip.ZipFile +import kotlin.io.path.exists @RunWith(Parameterized::class) abstract class GradleImportingTestCase : JavaExternalSystemImportingTestCase() { @@ -95,7 +89,7 @@ abstract class GradleImportingTestCase : JavaExternalSystemImportingTestCase() { @JvmField var myGradleVersion: String? = null - var gradleVersion: String + open var gradleVersion: String get() = myGradleVersion!! set(value) { myGradleVersion = value @@ -107,7 +101,6 @@ abstract class GradleImportingTestCase : JavaExternalSystemImportingTestCase() { protected val currentGradleBaseVersion: GradleVersion get() = GradleVersion.version(gradleVersion).baseVersion - private var myProjectSettings: GradleProjectSettings? = null var gradleJdkHome: String? = null private set @@ -116,13 +109,26 @@ abstract class GradleImportingTestCase : JavaExternalSystemImportingTestCase() { private val deprecationError = Ref.create?>() private val deprecationTextBuilder = StringBuilder() - private var myDistribution: PathAssembler.LocalDistribution? = null + private var myProjectSettings: GradleProjectSettings? = null + private var myGradleDistributionRoot: Path? = null private var deprecationTextLineCount = 0 private var originalGradleUserHome: Path? = null protected open val isWarningsAllowed: Boolean get() = false + protected val gradleUserHome: Path + get() { + val serviceDirectory = GradleSettings.getInstance(myProject).serviceDirectoryPath + return if (serviceDirectory != null) Path.of(serviceDirectory) else gradleUserHomeDir(Path.of(projectPath).getEelDescriptor()) + } + + override fun getTestsTempDir(): String = "tmp" + override fun getExternalSystemConfigFileName(): String = "build.gradle" + override fun getCurrentExternalProjectSettings(): GradleProjectSettings = myProjectSettings!! + override fun getExternalSystemId(): ProjectSystemId = GradleConstants.SYSTEM_ID + protected override fun runInDispatchThread(): Boolean = false + @Throws(Exception::class) override fun setUp() { Assume.assumeThat(gradleVersion, versionMatcherRule.matcher) @@ -141,138 +147,18 @@ abstract class GradleImportingTestCase : JavaExternalSystemImportingTestCase() { setRegistryPropertyForTest("unknown.sdk.auto", "false") unknownSdkFixMode = TestUnknownSdkResolver.TestUnknownSdkFixMode.REAL_LOCAL_FIX - cleanScriptsCacheIfNeeded() - installGradleJvmConfigurator() installExecutionDeprecationChecker() originalGradleUserHome = this.gradleUserHome } - protected open fun installGradleJvmConfigurator() { - ExternalSystemApiUtil.subscribe(myProject, GradleConstants.SYSTEM_ID, object : ExternalSystemSettingsListener { - override fun onProjectsLinked(settings: MutableCollection) { - for (projectSettings in settings) { - projectSettings.gradleJvm = GRADLE_JDK_NAME - } - } - }, myTestDisposable) - } - - protected open fun configureJdkTable() { - cleanJdkTable() - populateJdkTable(mutableListOf(createJdkFromJavaHome())) - } - - protected fun cleanJdkTable() { - removedSdks.clear() - for (sdk in ProjectJdkTable.getInstance().getAllJdks()) { - ProjectJdkTable.getInstance().removeJdk(sdk) - if (GRADLE_JDK_NAME == sdk.getName()) continue - removedSdks.add(sdk) - } - } - - protected fun populateJdkTable(jdks: List) { - for (jdk in jdks) { - ProjectJdkTable.getInstance().addJdk(jdk) - } - } - - protected open fun configureGradleVmOptions(options: MutableSet) { - if (isGradleAtLeast("7.0") && !this.isWarningsAllowed) { - options.add("-Dorg.gradle.warning.mode=fail") - } - } - - private val gradleVmOptions: MutableSet - get() { - val options: MutableSet = HashSet() - configureGradleVmOptions(options) - return options - } - - private fun setUpGradleVmOptions() { - val settings = GradleSystemSettings.getInstance() - val defaultVmOptions = settings.gradleVmOptions ?: "" - - val requiredVmOptions = this.gradleVmOptions - val effectiveVmOptions = String.format("%s %s", defaultVmOptions, Strings.join(requiredVmOptions, " ")).trim { it <= ' ' } - - settings.gradleVmOptions = effectiveVmOptions - } - - private fun createJdkFromJavaHome(): Sdk { - val jdkHomeDir = LocalFileSystem.getInstance().refreshAndFindFileByNioFile(Path.of(gradleJdkHome!!)) - val javaSdk = JavaSdk.getInstance() - val javaSdkType: SdkType = javaSdk ?: SimpleJavaSdkType.getInstance() - val jdk = SdkConfigurationUtil.setupSdk(arrayOfNulls(0), jdkHomeDir!!, javaSdkType, true, null, GRADLE_JDK_NAME) - assertNotNull("Cannot create JDK for " + this.gradleJdkHome, jdk) - return jdk!! - } - @Throws(Exception::class) override fun setUpInWriteAction() { super.setUpInWriteAction() this.gradleJdkHome = requireRealJdkHome() - myDistribution = configureWrapper() + myGradleDistributionRoot = configureWrapper() } - protected val gradleUserHome: Path - get() { - val serviceDirectory = GradleSettings.getInstance(myProject).serviceDirectoryPath - return if (serviceDirectory != null) Path.of(serviceDirectory) else gradleUserHomeDir().toPath() - } - - /** - * This is a workaround for the following issue on windows: - * "C:\Users\builduser\.gradle\caches\jars-1\cache.properties (The system cannot find the file specified)" - */ - private fun cleanScriptsCacheIfNeeded() { - if (SystemInfo.isWindows && isGradleOlderThan("3.5")) { - val gradleUserHome = this.gradleUserHome - val cacheFile = gradleUserHome.resolve("caches/jars-1/cache.properties") - if (Files.notExists(cacheFile)) { - try { - Files.createFile(NioFiles.createParentDirectories(cacheFile)) - } - catch (e: IOException) { - throw UncheckedIOException(e) - } - } - val scriptsCacheFolder = gradleUserHome.resolve("caches").resolve(gradleVersion).resolve("scripts") - try { - NioFiles.deleteRecursively(scriptsCacheFolder) - LOG.debug("Gradle scripts cache folder has been successfully removed at $scriptsCacheFolder") - } - catch (_: IOException) { - LOG.debug("Gradle scripts cache folder has not been removed at $scriptsCacheFolder") - } - val scriptsRemappedCacheFolder = gradleUserHome.resolve("caches").resolve(gradleVersion).resolve("scripts-remapped") - try { - NioFiles.deleteRecursively(scriptsRemappedCacheFolder) - LOG.debug("Gradle scripts-remapped cache folder has been successfully removed at $scriptsRemappedCacheFolder") - } - catch (_: IOException) { - LOG.debug("Gradle scripts-remapped cache folder has not been removed at $scriptsRemappedCacheFolder") - } - } - } - - protected override fun runInDispatchThread(): Boolean = false - - private fun requireRealJdkHome(): String { - if (myWSLDistribution != null) { - return requireWslJdkHome(myWSLDistribution!!) - } - return requireJdkHome() - } - - open fun requireJdkHome(): String { - return requireJdkHome(this.currentGradleVersion, myTargetJavaVersionWatcher.restriction) - } - - protected open fun collectAllowedRoots(roots: MutableList, distribution: PathAssembler.LocalDistribution) = Unit - @Throws(Exception::class) override fun tearDown() { if (this.gradleJdkHome == null) { @@ -312,27 +198,16 @@ abstract class GradleImportingTestCase : JavaExternalSystemImportingTestCase() { roots.add(this.gradleJdkHome!!) roots.addAll(collectRootsInside(this.gradleJdkHome!!)) roots.add(PathManager.getConfigPath()) - val gradleHomeEnv = getVariable("GRADLE_USER_HOME") - if (gradleHomeEnv != null) roots.add(gradleHomeEnv) + roots.add(myGradleDistributionRoot.toString()) + roots.add(gradleUserHome.toString()) val javaHome = getVariable("JAVA_HOME") if (javaHome != null) roots.add(javaHome) - - collectAllowedRoots(roots, myDistribution!!) } - override fun getTestsTempDir(): String = "tmp" - - override fun getExternalSystemConfigFileName(): String = "build.gradle" - - protected fun importProjectUsingSingeModulePerGradleProject() { - currentExternalProjectSettings.isResolveModulePerSourceSet = false - importProject() - } - - @Throws(IOException::class) - protected fun importProjectUsingSingeModulePerGradleProject(config: @NonNls String) { - currentExternalProjectSettings.isResolveModulePerSourceSet = false - importProject(config) + override fun createImportSpec(): ImportSpec { + val importSpecBuilder = ImportSpecBuilder(super.createImportSpec()) + importSpecBuilder.withArguments("--stacktrace") + return importSpecBuilder.build() } @Throws(IOException::class) @@ -345,11 +220,205 @@ abstract class GradleImportingTestCase : JavaExternalSystemImportingTestCase() { handleDeprecationError(deprecationError.get()) } + override fun handleImportFailure(errorMessage: String, errorDetails: String?) { + val combinedMessage = errorMessage + "\n" + errorDetails + if (combinedMessage.contains("org.gradle.wrapper.Download.download") && combinedMessage.contains("java.net.SocketException")) { + reportUnavailability("Gradle distribution service", null) + } + super.handleImportFailure(errorMessage, errorDetails) + } + + @Throws(IOException::class) + open fun importProject(config: @NonNls String) { + importProject(config, null) + } + + open fun requireJdkHome(): String { + return requireJdkHome(this.currentGradleVersion, myTargetJavaVersionWatcher.restriction) + } + + protected open fun configureGradleVmOptions(options: MutableSet) { + if (isGradleAtLeast("7.0") && !this.isWarningsAllowed) { + options.add("-Dorg.gradle.warning.mode=fail") + } + } + + protected open fun installGradleJvmConfigurator() { + ExternalSystemApiUtil.subscribe(myProject, GradleConstants.SYSTEM_ID, object : ExternalSystemSettingsListener { + override fun onProjectsLinked(settings: MutableCollection) { + for (projectSettings in settings) { + projectSettings.gradleJvm = GRADLE_JDK_NAME + } + } + }, myTestDisposable) + } + + protected open fun configureJdkTable() { + cleanJdkTable() + populateJdkTable(mutableListOf(createJdkFromJavaHome())) + } + protected open fun handleDeprecationError(errorInfo: Couple?) { if (errorInfo == null) return handleImportFailure(errorInfo.first!!, errorInfo.second) } + protected open fun injectRepo(config: @NonNls String): String { + val mavenRepositoryPatch = + """ + allprojects { + repositories { + maven { + url = 'https://repo.labs.intellij.net/repo1' + } + } + } + + """.trimIndent() + if (config.contains(MAVEN_REPOSITORY_PATCH_PLACE)) { + return config.replace(MAVEN_REPOSITORY_PATCH_PLACE, mavenRepositoryPatch) + } + else { + return mavenRepositoryPatch + config + } + } + + fun script(configure: Consumer): String { + val builder = createBuildScriptBuilder() + configure.accept(builder) + return builder.generate() + } + + fun settingsScript(configure: Consumer>): String { + val builder = create(this.currentGradleVersion, GradleDsl.GROOVY) + configure.accept(builder) + return builder.generate() + } + + protected fun cleanJdkTable() { + removedSdks.clear() + for (sdk in ProjectJdkTable.getInstance().getAllJdks()) { + ProjectJdkTable.getInstance().removeJdk(sdk) + if (GRADLE_JDK_NAME == sdk.getName()) continue + removedSdks.add(sdk) + } + } + + protected fun populateJdkTable(jdks: List) { + for (jdk in jdks) { + ProjectJdkTable.getInstance().addJdk(jdk) + } + } + + protected fun importProjectUsingSingeModulePerGradleProject() { + currentExternalProjectSettings.isResolveModulePerSourceSet = false + importProject() + } + + @Throws(IOException::class) + protected fun importProjectUsingSingeModulePerGradleProject(config: @NonNls String) { + currentExternalProjectSettings.isResolveModulePerSourceSet = false + importProject(config) + } + + protected fun createBuildScriptBuilder(): TestGradleBuildScriptBuilder = TestGradleBuildScriptBuilder(this.currentGradleVersion) + .addPrefix(MAVEN_REPOSITORY_PATCH_PLACE, "") + + protected fun createEmptyGradleRunConfiguration(name: String): GradleRunConfiguration { + val runManager = RunManagerEx.getInstanceEx(myProject) + val settings = runManager.createConfiguration(name, GradleExternalTaskConfigurationType::class.java) + return settings.getConfiguration() as GradleRunConfiguration + } + + @Throws(IOException::class) + protected fun createSettingsFile(content: @NonNls String): VirtualFile = createProjectSubFile("settings.gradle", content) + + /** + * Produces settings content and creates necessary directories. + * @param projects list of sub-project to create + * @return a block of `include 'project-name'` lines for settings.gradle + */ + protected fun including(vararg projects: String): String = including(myProjectRoot, *projects) + + protected fun including(root: VirtualFile?, vararg projects: String): String { + assertNotNull(root) + return TestGradleSettingsScriptHelper(root!!.toNioPath(), projects as Array).build() + } + + protected fun assertMergedModuleCompileLibDepScope(moduleName: String, depName: String) { + assertModuleLibDepScope(moduleName, depName, DependencyScope.COMPILE) + } + + protected fun assertMergedModuleCompileModuleDepScope(moduleName: String, depName: String) { + assertModuleModuleDepScope(moduleName, depName, DependencyScope.COMPILE) + } + + protected fun isGradleOlderThan(ver: String): Boolean { + return GradleVersionUtil.isGradleOlderThan(this.currentGradleBaseVersion, ver) + } + + protected fun isGradleAtLeast(ver: String): Boolean { + return GradleVersionUtil.isGradleAtLeast(this.currentGradleBaseVersion, ver) + } + + protected fun convertToLibraryName(fsRoot: VirtualFile): String = "Gradle: ${fsRoot.getName()}" + + protected fun enableGradleDebugWithSuspend() { + val settings = GradleSystemSettings.getInstance() + val currentOptions = settings.gradleVmOptions ?: "" + settings.gradleVmOptions = "$currentOptions -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005" + } + + @Throws(IOException::class) + protected fun overrideGradleUserHome(relativeUserHomePath: String) { + val gradleUserHome = myTestDir.resolve(relativeUserHomePath) + if (myGradleDistributionRoot!!.exists()) { + val targetGradleDistribution = getGradleDistributionRoot(gradleUserHome, currentGradleVersion) + targetGradleDistribution.createParentDirectories() + myGradleDistributionRoot!!.copyRecursively(targetGradleDistribution) + } + GradleSettings.getInstance(myProject).setServiceDirectoryPath(gradleUserHome.toString()) + } + + protected fun resetGradleUserHomeIfNeeded() { + if (originalGradleUserHome != this.gradleUserHome) { + val normalizedOldGradleUserHome = originalGradleUserHome!!.normalize().toString() + val canonicalOldGradleUserHome = FileUtil.toCanonicalPath(normalizedOldGradleUserHome) + GradleSettings.getInstance(myProject).setServiceDirectoryPath(canonicalOldGradleUserHome) + } + } + + private fun configureWrapper(): Path { + myProjectSettings!!.distributionType = DistributionType.DEFAULT_WRAPPED + val projectRoot = myProjectRoot.toNioPath() + generateGradleWrapper(projectRoot, currentGradleVersion) + + val gradleJarPath = getGradleDistributionJarPath(projectRoot) + val gradleDistributionRootPath = gradleJarPath.parent.parent + validateGradleJar(gradleJarPath) + + if (!gradleJarPath.exists()) { + val localDistributionRoot = getLocalGradleDistributionRoot(currentGradleVersion) + if (localDistributionRoot != gradleDistributionRootPath && localDistributionRoot.exists()) { + gradleDistributionRootPath.delete(true) + localDistributionRoot.copyRecursively(gradleDistributionRootPath) + } + } + return gradleDistributionRootPath + } + + private fun tearDownGradleVmOptions() { + GradleSystemSettings.getInstance().gradleVmOptions = "" + } + + private fun requireWslJdkHome(distribution: WSLDistribution): String { + var jdkPath = System.getProperty("wsl.jdk.path") + if (jdkPath == null) { + jdkPath = "/usr/lib/jvm/java-11-openjdk-amd64" + } + return distribution.getWindowsPath(jdkPath) + } + private fun installExecutionDeprecationChecker() { val notificationManager = ExternalSystemProgressNotificationManager.getInstance() val notificationListener: ExternalSystemTaskNotificationListener = object : ExternalSystemTaskNotificationListener { @@ -372,180 +441,31 @@ abstract class GradleImportingTestCase : JavaExternalSystemImportingTestCase() { notificationManager.addNotificationListener(notificationListener, myTestDisposable) } - override fun handleImportFailure(errorMessage: String, errorDetails: String?) { - val combinedMessage = errorMessage + "\n" + errorDetails - if (combinedMessage.contains("org.gradle.wrapper.Download.download") && combinedMessage.contains("java.net.SocketException")) { - reportUnavailability("Gradle distribution service", null) + private fun requireRealJdkHome(): String { + if (myWSLDistribution != null) { + return requireWslJdkHome(myWSLDistribution!!) } - super.handleImportFailure(errorMessage, errorDetails) + return requireJdkHome() } - @Throws(IOException::class) - open fun importProject(config: @NonNls String) { - importProject(config, null) - } - - protected fun createBuildScriptBuilder(): TestGradleBuildScriptBuilder = TestGradleBuildScriptBuilder(this.currentGradleVersion) - .addPrefix(MAVEN_REPOSITORY_PATCH_PLACE, "") - - fun script(configure: Consumer): String { - val builder = createBuildScriptBuilder() - configure.accept(builder) - return builder.generate() - } - - fun settingsScript(configure: Consumer>): String { - val builder = create(this.currentGradleVersion, GradleDsl.GROOVY) - configure.accept(builder) - return builder.generate() - } - - override fun createImportSpec(): ImportSpec { - val importSpecBuilder = ImportSpecBuilder(super.createImportSpec()) - importSpecBuilder.withArguments("--stacktrace") - return importSpecBuilder.build() - } - - protected open fun injectRepo(config: @NonNls String): String { - val mavenRepositoryPatch = - """ - allprojects { - repositories { - maven { - url = 'https://repo.labs.intellij.net/repo1' - } - } - } - """.trimIndent() - if (config.contains(MAVEN_REPOSITORY_PATCH_PLACE)) { - return config.replace(MAVEN_REPOSITORY_PATCH_PLACE, mavenRepositoryPatch) - } - else { - return mavenRepositoryPatch + config - } - } - - protected fun createEmptyGradleRunConfiguration(name: String): GradleRunConfiguration { - val runManager = RunManagerEx.getInstanceEx(myProject) - val settings = runManager.createConfiguration(name, GradleExternalTaskConfigurationType::class.java) - return settings.getConfiguration() as GradleRunConfiguration - } - - override fun getCurrentExternalProjectSettings(): GradleProjectSettings = myProjectSettings!! - - override fun getExternalSystemId(): ProjectSystemId = GradleConstants.SYSTEM_ID - - @Throws(IOException::class) - protected fun createSettingsFile(content: @NonNls String): VirtualFile = createProjectSubFile("settings.gradle", content) - - /** - * Produces settings content and creates necessary directories. - * @param projects list of sub-project to create - * @return a block of `include 'project-name'` lines for settings.gradle - */ - protected fun including(vararg projects: String): String = including(myProjectRoot, *projects) - - protected fun including(root: VirtualFile?, vararg projects: String): String { - assertNotNull(root) - return TestGradleSettingsScriptHelper(root!!.toNioPath(), projects as Array).build() - } - - private fun configureWrapper(): PathAssembler.LocalDistribution { - myProjectSettings!!.distributionType = DistributionType.DEFAULT_WRAPPED - - // Cannot generate Gradle wrapper using virtual files system. - // Because the K2MppHighlightingIntegrationTest.testJvmMultifileClass test implicitly depends on the VFS cache. - // Calling the for VFS refresh after Gradle wrapper generation using Java NIO API also fails this KMP test - generateGradleWrapper(myProjectRoot.toNioPath(), this.currentGradleVersion) - - // VfsUtil.markDirtyAndRefresh(false, true, true, myProjectRoot) - val wrapperConfiguration = GradleUtil.getWrapperConfiguration(myProjectRoot.toNioPath()) - val pathAssembler = PathAssembler(StartParameter.DEFAULT_GRADLE_USER_HOME, File(projectPath)) - val localDistribution = pathAssembler.getDistribution(wrapperConfiguration) - - val zip = localDistribution.zipFile - try { - if (zip.exists()) { - try { - ZipFile(zip).close() - } - catch (e: ZipException) { - e.printStackTrace() - println("Corrupted file will be removed: $zip") - Files.delete(zip.toPath()) - } - } - } - catch (e: IOException) { - e.printStackTrace() - } - return localDistribution - } - - protected fun assertMergedModuleCompileLibDepScope(moduleName: String, depName: String) { - assertModuleLibDepScope(moduleName, depName, DependencyScope.COMPILE) - } - - protected fun assertMergedModuleCompileModuleDepScope(moduleName: String, depName: String) { - assertModuleModuleDepScope(moduleName, depName, DependencyScope.COMPILE) - } - - protected fun isGradleOlderThan(ver: String): Boolean { - return GradleVersionUtil.isGradleOlderThan(this.currentGradleBaseVersion, ver) - } - - protected fun isGradleAtLeast(ver: String): Boolean { - return GradleVersionUtil.isGradleAtLeast(this.currentGradleBaseVersion, ver) - } - - protected fun enableGradleDebugWithSuspend() { + private fun setUpGradleVmOptions() { val settings = GradleSystemSettings.getInstance() - val currentOptions = settings.gradleVmOptions ?: "" - settings.gradleVmOptions = "$currentOptions -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005" + val defaultVmOptions = settings.gradleVmOptions ?: "" + + val requiredVmOptions = mutableSetOf() + configureGradleVmOptions(requiredVmOptions) + + val effectiveVmOptions = String.format("%s %s", defaultVmOptions, Strings.join(requiredVmOptions, " ")).trim { it <= ' ' } + settings.gradleVmOptions = effectiveVmOptions } - @Throws(IOException::class) - protected fun overrideGradleUserHome(relativeUserHomePath: String) { - val gradleUserHome = myTestDir.resolve(relativeUserHomePath) - val gradleCachedFolderName = "gradle-$gradleVersion-bin" - val cachedGradleDistribution = findGradleDistributionInCache(gradleCachedFolderName) - if (cachedGradleDistribution != null) { - val targetGradleDistribution = gradleUserHome.resolve("wrapper/dists/").resolve(gradleCachedFolderName) - Files.createDirectories(targetGradleDistribution.parent) - cachedGradleDistribution.copyRecursively(targetGradleDistribution) - } - GradleSettings.getInstance(myProject).setServiceDirectoryPath(gradleUserHome.toString()) - } - - protected fun resetGradleUserHomeIfNeeded() { - if (originalGradleUserHome != this.gradleUserHome) { - val normalizedOldGradleUserHome = originalGradleUserHome!!.normalize().toString() - val canonicalOldGradleUserHome = FileUtil.toCanonicalPath(normalizedOldGradleUserHome) - GradleSettings.getInstance(myProject).setServiceDirectoryPath(canonicalOldGradleUserHome) - } - } - - protected fun convertToLibraryName(fsRoot: VirtualFile): String = "Gradle: ${fsRoot.getName()}" - - private fun tearDownGradleVmOptions() { - val settings = GradleSystemSettings.getInstance() - settings.gradleVmOptions = "" - } - - private fun requireWslJdkHome(distribution: WSLDistribution): String { - var jdkPath = System.getProperty("wsl.jdk.path") - if (jdkPath == null) { - jdkPath = "/usr/lib/jvm/java-11-openjdk-amd64" - } - return distribution.getWindowsPath(jdkPath) - } - - private fun findGradleDistributionInCache(gradleCachedFolderName: String): Path? { - val gradleWrapperPath = StartParameter.DEFAULT_GRADLE_USER_HOME.toPath().resolve("wrapper/dists/$gradleCachedFolderName") - if (Files.exists(gradleWrapperPath)) { - return gradleWrapperPath - } - return null + private fun createJdkFromJavaHome(): Sdk { + val jdkHomeDir = LocalFileSystem.getInstance().refreshAndFindFileByNioFile(Path.of(gradleJdkHome!!)) + val javaSdk = JavaSdk.getInstance() + val javaSdkType: SdkType = javaSdk ?: SimpleJavaSdkType.getInstance() + val jdk = SdkConfigurationUtil.setupSdk(arrayOfNulls(0), jdkHomeDir!!, javaSdkType, true, null, GRADLE_JDK_NAME) + assertNotNull("Cannot create JDK for " + this.gradleJdkHome, jdk) + return jdk!! } companion object { diff --git a/plugins/gradle/testSources/org/jetbrains/plugins/gradle/util/testWrapperUtil.kt b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/util/testWrapperUtil.kt new file mode 100644 index 000000000000..01851a6b4532 --- /dev/null +++ b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/util/testWrapperUtil.kt @@ -0,0 +1,55 @@ +// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package org.jetbrains.plugins.gradle.util + +import com.intellij.openapi.project.Project +import com.intellij.platform.eel.provider.getEelDescriptor +import com.intellij.util.io.delete +import org.gradle.StartParameter +import org.gradle.util.GradleVersion +import org.gradle.wrapper.PathAssembler +import org.jetbrains.plugins.gradle.service.execution.gradleUserHomeDir +import java.nio.file.Files +import java.nio.file.Path +import java.util.zip.ZipInputStream +import kotlin.io.path.ExperimentalPathApi +import kotlin.io.path.exists + +@OptIn(ExperimentalPathApi::class) +internal fun validateGradleJar(gradleJar: Path) { + if (!gradleJar.exists()) { + return + } + try { + Files.newInputStream(gradleJar).use { + ZipInputStream(it).use { + // sanity check + } + } + } + catch (e: Exception) { + e.printStackTrace() + println("Corrupted Gradle wrapper file will be removed: $gradleJar") + gradleJar.delete() + } +} + +@Suppress("IO_FILE_USAGE") +internal fun getGradleDistributionJarPath(projectPath: Path): Path { + val descriptor = projectPath.getEelDescriptor() + // the file used only as a path-representation; no IO operations are expected + return PathAssembler(gradleUserHomeDir(descriptor).toFile(), projectPath.toFile()) + .getDistribution(GradleUtil.getWrapperConfiguration(projectPath)) + .zipFile + .toPath() +} + +internal fun getGradleDistributionRoot(project: Project, version: GradleVersion): Path { + val descriptor = project.getEelDescriptor() + return getGradleDistributionRoot(gradleUserHomeDir(descriptor), version) +} + +internal fun getLocalGradleDistributionRoot(version: GradleVersion): Path = + getGradleDistributionRoot(StartParameter.DEFAULT_GRADLE_USER_HOME.toPath(), version) + +internal fun getGradleDistributionRoot(gradleUserHome: Path, version: GradleVersion): Path = + gradleUserHome.resolve("wrapper/dists/gradle-${version.version}-bin") \ No newline at end of file diff --git a/plugins/kotlin/gradle/gradle-java/tests.shared/test/org/jetbrains/kotlin/gradle/multiplatformTests/AbstractKotlinMppGradleImportingTest.kt b/plugins/kotlin/gradle/gradle-java/tests.shared/test/org/jetbrains/kotlin/gradle/multiplatformTests/AbstractKotlinMppGradleImportingTest.kt index 450eaefc2bfc..c86bef49a378 100644 --- a/plugins/kotlin/gradle/gradle-java/tests.shared/test/org/jetbrains/kotlin/gradle/multiplatformTests/AbstractKotlinMppGradleImportingTest.kt +++ b/plugins/kotlin/gradle/gradle-java/tests.shared/test/org/jetbrains/kotlin/gradle/multiplatformTests/AbstractKotlinMppGradleImportingTest.kt @@ -122,8 +122,12 @@ abstract class AbstractKotlinMppGradleImportingTest : GradleImportingTestCase(), @get:Rule val pluginTargetVersionRule = PluginTargetVersionsRule() - // The property below are needed solely for compatibility with PluginTargetVersionsRule; - // please, use context.testPropertiesService if you need the version in your code + // Two properties below are needed solely for compatibility with PluginTargetVersionsRule; + // please, use context.testPropertiesService if you need those versions in your code + override var gradleVersion: String + get() = context.testProperties.gradleVersion.version + set(_) {} + final override val kotlinPluginVersion: KotlinToolingVersion get() = context.testProperties.kotlinVersion diff --git a/plugins/kotlin/gradle/gradle-java/tests.shared/test/org/jetbrains/kotlin/idea/codeInsight/gradle/MultiplePluginVersionGradleImportingTestCase.kt b/plugins/kotlin/gradle/gradle-java/tests.shared/test/org/jetbrains/kotlin/idea/codeInsight/gradle/MultiplePluginVersionGradleImportingTestCase.kt index 86e188dc1918..d75e5649d8d6 100644 --- a/plugins/kotlin/gradle/gradle-java/tests.shared/test/org/jetbrains/kotlin/idea/codeInsight/gradle/MultiplePluginVersionGradleImportingTestCase.kt +++ b/plugins/kotlin/gradle/gradle-java/tests.shared/test/org/jetbrains/kotlin/idea/codeInsight/gradle/MultiplePluginVersionGradleImportingTestCase.kt @@ -58,6 +58,10 @@ abstract class MultiplePluginVersionGradleImportingTestCase : KotlinGradleImport "Missing 'kotlinPluginVersionParam'" }.version + override var gradleVersion: String + get() = super.gradleVersion + set(_) {} + override fun setUp() { super.setUp() setupSystemProperties()