diff --git a/.idea/libraries/jline_terminal_jansi.xml b/.idea/libraries/jline_terminal_jansi.xml
new file mode 100644
index 000000000000..5f3969120bd4
--- /dev/null
+++ b/.idea/libraries/jline_terminal_jansi.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+ a7b369a2e697215fed314a58b736f10ae8bdedf218096916a6bdb7bc886fe470
+
+
+ 2e5e775a9dc58ffa6bbd6aa6f099d62f8b62dcdeb4c3c3bbbe5cf2301bc2dcc1
+
+
+ 2f461c75091ec3810a42184afc80c96910f54e9dd2110e9ecb9902a5ee6c244b
+
+
+ d8bc77bc80edc7d9a02a06a069b2d7085629421db0843aba7c153a869ffe525d
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/jline_terminal_jna.xml b/.idea/libraries/jline_terminal_jna.xml
new file mode 100644
index 000000000000..bb7d5482ff67
--- /dev/null
+++ b/.idea/libraries/jline_terminal_jna.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+ e7322e6d8dc1bd69f507b91865af741af4a95c649ccab36e79e8be89696bb432
+
+
+ a564158d28ab5127fc6a958028ed54279fe0999662c46425b6a3b09a2a52094d
+
+
+ 2f461c75091ec3810a42184afc80c96910f54e9dd2110e9ecb9902a5ee6c244b
+
+
+ d8bc77bc80edc7d9a02a06a069b2d7085629421db0843aba7c153a869ffe525d
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
index 865397a0b8c3..7c07a6171aff 100644
--- a/.idea/modules.xml
+++ b/.idea/modules.xml
@@ -647,6 +647,7 @@
+
diff --git a/platform/build-scripts/src/org/jetbrains/intellij/build/CommunityLibraryLicenses.kt b/platform/build-scripts/src/org/jetbrains/intellij/build/CommunityLibraryLicenses.kt
index 59822e421e7e..d5a871873a6a 100644
--- a/platform/build-scripts/src/org/jetbrains/intellij/build/CommunityLibraryLicenses.kt
+++ b/platform/build-scripts/src/org/jetbrains/intellij/build/CommunityLibraryLicenses.kt
@@ -639,6 +639,19 @@ object CommunityLibraryLicenses {
.newBsd("https://opensource.org/license/bsd-3-clause/")
.suppliedByOrganizations("Thai Open Source Software Center Ltd"),
+ LibraryLicense(null, libraryName = "jline.terminal", url = "https://github.com/jline/jline3")
+ .newBsd("https://github.com/jline/jline3/blob/master/LICENSE.txt")
+ .suppliedByPersons("Guillaume Nodet"),
+
+ LibraryLicense(null, libraryName = "jline.terminal.jansi", url = "https://github.com/jline/jline3")
+ .newBsd("https://github.com/jline/jline3/blob/master/LICENSE.txt")
+ .suppliedByPersons("Guillaume Nodet"),
+
+ LibraryLicense(null, libraryName = "jline.terminal.jna", url = "https://github.com/jline/jline3")
+ .newBsd("https://github.com/jline/jline3/blob/master/LICENSE.txt")
+ .suppliedByPersons("Guillaume Nodet"),
+
+
LibraryLicense("JNA", libraryName = "jna", url = "https://github.com/java-native-access/jna")
.apache("https://github.com/java-native-access/jna/blob/master/LICENSE"),
diff --git a/platform/platform-tests/eel-helper/README.txt b/platform/platform-tests/eel-helper/README.txt
new file mode 100644
index 000000000000..aa2c3e4ca057
--- /dev/null
+++ b/platform/platform-tests/eel-helper/README.txt
@@ -0,0 +1,5 @@
+Helper for EEL local execution test
+1. prints hello into stderr
+2. prints tty and its size to stdout
+3. waits for command exit (exit 0) or sleep (sleep 15_000)
+4. installs signal for SIGINT to return 42
\ No newline at end of file
diff --git a/platform/platform-tests/eel-helper/api-dump.txt b/platform/platform-tests/eel-helper/api-dump.txt
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/platform/platform-tests/eel-helper/intellij.platform.eelHelper.iml b/platform/platform-tests/eel-helper/intellij.platform.eelHelper.iml
new file mode 100644
index 000000000000..58a60ae13a06
--- /dev/null
+++ b/platform/platform-tests/eel-helper/intellij.platform.eelHelper.iml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 2f461c75091ec3810a42184afc80c96910f54e9dd2110e9ecb9902a5ee6c244b
+
+
+ d8bc77bc80edc7d9a02a06a069b2d7085629421db0843aba7c153a869ffe525d
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/platform/platform-tests/eel-helper/src/META-INF/MANIFEST.MF b/platform/platform-tests/eel-helper/src/META-INF/MANIFEST.MF
new file mode 100644
index 000000000000..75ad3297b4e7
--- /dev/null
+++ b/platform/platform-tests/eel-helper/src/META-INF/MANIFEST.MF
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Main-Class: com.intellij.platform.tests.eelHelper.EelHelper
+
diff --git a/platform/platform-tests/eel-helper/src/com/intellij/platform/tests/eelHelper/Command.kt b/platform/platform-tests/eel-helper/src/com/intellij/platform/tests/eelHelper/Command.kt
new file mode 100644
index 000000000000..78477ba01a84
--- /dev/null
+++ b/platform/platform-tests/eel-helper/src/com/intellij/platform/tests/eelHelper/Command.kt
@@ -0,0 +1,20 @@
+// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+package com.intellij.platform.tests.eelHelper
+
+import org.jetbrains.annotations.TestOnly
+
+/**
+ * Commands to be sent from test to this helper
+ */
+@TestOnly
+enum class Command {
+ /**
+ * Exit with [GRACEFUL_EXIT_CODE]
+ */
+ EXIT,
+
+ /**
+ * Sleep for a long time
+ */
+ SLEEP
+}
\ No newline at end of file
diff --git a/platform/platform-tests/eel-helper/src/com/intellij/platform/tests/eelHelper/EelHelper.java b/platform/platform-tests/eel-helper/src/com/intellij/platform/tests/eelHelper/EelHelper.java
new file mode 100644
index 000000000000..6805ba2ebef0
--- /dev/null
+++ b/platform/platform-tests/eel-helper/src/com/intellij/platform/tests/eelHelper/EelHelper.java
@@ -0,0 +1,20 @@
+// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+package com.intellij.platform.tests.eelHelper;
+
+import org.jetbrains.annotations.TestOnly;
+
+import static com.intellij.platform.tests.eelHelper.ImplKt.startHelper;
+
+/**
+ * Helper that is run by EEL test: should react on signals and commands
+ */
+@TestOnly
+public final class EelHelper {
+ private EelHelper() {
+ }
+
+ @TestOnly
+ public static void main(String[] args) {
+ startHelper();
+ }
+}
diff --git a/platform/platform-tests/eel-helper/src/com/intellij/platform/tests/eelHelper/Size.kt b/platform/platform-tests/eel-helper/src/com/intellij/platform/tests/eelHelper/Size.kt
new file mode 100644
index 000000000000..97d8f07d4dd8
--- /dev/null
+++ b/platform/platform-tests/eel-helper/src/com/intellij/platform/tests/eelHelper/Size.kt
@@ -0,0 +1,4 @@
+// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+package com.intellij.platform.tests.eelHelper
+
+data class Size(val cols:Int, val rows: Int)
\ No newline at end of file
diff --git a/platform/platform-tests/eel-helper/src/com/intellij/platform/tests/eelHelper/TTYState.kt b/platform/platform-tests/eel-helper/src/com/intellij/platform/tests/eelHelper/TTYState.kt
new file mode 100644
index 000000000000..ec4ae27cefb0
--- /dev/null
+++ b/platform/platform-tests/eel-helper/src/com/intellij/platform/tests/eelHelper/TTYState.kt
@@ -0,0 +1,19 @@
+// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+package com.intellij.platform.tests.eelHelper
+
+import com.fasterxml.jackson.databind.ObjectMapper
+import com.fasterxml.jackson.module.kotlin.registerKotlinModule
+import org.jetbrains.annotations.TestOnly
+
+@TestOnly
+data class TTYState(val size: Size?) {
+ companion object {
+ private val mapper = ObjectMapper().registerKotlinModule()
+
+ @TestOnly
+ fun deserialize(str: String): TTYState = mapper.readValue(str, TTYState::class.java)
+ }
+
+ @TestOnly
+ fun serialize(): String = mapper.writeValueAsString(this)
+}
\ No newline at end of file
diff --git a/platform/platform-tests/eel-helper/src/com/intellij/platform/tests/eelHelper/constants.kt b/platform/platform-tests/eel-helper/src/com/intellij/platform/tests/eelHelper/constants.kt
new file mode 100644
index 000000000000..9ec7ca704482
--- /dev/null
+++ b/platform/platform-tests/eel-helper/src/com/intellij/platform/tests/eelHelper/constants.kt
@@ -0,0 +1,23 @@
+// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+package com.intellij.platform.tests.eelHelper
+
+import org.jetbrains.annotations.TestOnly
+
+/**
+ * Messages to be printed to stderr by this helper
+ */
+@TestOnly
+const val HELLO: String = "hello"
+
+/**
+ * Exit code to be used on `SIGINT`
+ */
+
+@TestOnly
+const val INTERRUPT_EXIT_CODE: Int = 42
+
+/**
+ * Exit code for [Command.EXIT]
+ */
+@TestOnly
+const val GRACEFUL_EXIT_CODE: Int = 0
diff --git a/platform/platform-tests/eel-helper/src/com/intellij/platform/tests/eelHelper/impl.kt b/platform/platform-tests/eel-helper/src/com/intellij/platform/tests/eelHelper/impl.kt
new file mode 100644
index 000000000000..df99ad9a38fb
--- /dev/null
+++ b/platform/platform-tests/eel-helper/src/com/intellij/platform/tests/eelHelper/impl.kt
@@ -0,0 +1,57 @@
+// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+package com.intellij.platform.tests.eelHelper
+
+import org.jetbrains.annotations.TestOnly
+import org.jline.terminal.Terminal
+import org.jline.terminal.TerminalBuilder
+import sun.misc.Signal
+import sun.misc.SignalHandler
+import kotlin.system.exitProcess
+
+@TestOnly
+private object OnSigint : SignalHandler, Terminal.SignalHandler {
+ override fun handle(sig: Signal?) {
+ onSigInt()
+ }
+
+ override fun handle(signal: Terminal.Signal?) {
+ onSigInt()
+ }
+
+ private fun onSigInt() {
+ exitProcess(INTERRUPT_EXIT_CODE)
+ }
+}
+
+
+@TestOnly
+internal fun startHelper() {
+
+ // Exit once SIGINT sent without terminal
+ Signal.handle(Signal("INT"), OnSigint)
+
+ val terminal = TerminalBuilder.terminal()
+ val terminalSize = if (terminal.type == Terminal.TYPE_DUMB) null else terminal.size
+
+ // Exit once SIGINT sent with terminal
+ terminal.handle(Terminal.Signal.INT, OnSigint)
+
+ // First thing to do is to print this to the stderr
+ System.err.print(HELLO + "\n")
+ System.err.flush()
+
+ val ttyState = TTYState(terminalSize?.let{ Size(cols = it.columns, rows = it.rows )})
+
+ // Then, print terminal info to the stdout
+ println(ttyState.serialize())
+ System.out.flush()
+
+ when (Command.valueOf(readln().trim().uppercase())) {
+ Command.EXIT -> {
+ exitProcess(GRACEFUL_EXIT_CODE)
+ }
+ Command.SLEEP -> {
+ Thread.sleep(15_000)
+ }
+ }
+}
\ No newline at end of file
diff --git a/platform/platform-tests/eel-helper/src/com/intellij/platform/tests/eelHelper/package-info.java b/platform/platform-tests/eel-helper/src/com/intellij/platform/tests/eelHelper/package-info.java
new file mode 100644
index 000000000000..0b8e36962d58
--- /dev/null
+++ b/platform/platform-tests/eel-helper/src/com/intellij/platform/tests/eelHelper/package-info.java
@@ -0,0 +1,5 @@
+// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+@ApiStatus.Internal
+package com.intellij.platform.tests.eelHelper;
+
+import org.jetbrains.annotations.ApiStatus;
\ No newline at end of file
diff --git a/platform/platform-tests/intellij.platform.tests.iml b/platform/platform-tests/intellij.platform.tests.iml
index 8e2365c4dbc1..7b28251551d8 100644
--- a/platform/platform-tests/intellij.platform.tests.iml
+++ b/platform/platform-tests/intellij.platform.tests.iml
@@ -107,5 +107,6 @@
+
\ No newline at end of file
diff --git a/platform/platform-tests/testSrc/com/intellij/execution/eel/EelLocalExecApiTest.kt b/platform/platform-tests/testSrc/com/intellij/execution/eel/EelLocalExecApiTest.kt
index 58f4785f12b6..54346de475c1 100644
--- a/platform/platform-tests/testSrc/com/intellij/execution/eel/EelLocalExecApiTest.kt
+++ b/platform/platform-tests/testSrc/com/intellij/execution/eel/EelLocalExecApiTest.kt
@@ -1,64 +1,46 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.execution.eel
-import com.google.gson.Gson
import com.intellij.execution.process.UnixSignal
import com.intellij.openapi.util.SystemInfoRt
-import com.intellij.platform.eel.EelExecApi
import com.intellij.platform.eel.EelExecApi.Pty
import com.intellij.platform.eel.EelProcess
import com.intellij.platform.eel.EelResult
import com.intellij.platform.eel.impl.local.EelLocalExecApi
-import com.intellij.testFramework.UsefulTestCase.IS_UNDER_TEAMCITY
+import com.intellij.platform.tests.eelHelper.*
+import com.intellij.platform.tests.eelHelper.Size
import com.intellij.testFramework.common.timeoutRunBlocking
import com.intellij.testFramework.junit5.TestApplication
-import com.intellij.util.io.write
+import io.ktor.util.decodeString
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+import kotlinx.coroutines.withTimeoutOrNull
+import org.hamcrest.CoreMatchers
import org.hamcrest.CoreMatchers.anyOf
import org.hamcrest.CoreMatchers.`is`
import org.hamcrest.MatcherAssert.assertThat
import org.junit.jupiter.api.Assertions
-import org.junit.jupiter.api.Assumptions.assumeFalse
import org.junit.jupiter.api.BeforeAll
-import org.junit.jupiter.api.BeforeEach
-import org.junit.jupiter.api.io.TempDir
import org.junitpioneer.jupiter.cartesian.CartesianTest
-import java.nio.file.Path
-import kotlin.io.path.isExecutable
+import java.nio.ByteBuffer
import kotlin.test.assertEquals
import kotlin.test.assertNotEquals
+import kotlin.time.Duration.Companion.minutes
+import kotlin.time.Duration.Companion.seconds
@TestApplication
class EelLocalExecApiTest {
companion object {
- const val PYTHON_ENV = "PYTHON"
private const val PTY_COLS = 42
private const val PTY_ROWS = 24
private val NEW_LINES = Regex("\r?\n")
- // TODO: Remove as soon as we migrate to kotlin script from the python
+ private lateinit var executor: JavaMainClassExecutor
+
@BeforeAll
@JvmStatic
- fun skipTestOnTcWindows() {
- assumeFalse(SystemInfoRt.isWindows && IS_UNDER_TEAMCITY, "Test is disabled on TC@WIN as there is no python by default there")
- }
- }
-
- private data class TtySize(val cols: Int, val rows: Int)
- private data class PythonOutput(
- val tty: Boolean,
- val size: TtySize?,
- )
-
- private val helperContent = EelLocalExecApiTest::class.java.classLoader.getResource("helper.py")!!.readBytes()
-
- // TODO: This tests depends on python interpreter. Rewrite to kotlin script
- private val python = Path.of(System.getenv(PYTHON_ENV)
- ?: if (!SystemInfoRt.isWindows) "/usr/bin/python3" else error("Provide $PYTHON_ENV env var with path to python"))
-
- @BeforeEach
- fun setUp() {
- assert(python.isExecutable()) {
- "Can't find python or $python isn't executable. Please set $PYTHON_ENV env var to the path to python binary"
+ fun createExecutor() {
+ executor = JavaMainClassExecutor(EelHelper::class.java)
}
}
@@ -73,18 +55,15 @@ class EelLocalExecApiTest {
/**
- * Test runs `helper.py` checking stdin/stdout iteration, exit code, tty and signal/termination handling.
+ * Test runs [EelHelper] checking stdin/stdout iteration, exit code, tty and signal/termination handling.
*/
@CartesianTest
fun testOutput(
@CartesianTest.Enum exitType: ExitType,
@CartesianTest.Enum ptyManagement: PTYManagement,
- @TempDir tempDir: Path,
- ): Unit = timeoutRunBlocking {
- val helperScript = tempDir.resolve("helper.py")
- helperScript.write(helperContent)
+ ): Unit = timeoutRunBlocking(1.minutes) {
- val builder = EelExecApi.executeProcessBuilder(python.toString()).args(listOf(helperScript.toString()))
+ val builder = executor.createBuilderToExecuteMain()
builder.pty(when (ptyManagement) {
PTYManagement.NO_PTY -> null
PTYManagement.PTY_SIZE_FROM_START -> Pty(PTY_COLS, PTY_ROWS, true)
@@ -111,19 +90,29 @@ class EelLocalExecApiTest {
}
}
- Assertions.assertEquals("hello", process.stderr.receive().decodeToString().trim())
+ withContext(Dispatchers.Default) {
+ val text = ByteBuffer.allocate(1024)
+ withTimeoutOrNull(10.seconds) {
+ for (chunk in process.stderr) {
+ text.put(chunk)
+ if (HELLO in chunk.decodeToString()) break
+ }
+ }
+ text.limit(text.position()).rewind()
+ assertThat("No $HELLO reported in stderr", text.decodeString(), CoreMatchers.containsString(HELLO))
+ }
+
// Test tty api
// tty might insert "\r\n", we need to remove them. Hence, NEW_LINES.
- val pyOutputStr = process.stdout.receive().decodeToString().replace(NEW_LINES, "")
- val pyOutputObj = Gson().fromJson(pyOutputStr, PythonOutput::class.java)
+ val outputStr = process.stdout.receive().decodeToString().replace(NEW_LINES, "")
+ val pyOutputObj = TTYState.deserialize(outputStr)
when (ptyManagement) {
PTYManagement.PTY_SIZE_FROM_START, PTYManagement.PTY_RESIZE_LATER -> {
- Assertions.assertTrue(pyOutputObj.tty)
- Assertions.assertEquals(TtySize(PTY_COLS, PTY_ROWS), pyOutputObj.size, "size must be set for tty")
+ Assertions.assertNotNull(pyOutputObj.size)
+ Assertions.assertEquals(Size(PTY_COLS, PTY_ROWS), pyOutputObj.size, "size must be set for tty")
}
PTYManagement.NO_PTY -> {
- Assertions.assertFalse(pyOutputObj.tty)
Assertions.assertNull(pyOutputObj.size, "size must not be set if no tty")
}
}
@@ -135,13 +124,12 @@ class EelLocalExecApiTest {
ExitType.TERMINATE -> process.terminate()
ExitType.INTERRUPT -> {
// Terminate sleep with interrupt/CTRL+C signal
- process.stdin.send("sleep\n".encodeToByteArray())
- assertEquals("sleeping", process.stdout.receive().decodeToString().trim())
+ process.sendCommand(Command.SLEEP)
process.interrupt()
}
ExitType.EXIT_WITH_COMMAND -> {
// Just command to ask script return gracefully
- process.stdin.send("exit\n".encodeToByteArray())
+ process.sendCommand(Command.EXIT)
}
}
val exitCode = process.exitCode.await()
@@ -161,13 +149,27 @@ class EelLocalExecApiTest {
}
}
ExitType.INTERRUPT -> {
- assertEquals(42, exitCode) // CTRL+C/SIGINT handler returns 42, see script
+ when (ptyManagement) {
+ PTYManagement.NO_PTY -> Unit // SIGINT is doubtful without PTY especially without console on Windows
+ PTYManagement.PTY_SIZE_FROM_START, PTYManagement.PTY_RESIZE_LATER -> {
+ // CTRL+C/SIGINT handler returns 42, see script
+ assertEquals(INTERRUPT_EXIT_CODE, exitCode)
+ }
+ }
}
ExitType.EXIT_WITH_COMMAND -> {
- assertEquals(0, exitCode) // Graceful exit
+ assertEquals(GRACEFUL_EXIT_CODE, exitCode) // Graceful exit
}
}
}
}
}
+
+ /**
+ * Sends [command] to the helper and flush
+ */
+ private suspend fun EelProcess.sendCommand(command: Command) {
+ val text = command.name + "\n"
+ stdin.send(text.encodeToByteArray())
+ }
}
\ No newline at end of file
diff --git a/platform/platform-tests/testSrc/com/intellij/execution/eel/JavaMainClassExecutor.kt b/platform/platform-tests/testSrc/com/intellij/execution/eel/JavaMainClassExecutor.kt
new file mode 100644
index 000000000000..3cb299312b84
--- /dev/null
+++ b/platform/platform-tests/testSrc/com/intellij/execution/eel/JavaMainClassExecutor.kt
@@ -0,0 +1,100 @@
+// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+package com.intellij.execution.eel
+
+import com.intellij.application.options.PathMacrosImpl
+import com.intellij.openapi.application.PathManager
+import com.intellij.openapi.diagnostic.fileLogger
+import com.intellij.openapi.util.SystemInfoRt
+import com.intellij.platform.eel.EelExecApi
+import com.intellij.util.SystemProperties
+import org.jetbrains.jps.model.java.JpsJavaExtensionService
+import org.jetbrains.jps.model.library.JpsOrderRootType
+import org.jetbrains.jps.model.module.JpsModule
+import org.jetbrains.jps.model.serialization.JpsSerializationManager
+import java.io.File
+import java.nio.file.Path
+import kotlin.io.path.Path
+import kotlin.io.path.exists
+import kotlin.io.path.name
+import kotlin.io.path.pathString
+
+
+/**
+ * Searches for module with [clazz] in [PathManager.ourHomePath] an executes [clazz] `main` with all dependencies
+ */
+internal class JavaMainClassExecutor(clazz: Class<*>) {
+ private val exe = Path(ProcessHandle.current().info().command().get()).toString()
+ private val env = mapOf("CLASSPATH" to getClassPathForClass(clazz))
+ private val args = listOf(clazz.canonicalName)
+
+ /**
+ * Execute `main` method
+ */
+ fun createBuilderToExecuteMain(): EelExecApi.ExecuteProcessBuilder = EelExecApi.executeProcessBuilder(exe).env(env).args(args)
+
+ private companion object {
+ private fun getClassPathForClass(clazz: Class<*>): String {
+ val mavenPath = Path(SystemProperties.getUserHome()).resolve(".m2").resolve("repository").toString()
+ val helperModuleOutputPath = getJpsModuleOutputPathForClass(clazz)
+ logger.value.info("helper module path: $helperModuleOutputPath")
+ val helperModuleName = helperModuleOutputPath.name
+
+ val helperModule = module(helperModuleName)
+
+
+ var dependencies = JpsJavaExtensionService
+ .dependencies(helperModule)
+ .recursively()
+
+ val libraries = dependencies
+ .libraries
+ .flatMap { it.getPaths(JpsOrderRootType.COMPILED) }
+ .map { Path(it.pathString.replace('$' + PathMacrosImpl.MAVEN_REPOSITORY + '$', mavenPath)) }
+ val modulesOutputPath = helperModuleOutputPath.parent
+
+ val modules = dependencies
+ .modules
+ .map { module -> modulesOutputPath.resolve(module.name) }
+
+ return (modules + libraries)
+ .filter { path ->
+ path.exists().also {
+ if (!it) {
+ logger.value.info("$path doesn't exist")
+ }
+ }
+ }
+ .joinToString(File.pathSeparator)
+ }
+
+ private fun module(helperModuleName: String): JpsModule {
+ for (homePath in arrayOf(PathManager.getHomePath(), PathManager.getCommunityHomePath())) {
+ val jpsProject = JpsSerializationManager.getInstance().loadProject(homePath, mapOf())
+ val helperModule = jpsProject.modules.firstOrNull { module -> module.name == helperModuleName }
+ if (helperModule != null) return helperModule
+ logger.value.warn("$helperModuleName not found in $homePath modules. Checked: ${jpsProject.modules}")
+ }
+ throw AssertionError("Couldn't find module $helperModuleName")
+ }
+
+
+ private fun getJpsModuleOutputPathForClass(clazz: Class<*>): Path {
+ val classFile = clazz.name.replace(".", "/") + ".class"
+
+ val absoluteClassPath = clazz.classLoader.getResource(classFile)!!.path.let { path ->
+ Path(if (SystemInfoRt.isWindows) path.trimStart('/') else path)
+ }
+ logger.value.info("Looking for class file $absoluteClassPath")
+
+ val relativeClassPath = Path(classFile)
+ var pathToModule = absoluteClassPath
+ while (pathToModule.toList().size > 1) {
+ if (pathToModule.resolve(relativeClassPath).exists()) break
+ pathToModule = pathToModule.parent
+ }
+ return pathToModule
+ }
+
+ private val logger = lazy { fileLogger() }
+ }
+}
diff --git a/platform/platform-tests/testSrc/helper.py b/platform/platform-tests/testSrc/helper.py
deleted file mode 100644
index 7695b7f7679c..000000000000
--- a/platform/platform-tests/testSrc/helper.py
+++ /dev/null
@@ -1,42 +0,0 @@
-# Script for EEL local execution test
-# 1.prints tty and its size
-# 2.waits for command exit (exit 0) or sleep (sleep 10_000)
-# 3. installs signal for SIGINT to return 42
-import os
-import signal
-import sys
-import json
-from time import sleep
-
-
-def exit_42(*_):
- exit(42)
-
-signal.signal(signal.SIGINT, exit_42)
-
-is_tty = sys.stdin.isatty()
-terminal_size = None
-
-try:
- terminal_size = os.get_terminal_size()
-except OSError:
- pass
-
-
-sys.stderr.write("hello\n")
-sys.stderr.flush()
-
-json.dump({
- "tty": is_tty,
- "size": {"cols": terminal_size.columns, "rows":terminal_size.lines} if terminal_size else None
-}, sys.stdout)
-sys.stdout.flush()
-
-
-command = input().strip()
-if command == "exit":
- exit(0)
-elif command == "sleep":
- print("sleeping")
- sys.stdout.flush()
- sleep(10_000)