diff --git a/.github/workflows/ci-test.yml b/.github/workflows/ci-test.yml
index 959d482ec..6a2f09928 100644
--- a/.github/workflows/ci-test.yml
+++ b/.github/workflows/ci-test.yml
@@ -51,6 +51,12 @@ jobs:
shell: pwsh
run: ./pwsh/tools/install-powershell.ps1 -Daily
+ - name: If Debugging, start upterm for interactive pipeline troubleshooting
+ if: ${{ runner.debug == 1 }}
+ uses: lhotari/action-upterm@v1
+ with:
+ wait-timeout-minutes: 1
+
- name: Build and test
shell: pwsh
run: Invoke-Build -Configuration Release ${{ github.event_name == 'merge_group' && 'TestFull' || 'Test' }}
diff --git a/PowerShellEditorServices.build.ps1 b/PowerShellEditorServices.build.ps1
index cf2666517..116ae70eb 100644
--- a/PowerShellEditorServices.build.ps1
+++ b/PowerShellEditorServices.build.ps1
@@ -193,8 +193,88 @@ Task BuildCmdletHelp -After AssembleModule {
}
Task SetupHelpForTests {
- Write-Build DarkMagenta 'Updating help (for tests)'
- Update-Help -Module Microsoft.PowerShell.Management, Microsoft.PowerShell.Utility -Force -Scope CurrentUser -UICulture en-US
+ # Some CI do not ship with help included, and the secure devops pipeline also does not allow internet access, so we must update help from our local repository source.
+
+ # Only commands in Microsoft.PowerShell.Archive can be tested for help so as to minimize the repository storage.
+ # This requires admin rights for PS5.1
+
+ # NOTE: You can run this task once as admin or update help separately, and continue to run tests as non-admin, if for instance developing locally.
+
+ $installHelpScript = {
+ param(
+ [Parameter(Position = 0)][string]$helpPath
+ )
+ $PSVersion = $PSVersionTable.PSVersion
+ $ErrorActionPreference = 'Stop'
+ $helpPath = Resolve-Path $helpPath
+ if ($PSEdition -ne 'Desktop') {
+ $helpPath = Join-Path $helpPath '7'
+ }
+
+ if ((Get-Help Expand-Archive).remarks -notlike 'Get-Help cannot find the Help files*') {
+ Write-Host -ForegroundColor Green "PowerShell $PSVersion Archive help is already installed"
+ return
+ }
+
+ if ($PSEdition -eq 'Desktop') {
+ # Cant use requires RunAsAdministrator because PS isn't smart enough to know this is a subscript.
+ if (-not [Security.Principal.WindowsPrincipal]::new(
+ [Security.Principal.WindowsIdentity]::GetCurrent()
+ ).IsInRole(
+ [Security.Principal.WindowsBuiltInRole]::Administrator
+ )) {
+ throw 'Windows PowerShell Update-Help requires admin rights. Please re-run the script in an elevated PowerShell session!'
+ }
+ }
+
+ Write-Host -ForegroundColor Magenta "PowerShell $PSVersion Archive help is not installed, installing from $helpPath"
+
+ $updateHelpParams = @{
+ Module = 'Microsoft.PowerShell.Archive'
+ SourcePath = $helpPath
+ UICulture = 'en-US'
+ Force = $true
+ Verbose = $true
+ }
+
+ # PS7+ does not require admin rights if CurrentUser is used for scope. PS5.1 does not have this option.
+ if ($PSEdition -ne 'Desktop') {
+ $updateHelpParams.'Scope' = 'CurrentUser'
+ }
+ # Update the help and capture verbose output
+ $updateHelpOutput = Update-Help @updateHelpParams *>&1
+
+ if ((Get-Help Expand-Archive).remarks -like 'Get-Help cannot find the Help files*') {
+ throw "Failed to install PowerShell $PSVersion Help: $updateHelpOutput"
+ } else {
+ Write-Host -ForegroundColor Green "PowerShell $PSVersion Archive help installed successfully"
+ }
+ }
+
+ # Need this to inject the help file path since PSScriptRoot won't work inside the script
+ $helpPath = Resolve-Path "$PSScriptRoot\test\PowerShellEditorServices.Test.Shared\PSHelp" -ErrorAction Stop
+ Write-Build DarkMagenta "Runner help located at $helpPath"
+
+ if (Get-Command powershell.exe -CommandType Application -ea 0) {
+ Write-Build DarkMagenta 'Checking PowerShell 5.1 help'
+ & powershell.exe -NoProfile -NonInteractive -Command $installHelpScript -args $helpPath
+ if ($LASTEXITCODE -ne 0) {
+ throw 'Failed to install PowerShell 5.1 help!'
+ }
+ }
+
+ if ($PwshDaily -and (Get-Command $PwshDaily -ea 0)) {
+ Write-Build DarkMagenta "Checking PowerShell Daily help at $PwshDaily"
+ Invoke-BuildExec { & $PwshDaily -NoProfile -NonInteractive -Command $installHelpScript -args $helpPath }
+ if ($LASTEXITCODE -ne 0) {
+ throw 'Failed to install PowerShell Daily help!'
+ }
+ }
+
+ if ($PSEdition -eq 'Core') {
+ Write-Build DarkMagenta "Checking this PowerShell process's help"
+ & $installHelpScript $helpPath
+ }
}
Task TestPS74 Build, SetupHelpForTests, {
diff --git a/src/PowerShellEditorServices/Server/PsesDebugServer.cs b/src/PowerShellEditorServices/Server/PsesDebugServer.cs
index 6fbecce9c..9f48a0f2d 100644
--- a/src/PowerShellEditorServices/Server/PsesDebugServer.cs
+++ b/src/PowerShellEditorServices/Server/PsesDebugServer.cs
@@ -120,7 +120,8 @@ public async Task StartAsync()
response.SupportsDelayedStackTraceLoading = true;
return Task.CompletedTask;
- });
+ })
+ ;
}).ConfigureAwait(false);
}
diff --git a/test/PowerShellEditorServices.Test.E2E/DebugAdapterProtocolMessageTests.cs b/test/PowerShellEditorServices.Test.E2E/DebugAdapterProtocolMessageTests.cs
index 523ced631..881e55884 100644
--- a/test/PowerShellEditorServices.Test.E2E/DebugAdapterProtocolMessageTests.cs
+++ b/test/PowerShellEditorServices.Test.E2E/DebugAdapterProtocolMessageTests.cs
@@ -2,140 +2,132 @@
// Licensed under the MIT License.
using System;
-using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
-using System.Threading;
using System.Threading.Tasks;
+using Nerdbank.Streams;
using OmniSharp.Extensions.DebugAdapter.Client;
-using DapStackFrame = OmniSharp.Extensions.DebugAdapter.Protocol.Models.StackFrame;
+using OmniSharp.Extensions.DebugAdapter.Protocol.Client;
using OmniSharp.Extensions.DebugAdapter.Protocol.Events;
using OmniSharp.Extensions.DebugAdapter.Protocol.Models;
using OmniSharp.Extensions.DebugAdapter.Protocol.Requests;
using OmniSharp.Extensions.JsonRpc.Server;
using Xunit;
using Xunit.Abstractions;
-using Microsoft.Extensions.Logging.Abstractions;
+using DapStackFrame = OmniSharp.Extensions.DebugAdapter.Protocol.Models.StackFrame;
namespace PowerShellEditorServices.Test.E2E
{
- public class XunitOutputTraceListener(ITestOutputHelper output) : TraceListener
- {
- public override void Write(string message) => output.WriteLine(message);
- public override void WriteLine(string message) => output.WriteLine(message);
- }
-
[Trait("Category", "DAP")]
- public class DebugAdapterProtocolMessageTests : IAsyncLifetime, IDisposable
+ // ITestOutputHelper is injected by XUnit
+ // https://xunit.net/docs/capturing-output
+ public class DebugAdapterProtocolMessageTests(ITestOutputHelper output) : IAsyncLifetime
{
- private static readonly bool s_isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
- private static readonly string s_testOutputPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
+ // After initialization, use this client to send messages for E2E tests and check results
+ private IDebugAdapterClient client;
- private readonly ITestOutputHelper _output;
- private DebugAdapterClient PsesDebugAdapterClient;
- private PsesStdioProcess _psesProcess;
+ private static readonly bool s_isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
///
- /// Completes when the debug adapter is started.
+ /// Test scripts output here, where the output can be read to verify script progress against breakpointing
///
- public TaskCompletionSource