diff --git a/python/junit5Tests-framework/intellij.python.community.junit5Tests.framework.iml b/python/junit5Tests-framework/intellij.python.community.junit5Tests.framework.iml index bcb5d88f5b90..9615c213adee 100644 --- a/python/junit5Tests-framework/intellij.python.community.junit5Tests.framework.iml +++ b/python/junit5Tests-framework/intellij.python.community.junit5Tests.framework.iml @@ -32,5 +32,44 @@ + + + + + + 9a24867a51f5d22db67d9052a06bc5dd2e9a3e273bc2ee9814620f2d9f25d0a8 + + + 9c27322cfc7043c75384ad444007b0880ca18fe7231d69bfa69616bc773cafe1 + + + 74768731219a5b2e0cc9d2b1c9e2092e0622242e41b1925a6ba7346e160731bc + + + e1dae7efc5562c29ad3b625b90e6864208de69bad5632c3f93a547f17622ac51 + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/python/junit5Tests-framework/src/com/intellij/python/junit5Tests/framework/EnvironmentVariablesPathMock.kt b/python/junit5Tests-framework/src/com/intellij/python/junit5Tests/framework/EnvironmentVariablesPathMock.kt new file mode 100644 index 000000000000..721a2b16aa91 --- /dev/null +++ b/python/junit5Tests-framework/src/com/intellij/python/junit5Tests/framework/EnvironmentVariablesPathMock.kt @@ -0,0 +1,40 @@ +// 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.python.junit5Tests.framework + +import com.intellij.util.EnvironmentUtil +import io.mockk.every +import io.mockk.mockkStatic +import io.mockk.unmockkStatic +import uk.org.webcompere.systemstubs.environment.EnvironmentVariables +import java.io.File +import java.nio.file.Path +import kotlin.io.path.pathString + +/** + * Extension on top of [EnvironmentVariables] to add [pathsToAdd] to the [PATH]. + * Call it before the test. After the test please call [unMockPath]. + * See [com.intellij.python.junit5Tests.unit.alsoWin.showCase.EnvironmentVariablesPathMockTest]. + * + * It mocks both [System.getenv] and [EnvironmentUtil] used by IJ (latter might cache something). + */ +fun EnvironmentVariables.mockPathAndAdd(vararg pathsToAdd: Path) { + // TODO: Use native calls to `SetEnvironmentVariable` and `setenv(3)` to change env for children processes (they inherit parent envs). + val (pathKey, paths) = variables.entries.find { it.key.isPath }?.toPair() + ?: Pair(PATH, "") + val newPathVal = (paths.split(File.pathSeparator) + pathsToAdd.map { it.pathString }).joinToString(File.pathSeparator) + set(pathKey, newPathVal) // Mock System.env + mockkStatic(EnvironmentUtil::class) // Mock EnvironmentUtil + every { EnvironmentUtil.getValue(any()) }.coAnswers { + if ((it.invocation.args[0] as String).isPath) newPathVal else it.invocation.originalCall.invoke() as String + } +} + +/** + * See [mockPathAndAdd] + */ +fun EnvironmentVariables.unMockPath() { + unmockkStatic(EnvironmentUtil::class) +} + +private const val PATH = "PATH" +private val String.isPath: Boolean get() = uppercase() == PATH \ No newline at end of file diff --git a/python/junit5Tests-framework/src/com/intellij/python/junit5Tests/unit/alsoWin/showCase/EnvironmentVariablesPathMockTest.kt b/python/junit5Tests-framework/src/com/intellij/python/junit5Tests/unit/alsoWin/showCase/EnvironmentVariablesPathMockTest.kt new file mode 100644 index 000000000000..8d74df4c3c29 --- /dev/null +++ b/python/junit5Tests-framework/src/com/intellij/python/junit5Tests/unit/alsoWin/showCase/EnvironmentVariablesPathMockTest.kt @@ -0,0 +1,63 @@ +// 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.python.junit5Tests.unit.alsoWin.showCase + +import com.intellij.execution.configurations.PathEnvironmentVariableUtil +import com.intellij.python.junit5Tests.framework.mockPathAndAdd +import com.intellij.python.junit5Tests.framework.unMockPath +import com.intellij.util.EnvironmentUtil +import org.hamcrest.CoreMatchers +import org.hamcrest.MatcherAssert +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import org.junit.jupiter.api.io.TempDir +import uk.org.webcompere.systemstubs.environment.EnvironmentVariables +import uk.org.webcompere.systemstubs.jupiter.SystemStub +import uk.org.webcompere.systemstubs.jupiter.SystemStubsExtension +import java.nio.file.Path +import kotlin.io.path.pathString + +/** + * This is how you use [mockPathAndAdd]: + * Extend a case with [SystemStubsExtension], add [EnvironmentVariables], mark it with [SystemStub], call [mockPathAndAdd], [unMockPath] + */ +@ExtendWith(SystemStubsExtension::class) +class EnvironmentVariablesPathMockTest { + + @SystemStub + private val environment = EnvironmentVariables() + + @TempDir + private lateinit var fakeDir: Path + + + @BeforeEach + fun setUp() { + PathEnvironmentVariableUtil.findInPath("someData") // Let this class cache something to see how we mock it + environment.mockPathAndAdd(fakeDir) + } + + @AfterEach + fun tearDown() { + environment.unMockPath() + } + + @Test + fun ensurePathFixedTest() { + Assertions.assertTrue(System.getenv().keys.count() > 2, "Env is too small, seems to be broken") + val pathKey = System.getenv().keys.find { it.lowercase() == "path" } + Assertions.assertNotNull(pathKey, "Env doesn't have path") + MatcherAssert.assertThat("System.env hasn't been changed", + System.getenv(pathKey!!), + CoreMatchers.containsString(fakeDir.pathString)) + MatcherAssert.assertThat("PathEnvironmentVariableUtil hasn't been changed", + PathEnvironmentVariableUtil.getPathVariableValue(), + CoreMatchers.containsString(fakeDir.pathString)) + for (envVarKey in System.getenv().keys) { + // Make sure we didn't break other vars for EnvironmentUtil + Assertions.assertNotNull(EnvironmentUtil.getValue(envVarKey), "env $envVarKey is broken in EnvironmentUtil") + } + } +} \ No newline at end of file