From fc46302582e592d17803b3b914e4ca2a4d1df167 Mon Sep 17 00:00:00 2001 From: Friedrich von Never Date: Sat, 10 Aug 2024 22:35:09 +0200 Subject: [PATCH 01/10] (#291) Test: try migrating to JUnit 5 --- .../run/PowerShellRunConfigurationTests.kt | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/test/kotlin/com/intellij/plugin/powershell/ide/run/PowerShellRunConfigurationTests.kt b/src/test/kotlin/com/intellij/plugin/powershell/ide/run/PowerShellRunConfigurationTests.kt index e567dcd5..a561ec90 100644 --- a/src/test/kotlin/com/intellij/plugin/powershell/ide/run/PowerShellRunConfigurationTests.kt +++ b/src/test/kotlin/com/intellij/plugin/powershell/ide/run/PowerShellRunConfigurationTests.kt @@ -5,38 +5,53 @@ import com.intellij.execution.impl.RunManagerImpl import com.intellij.execution.impl.RunnerAndConfigurationSettingsImpl import com.intellij.execution.runners.ExecutionEnvironment import com.intellij.execution.runners.ProgramRunner -import com.intellij.testFramework.fixtures.BasePlatformTestCase +import com.intellij.openapi.project.Project +import com.intellij.testFramework.junit5.TestApplication +import com.intellij.testFramework.junit5.fixture.projectFixture import kotlinx.coroutines.runBlocking +import org.junit.Assert.assertEquals +import org.junit.Test import java.nio.file.Path import kotlin.io.path.Path -class PowerShellRunConfigurationTests : BasePlatformTestCase() { +@TestApplication +class PowerShellRunConfigurationTests { + private val projectFixture = projectFixture() + + private val project: Project + get() = projectFixture.get() private val projectPath: Path get() = Path(project.basePath!!) private val defaultWorkingDirectory get() = projectPath.resolve("scripts") + @Test fun testCustomWorkingDirectoryPath() { val customDir = projectPath.resolve("ttt") assertWorkingDirectory(custom = customDir.toString(), expected = customDir) } + @Test fun testNoCustomWorkingDirectory() { assertWorkingDirectory(custom = null, expected = defaultWorkingDirectory) } + @Test fun testEmptyCustomWorkingDirectory() { assertWorkingDirectory(custom = "", expected = defaultWorkingDirectory) } + @Test fun testCustomWorkingDirectoryPathVariable() { assertWorkingDirectory(custom = "\$PROJECT_DIR$/foobar", expected = projectPath.resolve("foobar")) } + @Test fun testInvalidWorkingDir() { assertWorkingDirectory(custom = invalidPath, expected = defaultWorkingDirectory) } + @Test fun testInvalidScriptPath() { assertWorkingDirectory(custom = null, scriptPath = invalidPath, expected = Path(System.getProperty("user.home"))) } From c5171bac029f957197901188d0ac7126f38035fb Mon Sep 17 00:00:00 2001 From: Friedrich von Never Date: Mon, 12 Aug 2024 16:25:58 +0200 Subject: [PATCH 02/10] (#291) Tests: run JUnit 5 tests --- build.gradle.kts | 7 +++++++ gradle/libs.versions.toml | 2 +- .../powershell/ide/run/PowerShellRunConfigurationTests.kt | 4 ++-- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index aa30bb78..71d9a4f1 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -67,6 +67,9 @@ dependencies { pluginVerifier() } + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.1") + testRuntimeOnly("org.junit.vintage:junit-vintage-engine:5.8.1") + implementation(libs.bundles.junixsocket) implementation(libs.lsp4j) implementation(libs.lsp4jdebug) @@ -138,6 +141,10 @@ tasks { defaultCharacterEncoding = "UTF-8" } + withType { + useJUnitPlatform() + } + withType { dependsOn(generateLexer, generateParser) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 0facc132..e69ca027 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,7 +5,7 @@ junixsocket = "2.10.1" lsp4j = "0.24.0" [libraries] -junit = "junit:junit:4.13.2" +junit = "org.junit.jupiter:junit-jupiter-api:5.10.3" junixsocket-common = { module = "com.kohlschutter.junixsocket:junixsocket-common", version.ref = "junixsocket" } junixsocket-native-common = { module = "com.kohlschutter.junixsocket:junixsocket-native-common", version.ref = "junixsocket" } lsp4j = {module = "org.eclipse.lsp4j:org.eclipse.lsp4j", version.ref = "lsp4j"} diff --git a/src/test/kotlin/com/intellij/plugin/powershell/ide/run/PowerShellRunConfigurationTests.kt b/src/test/kotlin/com/intellij/plugin/powershell/ide/run/PowerShellRunConfigurationTests.kt index a561ec90..a91f7a80 100644 --- a/src/test/kotlin/com/intellij/plugin/powershell/ide/run/PowerShellRunConfigurationTests.kt +++ b/src/test/kotlin/com/intellij/plugin/powershell/ide/run/PowerShellRunConfigurationTests.kt @@ -9,8 +9,8 @@ import com.intellij.openapi.project.Project import com.intellij.testFramework.junit5.TestApplication import com.intellij.testFramework.junit5.fixture.projectFixture import kotlinx.coroutines.runBlocking -import org.junit.Assert.assertEquals -import org.junit.Test +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test import java.nio.file.Path import kotlin.io.path.Path From ad53c40f0b2ce3c7d8e001461f6910c22df852bc Mon Sep 17 00:00:00 2001 From: Phantom Date: Thu, 10 Apr 2025 20:12:00 +0400 Subject: [PATCH 03/10] (#291) Tests: Migrate debugger tests to JUnit 5 --- .../powershell/debugger/BreakpointTest.kt | 50 +++++++---- .../powershell/debugger/EvaluationTest.kt | 15 ++-- .../plugin/powershell/debugger/StepTest.kt | 90 ++++++++++++------- .../powershell/debugger/VariableTest.kt | 73 +++++++++------ .../run/PowerShellRunConfigurationTests.kt | 11 +-- .../testFramework/DebuggerTestBase.kt | 40 +++------ .../testFramework/PowerShellTestBase.kt | 37 ++++++++ .../testFramework/PowerShellTestSession.kt | 2 - .../testFramework/PowerShellTestUtils.kt | 14 +++ 9 files changed, 209 insertions(+), 123 deletions(-) create mode 100644 src/test/kotlin/com/intellij/plugin/powershell/testFramework/PowerShellTestBase.kt create mode 100644 src/test/kotlin/com/intellij/plugin/powershell/testFramework/PowerShellTestUtils.kt diff --git a/src/test/kotlin/com/intellij/plugin/powershell/debugger/BreakpointTest.kt b/src/test/kotlin/com/intellij/plugin/powershell/debugger/BreakpointTest.kt index 6e72e0d5..cb7087ef 100644 --- a/src/test/kotlin/com/intellij/plugin/powershell/debugger/BreakpointTest.kt +++ b/src/test/kotlin/com/intellij/plugin/powershell/debugger/BreakpointTest.kt @@ -4,14 +4,18 @@ import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx import com.intellij.plugin.powershell.ide.debugger.PowerShellSuspendContext import com.intellij.plugin.powershell.testFramework.DebuggerTestBase import com.intellij.plugin.powershell.testFramework.PowerShellTestSession -import com.intellij.testFramework.HeavyPlatformTestCase.assertTrue +import com.intellij.plugin.powershell.testFramework.runInEdt +import com.intellij.testFramework.junit5.TestApplication import com.intellij.xdebugger.XDebuggerTestUtil import com.intellij.xdebugger.XTestCompositeNode import com.jetbrains.rd.util.lifetime.Lifetime -import junit.framework.TestCase +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test +@TestApplication class BreakpointTest : DebuggerTestBase() { + @Test fun testBreakpoint() { runInEdt { val psiFile = copyAndOpenFile("debugger/testBreakpoint.ps1") @@ -23,16 +27,19 @@ class BreakpointTest : DebuggerTestBase() { XDebuggerTestUtil.toggleBreakpoint(project, file, line) Lifetime.using { lt -> val debugSession = testSession.startDebugSession(lt) - assertTrue("Pause should be triggered in ${testSession.waitForBackgroundTimeout}", XDebuggerTestUtil.waitFor( - testSession.sessionListener.pausedSemaphore, - testSession.waitForBackgroundTimeout.toMillis() - )) + Assertions.assertTrue( + XDebuggerTestUtil.waitFor( + testSession.sessionListener.pausedSemaphore, + testSession.waitForBackgroundTimeout.toMillis() + ), "Pause should be triggered in ${testSession.waitForBackgroundTimeout}" + ) val suspendContext = debugSession.suspendContext as PowerShellSuspendContext - TestCase.assertEquals(line, suspendContext.activeExecutionStack.topFrame?.sourcePosition?.line) + Assertions.assertEquals(line, suspendContext.activeExecutionStack.topFrame?.sourcePosition?.line) } } } + @Test fun testBreakpointTwoFiles() { runInEdt { val psiFile = copyAndOpenFile("debugger/testBreakpointTwoFiles.ps1") @@ -47,20 +54,23 @@ class BreakpointTest : DebuggerTestBase() { Lifetime.using { lt -> val debugSession = testSession.startDebugSession(lt) - assertTrue("Pause should be triggered in ${testSession.waitForBackgroundTimeout}", XDebuggerTestUtil.waitFor( - testSession.sessionListener.pausedSemaphore, - testSession.waitForBackgroundTimeout.toMillis() - )) + Assertions.assertTrue( + XDebuggerTestUtil.waitFor( + testSession.sessionListener.pausedSemaphore, + testSession.waitForBackgroundTimeout.toMillis() + ), "Pause should be triggered in ${testSession.waitForBackgroundTimeout}" + ) val suspendContext = debugSession.suspendContext as PowerShellSuspendContext - TestCase.assertEquals( + Assertions.assertEquals( psiSecondFile.virtualFile.toNioPath(), suspendContext.activeExecutionStack.topFrame?.sourcePosition?.file?.toNioPath() ) - TestCase.assertEquals(line, suspendContext.activeExecutionStack.topFrame?.sourcePosition?.line) + Assertions.assertEquals(line, suspendContext.activeExecutionStack.topFrame?.sourcePosition?.line) } } } + @Test fun testConditionalBreakpoint() { runInEdt { @@ -78,21 +88,23 @@ class BreakpointTest : DebuggerTestBase() { XDebuggerTestUtil.setBreakpointCondition(project, line, condition) Lifetime.using { lt -> val debugSession = testSession.startDebugSession(lt) - assertTrue("Pause should be triggered in ${testSession.waitForBackgroundTimeout}", XDebuggerTestUtil.waitFor( - testSession.sessionListener.pausedSemaphore, - testSession.waitForBackgroundTimeout.toMillis() - )) + Assertions.assertTrue( + XDebuggerTestUtil.waitFor( + testSession.sessionListener.pausedSemaphore, + testSession.waitForBackgroundTimeout.toMillis() + ), "Pause should be triggered in ${testSession.waitForBackgroundTimeout}" + ) val suspendContext = debugSession.suspendContext as PowerShellSuspendContext val topFrame = suspendContext.activeExecutionStack.topFrame!! val children = XTestCompositeNode(topFrame).collectChildren() val variableValue = XDebuggerTestUtil.findVar(children, variableName) val variableValueNode = XDebuggerTestUtil.computePresentation(variableValue) - TestCase.assertEquals(value.toString(), variableValueNode.myValue) + Assertions.assertEquals(value.toString(), variableValueNode.myValue) } } } - override fun tearDownEdt() { + override fun tearDownInEdt() { FileEditorManagerEx.getInstanceEx(project).closeAllFiles() } } diff --git a/src/test/kotlin/com/intellij/plugin/powershell/debugger/EvaluationTest.kt b/src/test/kotlin/com/intellij/plugin/powershell/debugger/EvaluationTest.kt index 4ecd166e..e1b0d3b0 100644 --- a/src/test/kotlin/com/intellij/plugin/powershell/debugger/EvaluationTest.kt +++ b/src/test/kotlin/com/intellij/plugin/powershell/debugger/EvaluationTest.kt @@ -3,12 +3,17 @@ package com.intellij.plugin.powershell.debugger import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx import com.intellij.plugin.powershell.testFramework.DebuggerTestBase import com.intellij.plugin.powershell.testFramework.PowerShellTestSession +import com.intellij.plugin.powershell.testFramework.runInEdt +import com.intellij.testFramework.junit5.TestApplication import com.intellij.xdebugger.XDebuggerTestUtil import com.jetbrains.rd.util.lifetime.Lifetime -import junit.framework.TestCase +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test +@TestApplication class EvaluationTest: DebuggerTestBase() { + @Test fun testEvaluation() { runInEdt { val psiFile = copyAndOpenFile("debugger/testBreakpoint.ps1") @@ -23,19 +28,19 @@ class EvaluationTest: DebuggerTestBase() { XDebuggerTestUtil.toggleBreakpoint(project, file, line) Lifetime.using { lt -> val debugSession = testSession.startDebugSession(lt) - assertTrue("Pause should be triggered in ${testSession.waitForBackgroundTimeout}", XDebuggerTestUtil.waitFor( + Assertions.assertTrue(XDebuggerTestUtil.waitFor( testSession.sessionListener.pausedSemaphore, testSession.waitForBackgroundTimeout.toMillis() - )) + ), "Pause should be triggered in ${testSession.waitForBackgroundTimeout}") val variableValue = XDebuggerTestUtil.evaluate(debugSession, expression, testSession.waitForBackgroundTimeout.toMillis()).first val variableValueNode = XDebuggerTestUtil.computePresentation(variableValue) - TestCase.assertEquals(expectedResult, variableValueNode.myValue) + Assertions.assertEquals(expectedResult, variableValueNode.myValue) } } } - override fun tearDownEdt() { + override fun tearDownInEdt() { FileEditorManagerEx.getInstanceEx(project).closeAllFiles() } } diff --git a/src/test/kotlin/com/intellij/plugin/powershell/debugger/StepTest.kt b/src/test/kotlin/com/intellij/plugin/powershell/debugger/StepTest.kt index b9e1eb96..87f33498 100644 --- a/src/test/kotlin/com/intellij/plugin/powershell/debugger/StepTest.kt +++ b/src/test/kotlin/com/intellij/plugin/powershell/debugger/StepTest.kt @@ -4,12 +4,17 @@ import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx import com.intellij.plugin.powershell.ide.debugger.PowerShellSuspendContext import com.intellij.plugin.powershell.testFramework.DebuggerTestBase import com.intellij.plugin.powershell.testFramework.PowerShellTestSession +import com.intellij.plugin.powershell.testFramework.runInEdt +import com.intellij.testFramework.junit5.TestApplication import com.intellij.xdebugger.XDebuggerTestUtil import com.jetbrains.rd.util.lifetime.Lifetime -import junit.framework.TestCase +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test -class StepTest: DebuggerTestBase() { +@TestApplication +class StepTest : DebuggerTestBase() { + @Test fun testStepOver() { runInEdt { val psiFile = copyAndOpenFile("debugger/stepTest.ps1") @@ -22,18 +27,23 @@ class StepTest: DebuggerTestBase() { XDebuggerTestUtil.toggleBreakpoint(project, file, line) Lifetime.using { lt -> val debugSession = testSession.startDebugSession(lt) - assertTrue("Pause should be triggered in ${testSession.waitForBackgroundTimeout}", XDebuggerTestUtil.waitFor( - testSession.sessionListener.pausedSemaphore, - testSession.waitForBackgroundTimeout.toMillis() - )) + + Assertions.assertTrue( + XDebuggerTestUtil.waitFor( + testSession.sessionListener.pausedSemaphore, testSession.waitForBackgroundTimeout.toMillis() + ), "Pause should be triggered in ${testSession.waitForBackgroundTimeout}" + ) + val suspendContext = debugSession.suspendContext as PowerShellSuspendContext - TestCase.assertEquals(line, suspendContext.activeExecutionStack.topFrame?.sourcePosition?.line) + Assertions.assertEquals(line, suspendContext.activeExecutionStack.topFrame?.sourcePosition?.line) debugSession.stepOver(false) - assertTrue("Pause should be triggered in ${testSession.waitForBackgroundTimeout}", XDebuggerTestUtil.waitFor( - testSession.sessionListener.pausedSemaphore, - testSession.waitForBackgroundTimeout.toMillis() - )) - TestCase.assertEquals( + + Assertions.assertTrue( + XDebuggerTestUtil.waitFor( + testSession.sessionListener.pausedSemaphore, testSession.waitForBackgroundTimeout.toMillis() + ), "Pause should be triggered in ${testSession.waitForBackgroundTimeout}" + ) + Assertions.assertEquals( line + 1, (debugSession.suspendContext as PowerShellSuspendContext).activeExecutionStack.topFrame?.sourcePosition?.line ) @@ -41,6 +51,7 @@ class StepTest: DebuggerTestBase() { } } + @Test fun testStepIn() { runInEdt { val psiFile = copyAndOpenFile("debugger/stepTest.ps1") @@ -55,18 +66,26 @@ class StepTest: DebuggerTestBase() { XDebuggerTestUtil.toggleBreakpoint(project, file, line) Lifetime.using { lt -> val debugSession = testSession.startDebugSession(lt) - assertTrue("Pause should be triggered in ${testSession.waitForBackgroundTimeout}", XDebuggerTestUtil.waitFor( - testSession.sessionListener.pausedSemaphore, - testSession.waitForBackgroundTimeout.toMillis() - )) - val suspendContext = debugSession.suspendContext as PowerShellSuspendContext - TestCase.assertEquals(line, suspendContext.activeExecutionStack.topFrame?.sourcePosition?.line) + + Assertions.assertTrue( + XDebuggerTestUtil.waitFor( + testSession.sessionListener.pausedSemaphore, testSession.waitForBackgroundTimeout.toMillis() + ), "Pause should be triggered in ${testSession.waitForBackgroundTimeout}" + ) + + Assertions.assertEquals( + line, + (debugSession.suspendContext as PowerShellSuspendContext).activeExecutionStack.topFrame?.sourcePosition?.line + ) + debugSession.stepInto() - assertTrue("Pause should be triggered in ${testSession.waitForBackgroundTimeout}", XDebuggerTestUtil.waitFor( - testSession.sessionListener.pausedSemaphore, - testSession.waitForBackgroundTimeout.toMillis() - )) - TestCase.assertEquals( + + Assertions.assertTrue( + XDebuggerTestUtil.waitFor( + testSession.sessionListener.pausedSemaphore, testSession.waitForBackgroundTimeout.toMillis() + ), "Pause should be triggered in ${testSession.waitForBackgroundTimeout}" + ) + Assertions.assertEquals( stepInLine, (debugSession.suspendContext as PowerShellSuspendContext).activeExecutionStack.topFrame?.sourcePosition?.line ) @@ -74,6 +93,7 @@ class StepTest: DebuggerTestBase() { } } + @Test fun testStepOut() { runInEdt { val psiFile = copyAndOpenFile("debugger/stepTest.ps1") @@ -89,18 +109,20 @@ class StepTest: DebuggerTestBase() { XDebuggerTestUtil.toggleBreakpoint(project, file, line) Lifetime.using { lt -> val debugSession = testSession.startDebugSession(lt) - assertTrue("Pause should be triggered in ${testSession.waitForBackgroundTimeout}", XDebuggerTestUtil.waitFor( - testSession.sessionListener.pausedSemaphore, - testSession.waitForBackgroundTimeout.toMillis() - )) + Assertions.assertTrue( + XDebuggerTestUtil.waitFor( + testSession.sessionListener.pausedSemaphore, testSession.waitForBackgroundTimeout.toMillis() + ), "Pause should be triggered in ${testSession.waitForBackgroundTimeout}" + ) val suspendContext = debugSession.suspendContext as PowerShellSuspendContext - TestCase.assertEquals(line, suspendContext.activeExecutionStack.topFrame?.sourcePosition?.line) + Assertions.assertEquals(line, suspendContext.activeExecutionStack.topFrame?.sourcePosition?.line) debugSession.stepOut() - assertTrue("Pause should be triggered in ${testSession.waitForBackgroundTimeout}", XDebuggerTestUtil.waitFor( - testSession.sessionListener.pausedSemaphore, - testSession.waitForBackgroundTimeout.toMillis() - )) - TestCase.assertEquals( + Assertions.assertTrue( + XDebuggerTestUtil.waitFor( + testSession.sessionListener.pausedSemaphore, testSession.waitForBackgroundTimeout.toMillis() + ), "Pause should be triggered in ${testSession.waitForBackgroundTimeout}" + ) + Assertions.assertEquals( stepOutLine, (debugSession.suspendContext as PowerShellSuspendContext).activeExecutionStack.topFrame?.sourcePosition?.line ) @@ -108,7 +130,7 @@ class StepTest: DebuggerTestBase() { } } - override fun tearDownEdt() { + override fun tearDownInEdt() { FileEditorManagerEx.getInstanceEx(project).closeAllFiles() } } diff --git a/src/test/kotlin/com/intellij/plugin/powershell/debugger/VariableTest.kt b/src/test/kotlin/com/intellij/plugin/powershell/debugger/VariableTest.kt index 4b0edc21..817f75cb 100644 --- a/src/test/kotlin/com/intellij/plugin/powershell/debugger/VariableTest.kt +++ b/src/test/kotlin/com/intellij/plugin/powershell/debugger/VariableTest.kt @@ -7,17 +7,21 @@ import com.intellij.plugin.powershell.ide.debugger.PowerShellSuspendContext import com.intellij.plugin.powershell.lang.PowerShellLanguage import com.intellij.plugin.powershell.testFramework.DebuggerTestBase import com.intellij.plugin.powershell.testFramework.PowerShellTestSession +import com.intellij.plugin.powershell.testFramework.runInEdt +import com.intellij.testFramework.junit5.TestApplication import com.intellij.xdebugger.XDebuggerTestUtil import com.intellij.xdebugger.XTestCompositeNode import com.intellij.xdebugger.frame.XValueModifier.XModificationCallback import com.intellij.xdebugger.impl.breakpoints.XExpressionImpl import com.jetbrains.rd.util.lifetime.Lifetime -import junit.framework.TestCase -import org.junit.Assert +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test import java.util.concurrent.Semaphore +@TestApplication class VariableTest : DebuggerTestBase() { + @Test fun testPrimitiveVariable() { runInEdt { val psiFile = copyAndOpenFile("debugger/variableTest.ps1") @@ -32,20 +36,26 @@ class VariableTest : DebuggerTestBase() { XDebuggerTestUtil.toggleBreakpoint(project, file, line) Lifetime.using { lt -> val debugSession = testSession.startDebugSession(lt) - assertTrue("Pause should be triggered in ${testSession.waitForBackgroundTimeout}", XDebuggerTestUtil.waitFor( - testSession.sessionListener.pausedSemaphore, - testSession.waitForBackgroundTimeout.toMillis() - )) + + Assertions.assertTrue( + XDebuggerTestUtil.waitFor( + testSession.sessionListener.pausedSemaphore, + testSession.waitForBackgroundTimeout.toMillis() + ), "Pause should be triggered in ${testSession.waitForBackgroundTimeout}" + ) + val suspendContext = debugSession.suspendContext as PowerShellSuspendContext val topFrame = suspendContext.activeExecutionStack.topFrame!! val children = XTestCompositeNode(topFrame).collectChildren() val variableValue = XDebuggerTestUtil.findVar(children, variableName) val variableValueNode = XDebuggerTestUtil.computePresentation(variableValue) - TestCase.assertEquals(value.toString(), variableValueNode.myValue) + + Assertions.assertEquals(value.toString(), variableValueNode.myValue) } } } + @Test fun testComplexVariable() { runInEdt { val psiFile = copyAndOpenFile("debugger/variableTest.ps1") @@ -63,26 +73,31 @@ class VariableTest : DebuggerTestBase() { XDebuggerTestUtil.toggleBreakpoint(project, file, line) Lifetime.using { lt -> val debugSession = testSession.startDebugSession(lt) - assertTrue( - "Pause should be triggered in $millis ms.", - XDebuggerTestUtil.waitFor(testSession.sessionListener.pausedSemaphore, millis) + + Assertions.assertTrue( + XDebuggerTestUtil.waitFor(testSession.sessionListener.pausedSemaphore, millis), + "Pause should be triggered in $millis ms." ) + val suspendContext = debugSession.suspendContext as PowerShellSuspendContext val topFrame = suspendContext.activeExecutionStack.topFrame!! val children = XTestCompositeNode(topFrame).collectChildren(millis) val variableValue = XDebuggerTestUtil.findVar(children, variableName) val variableChildren = XTestCompositeNode(variableValue).collectChildren(millis) - TestCase.assertTrue( - "variableChildren.size (${variableChildren.size}) is not greater than zero", - variableChildren.size > 0 + + Assertions.assertTrue( + variableChildren.isNotEmpty(), + "variableChildren.size (${variableChildren.size}) is not greater than zero" ) + val nestedField = XDebuggerTestUtil.findVar(variableChildren, nestedFieldName) val nestedFieldValueNode = XDebuggerTestUtil.computePresentation(nestedField) - TestCase.assertEquals(nestedFieldValue.toString(), nestedFieldValueNode.myValue) + Assertions.assertEquals(nestedFieldValue.toString(), nestedFieldValueNode.myValue) } } } + @Test fun testSetVariable() { runInEdt { val psiFile = copyAndOpenFile("debugger/variableTest.ps1") @@ -90,8 +105,8 @@ class VariableTest : DebuggerTestBase() { val fileLine = 2 // line in file, starting from 1 val line = fileLine - 1 // breakpoint line, starting from 0 - val secondfileLine = 5 // line in file, starting from 1 - val secondLine = secondfileLine - 1 // breakpoint line, starting from 0 + val secondFileLine = 5 // line in file, starting from 1 + val secondLine = secondFileLine - 1 // breakpoint line, starting from 0 val variableName = "\$myPrimitiveVar" val value = 123 @@ -105,30 +120,34 @@ class VariableTest : DebuggerTestBase() { Lifetime.using { lt -> val debugSession = testSession.startDebugSession(lt) - assertTrue( - "Pause should be triggered in $millis ms.", - XDebuggerTestUtil.waitFor(testSession.sessionListener.pausedSemaphore, millis) + + Assertions.assertTrue( + XDebuggerTestUtil.waitFor(testSession.sessionListener.pausedSemaphore, millis), + "Pause should be triggered in $millis ms." ) - val suspendContext = debugSession.suspendContext as PowerShellSuspendContext + val suspendContext = debugSession.suspendContext as PowerShellSuspendContext val variableValue = PowerShellDebuggerTestUtil.getVariable(suspendContext, variableName) val callback = XTestModificationCallback() variableValue.modifier!!.setValue(setValueExpression, callback) callback.waitFor(millis) debugSession.resume() - assertTrue("Pause should be triggered in ${testSession.waitForBackgroundTimeout}", XDebuggerTestUtil.waitFor( - testSession.sessionListener.pausedSemaphore, - testSession.waitForBackgroundTimeout.toMillis() - )) + + Assertions.assertTrue( + XDebuggerTestUtil.waitFor( + testSession.sessionListener.pausedSemaphore, + testSession.waitForBackgroundTimeout.toMillis() + ), "Pause should be triggered in ${testSession.waitForBackgroundTimeout}" + ) val resultVariable = PowerShellDebuggerTestUtil.getVariable(debugSession.suspendContext as PowerShellSuspendContext, variableName) val variableValueNode = XDebuggerTestUtil.computePresentation(resultVariable) - TestCase.assertEquals(value.toString(), variableValueNode.myValue) + Assertions.assertEquals(value.toString(), variableValueNode.myValue) } } } - override fun tearDownEdt() { + override fun tearDownInEdt() { FileEditorManagerEx.getInstanceEx(project).closeAllFiles() } } @@ -148,7 +167,7 @@ class XTestModificationCallback : XModificationCallback { } fun waitFor(timeoutInMilliseconds: Long): Pair { - Assert.assertTrue("timed out", XDebuggerTestUtil.waitFor(myFinished, timeoutInMilliseconds)) + Assertions.assertTrue(XDebuggerTestUtil.waitFor(myFinished, timeoutInMilliseconds), "timed out") return Pair.create(true, myErrorMessage) } } diff --git a/src/test/kotlin/com/intellij/plugin/powershell/ide/run/PowerShellRunConfigurationTests.kt b/src/test/kotlin/com/intellij/plugin/powershell/ide/run/PowerShellRunConfigurationTests.kt index a91f7a80..91424abc 100644 --- a/src/test/kotlin/com/intellij/plugin/powershell/ide/run/PowerShellRunConfigurationTests.kt +++ b/src/test/kotlin/com/intellij/plugin/powershell/ide/run/PowerShellRunConfigurationTests.kt @@ -5,9 +5,8 @@ import com.intellij.execution.impl.RunManagerImpl import com.intellij.execution.impl.RunnerAndConfigurationSettingsImpl import com.intellij.execution.runners.ExecutionEnvironment import com.intellij.execution.runners.ProgramRunner -import com.intellij.openapi.project.Project +import com.intellij.plugin.powershell.testFramework.PowerShellTestBase import com.intellij.testFramework.junit5.TestApplication -import com.intellij.testFramework.junit5.fixture.projectFixture import kotlinx.coroutines.runBlocking import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test @@ -15,14 +14,8 @@ import java.nio.file.Path import kotlin.io.path.Path @TestApplication -class PowerShellRunConfigurationTests { +class PowerShellRunConfigurationTests: PowerShellTestBase() { - private val projectFixture = projectFixture() - - private val project: Project - get() = projectFixture.get() - private val projectPath: Path - get() = Path(project.basePath!!) private val defaultWorkingDirectory get() = projectPath.resolve("scripts") diff --git a/src/test/kotlin/com/intellij/plugin/powershell/testFramework/DebuggerTestBase.kt b/src/test/kotlin/com/intellij/plugin/powershell/testFramework/DebuggerTestBase.kt index b851efd2..135ee8fd 100644 --- a/src/test/kotlin/com/intellij/plugin/powershell/testFramework/DebuggerTestBase.kt +++ b/src/test/kotlin/com/intellij/plugin/powershell/testFramework/DebuggerTestBase.kt @@ -1,31 +1,19 @@ package com.intellij.plugin.powershell.testFramework -import com.intellij.openapi.application.EDT +import com.intellij.openapi.command.WriteCommandAction import com.intellij.openapi.vfs.VfsUtil +import com.intellij.openapi.vfs.VirtualFile import com.intellij.psi.PsiFile import com.intellij.psi.PsiManager -import com.intellij.testFramework.HeavyPlatformTestCase -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.withContext +import com.intellij.util.ThrowableRunnable +import java.io.IOException import kotlin.io.path.Path import kotlin.io.path.createDirectories -abstract class DebuggerTestBase : HeavyPlatformTestCase() { +abstract class DebuggerTestBase: PowerShellTestBase() { private val testData = "testData" - override fun runInDispatchThread() = false - - fun runInEdt(test: suspend () -> Unit) { - // Allowed here because of runInDispatchThread = false - runBlocking { - withContext(Dispatchers.EDT) { - test() - } - } - } - protected fun copyAndOpenFile(nameRelativeToTestData: String): PsiFile { val fullName = "$testData/$nameRelativeToTestData" val original = DebuggerTestBase::class.java.classLoader.getResource(fullName) @@ -37,17 +25,15 @@ abstract class DebuggerTestBase : HeavyPlatformTestCase() { val baseDir = VfsUtil.findFile(basePath, /* refreshIfNeeded = */ true) ?: error("Cannot find the base directory for project $project.") - val result = copy(originalVirtual, baseDir, originalVirtual.name) - return PsiManager.getInstance(project).findFile(result) ?: error("Cannot find the PSI file for \"$result\".") - } + var result: VirtualFile? = null - final override fun tearDown() { - runInEdt { - tearDownEdt() - super.tearDown() - } - } + WriteCommandAction.writeCommandAction(null).run(ThrowableRunnable { + result = originalVirtual.copy(originalVirtual, baseDir, originalVirtual.name) + }) + + if (result == null) error("Cannot copy the original file \"$originalVirtual\".") - protected open fun tearDownEdt() {} + return PsiManager.getInstance(project).findFile(result) ?: error("Cannot find the PSI file for \"$result\".") + } } diff --git a/src/test/kotlin/com/intellij/plugin/powershell/testFramework/PowerShellTestBase.kt b/src/test/kotlin/com/intellij/plugin/powershell/testFramework/PowerShellTestBase.kt new file mode 100644 index 00000000..1057a988 --- /dev/null +++ b/src/test/kotlin/com/intellij/plugin/powershell/testFramework/PowerShellTestBase.kt @@ -0,0 +1,37 @@ +package com.intellij.plugin.powershell.testFramework + +import com.intellij.openapi.project.Project +import com.intellij.plugin.powershell.lang.lsp.LSPInitMain +import com.intellij.testFramework.junit5.fixture.projectFixture +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import java.nio.file.Path +import kotlin.io.path.Path + +abstract class PowerShellTestBase { + protected val projectFixture = projectFixture() + + lateinit var project: Project + + val projectPath: Path + get() = Path(project.basePath!!) + + @BeforeEach + open fun setUp() { + project = projectFixture.get() + } + + @AfterEach + open fun tearDown() { + } + + @AfterEach + fun tearDownEdt() { + runInEdt { + LSPInitMain.getInstance().dispose() // TODO: Remove this after LSPInitMain is refactored + tearDownInEdt() + } + } + + protected open fun tearDownInEdt() {} +} diff --git a/src/test/kotlin/com/intellij/plugin/powershell/testFramework/PowerShellTestSession.kt b/src/test/kotlin/com/intellij/plugin/powershell/testFramework/PowerShellTestSession.kt index 0c130526..fdedbfa5 100644 --- a/src/test/kotlin/com/intellij/plugin/powershell/testFramework/PowerShellTestSession.kt +++ b/src/test/kotlin/com/intellij/plugin/powershell/testFramework/PowerShellTestSession.kt @@ -64,7 +64,6 @@ class PowerShellTestSession(val project: Project, scriptPath: Path) { } class PowerShellDebugSessionListener : XDebugSessionListener { - // if greater than 0 → paused, resumed, stopped val pausedSemaphore = Semaphore(0) private val resumedSemaphore = Semaphore(0) @@ -94,5 +93,4 @@ class PowerShellDebugSessionListener : XDebugSessionListener { return stoppedSemaphore.release() } - } diff --git a/src/test/kotlin/com/intellij/plugin/powershell/testFramework/PowerShellTestUtils.kt b/src/test/kotlin/com/intellij/plugin/powershell/testFramework/PowerShellTestUtils.kt new file mode 100644 index 00000000..d84bc1d0 --- /dev/null +++ b/src/test/kotlin/com/intellij/plugin/powershell/testFramework/PowerShellTestUtils.kt @@ -0,0 +1,14 @@ +package com.intellij.plugin.powershell.testFramework + +import com.intellij.openapi.application.EDT +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.withContext + +fun runInEdt(test: suspend () -> Unit) { + runBlocking { + withContext(Dispatchers.EDT) { + test() + } + } +} From 1068c5e64b5c97a3e846c99158a5147f1de5bf3f Mon Sep 17 00:00:00 2001 From: Phantom Date: Thu, 10 Apr 2025 20:13:48 +0400 Subject: [PATCH 04/10] (#291) Tests: fix cancellation exception being caught instead of rethrowing --- .../powershell/lang/lsp/languagehost/LanguageServerEndpoint.kt | 1 + .../powershell/lang/lsp/languagehost/PSLanguageHostUtils.kt | 1 + 2 files changed, 2 insertions(+) diff --git a/src/main/kotlin/com/intellij/plugin/powershell/lang/lsp/languagehost/LanguageServerEndpoint.kt b/src/main/kotlin/com/intellij/plugin/powershell/lang/lsp/languagehost/LanguageServerEndpoint.kt index 6980ad11..e191c661 100644 --- a/src/main/kotlin/com/intellij/plugin/powershell/lang/lsp/languagehost/LanguageServerEndpoint.kt +++ b/src/main/kotlin/com/intellij/plugin/powershell/lang/lsp/languagehost/LanguageServerEndpoint.kt @@ -233,6 +233,7 @@ class LanguageServerEndpoint( return@job result.await() } catch (e: Exception) { when (e) { + is CancellationException -> throw e is PowerShellExtensionError -> { logger.warn("PowerShell extension error: ${e.message}") showPowerShellNotConfiguredNotification() diff --git a/src/main/kotlin/com/intellij/plugin/powershell/lang/lsp/languagehost/PSLanguageHostUtils.kt b/src/main/kotlin/com/intellij/plugin/powershell/lang/lsp/languagehost/PSLanguageHostUtils.kt index 9cbb9d13..2d851347 100644 --- a/src/main/kotlin/com/intellij/plugin/powershell/lang/lsp/languagehost/PSLanguageHostUtils.kt +++ b/src/main/kotlin/com/intellij/plugin/powershell/lang/lsp/languagehost/PSLanguageHostUtils.kt @@ -81,6 +81,7 @@ private suspend fun readPowerShellVersion(exePath: String): PSVersionInfo { PSVersionInfo.parse(stdOutReader.await().trim()) } catch (e: Exception) { + if(e is CancellationException) throw e PSLanguageHostUtils.LOG.warn("Command execution failed for ${commandLine.preparedCommandLine}", e) throw PowerShellControlFlowException(e.message, e.cause) } finally { From a8f6c50aded44fc2352ec2ee9758d5cd4e28b628 Mon Sep 17 00:00:00 2001 From: Phantom Date: Sun, 13 Apr 2025 22:45:59 +0400 Subject: [PATCH 05/10] (#291) Tests: try to migrate codeinsight tests to JUnit 5 --- .../languagehost/LanguageServerEndpoint.kt | 7 ++ .../lang/PowerShellCodeInsightTest.kt | 26 ++++--- .../lang/PowerShellCommenterTest.kt | 25 +++++-- .../lang/PowerShellCompletionTests.kt | 39 ++++------- .../PowerShellCodeInsightTestBase.kt | 68 +++++++++++++++++++ 5 files changed, 124 insertions(+), 41 deletions(-) create mode 100644 src/test/kotlin/com/intellij/plugin/powershell/testFramework/PowerShellCodeInsightTestBase.kt diff --git a/src/main/kotlin/com/intellij/plugin/powershell/lang/lsp/languagehost/LanguageServerEndpoint.kt b/src/main/kotlin/com/intellij/plugin/powershell/lang/lsp/languagehost/LanguageServerEndpoint.kt index e191c661..013c6136 100644 --- a/src/main/kotlin/com/intellij/plugin/powershell/lang/lsp/languagehost/LanguageServerEndpoint.kt +++ b/src/main/kotlin/com/intellij/plugin/powershell/lang/lsp/languagehost/LanguageServerEndpoint.kt @@ -395,6 +395,13 @@ class LanguageServerEndpoint( delay(50) } } + + @TestOnly + suspend fun waitForEditorManagerCreated(filePath: Path) { + while (!connectedEditors.containsKey(filePath.toUri()) || connectedEditors[filePath.toUri()]?.job?.isCompleted == false) { + delay(50) + } + } } private val logger = logger() diff --git a/src/test/kotlin/com/intellij/plugin/powershell/lang/PowerShellCodeInsightTest.kt b/src/test/kotlin/com/intellij/plugin/powershell/lang/PowerShellCodeInsightTest.kt index 7c4efc8d..fb0005ec 100644 --- a/src/test/kotlin/com/intellij/plugin/powershell/lang/PowerShellCodeInsightTest.kt +++ b/src/test/kotlin/com/intellij/plugin/powershell/lang/PowerShellCodeInsightTest.kt @@ -2,17 +2,21 @@ package com.intellij.plugin.powershell.lang import com.intellij.openapi.command.WriteCommandAction import com.intellij.plugin.powershell.psi.PowerShellStringLiteralExpression +import com.intellij.plugin.powershell.testFramework.PowerShellCodeInsightTestBase import com.intellij.psi.util.PsiTreeUtil -import com.intellij.testFramework.fixtures.BasePlatformTestCase +import com.intellij.testFramework.junit5.TestApplication +import org.junit.jupiter.api.Test import java.io.File -class PowerShellCodeInsightTest : BasePlatformTestCase() { +@TestApplication +class PowerShellCodeInsightTest : PowerShellCodeInsightTestBase() { private val TEST_DATA_PATH = "src" + File.separatorChar + "test" + File.separatorChar + "resources" + File.separatorChar + "testData" override fun getTestDataPath() = TEST_DATA_PATH + @Test fun testDecodeExpandableString() { //quotes checkStringDecode("This is sample host \"\"double quoted\"\" string", @@ -44,6 +48,7 @@ class PowerShellCodeInsightTest : BasePlatformTestCase() { "$(dsdkkdkd) $(ddd) \" $(sss)sasa $(wqwq) \$eee $(") } + @Test fun testDecodeExpandableHereString() { //quotes decodeExpandableHereString("This is sample host \"string\" content", @@ -83,6 +88,7 @@ class PowerShellCodeInsightTest : BasePlatformTestCase() { "text1 `` \$foo `` \"`'text2") } + @Test fun testDecodeVerbatimString() { //quotes decodeVerbatimString("This is sample host \"string\" content", @@ -105,6 +111,7 @@ class PowerShellCodeInsightTest : BasePlatformTestCase() { private fun decodeVerbatimHereString(stringContent: String, expectedOutText: String) = checkStringDecode(stringContent, expectedOutText, false, true) private fun decodeExpandableHereString(stringContent: String, expectedOutText: String) = checkStringDecode(stringContent, expectedOutText, true, true) + @Test fun testDecodeVerbatimHereString() { decodeVerbatimHereString("This is sample host \"string\" content", "This is sample host \"string\" content") @@ -112,7 +119,7 @@ class PowerShellCodeInsightTest : BasePlatformTestCase() { "This is sample host 'string' content") } - + @Test fun testContentChangeExpandableString() { //quotes checkInjectedText("var foo = \"text\";", @@ -147,7 +154,7 @@ class PowerShellCodeInsightTest : BasePlatformTestCase() { "`$(ddd) `\" `$(sss)sasa `$(wqwq) `\$eee `$(") } - + @Test fun testContentChangeExpandableHereString() { //quotes updateExpandableHereString("var foo = \"text\";", @@ -163,6 +170,7 @@ class PowerShellCodeInsightTest : BasePlatformTestCase() { "text1 `\$newVar text2") } + @Test fun testContentChangeVerbatimString() { //quotes updateVerbatimString("var foo = \"text\";", @@ -183,6 +191,7 @@ class PowerShellCodeInsightTest : BasePlatformTestCase() { "test `\$foo escape") } + @Test fun testContentChangeVerbatimHereString() { //quotes updateVerbatimHereString("var foo = \"text\";", @@ -203,8 +212,9 @@ class PowerShellCodeInsightTest : BasePlatformTestCase() { "test escape \$foo chars") } + @Test fun testFolding() { - myFixture.testFolding("$testDataPath/codeinsight/folding.ps1") + codeInsightTestFixture.testFolding("${getTestDataPath()}/codeinsight/folding.ps1") } private fun updateExpandableHereString(newInjectedText: String, expectedStringContent: String) = checkInjectedText(newInjectedText, expectedStringContent, true, true) @@ -214,9 +224,9 @@ class PowerShellCodeInsightTest : BasePlatformTestCase() { //external editor -> PowerShell string private fun checkInjectedText(newInjectedText: String, expectedStringContent: String, isExpandable: Boolean = true, isHere: Boolean = false, defaultText: String = "var foo = 1") { val string = createStringExpression(isHere, isExpandable, defaultText) - WriteCommandAction.writeCommandAction(project, myFixture.file).withName("Test checkInjectedText. New Text='$newInjectedText'").run { + WriteCommandAction.writeCommandAction(project, codeInsightTestFixture.file).withName("Test checkInjectedText. New Text='$newInjectedText'").run { string.updateText(newInjectedText) - val newString = PsiTreeUtil.findChildOfType(myFixture.file, PowerShellStringLiteralExpression::class.java) ?: error("file text='${myFixture.file}'") + val newString = PsiTreeUtil.findChildOfType(codeInsightTestFixture.file, PowerShellStringLiteralExpression::class.java) ?: error("file text='${codeInsightTestFixture.file}'") assert(newString.getStringContent() == expectedStringContent) { "expected=\t'$expectedStringContent'\nactual=\t\t'${newString.getStringContent()}'" } } } @@ -245,7 +255,7 @@ class PowerShellCodeInsightTest : BasePlatformTestCase() { } private fun createStringExpression(nodeText: String): PowerShellStringLiteralExpression { - val file = myFixture.configureByText("foo.ps1", nodeText) + val file = codeInsightTestFixture.configureByText("foo.ps1", nodeText) return PsiTreeUtil.findChildOfType(file, PowerShellStringLiteralExpression::class.java) ?: error("text='$nodeText'") } diff --git a/src/test/kotlin/com/intellij/plugin/powershell/lang/PowerShellCommenterTest.kt b/src/test/kotlin/com/intellij/plugin/powershell/lang/PowerShellCommenterTest.kt index 31898831..6eae5701 100644 --- a/src/test/kotlin/com/intellij/plugin/powershell/lang/PowerShellCommenterTest.kt +++ b/src/test/kotlin/com/intellij/plugin/powershell/lang/PowerShellCommenterTest.kt @@ -1,20 +1,31 @@ package com.intellij.plugin.powershell.lang import com.intellij.openapi.actionSystem.IdeActions -import com.intellij.testFramework.fixtures.BasePlatformTestCase +import com.intellij.plugin.powershell.testFramework.PowerShellCodeInsightTestBase +import com.intellij.testFramework.junit5.TestApplication +import org.junit.jupiter.api.Test -class PowerShellCommenterTest : BasePlatformTestCase() { +@TestApplication +class PowerShellCommenterTest : PowerShellCodeInsightTestBase() { + @Test fun testCommentExtension() { - myFixture.configureByText("file.ps1", """ + codeInsightTestFixture.configureByText( + "file.ps1", """ <# #> - """.trimIndent()) - myFixture.performEditorAction(IdeActions.ACTION_EDITOR_ENTER) - myFixture.checkResult(""" + """.trimIndent() + ) + waitForEditorManagerCreated(codeInsightTestFixture.file.virtualFile.toNioPath()) + codeInsightTestFixture.performEditorAction(IdeActions.ACTION_EDITOR_ENTER) + waitForEditorManagerCreated(codeInsightTestFixture.file.virtualFile.toNioPath()) + codeInsightTestFixture.checkResult( + """ <# #> - """.trimIndent()) + """.trimIndent() + ) + waitForEditorManagerCreated(codeInsightTestFixture.file.virtualFile.toNioPath()) } } diff --git a/src/test/kotlin/com/intellij/plugin/powershell/lang/PowerShellCompletionTests.kt b/src/test/kotlin/com/intellij/plugin/powershell/lang/PowerShellCompletionTests.kt index 2d8af831..a84aa830 100644 --- a/src/test/kotlin/com/intellij/plugin/powershell/lang/PowerShellCompletionTests.kt +++ b/src/test/kotlin/com/intellij/plugin/powershell/lang/PowerShellCompletionTests.kt @@ -1,37 +1,24 @@ package com.intellij.plugin.powershell.lang import com.intellij.codeInsight.completion.CompletionType -import com.intellij.plugin.powershell.lang.lsp.LSPInitMain -import com.intellij.testFramework.fixtures.BasePlatformTestCase -import com.intellij.testFramework.fixtures.TempDirTestFixture -import com.intellij.testFramework.fixtures.impl.TempDirTestFixtureImpl -import junit.framework.TestCase -import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.withTimeout -import kotlin.time.Duration.Companion.seconds +import com.intellij.plugin.powershell.testFramework.PowerShellCodeInsightTestBase +import com.intellij.testFramework.junit5.TestApplication +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test -class PowerShellCompletionTests : BasePlatformTestCase() { +@TestApplication +class PowerShellCompletionTests : PowerShellCodeInsightTestBase() { override fun getTestDataPath() = "src/test/resources/testData" - override fun createTempDirTestFixture(): TempDirTestFixture { - return TempDirTestFixtureImpl() - } - + @Test fun testCompletion() { - val psiFile = myFixture.configureByFile("codeinsight/completion.ps1") - runBlocking { - withTimeout(20.seconds) { - LSPInitMain.getEditorLanguageServer(project).apply { - waitForInit() - waitForEditorConnect(psiFile.virtualFile.toNioPath()) - } - } - } + val psiFile = codeInsightTestFixture.configureByFile("codeinsight/completion.ps1") - myFixture.complete(CompletionType.BASIC) - val lookupElementStrings = myFixture.lookupElementStrings - TestCase.assertNotNull(lookupElementStrings) - assertContainsElements(lookupElementStrings!!, "Get-Alias") + waitForEditorConnects(psiFile.virtualFile.toNioPath()) + codeInsightTestFixture.complete(CompletionType.BASIC) + val lookupElementStrings = codeInsightTestFixture.lookupElementStrings + Assertions.assertNotNull(lookupElementStrings) + Assertions.assertTrue { lookupElementStrings!!.contains("Get-Alias") } } } diff --git a/src/test/kotlin/com/intellij/plugin/powershell/testFramework/PowerShellCodeInsightTestBase.kt b/src/test/kotlin/com/intellij/plugin/powershell/testFramework/PowerShellCodeInsightTestBase.kt new file mode 100644 index 00000000..3136029d --- /dev/null +++ b/src/test/kotlin/com/intellij/plugin/powershell/testFramework/PowerShellCodeInsightTestBase.kt @@ -0,0 +1,68 @@ +package com.intellij.plugin.powershell.testFramework + +import com.intellij.plugin.powershell.lang.lsp.LSPInitMain +import com.intellij.testFramework.fixtures.CodeInsightTestFixture +import com.intellij.testFramework.fixtures.IdeaTestFixtureFactory +import com.intellij.testFramework.fixtures.impl.TempDirTestFixtureImpl +import com.intellij.testFramework.junit5.fixture.tempPathFixture +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.withTimeout +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.TestInfo +import java.nio.file.Path +import kotlin.time.Duration.Companion.seconds + +open class PowerShellCodeInsightTestBase { + val tempPathFixture = tempPathFixture() + lateinit var tempPath: Path + lateinit var codeInsightTestFixture: CodeInsightTestFixture + + val project get() = codeInsightTestFixture.project + + @BeforeEach + fun setupFixture(testInfo: TestInfo){ + tempPath = tempPathFixture.get() + val factory = IdeaTestFixtureFactory.getFixtureFactory() + val fixtureBuilder = factory.createLightFixtureBuilder(null, testInfo.displayName) + val fixture = fixtureBuilder.getFixture() + codeInsightTestFixture = IdeaTestFixtureFactory.getFixtureFactory().createCodeInsightFixture(fixture, + TempDirTestFixtureImpl()) + codeInsightTestFixture.testDataPath = getTestDataPath() + codeInsightTestFixture.setUp() + } + + @AfterEach + fun tearDownEdt() { + runInEdt { + LSPInitMain.getInstance().dispose() // TODO: Remove this after LSPInitMain is refactored + codeInsightTestFixture.tearDown() + tearDownInEdt() + } + } + + protected open fun tearDownInEdt() {} + + open fun getTestDataPath(): String = tempPath.toString() + + fun waitForEditorConnects(path: Path) { + runBlocking { + withTimeout(20.seconds) { + LSPInitMain.getEditorLanguageServer(project).apply { + waitForInit() + waitForEditorConnect(path) + } + } + } + } + + fun waitForEditorManagerCreated(path: Path) { + runBlocking { + withTimeout(20.seconds) { + LSPInitMain.getEditorLanguageServer(project).apply { + waitForEditorManagerCreated(path) + } + } + } + } +} From 29faa88d925457f68bb3312e2e9f7e88480fb899 Mon Sep 17 00:00:00 2001 From: Phantom Date: Mon, 14 Apr 2025 19:41:35 +0400 Subject: [PATCH 06/10] (#291) Tests: fix CodeInsight tests and add EdtInterceptor --- .../plugin/powershell/lang/lsp/LSPInitMain.kt | 9 ++++++-- .../lang/PSLanguageHostUtilsTests.kt | 15 +++++++++---- .../lang/PowerShellCodeInsightTest.kt | 2 ++ .../testFramework/EdtInterceptor.kt | 21 +++++++++++++++++++ .../PowerShellCodeInsightTestBase.kt | 11 +++++++--- 5 files changed, 49 insertions(+), 9 deletions(-) create mode 100644 src/test/kotlin/com/intellij/plugin/powershell/testFramework/EdtInterceptor.kt diff --git a/src/main/kotlin/com/intellij/plugin/powershell/lang/lsp/LSPInitMain.kt b/src/main/kotlin/com/intellij/plugin/powershell/lang/lsp/LSPInitMain.kt index cadef8eb..b2fe5b33 100644 --- a/src/main/kotlin/com/intellij/plugin/powershell/lang/lsp/LSPInitMain.kt +++ b/src/main/kotlin/com/intellij/plugin/powershell/lang/lsp/LSPInitMain.kt @@ -129,7 +129,12 @@ class LSPInitMain : PersistentStateComponent, Dispos } override fun dispose() { - psEditorLanguageServer.clear() - psConsoleLanguageServer.clear() + fun ConcurrentHashMap.shutdownAndClear() { + //forEach { it.value.shutdown() } + clear() + } + + psEditorLanguageServer.shutdownAndClear() + psConsoleLanguageServer.shutdownAndClear() } } diff --git a/src/test/kotlin/com/intellij/plugin/powershell/lang/PSLanguageHostUtilsTests.kt b/src/test/kotlin/com/intellij/plugin/powershell/lang/PSLanguageHostUtilsTests.kt index 33761881..abfeb86a 100644 --- a/src/test/kotlin/com/intellij/plugin/powershell/lang/PSLanguageHostUtilsTests.kt +++ b/src/test/kotlin/com/intellij/plugin/powershell/lang/PSLanguageHostUtilsTests.kt @@ -4,16 +4,22 @@ import com.intellij.execution.configurations.PathEnvironmentVariableUtil import com.intellij.openapi.util.SystemInfo import com.intellij.plugin.powershell.isOnCiServer import com.intellij.plugin.powershell.lang.lsp.languagehost.PSLanguageHostUtils +import com.intellij.plugin.powershell.lang.lsp.languagehost.PSVersionInfo import com.intellij.plugin.powershell.lang.lsp.languagehost.PowerShellEdition -import com.intellij.testFramework.fixtures.BasePlatformTestCase +import com.intellij.testFramework.junit5.TestApplication import kotlinx.coroutines.future.await import kotlinx.coroutines.runBlocking +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test import java.nio.file.Path import kotlin.io.path.pathString import kotlin.test.assertTrue +import kotlin.test.fail -class PSLanguageHostUtilsTests : BasePlatformTestCase() { +@TestApplication +class PSLanguageHostUtilsTests { + @Test fun testPowerShell5VersionDetector() { if (!SystemInfo.isWindows) return @@ -33,6 +39,7 @@ class PSLanguageHostUtilsTests : BasePlatformTestCase() { doTest(executable, "5\\..+".toRegex(), PowerShellEdition.Desktop) } + @Test fun testPowerShellCoreVersionDetector() { val executable = findExecutable("pwsh") if (executable == null) { @@ -53,11 +60,11 @@ class PSLanguageHostUtilsTests : BasePlatformTestCase() { } private fun doTest(executable: Path, expectedVersionRegex: Regex, expectedEdition: PowerShellEdition) { - val version = runBlocking { PSLanguageHostUtils.getPowerShellVersion(executable.pathString).await() } + val version: PSVersionInfo = runBlocking { PSLanguageHostUtils.getPowerShellVersion(executable.pathString).await() } assertTrue( expectedVersionRegex.matches(version.versionString), "Version string ${version.versionString} is expected to satisfy a regular expression $expectedVersionRegex." ) - assertEquals(expectedEdition, version.edition) + Assertions.assertEquals(expectedEdition, version.edition) } } diff --git a/src/test/kotlin/com/intellij/plugin/powershell/lang/PowerShellCodeInsightTest.kt b/src/test/kotlin/com/intellij/plugin/powershell/lang/PowerShellCodeInsightTest.kt index fb0005ec..1874ec5d 100644 --- a/src/test/kotlin/com/intellij/plugin/powershell/lang/PowerShellCodeInsightTest.kt +++ b/src/test/kotlin/com/intellij/plugin/powershell/lang/PowerShellCodeInsightTest.kt @@ -3,12 +3,14 @@ package com.intellij.plugin.powershell.lang import com.intellij.openapi.command.WriteCommandAction import com.intellij.plugin.powershell.psi.PowerShellStringLiteralExpression import com.intellij.plugin.powershell.testFramework.PowerShellCodeInsightTestBase +import com.intellij.plugin.powershell.testFramework.RunInEdt import com.intellij.psi.util.PsiTreeUtil import com.intellij.testFramework.junit5.TestApplication import org.junit.jupiter.api.Test import java.io.File @TestApplication +@RunInEdt class PowerShellCodeInsightTest : PowerShellCodeInsightTestBase() { private val TEST_DATA_PATH = "src" + File.separatorChar + "test" + File.separatorChar + "resources" + File.separatorChar + diff --git a/src/test/kotlin/com/intellij/plugin/powershell/testFramework/EdtInterceptor.kt b/src/test/kotlin/com/intellij/plugin/powershell/testFramework/EdtInterceptor.kt new file mode 100644 index 00000000..cd0d47ca --- /dev/null +++ b/src/test/kotlin/com/intellij/plugin/powershell/testFramework/EdtInterceptor.kt @@ -0,0 +1,21 @@ +package com.intellij.plugin.powershell.testFramework + +import org.junit.jupiter.api.extension.ExtensionContext +import org.junit.jupiter.api.extension.InvocationInterceptor +import org.junit.jupiter.api.extension.ReflectiveInvocationContext +import java.lang.reflect.Method + +class EdtInterceptor: InvocationInterceptor { + override fun interceptTestMethod( + invocation: InvocationInterceptor.Invocation, + invocationContext: ReflectiveInvocationContext, + extensionContext: ExtensionContext + ) { + if(invocationContext.executable?.getAnnotation(RunInEdt::class.java) != null || invocationContext.targetClass?.getAnnotation(RunInEdt::class.java) != null) + runInEdt { invocation.proceed() } + else invocation.proceed() + } +} + +@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS) +annotation class RunInEdt diff --git a/src/test/kotlin/com/intellij/plugin/powershell/testFramework/PowerShellCodeInsightTestBase.kt b/src/test/kotlin/com/intellij/plugin/powershell/testFramework/PowerShellCodeInsightTestBase.kt index 3136029d..d94a61d7 100644 --- a/src/test/kotlin/com/intellij/plugin/powershell/testFramework/PowerShellCodeInsightTestBase.kt +++ b/src/test/kotlin/com/intellij/plugin/powershell/testFramework/PowerShellCodeInsightTestBase.kt @@ -1,7 +1,9 @@ package com.intellij.plugin.powershell.testFramework import com.intellij.plugin.powershell.lang.lsp.LSPInitMain +import com.intellij.testFramework.LightPlatformTestCase import com.intellij.testFramework.fixtures.CodeInsightTestFixture +import com.intellij.testFramework.fixtures.IdeaProjectTestFixture import com.intellij.testFramework.fixtures.IdeaTestFixtureFactory import com.intellij.testFramework.fixtures.impl.TempDirTestFixtureImpl import com.intellij.testFramework.junit5.fixture.tempPathFixture @@ -10,14 +12,16 @@ import kotlinx.coroutines.withTimeout import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.TestInfo +import org.junit.jupiter.api.extension.ExtendWith import java.nio.file.Path import kotlin.time.Duration.Companion.seconds +@ExtendWith(EdtInterceptor::class) open class PowerShellCodeInsightTestBase { val tempPathFixture = tempPathFixture() lateinit var tempPath: Path lateinit var codeInsightTestFixture: CodeInsightTestFixture - + lateinit var ideaProjectTestFixture: IdeaProjectTestFixture val project get() = codeInsightTestFixture.project @BeforeEach @@ -25,8 +29,8 @@ open class PowerShellCodeInsightTestBase { tempPath = tempPathFixture.get() val factory = IdeaTestFixtureFactory.getFixtureFactory() val fixtureBuilder = factory.createLightFixtureBuilder(null, testInfo.displayName) - val fixture = fixtureBuilder.getFixture() - codeInsightTestFixture = IdeaTestFixtureFactory.getFixtureFactory().createCodeInsightFixture(fixture, + ideaProjectTestFixture = fixtureBuilder.getFixture() + codeInsightTestFixture = IdeaTestFixtureFactory.getFixtureFactory().createCodeInsightFixture(ideaProjectTestFixture, TempDirTestFixtureImpl()) codeInsightTestFixture.testDataPath = getTestDataPath() codeInsightTestFixture.setUp() @@ -37,6 +41,7 @@ open class PowerShellCodeInsightTestBase { runInEdt { LSPInitMain.getInstance().dispose() // TODO: Remove this after LSPInitMain is refactored codeInsightTestFixture.tearDown() + LightPlatformTestCase.closeAndDeleteProject() tearDownInEdt() } } From 1cc7ac3bc019f479130e8c9fb9c8747f64c46cb8 Mon Sep 17 00:00:00 2001 From: Friedrich von Never Date: Wed, 16 Apr 2025 22:35:18 +0200 Subject: [PATCH 07/10] (#291) DebuggerTestBase: minor cleanup --- .../powershell/testFramework/DebuggerTestBase.kt | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/test/kotlin/com/intellij/plugin/powershell/testFramework/DebuggerTestBase.kt b/src/test/kotlin/com/intellij/plugin/powershell/testFramework/DebuggerTestBase.kt index 135ee8fd..ba988f49 100644 --- a/src/test/kotlin/com/intellij/plugin/powershell/testFramework/DebuggerTestBase.kt +++ b/src/test/kotlin/com/intellij/plugin/powershell/testFramework/DebuggerTestBase.kt @@ -1,12 +1,10 @@ package com.intellij.plugin.powershell.testFramework import com.intellij.openapi.command.WriteCommandAction +import com.intellij.openapi.util.ThrowableComputable import com.intellij.openapi.vfs.VfsUtil -import com.intellij.openapi.vfs.VirtualFile import com.intellij.psi.PsiFile import com.intellij.psi.PsiManager -import com.intellij.util.ThrowableRunnable -import java.io.IOException import kotlin.io.path.Path import kotlin.io.path.createDirectories @@ -26,14 +24,10 @@ abstract class DebuggerTestBase: PowerShellTestBase() { val baseDir = VfsUtil.findFile(basePath, /* refreshIfNeeded = */ true) ?: error("Cannot find the base directory for project $project.") - var result: VirtualFile? = null - - WriteCommandAction.writeCommandAction(null).run(ThrowableRunnable { - result = originalVirtual.copy(originalVirtual, baseDir, originalVirtual.name) + val result = WriteCommandAction.writeCommandAction(/* project = */ null).compute(ThrowableComputable { + originalVirtual.copy(originalVirtual, baseDir, originalVirtual.name) }) - if (result == null) error("Cannot copy the original file \"$originalVirtual\".") - return PsiManager.getInstance(project).findFile(result) ?: error("Cannot find the PSI file for \"$result\".") } } From cf35ac414d0f88f27d0207ecc34c8601baa2e056 Mon Sep 17 00:00:00 2001 From: Friedrich von Never Date: Wed, 16 Apr 2025 23:26:09 +0200 Subject: [PATCH 08/10] (#291) LSPMain: refactor into PowerShellSettings (app service) and LanguageServer (project service) --- .../lang/lsp/ide/settings/FormUIUtil.kt | 4 +- .../ide/settings/PowerShellConfigurable.java | 10 +- .../PowerShellExecutableChooserPanel.java | 4 +- .../settings/PowerShellJPanelComponent.java | 4 +- .../ide/actions/PowerShellConsoleAction.kt | 6 +- .../run/PowerShellScriptCommandLineState.kt | 4 +- .../plugin/powershell/lang/lsp/LSPInitMain.kt | 140 ------------------ .../powershell/lang/lsp/LanguageServer.kt | 78 ++++++++++ .../powershell/lang/lsp/PowerShellSettings.kt | 42 ++++++ .../lsp/ide/listeners/EditorLSPListener.kt | 8 +- .../EditorServicesLanguageHostStarter.kt | 10 +- src/main/resources/META-INF/plugin.xml | 2 - .../PowerShellCodeInsightTestBase.kt | 7 +- .../testFramework/PowerShellTestBase.kt | 2 - 14 files changed, 148 insertions(+), 173 deletions(-) delete mode 100644 src/main/kotlin/com/intellij/plugin/powershell/lang/lsp/LSPInitMain.kt create mode 100644 src/main/kotlin/com/intellij/plugin/powershell/lang/lsp/LanguageServer.kt create mode 100644 src/main/kotlin/com/intellij/plugin/powershell/lang/lsp/PowerShellSettings.kt diff --git a/src/main/java/com/intellij/plugin/powershell/lang/lsp/ide/settings/FormUIUtil.kt b/src/main/java/com/intellij/plugin/powershell/lang/lsp/ide/settings/FormUIUtil.kt index df77941b..a5c62fa6 100644 --- a/src/main/java/com/intellij/plugin/powershell/lang/lsp/ide/settings/FormUIUtil.kt +++ b/src/main/java/com/intellij/plugin/powershell/lang/lsp/ide/settings/FormUIUtil.kt @@ -9,7 +9,7 @@ import com.intellij.openapi.ui.TextFieldWithBrowseButton import com.intellij.openapi.util.io.FileUtil import com.intellij.openapi.util.text.StringUtil import com.intellij.plugin.powershell.ide.MessagesBundle -import com.intellij.plugin.powershell.lang.lsp.LSPInitMain +import com.intellij.plugin.powershell.lang.lsp.PowerShellSettings import com.intellij.plugin.powershell.lang.lsp.languagehost.PSLanguageHostUtils.getEditorServicesModuleVersion import com.intellij.plugin.powershell.lang.lsp.languagehost.PSLanguageHostUtils.getEditorServicesStartupScript import com.intellij.plugin.powershell.lang.lsp.languagehost.PSLanguageHostUtils.getPSExtensionModulesDir @@ -76,5 +76,5 @@ object FormUIUtil { } val globalSettingsExecutablePath: String? - get() = LSPInitMain.getInstance().state.powerShellExePath + get() = PowerShellSettings.getInstance().state.powerShellExePath } diff --git a/src/main/java/com/intellij/plugin/powershell/lang/lsp/ide/settings/PowerShellConfigurable.java b/src/main/java/com/intellij/plugin/powershell/lang/lsp/ide/settings/PowerShellConfigurable.java index 28805262..63212e58 100644 --- a/src/main/java/com/intellij/plugin/powershell/lang/lsp/ide/settings/PowerShellConfigurable.java +++ b/src/main/java/com/intellij/plugin/powershell/lang/lsp/ide/settings/PowerShellConfigurable.java @@ -4,7 +4,7 @@ import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.options.SearchableConfigurable; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.plugin.powershell.lang.lsp.LSPInitMain; +import com.intellij.plugin.powershell.lang.lsp.PowerShellSettings; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -49,8 +49,8 @@ public boolean isModified() { @Override public void reset() { - LSPInitMain lspInitMain = LSPInitMain.getInstance(); - LSPInitMain.PowerShellInfo powerShellInfo = lspInitMain.getState(); + PowerShellSettings powerShellSettings = PowerShellSettings.getInstance(); + PowerShellSettings.PowerShellInfo powerShellInfo = powerShellSettings.getState(); String psEsPathFromSettings = powerShellInfo.getPowerShellExtensionPath(); String exePathFromSettings = powerShellInfo.getPowerShellExePath(); boolean isEnabledInSettings = powerShellInfo.isUseLanguageServer(); @@ -65,8 +65,8 @@ public void apply() throws ConfigurationException { String psExtensionPath = getPSExtensionPathFromForm(); String powerShellExePath = getPowerShellExePathFromForm(); boolean isEnabled = getPSJpanel().getIsUseLanguageServer(); - LSPInitMain lspInitMain = LSPInitMain.getInstance(); - LSPInitMain.PowerShellInfo powerShellInfo = lspInitMain.getState(); + PowerShellSettings powerShellSettings = PowerShellSettings.getInstance(); + PowerShellSettings.PowerShellInfo powerShellInfo = powerShellSettings.getState(); FormUIUtil.validatePowerShellExecutablePath(powerShellExePath); String powerShellVersion = getPSJpanel().getPowerShellVersionValue(); if (StringUtil.isEmpty(powerShellVersion)) throw new ConfigurationException("Can not detect PowerShell version"); diff --git a/src/main/java/com/intellij/plugin/powershell/lang/lsp/ide/settings/PowerShellExecutableChooserPanel.java b/src/main/java/com/intellij/plugin/powershell/lang/lsp/ide/settings/PowerShellExecutableChooserPanel.java index 6d4532cc..b505dbc1 100644 --- a/src/main/java/com/intellij/plugin/powershell/lang/lsp/ide/settings/PowerShellExecutableChooserPanel.java +++ b/src/main/java/com/intellij/plugin/powershell/lang/lsp/ide/settings/PowerShellExecutableChooserPanel.java @@ -5,7 +5,7 @@ import com.intellij.openapi.ui.TextFieldWithBrowseButton; import com.intellij.openapi.util.text.StringUtil; import com.intellij.plugin.powershell.ide.MessagesBundle; -import com.intellij.plugin.powershell.lang.lsp.LSPInitMain; +import com.intellij.plugin.powershell.lang.lsp.PowerShellSettings; import com.intellij.plugin.powershell.lang.lsp.languagehost.PSLanguageHostUtils; import com.intellij.ui.components.JBTextField; import org.jetbrains.annotations.NotNull; @@ -23,7 +23,7 @@ public class PowerShellExecutableChooserPanel extends JComponent { private JPanel myJpanel; public PowerShellExecutableChooserPanel(@Nullable String executablePath) { - String globalSettingsPath = LSPInitMain.getInstance().getState().getPowerShellExePath(); + String globalSettingsPath = PowerShellSettings.getInstance().getState().getPowerShellExePath(); updateExecutablePath(StringUtil.isEmpty(executablePath) ? globalSettingsPath : executablePath); } diff --git a/src/main/java/com/intellij/plugin/powershell/lang/lsp/ide/settings/PowerShellJPanelComponent.java b/src/main/java/com/intellij/plugin/powershell/lang/lsp/ide/settings/PowerShellJPanelComponent.java index 29cd5f68..72db6a84 100644 --- a/src/main/java/com/intellij/plugin/powershell/lang/lsp/ide/settings/PowerShellJPanelComponent.java +++ b/src/main/java/com/intellij/plugin/powershell/lang/lsp/ide/settings/PowerShellJPanelComponent.java @@ -8,7 +8,7 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.plugin.powershell.ide.MessagesBundle; -import com.intellij.plugin.powershell.lang.lsp.LSPInitMain; +import com.intellij.plugin.powershell.lang.lsp.PowerShellSettings; import com.intellij.plugin.powershell.lang.lsp.languagehost.PSLanguageHostUtils; import com.intellij.plugin.powershell.lang.lsp.languagehost.PowerShellNotInstalled; import com.intellij.ui.HyperlinkAdapter; @@ -177,7 +177,7 @@ private void setBundledPowerShellExtensionPath() throws ConfigurationException { Companion.setUseBundledPowerShellExtension(true); } - void fillPowerShellInfo(@NotNull LSPInitMain.PowerShellInfo powerShellInfo) { + void fillPowerShellInfo(@NotNull PowerShellSettings.PowerShellInfo powerShellInfo) { setEditorServicesVersionLabelValue(powerShellInfo.getEditorServicesModuleVersion()); setPowerShellExtensionPath(powerShellInfo.getPowerShellExtensionPath()); setPowerShellExePath(powerShellInfo.getPowerShellExePath()); diff --git a/src/main/kotlin/com/intellij/plugin/powershell/ide/actions/PowerShellConsoleAction.kt b/src/main/kotlin/com/intellij/plugin/powershell/ide/actions/PowerShellConsoleAction.kt index 3f599f15..dc7fd17f 100644 --- a/src/main/kotlin/com/intellij/plugin/powershell/ide/actions/PowerShellConsoleAction.kt +++ b/src/main/kotlin/com/intellij/plugin/powershell/ide/actions/PowerShellConsoleAction.kt @@ -6,13 +6,13 @@ import com.intellij.openapi.progress.ProgressIndicator import com.intellij.openapi.progress.ProgressManager import com.intellij.openapi.progress.Task import com.intellij.plugin.powershell.PowerShellIcons -import com.intellij.plugin.powershell.lang.lsp.LSPInitMain +import com.intellij.plugin.powershell.lang.lsp.LanguageServer class PowerShellConsoleAction : AnAction(PowerShellIcons.FILE) { override fun actionPerformed(e: AnActionEvent) { val project = e.project ?: return - val server = LSPInitMain.getServerWithConsoleProcess(project) + val server = LanguageServer.getInstance(project).serverWithConsoleProcess.value ProgressManager.getInstance().run(object : Task.Backgroundable(project, "Starting PowerShell terminal console", false) { override fun run(indicator: ProgressIndicator) { indicator.text = "Starting PowerShell terminal console..." @@ -20,4 +20,4 @@ class PowerShellConsoleAction : AnAction(PowerShellIcons.FILE) { } }) } -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/intellij/plugin/powershell/ide/run/PowerShellScriptCommandLineState.kt b/src/main/kotlin/com/intellij/plugin/powershell/ide/run/PowerShellScriptCommandLineState.kt index 828a671d..306a9ea2 100644 --- a/src/main/kotlin/com/intellij/plugin/powershell/ide/run/PowerShellScriptCommandLineState.kt +++ b/src/main/kotlin/com/intellij/plugin/powershell/ide/run/PowerShellScriptCommandLineState.kt @@ -19,7 +19,7 @@ import com.intellij.openapi.util.io.NioFiles.toPath import com.intellij.openapi.util.text.StringUtil import com.intellij.openapi.vfs.LocalFileSystem import com.intellij.plugin.powershell.ide.runAndLogException -import com.intellij.plugin.powershell.lang.lsp.LSPInitMain +import com.intellij.plugin.powershell.lang.lsp.PowerShellSettings import com.intellij.plugin.powershell.lang.lsp.languagehost.PowerShellNotInstalled import com.intellij.terminal.TerminalExecutionConsole import com.intellij.util.text.nullize @@ -54,7 +54,7 @@ class PowerShellScriptCommandLineState( private fun startProcess(): ProcessHandler { try { val command = buildCommand( - runConfiguration.executablePath ?: LSPInitMain.getInstance().getPowerShellExecutable(), + runConfiguration.executablePath ?: PowerShellSettings.getInstance().getPowerShellExecutable(), runConfiguration.scriptPath, runConfiguration.getCommandOptions(), runConfiguration.scriptParameters diff --git a/src/main/kotlin/com/intellij/plugin/powershell/lang/lsp/LSPInitMain.kt b/src/main/kotlin/com/intellij/plugin/powershell/lang/lsp/LSPInitMain.kt deleted file mode 100644 index b2fe5b33..00000000 --- a/src/main/kotlin/com/intellij/plugin/powershell/lang/lsp/LSPInitMain.kt +++ /dev/null @@ -1,140 +0,0 @@ -/** - * adopted from https://github.com/gtache/intellij-lsp - */ -package com.intellij.plugin.powershell.lang.lsp - -import com.intellij.openapi.Disposable -import com.intellij.openapi.application.ApplicationManager -import com.intellij.openapi.components.PersistentStateComponent -import com.intellij.openapi.components.RoamingType -import com.intellij.openapi.components.State -import com.intellij.openapi.components.Storage -import com.intellij.openapi.diagnostic.Logger -import com.intellij.openapi.editor.Editor -import com.intellij.openapi.fileEditor.FileDocumentManager -import com.intellij.openapi.project.Project -import com.intellij.openapi.project.ProjectManager -import com.intellij.openapi.project.ProjectManagerListener -import com.intellij.openapi.util.io.toNioPathOrNull -import com.intellij.openapi.vfs.VirtualFile -import com.intellij.plugin.powershell.PowerShellFileType -import com.intellij.plugin.powershell.ide.PluginProjectRoot -import com.intellij.plugin.powershell.ide.run.findPsExecutable -import com.intellij.plugin.powershell.lang.lsp.languagehost.EditorServicesLanguageHostStarter -import com.intellij.plugin.powershell.lang.lsp.languagehost.LanguageServerEndpoint -import com.intellij.plugin.powershell.lang.lsp.languagehost.terminal.PowerShellConsoleTerminalRunner -import com.intellij.plugin.powershell.lang.lsp.util.isRemotePath -import java.util.concurrent.ConcurrentHashMap - -@State(name = "PowerShellSettings", storages = [Storage(value = "powerShellSettings.xml", roamingType = RoamingType.DISABLED)]) -class LSPInitMain : PersistentStateComponent, Disposable { - - private val psEditorLanguageServer = ConcurrentHashMap() - private val psConsoleLanguageServer = ConcurrentHashMap() - - override fun initializeComponent() { - ApplicationManager.getApplication().messageBus.connect(this) - .subscribe(ProjectManager.TOPIC, object : ProjectManagerListener { - override fun projectClosed(project: Project) { - psEditorLanguageServer.remove(project) - psConsoleLanguageServer.remove(project) - } - }) - - LOG.debug("PluginMain init finished") - } - - data class PowerShellInfo( - var editorServicesStartupScript: String = "", - var powerShellExePath: String? = null, - var powerShellVersion: String? = null, - var powerShellExtensionPath: String? = null, - var editorServicesModuleVersion: String? = null, - var isUseLanguageServer: Boolean = true - ) - - fun getPowerShellExecutable(): String { - val psExecutable = myPowerShellInfo.powerShellExePath ?: findPsExecutable() - myPowerShellInfo.powerShellExePath = psExecutable - return psExecutable - } - - private var myPowerShellInfo: PowerShellInfo = PowerShellInfo() - - override fun loadState(powerShellInfo: PowerShellInfo) { - myPowerShellInfo = powerShellInfo - } - - override fun getState(): PowerShellInfo { - return myPowerShellInfo - } - - companion object { - private val LOG: Logger = Logger.getInstance(LSPInitMain::class.java) - - @JvmStatic - fun getInstance(): LSPInitMain { - return ApplicationManager.getApplication().getService(LSPInitMain::class.java) - } - - fun getServerWithConsoleProcess(project: Project): LanguageServerEndpoint { - return getInstance().psConsoleLanguageServer.computeIfAbsent(project) { prj -> - val scope = PluginProjectRoot.getInstance(project).coroutineScope - LanguageServerEndpoint(scope, PowerShellConsoleTerminalRunner(prj), prj) - } - } - - fun getEditorLanguageServer(project: Project): LanguageServerEndpoint { - return getInstance().psEditorLanguageServer.computeIfAbsent(project) { prj -> - val scope = PluginProjectRoot.getInstance(project).coroutineScope - LanguageServerEndpoint(scope, EditorServicesLanguageHostStarter(prj), prj) - } - } - - fun editorOpened(editor: Editor) { - val lspMain = getInstance() - if (!lspMain.myPowerShellInfo.isUseLanguageServer) return - - val project = editor.project ?: return - val file = FileDocumentManager.getInstance().getFile(editor.document) - if (file == null || file.fileType !is PowerShellFileType && !isRemotePath(file.path)) return - ApplicationManager.getApplication().executeOnPooledThread { - val server = getServer(file, project) - server.connectEditor(editor) - LOG.info("Registered ${file.path} script for server: $server") - } - } - - private fun getServer(file: VirtualFile, project: Project): LanguageServerEndpoint { - return findServerForRemoteFile(file, project) ?: getEditorLanguageServer(project) - } - - fun editorClosed(editor: Editor) { - val project = editor.project ?: return - val vfile = FileDocumentManager.getInstance().getFile(editor.document) ?: return - if (vfile.fileType !is PowerShellFileType) return - val server = findServer(vfile, project) ?: return - server.disconnectEditor(vfile.path.toNioPathOrNull()?.toUri() ?: return) - LOG.debug("Removed ${vfile.name} script from server: $server") - } - - private fun findServer(file: VirtualFile, project: Project): LanguageServerEndpoint? { - return findServerForRemoteFile(file, project) ?: getInstance().psEditorLanguageServer[project] - } - - private fun findServerForRemoteFile(file: VirtualFile, project: Project): LanguageServerEndpoint? { - val consoleServer = getInstance().psConsoleLanguageServer[project] ?: return null - return if (consoleServer.isRunning && isRemotePath(file.canonicalPath)) consoleServer else null - } - } - - override fun dispose() { - fun ConcurrentHashMap.shutdownAndClear() { - //forEach { it.value.shutdown() } - clear() - } - - psEditorLanguageServer.shutdownAndClear() - psConsoleLanguageServer.shutdownAndClear() - } -} diff --git a/src/main/kotlin/com/intellij/plugin/powershell/lang/lsp/LanguageServer.kt b/src/main/kotlin/com/intellij/plugin/powershell/lang/lsp/LanguageServer.kt new file mode 100644 index 00000000..210371f9 --- /dev/null +++ b/src/main/kotlin/com/intellij/plugin/powershell/lang/lsp/LanguageServer.kt @@ -0,0 +1,78 @@ +/** + * adopted from https://github.com/gtache/intellij-lsp + */ +package com.intellij.plugin.powershell.lang.lsp + +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.components.Service +import com.intellij.openapi.components.service +import com.intellij.openapi.diagnostic.Logger +import com.intellij.openapi.diagnostic.logger +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.fileEditor.FileDocumentManager +import com.intellij.openapi.project.Project +import com.intellij.openapi.util.io.toNioPathOrNull +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.plugin.powershell.PowerShellFileType +import com.intellij.plugin.powershell.ide.PluginProjectRoot +import com.intellij.plugin.powershell.lang.lsp.languagehost.EditorServicesLanguageHostStarter +import com.intellij.plugin.powershell.lang.lsp.languagehost.LanguageServerEndpoint +import com.intellij.plugin.powershell.lang.lsp.languagehost.terminal.PowerShellConsoleTerminalRunner +import com.intellij.plugin.powershell.lang.lsp.util.isRemotePath + +@Service(Service.Level.PROJECT) +class LanguageServer(private val project: Project) { + companion object { + private val LOG: Logger = logger() + + fun getInstance(project: Project): LanguageServer { + return project.service() + } + + fun editorOpened(editor: Editor) { + if (!PowerShellSettings.getInstance().state.isUseLanguageServer) return + + val project = editor.project ?: return + val file = FileDocumentManager.getInstance().getFile(editor.document) + if (file == null || file.fileType !is PowerShellFileType && !isRemotePath(file.path)) return + ApplicationManager.getApplication().executeOnPooledThread { + val server = getServer(file, project) + server.connectEditor(editor) + LOG.info("Registered ${file.path} script for server: $server") + } + } + + private fun getServer(file: VirtualFile, project: Project): LanguageServerEndpoint { + return findServerForRemoteFile(file, project) ?: getInstance(project).editorLanguageServer.value + } + + fun editorClosed(editor: Editor) { + val project = editor.project ?: return + val vfile = FileDocumentManager.getInstance().getFile(editor.document) ?: return + if (vfile.fileType !is PowerShellFileType) return + val server = findServer(vfile, project) ?: return + server.disconnectEditor(vfile.path.toNioPathOrNull()?.toUri() ?: return) + LOG.debug("Removed ${vfile.name} script from server: $server") + } + + private fun findServer(file: VirtualFile, project: Project): LanguageServerEndpoint? { + return findServerForRemoteFile(file, project) + ?: getInstance(project).editorLanguageServer.takeIf { it.isInitialized() }?.value + } + + private fun findServerForRemoteFile(file: VirtualFile, project: Project): LanguageServerEndpoint? { + val consoleServer = getInstance(project).serverWithConsoleProcess.takeIf { it.isInitialized() }?.value ?: return null + return if (consoleServer.isRunning && isRemotePath(file.canonicalPath)) consoleServer else null + } + } + + val serverWithConsoleProcess: Lazy = lazy { + val scope = PluginProjectRoot.getInstance(project).coroutineScope + LanguageServerEndpoint(scope, PowerShellConsoleTerminalRunner(project), project) + } + + val editorLanguageServer: Lazy = lazy { + val scope = PluginProjectRoot.getInstance(project).coroutineScope + LanguageServerEndpoint(scope, EditorServicesLanguageHostStarter(project), project) + } +} diff --git a/src/main/kotlin/com/intellij/plugin/powershell/lang/lsp/PowerShellSettings.kt b/src/main/kotlin/com/intellij/plugin/powershell/lang/lsp/PowerShellSettings.kt new file mode 100644 index 00000000..fc3abb82 --- /dev/null +++ b/src/main/kotlin/com/intellij/plugin/powershell/lang/lsp/PowerShellSettings.kt @@ -0,0 +1,42 @@ +/** + * adopted from https://github.com/gtache/intellij-lsp + */ +package com.intellij.plugin.powershell.lang.lsp + +import com.intellij.openapi.components.* +import com.intellij.plugin.powershell.ide.run.findPsExecutable + +@Service +@State(name = "PowerShellSettings", storages = [Storage(value = "powerShellSettings.xml", roamingType = RoamingType.DISABLED)]) +class PowerShellSettings : PersistentStateComponent { + + companion object { + @JvmStatic + fun getInstance(): PowerShellSettings = service() + } + + data class PowerShellInfo( + var editorServicesStartupScript: String = "", + var powerShellExePath: String? = null, + var powerShellVersion: String? = null, + var powerShellExtensionPath: String? = null, + var editorServicesModuleVersion: String? = null, + var isUseLanguageServer: Boolean = true + ) + + fun getPowerShellExecutable(): String { + val psExecutable = myPowerShellInfo.powerShellExePath ?: findPsExecutable() + myPowerShellInfo.powerShellExePath = psExecutable + return psExecutable + } + + private var myPowerShellInfo: PowerShellInfo = PowerShellInfo() + + override fun loadState(powerShellInfo: PowerShellInfo) { + myPowerShellInfo = powerShellInfo + } + + override fun getState(): PowerShellInfo { + return myPowerShellInfo + } +} diff --git a/src/main/kotlin/com/intellij/plugin/powershell/lang/lsp/ide/listeners/EditorLSPListener.kt b/src/main/kotlin/com/intellij/plugin/powershell/lang/lsp/ide/listeners/EditorLSPListener.kt index 6178136d..ffee12b8 100644 --- a/src/main/kotlin/com/intellij/plugin/powershell/lang/lsp/ide/listeners/EditorLSPListener.kt +++ b/src/main/kotlin/com/intellij/plugin/powershell/lang/lsp/ide/listeners/EditorLSPListener.kt @@ -5,15 +5,15 @@ package com.intellij.plugin.powershell.lang.lsp.ide.listeners import com.intellij.openapi.editor.event.EditorFactoryEvent import com.intellij.openapi.editor.event.EditorFactoryListener -import com.intellij.plugin.powershell.lang.lsp.LSPInitMain +import com.intellij.plugin.powershell.lang.lsp.LanguageServer class EditorLSPListener : EditorFactoryListener { override fun editorReleased(event: EditorFactoryEvent) { - LSPInitMain.editorClosed(event.editor) + LanguageServer.editorClosed(event.editor) } override fun editorCreated(event: EditorFactoryEvent) { - LSPInitMain.editorOpened(event.editor) + LanguageServer.editorOpened(event.editor) } -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/intellij/plugin/powershell/lang/lsp/languagehost/EditorServicesLanguageHostStarter.kt b/src/main/kotlin/com/intellij/plugin/powershell/lang/lsp/languagehost/EditorServicesLanguageHostStarter.kt index 8fedb760..26f21d8c 100644 --- a/src/main/kotlin/com/intellij/plugin/powershell/lang/lsp/languagehost/EditorServicesLanguageHostStarter.kt +++ b/src/main/kotlin/com/intellij/plugin/powershell/lang/lsp/languagehost/EditorServicesLanguageHostStarter.kt @@ -21,7 +21,7 @@ import com.intellij.plugin.powershell.PowerShellIcons import com.intellij.plugin.powershell.ide.MessagesBundle import com.intellij.plugin.powershell.ide.run.checkExists import com.intellij.plugin.powershell.ide.run.escapePath -import com.intellij.plugin.powershell.lang.lsp.LSPInitMain +import com.intellij.plugin.powershell.lang.lsp.PowerShellSettings import com.intellij.plugin.powershell.lang.lsp.languagehost.PSLanguageHostUtils.BUNDLED_PSES_PATH import com.intellij.plugin.powershell.lang.lsp.languagehost.PSLanguageHostUtils.getEditorServicesModuleVersion import com.intellij.plugin.powershell.lang.lsp.languagehost.PSLanguageHostUtils.getEditorServicesStartupScript @@ -104,8 +104,8 @@ open class EditorServicesLanguageHostStarter(protected val myProject: Project) : var result = cachedPowerShellExtensionDir if (StringUtil.isNotEmpty(result)) return result!! - val lspMain = LSPInitMain.getInstance() - result = lspMain.state.powerShellExtensionPath?.trim() + val settings = PowerShellSettings.getInstance() + result = settings.state.powerShellExtensionPath?.trim() if (StringUtil.isEmpty(result)) { result = BUNDLED_PSES_PATH isUseBundledPowerShellExtensionPath = true @@ -206,8 +206,8 @@ open class EditorServicesLanguageHostStarter(protected val myProject: Project) : } val sessionDetailsPath = FileUtil.toCanonicalPath(getSessionDetailsFile().canonicalPath) val logPath = createLogPath(psExtensionPath) - val lspInitMain = LSPInitMain.getInstance() - val psExecutable = lspInitMain.getPowerShellExecutable() + val settings = PowerShellSettings.getInstance() + val psExecutable = settings.getPowerShellExecutable() val psVersion = PSLanguageHostUtils.getPowerShellVersion(psExecutable).await() diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 8ed84eb6..13e94646 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -110,8 +110,6 @@ bundle="messages.MessagesBundle" key="settings.powershell"/> - - diff --git a/src/test/kotlin/com/intellij/plugin/powershell/testFramework/PowerShellCodeInsightTestBase.kt b/src/test/kotlin/com/intellij/plugin/powershell/testFramework/PowerShellCodeInsightTestBase.kt index d94a61d7..f07a0081 100644 --- a/src/test/kotlin/com/intellij/plugin/powershell/testFramework/PowerShellCodeInsightTestBase.kt +++ b/src/test/kotlin/com/intellij/plugin/powershell/testFramework/PowerShellCodeInsightTestBase.kt @@ -1,6 +1,6 @@ package com.intellij.plugin.powershell.testFramework -import com.intellij.plugin.powershell.lang.lsp.LSPInitMain +import com.intellij.plugin.powershell.lang.lsp.LanguageServer import com.intellij.testFramework.LightPlatformTestCase import com.intellij.testFramework.fixtures.CodeInsightTestFixture import com.intellij.testFramework.fixtures.IdeaProjectTestFixture @@ -39,7 +39,6 @@ open class PowerShellCodeInsightTestBase { @AfterEach fun tearDownEdt() { runInEdt { - LSPInitMain.getInstance().dispose() // TODO: Remove this after LSPInitMain is refactored codeInsightTestFixture.tearDown() LightPlatformTestCase.closeAndDeleteProject() tearDownInEdt() @@ -53,7 +52,7 @@ open class PowerShellCodeInsightTestBase { fun waitForEditorConnects(path: Path) { runBlocking { withTimeout(20.seconds) { - LSPInitMain.getEditorLanguageServer(project).apply { + LanguageServer.getInstance(project).editorLanguageServer.value.apply { waitForInit() waitForEditorConnect(path) } @@ -64,7 +63,7 @@ open class PowerShellCodeInsightTestBase { fun waitForEditorManagerCreated(path: Path) { runBlocking { withTimeout(20.seconds) { - LSPInitMain.getEditorLanguageServer(project).apply { + LanguageServer.getInstance(project).editorLanguageServer.value.apply { waitForEditorManagerCreated(path) } } diff --git a/src/test/kotlin/com/intellij/plugin/powershell/testFramework/PowerShellTestBase.kt b/src/test/kotlin/com/intellij/plugin/powershell/testFramework/PowerShellTestBase.kt index 1057a988..1c9b61ff 100644 --- a/src/test/kotlin/com/intellij/plugin/powershell/testFramework/PowerShellTestBase.kt +++ b/src/test/kotlin/com/intellij/plugin/powershell/testFramework/PowerShellTestBase.kt @@ -1,7 +1,6 @@ package com.intellij.plugin.powershell.testFramework import com.intellij.openapi.project.Project -import com.intellij.plugin.powershell.lang.lsp.LSPInitMain import com.intellij.testFramework.junit5.fixture.projectFixture import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeEach @@ -28,7 +27,6 @@ abstract class PowerShellTestBase { @AfterEach fun tearDownEdt() { runInEdt { - LSPInitMain.getInstance().dispose() // TODO: Remove this after LSPInitMain is refactored tearDownInEdt() } } From 516237e7743435c0601adf707b92fd85af959afd Mon Sep 17 00:00:00 2001 From: Friedrich von Never Date: Wed, 16 Apr 2025 23:43:56 +0200 Subject: [PATCH 09/10] (#291) Extract JUnit 5 versions to the version catalog --- build.gradle.kts | 7 +++---- gradle/libs.versions.toml | 5 ++++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 71d9a4f1..895d8d69 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -67,15 +67,14 @@ dependencies { pluginVerifier() } - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.1") - testRuntimeOnly("org.junit.vintage:junit-vintage-engine:5.8.1") - implementation(libs.bundles.junixsocket) implementation(libs.lsp4j) implementation(libs.lsp4jdebug) testImplementation("org.jetbrains.kotlin:kotlin-test-junit") - testImplementation(libs.junit) + testImplementation(libs.junit.jupiter.api) testImplementation(libs.openTest4J) + testRuntimeOnly(libs.junit.jupiter.engine) + testRuntimeOnly(libs.junit.vintage.engine) psScriptAnalyzer( group = "PSScriptAnalyzer", diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e69ca027..348dbfc2 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,11 +1,14 @@ [versions] intellij = "251.23774.318-EAP-SNAPSHOT" intellijPreview = "251.23774.318-EAP-SNAPSHOT" +junit5 = "5.10.3" junixsocket = "2.10.1" lsp4j = "0.24.0" [libraries] -junit = "org.junit.jupiter:junit-jupiter-api:5.10.3" +junit-jupiter-api = { module = "org.junit.jupiter:junit-jupiter-api", version.ref = "junit5" } +junit-jupiter-engine = { module = "org.junit.jupiter:junit-jupiter-engine", version.ref = "junit5" } +junit-vintage-engine = { module = "org.junit.vintage:junit-vintage-engine", version.ref = "junit5" } junixsocket-common = { module = "com.kohlschutter.junixsocket:junixsocket-common", version.ref = "junixsocket" } junixsocket-native-common = { module = "com.kohlschutter.junixsocket:junixsocket-native-common", version.ref = "junixsocket" } lsp4j = {module = "org.eclipse.lsp4j:org.eclipse.lsp4j", version.ref = "lsp4j"} From 5e36f88ed9b5d73a24e402222e918c4140de08d3 Mon Sep 17 00:00:00 2001 From: Friedrich von Never Date: Thu, 17 Apr 2025 23:54:59 +0200 Subject: [PATCH 10/10] (#291) Debugger: use canonical paths for all files --- .../ide/debugger/PowerShellDebugSession.kt | 31 ++++++++++--------- .../ide/run/PowerShellProgramDebugRunner.kt | 17 +++++----- .../powershell/debugger/BreakpointTest.kt | 5 ++- 3 files changed, 27 insertions(+), 26 deletions(-) diff --git a/src/main/kotlin/com/intellij/plugin/powershell/ide/debugger/PowerShellDebugSession.kt b/src/main/kotlin/com/intellij/plugin/powershell/ide/debugger/PowerShellDebugSession.kt index 110ea751..97eca423 100644 --- a/src/main/kotlin/com/intellij/plugin/powershell/ide/debugger/PowerShellDebugSession.kt +++ b/src/main/kotlin/com/intellij/plugin/powershell/ide/debugger/PowerShellDebugSession.kt @@ -22,6 +22,7 @@ import org.eclipse.lsp4j.debug.* import org.eclipse.lsp4j.debug.services.IDebugProtocolServer import org.jetbrains.concurrency.await import java.nio.file.Path +import kotlin.io.path.pathString class PowerShellDebugSession( client: PSDebugClient, val server: IDebugProtocolServer, @@ -50,11 +51,12 @@ class PowerShellDebugSession( } fun setBreakpoint(filePath: Path, breakpoint: XLineBreakpoint>) { + val path = filePath.toRealPath() coroutineScope.launch(start = CoroutineStart.UNDISPATCHED) { breakpointsMapMutex.withLock { - if (!breakpointMap.containsKey(filePath)) - breakpointMap[filePath] = mutableMapOf() - val bpMap = breakpointMap[filePath]!! + if (!breakpointMap.containsKey(path)) + breakpointMap[path] = mutableMapOf() + val bpMap = breakpointMap[path]!! bpMap[breakpoint.line] = breakpoint sendBreakpointRequest() } @@ -62,9 +64,10 @@ class PowerShellDebugSession( } fun removeBreakpoint(filePath: Path, breakpoint: XLineBreakpoint>) { + val path = filePath.toRealPath() coroutineScope.launch(start = CoroutineStart.UNDISPATCHED) { breakpointsMapMutex.withLock { - val bpMap = breakpointMap[filePath] ?: return@launch + val bpMap = breakpointMap[path] ?: return@launch if (!bpMap.containsKey(breakpoint.line)) return@launch bpMap.remove(breakpoint.line) @@ -124,35 +127,35 @@ class PowerShellDebugSession( } private suspend fun sendBreakpointRequest(breakpointMap: Map>>>) { - for (breakpointMapEntry in breakpointMap) { + for ((file, breakpointsInFile) in breakpointMap) { val breakpointArgs = SetBreakpointsArguments() val source = Source() - source.path = breakpointMapEntry.key.toString() + source.path = file.pathString breakpointArgs.source = source - breakpointArgs.breakpoints = breakpointMapEntry.value.map { - val bp = it.value + breakpointArgs.breakpoints = breakpointsInFile.map { + val breakpoint = it.value SourceBreakpoint().apply { - line = bp.line + 1 // ide breakpoints line numbering starts from 0, while PSES start from 1 - condition = bp.conditionExpression?.expression - logMessage = bp.logExpressionObject?.expression + line = breakpoint.line + 1 // ide breakpoints line numbering starts from 0, while PSES start from 1 + condition = breakpoint.conditionExpression?.expression + logMessage = breakpoint.logExpressionObject?.expression } }.toTypedArray() try { val setBreakpointsResponse = server.setBreakpoints(breakpointArgs).await() val responseMap = setBreakpointsResponse.breakpoints.associateBy { x -> x.line - 1 } - breakpointMapEntry.value.forEach { + breakpointsInFile.forEach { val bp = responseMap[it.value.line] if (bp?.isVerified == true) { session.setBreakpointVerified(it.value) - logger.info("Set breakpoint at ${breakpointMapEntry.key}:${bp.line} successfully.") + logger.info("Set breakpoint at $file:${bp.line} successfully.") } else { session.setBreakpointInvalid( it.value, bp?.message.nullize(nullizeSpaces = true) ?: MessagesBundle.message("powershell.debugger.breakpoints.invalidBreakPoint") ) - logger.info("Invalid breakpoint at ${breakpointMapEntry.key}:${bp?.line}: ${bp?.message}") + logger.info("Invalid breakpoint at $file:${bp?.line}: ${bp?.message}") } } } catch (e: Throwable) { diff --git a/src/main/kotlin/com/intellij/plugin/powershell/ide/run/PowerShellProgramDebugRunner.kt b/src/main/kotlin/com/intellij/plugin/powershell/ide/run/PowerShellProgramDebugRunner.kt index 2074ae9d..a4e9292b 100644 --- a/src/main/kotlin/com/intellij/plugin/powershell/ide/run/PowerShellProgramDebugRunner.kt +++ b/src/main/kotlin/com/intellij/plugin/powershell/ide/run/PowerShellProgramDebugRunner.kt @@ -16,7 +16,6 @@ import com.intellij.openapi.diagnostic.logger import com.intellij.openapi.fileEditor.FileDocumentManager import com.intellij.openapi.project.Project import com.intellij.openapi.rd.util.toPromise -import com.intellij.openapi.vfs.VfsUtil import com.intellij.plugin.powershell.ide.MessagesBundle import com.intellij.plugin.powershell.ide.PluginProjectRoot import com.intellij.plugin.powershell.ide.debugger.EditorServicesDebuggerHostStarter @@ -43,6 +42,7 @@ import org.jetbrains.concurrency.Promise import java.nio.file.Path import java.util.concurrent.atomic.AtomicBoolean import kotlin.io.path.Path +import kotlin.io.path.pathString class PowerShellProgramDebugRunner : AsyncProgramRunner() { @@ -158,15 +158,13 @@ private suspend fun initializeBreakpoints( ) { if (!debugSession.areBreakpointsMuted()) { breakpoints.filter { it.sourcePosition != null && it.sourcePosition!!.file.exists() && it.sourcePosition!!.file.isValid && it.isEnabled } - .groupBy { VfsUtil.virtualToIoFile(it.sourcePosition!!.file).toURI().toASCIIString() } - .forEach { entry -> - val fileURL = entry.key + .groupBy { it.sourcePosition!!.file.toNioPath().toRealPath() } + .forEach { (file, breakpoints) -> val breakpointArgs = SetBreakpointsArguments() val source = Source() - source.path = fileURL + source.path = file.pathString breakpointArgs.source = source - val bps = entry.value - breakpointArgs.breakpoints = bps.map { + breakpointArgs.breakpoints = breakpoints.map { val bp = it SourceBreakpoint().apply { line = bp.line + 1 // ide breakpoints line numbering starts from 0, while PSES start from 1 @@ -185,12 +183,13 @@ private suspend fun launchDebuggee(scriptPath: Path, remoteProxy: IDebugProtocol val launchArgs: MutableMap = HashMap() launchArgs["terminal"] = "none" - launchArgs["script"] = scriptPath.toString() + val scriptPathString = scriptPath.toRealPath().pathString + launchArgs["script"] = scriptPathString launchArgs["noDebug"] = false launchArgs["__sessionId"] = "sessionId" launchArgs["Args"] = runtimeArgs launchArgs["Env"] = envs - logger.info("Starting script file \"$scriptPath\" in a debug session.") + logger.info("Starting script file \"$scriptPathString\" in a debug session.") remoteProxy.launch(launchArgs).await() } diff --git a/src/test/kotlin/com/intellij/plugin/powershell/debugger/BreakpointTest.kt b/src/test/kotlin/com/intellij/plugin/powershell/debugger/BreakpointTest.kt index cb7087ef..be78ba50 100644 --- a/src/test/kotlin/com/intellij/plugin/powershell/debugger/BreakpointTest.kt +++ b/src/test/kotlin/com/intellij/plugin/powershell/debugger/BreakpointTest.kt @@ -62,7 +62,7 @@ class BreakpointTest : DebuggerTestBase() { ) val suspendContext = debugSession.suspendContext as PowerShellSuspendContext Assertions.assertEquals( - psiSecondFile.virtualFile.toNioPath(), + psiSecondFile.virtualFile.canonicalFile?.toNioPath(), suspendContext.activeExecutionStack.topFrame?.sourcePosition?.file?.toNioPath() ) Assertions.assertEquals(line, suspendContext.activeExecutionStack.topFrame?.sourcePosition?.line) @@ -71,8 +71,7 @@ class BreakpointTest : DebuggerTestBase() { } @Test - fun testConditionalBreakpoint() - { + fun testConditionalBreakpoint() { runInEdt { val psiFile = copyAndOpenFile("debugger/testBreakpoint.ps1") val file = psiFile.virtualFile