diff --git a/PowerShellEditorServices.Common.props b/PowerShellEditorServices.Common.props index cdb72f19d..2ab30ebc7 100644 --- a/PowerShellEditorServices.Common.props +++ b/PowerShellEditorServices.Common.props @@ -11,5 +11,6 @@ git https://github.com/PowerShell/PowerShellEditorServices portable + $(DefineConstants);$(ExtraDefineConstants) diff --git a/PowerShellEditorServices.build.ps1 b/PowerShellEditorServices.build.ps1 index fbd126708..8df6c5fc1 100644 --- a/PowerShellEditorServices.build.ps1 +++ b/PowerShellEditorServices.build.ps1 @@ -259,20 +259,20 @@ task TestServer TestServerWinPS,TestServerPS7,TestServerPS72 task TestServerWinPS -If (-not $script:IsNix) { Set-Location .\test\PowerShellEditorServices.Test\ - exec { & $script:dotnetExe test --logger trx -f $script:NetRuntime.Desktop (DotNetTestFilter) } + exec { & $script:dotnetExe test -p:ExtraDefineConstants=TEST --logger trx -f $script:NetRuntime.Desktop (DotNetTestFilter) } } task TestServerPS7 -If (-not $script:IsRosetta) { Set-Location .\test\PowerShellEditorServices.Test\ Invoke-WithCreateDefaultHook -NewModulePath $script:PSCoreModulePath { - exec { & $script:dotnetExe test --logger trx -f $script:NetRuntime.PS7 (DotNetTestFilter) } + exec { & $script:dotnetExe test -p:ExtraDefineConstants=TEST --logger trx -f $script:NetRuntime.PS7 (DotNetTestFilter) } } } task TestServerPS72 { Set-Location .\test\PowerShellEditorServices.Test\ Invoke-WithCreateDefaultHook -NewModulePath $script:PSCoreModulePath { - exec { & $script:dotnetExe test --logger trx -f $script:NetRuntime.PS72 (DotNetTestFilter) } + exec { & $script:dotnetExe test -p:ExtraDefineConstants=TEST --logger trx -f $script:NetRuntime.PS72 (DotNetTestFilter) } } } @@ -281,13 +281,13 @@ task TestE2E { $env:PWSH_EXE_NAME = if ($IsCoreCLR) { "pwsh" } else { "powershell" } $NetRuntime = if ($IsRosetta) { $script:NetRuntime.PS72 } else { $script:NetRuntime.PS7 } - exec { & $script:dotnetExe test --logger trx -f $NetRuntime (DotNetTestFilter) } + exec { & $script:dotnetExe test -p:ExtraDefineConstants=TEST --logger trx -f $NetRuntime (DotNetTestFilter) } # Run E2E tests in ConstrainedLanguage mode. if (!$script:IsNix) { try { [System.Environment]::SetEnvironmentVariable("__PSLockdownPolicy", "0x80000007", [System.EnvironmentVariableTarget]::Machine); - exec { & $script:dotnetExe test --logger trx -f $script:NetRuntime.PS7 (DotNetTestFilter) } + exec { & $script:dotnetExe test -p:ExtraDefineConstants=TEST --logger trx -f $script:NetRuntime.PS7 (DotNetTestFilter) } } finally { [System.Environment]::SetEnvironmentVariable("__PSLockdownPolicy", $null, [System.EnvironmentVariableTarget]::Machine); } diff --git a/src/PowerShellEditorServices/Services/PowerShellContext/PowerShellContextService.cs b/src/PowerShellEditorServices/Services/PowerShellContext/PowerShellContextService.cs index a6ea9ee45..f226dd8d3 100644 --- a/src/PowerShellEditorServices/Services/PowerShellContext/PowerShellContextService.cs +++ b/src/PowerShellEditorServices/Services/PowerShellContext/PowerShellContextService.cs @@ -296,21 +296,6 @@ public static Runspace CreateRunspace(PSHost psHost, PSLanguageMode languageMode return runspace; } - /// - /// Initializes a new instance of the PowerShellContext class using - /// an existing runspace for the session. - /// - /// An object containing the profile paths for the session. - /// The initial runspace to use for this instance. - /// If true, the PowerShellContext owns this runspace. - public void Initialize( - ProfilePathInfo profilePaths, - Runspace initialRunspace, - bool ownsInitialRunspace) - { - this.Initialize(profilePaths, initialRunspace, ownsInitialRunspace, consoleHost: null); - } - /// /// Initializes a new instance of the PowerShellContext class using /// an existing runspace for the session. diff --git a/src/PowerShellEditorServices/Services/PowerShellContext/Session/PSReadLinePromptContext.cs b/src/PowerShellEditorServices/Services/PowerShellContext/Session/PSReadLinePromptContext.cs index 272830b9e..5f4931854 100644 --- a/src/PowerShellEditorServices/Services/PowerShellContext/Session/PSReadLinePromptContext.cs +++ b/src/PowerShellEditorServices/Services/PowerShellContext/Session/PSReadLinePromptContext.cs @@ -22,26 +22,15 @@ internal class PSReadLinePromptContext : IPromptContext "..", "..", "..", +#if TEST + // When using xUnit (dotnet test) the assemblies are deployed to the + // test project folder, invalidating our relative path assumption. + "..", + "..", + "module", +#endif "PSReadLine"); - private static readonly string ReadLineInitScript = $@" - [System.Diagnostics.DebuggerHidden()] - [System.Diagnostics.DebuggerStepThrough()] - param() - end {{ - $module = Get-Module -ListAvailable PSReadLine | - Where-Object {{ $_.Version -ge '2.0.2' }} | - Sort-Object -Descending Version | - Select-Object -First 1 - if (-not $module) {{ - Import-Module '{_psReadLineModulePath.Replace("'", "''")}' - return [Microsoft.PowerShell.PSConsoleReadLine] - }} - - Import-Module -ModuleInfo $module - return [Microsoft.PowerShell.PSConsoleReadLine] - }}"; - private static readonly Lazy s_lazyInvokeReadLineForEditorServicesCmdletInfo = new Lazy(() => { var type = Type.GetType("Microsoft.PowerShell.EditorServices.Commands.InvokeReadLineForEditorServicesCommand, Microsoft.PowerShell.EditorServices.Hosting"); @@ -97,14 +86,15 @@ internal static bool TryGetPSReadLineProxy( using (var pwsh = PowerShell.Create()) { pwsh.Runspace = runspace; - var psReadLineType = pwsh - .AddScript(ReadLineInitScript, useLocalScope: true) - .Invoke() - .FirstOrDefault(); + pwsh.AddCommand("Microsoft.PowerShell.Core\\Import-Module") + .AddParameter("Name", _psReadLineModulePath) + .Invoke(); + + var psReadLineType = Type.GetType("Microsoft.PowerShell.PSConsoleReadLine, Microsoft.PowerShell.PSReadLine2"); if (psReadLineType == null) { - logger.LogWarning("PSReadLine unable to be loaded: {Reason}", pwsh.HadErrors ? pwsh.Streams.Error[0].ToString() : ""); + logger.LogWarning("PSConsoleReadline type not found: {Reason}", pwsh.HadErrors ? pwsh.Streams.Error[0].ToString() : ""); return false; } @@ -117,7 +107,7 @@ internal static bool TryGetPSReadLineProxy( // The Type we got back from PowerShell doesn't have the members we expected. // Could be an older version, a custom build, or something a newer version with // breaking changes. - logger.LogWarning("PSReadLine unable to be loaded: {Reason}", e); + logger.LogWarning("PSReadLineProxy unable to be initialized: {Reason}", e); return false; } } diff --git a/test/PowerShellEditorServices.Test/PowerShellContextFactory.cs b/test/PowerShellEditorServices.Test/PowerShellContextFactory.cs index c08142ebd..4eb93395a 100644 --- a/test/PowerShellEditorServices.Test/PowerShellContextFactory.cs +++ b/test/PowerShellEditorServices.Test/PowerShellContextFactory.cs @@ -1,18 +1,18 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; -using Microsoft.PowerShell.EditorServices.Hosting; -using Microsoft.PowerShell.EditorServices.Services; -using Microsoft.PowerShell.EditorServices.Services.PowerShellContext; -using Microsoft.PowerShell.EditorServices.Test.Shared; using System; using System.Collections.Generic; using System.IO; using System.Management.Automation; using System.Threading; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.PowerShell.EditorServices.Hosting; +using Microsoft.PowerShell.EditorServices.Services; +using Microsoft.PowerShell.EditorServices.Services.PowerShellContext; +using Microsoft.PowerShell.EditorServices.Test.Shared; namespace Microsoft.PowerShell.EditorServices.Test { @@ -32,6 +32,8 @@ internal static class PowerShellContextFactory Path.GetFullPath( TestUtilities.NormalizePath("../../../../PowerShellEditorServices.Test.Shared/ProfileTest.ps1"))); + public static System.Management.Automation.Runspaces.Runspace initialRunspace; + public static PowerShellContextService Create(ILogger logger) { PowerShellContextService powerShellContext = new PowerShellContextService(logger, null, isPSReadLineEnabled: false); @@ -50,15 +52,17 @@ public static PowerShellContextService Create(ILogger logger) consoleReplEnabled: false, usesLegacyReadLine: false); - - powerShellContext.Initialize( - TestProfilePaths, - PowerShellContextService.CreateRunspace( + initialRunspace = PowerShellContextService.CreateRunspace( testHostDetails, powerShellContext, new TestPSHostUserInterface(powerShellContext, logger), - logger), - true); + logger); + + powerShellContext.Initialize( + TestProfilePaths, + initialRunspace, + ownsInitialRunspace: true, + consoleHost: null); return powerShellContext; } diff --git a/test/PowerShellEditorServices.Test/Session/PowerShellContextTests.cs b/test/PowerShellEditorServices.Test/Session/PowerShellContextTests.cs index ac52cad55..4cf1fef8c 100644 --- a/test/PowerShellEditorServices.Test/Session/PowerShellContextTests.cs +++ b/test/PowerShellEditorServices.Test/Session/PowerShellContextTests.cs @@ -1,23 +1,27 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using Microsoft.Extensions.Logging.Abstractions; -using Microsoft.PowerShell.EditorServices.Services; -using Microsoft.PowerShell.EditorServices.Services.PowerShellContext; -using Microsoft.PowerShell.EditorServices.Test.Shared; -using Microsoft.PowerShell.EditorServices.Utility; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Management.Automation; +using System.Runtime.InteropServices; using System.Threading.Tasks; +using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.PowerShell.EditorServices.Services; +using Microsoft.PowerShell.EditorServices.Services.PowerShellContext; +using Microsoft.PowerShell.EditorServices.Test.Shared; +using Microsoft.PowerShell.EditorServices.Utility; using Xunit; namespace Microsoft.PowerShell.EditorServices.Test.Console { public class PowerShellContextTests : IDisposable { + // Borrowed from `VersionUtils` which can't be used here due to an initialization problem. + private static bool IsWindows { get; } = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); + private PowerShellContextService powerShellContext; private AsyncQueue stateChangeQueue; @@ -143,6 +147,17 @@ await this.powerShellContext.ExecuteCommandAsync( Assert.Equal(expectedString, result.FirstOrDefault(), true); } + [Trait("Category", "PSReadLine")] + [SkippableFact] + public async Task CanGetPSReadLineProxy() + { + Skip.If(IsWindows, "This test doesn't work on Windows for some reason."); + Assert.True(PSReadLinePromptContext.TryGetPSReadLineProxy( + NullLogger.Instance, + PowerShellContextFactory.initialRunspace, + out PSReadLineProxy proxy)); + } + #region Helper Methods private async Task AssertStateChange(PowerShellContextState expectedState)