From 22bb348d05e229a54b4ad8e4a1164e3e26e1a42a Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Mon, 27 Jun 2022 13:44:48 -0700 Subject: [PATCH 1/8] Small cleanups --- .../Debugging/PowerShellDebugContext.cs | 2 +- .../DebugAdapterProtocolMessageTests.cs | 44 ++----- .../Debugging/DebugServiceTests.cs | 4 +- .../Language/CompletionHandlerTests.cs | 1 + .../Session/PsesInternalHostTests.cs | 7 +- .../Session/WorkspaceTests.cs | 111 ++++++++---------- 6 files changed, 64 insertions(+), 105 deletions(-) diff --git a/src/PowerShellEditorServices/Services/PowerShell/Debugging/PowerShellDebugContext.cs b/src/PowerShellEditorServices/Services/PowerShell/Debugging/PowerShellDebugContext.cs index a66b0d93b..36aaad0e2 100644 --- a/src/PowerShellEditorServices/Services/PowerShell/Debugging/PowerShellDebugContext.cs +++ b/src/PowerShellEditorServices/Services/PowerShell/Debugging/PowerShellDebugContext.cs @@ -187,7 +187,7 @@ public void ProcessDebuggerResult(DebuggerCommandResults debuggerResult) RaiseDebuggerResumingEvent(new DebuggerResumingEventArgs(debuggerResult.ResumeAction.Value)); // The Terminate exception is used by the engine for flow control - // when it needs to unwind the callstack out of the debugger. + // when it needs to unwind the call stack out of the debugger. if (debuggerResult.ResumeAction is DebuggerResumeAction.Stop) { throw new TerminateException(); diff --git a/test/PowerShellEditorServices.Test.E2E/DebugAdapterProtocolMessageTests.cs b/test/PowerShellEditorServices.Test.E2E/DebugAdapterProtocolMessageTests.cs index 34352291f..14e250823 100644 --- a/test/PowerShellEditorServices.Test.E2E/DebugAdapterProtocolMessageTests.cs +++ b/test/PowerShellEditorServices.Test.E2E/DebugAdapterProtocolMessageTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System; @@ -18,6 +18,7 @@ namespace PowerShellEditorServices.Test.E2E { + [Trait("Category", "DAP")] public class DebugAdapterProtocolMessageTests : IAsyncLifetime { private const string TestOutputFileName = "__dapTestOutputFile.txt"; @@ -148,7 +149,6 @@ private string GenerateScriptFromLoggingStatements(params string[] logStatements private static string[] GetLog() => File.ReadLines(s_testOutputPath).ToArray(); - [Trait("Category", "DAP")] [Fact] public void CanInitializeWithCorrectServerSettings() { @@ -160,7 +160,6 @@ public void CanInitializeWithCorrectServerSettings() Assert.True(PsesDebugAdapterClient.ServerSettings.SupportsSetVariable); } - [Trait("Category", "DAP")] [Fact] public async Task CanLaunchScriptWithNoBreakpointsAsync() { @@ -170,15 +169,12 @@ public async Task CanLaunchScriptWithNoBreakpointsAsync() ConfigurationDoneResponse configDoneResponse = await PsesDebugAdapterClient.RequestConfigurationDone(new ConfigurationDoneArguments()).ConfigureAwait(false); Assert.NotNull(configDoneResponse); - - // At this point the script should be running so lets give it time await Task.Delay(2000).ConfigureAwait(false); string[] log = GetLog(); Assert.Equal("works", log[0]); } - [Trait("Category", "DAP")] [SkippableFact] public async Task CanSetBreakpointsAsync() { @@ -197,19 +193,8 @@ public async Task CanSetBreakpointsAsync() // {"command":"setBreakpoints","arguments":{"source":{"name":"dfsdfg.ps1","path":"/Users/tyleonha/Code/PowerShell/Misc/foo/dfsdfg.ps1"},"lines":[2],"breakpoints":[{"line":2}],"sourceModified":false},"type":"request","seq":3} SetBreakpointsResponse setBreakpointsResponse = await PsesDebugAdapterClient.SetBreakpoints(new SetBreakpointsArguments { - Source = new Source - { - Name = Path.GetFileName(filePath), - Path = filePath - }, - Lines = new long[] { 2 }, - Breakpoints = new SourceBreakpoint[] - { - new SourceBreakpoint - { - Line = 2, - } - }, + Source = new Source { Name = Path.GetFileName(filePath), Path = filePath }, + Breakpoints = new SourceBreakpoint[] { new SourceBreakpoint { Line = 2 } }, SourceModified = false, }).ConfigureAwait(false); @@ -220,21 +205,15 @@ public async Task CanSetBreakpointsAsync() ConfigurationDoneResponse configDoneResponse = await PsesDebugAdapterClient.RequestConfigurationDone(new ConfigurationDoneArguments()).ConfigureAwait(false); Assert.NotNull(configDoneResponse); - - // At this point the script should be running so lets give it time await Task.Delay(2000).ConfigureAwait(false); string[] log = GetLog(); Assert.Single(log, (i) => i == "before breakpoint"); - ContinueResponse continueResponse = await PsesDebugAdapterClient.RequestContinue(new ContinueArguments - { - ThreadId = 1, - }).ConfigureAwait(true); + ContinueResponse continueResponse = await PsesDebugAdapterClient.RequestContinue( + new ContinueArguments { ThreadId = 1 }).ConfigureAwait(true); Assert.NotNull(continueResponse); - - // At this point the script should be running so lets give it time await Task.Delay(2000).ConfigureAwait(false); log = GetLog(); @@ -255,7 +234,6 @@ public async Task CanSetBreakpointsAsync() // PowerShell, we avoid all issues with our test project (and the xUnit executable) not // having System.Windows.Forms deployed, and can instead rely on the Windows Global Assembly // Cache (GAC) to find it. - [Trait("Category", "DAP")] [SkippableFact] public async Task CanStepPastSystemWindowsForms() { @@ -283,15 +261,10 @@ public async Task CanStepPastSystemWindowsForms() ConfigurationDoneResponse configDoneResponse = await PsesDebugAdapterClient.RequestConfigurationDone(new ConfigurationDoneArguments()).ConfigureAwait(false); Assert.NotNull(configDoneResponse); - - // At this point the script should be running so lets give it time await Task.Delay(2000).ConfigureAwait(false); VariablesResponse variablesResponse = await PsesDebugAdapterClient.RequestVariables( - new VariablesArguments - { - VariablesReference = 1 - }).ConfigureAwait(false); + new VariablesArguments { VariablesReference = 1 }).ConfigureAwait(false); Variable form = variablesResponse.Variables.FirstOrDefault(v => v.Name == "$form"); Assert.NotNull(form); @@ -302,7 +275,6 @@ public async Task CanStepPastSystemWindowsForms() // commented. Since in some cases (such as Windows PowerShell, or the script not having a // backing ScriptFile) we just wrap the script with braces, we had a bug where the last // brace would be after the comment. We had to ensure we wrapped with newlines instead. - [Trait("Category", "DAP")] [Fact] public async Task CanLaunchScriptWithCommentedLastLineAsync() { @@ -318,8 +290,6 @@ public async Task CanLaunchScriptWithCommentedLastLineAsync() ConfigurationDoneResponse configDoneResponse = await PsesDebugAdapterClient.RequestConfigurationDone(new ConfigurationDoneArguments()).ConfigureAwait(false); Assert.NotNull(configDoneResponse); - - // At this point the script should be running so lets give it time await Task.Delay(2000).ConfigureAwait(false); Assert.Collection(GetLog(), (i) => Assert.Equal("a log statement", i)); diff --git a/test/PowerShellEditorServices.Test/Debugging/DebugServiceTests.cs b/test/PowerShellEditorServices.Test/Debugging/DebugServiceTests.cs index b7b55ad61..38f3b6231 100644 --- a/test/PowerShellEditorServices.Test/Debugging/DebugServiceTests.cs +++ b/test/PowerShellEditorServices.Test/Debugging/DebugServiceTests.cs @@ -183,7 +183,7 @@ await debugService.SetCommandBreakpointsAsync( public async Task DebuggerAcceptsScriptArgs(string[] args) { // The path is intentionally odd (some escaped chars but not all) because we are testing - // the internal path escaping mechanism - it should escape certains chars ([, ] and space) but + // the internal path escaping mechanism - it should escape certain chars ([, ] and space) but // it should not escape already escaped chars. ScriptFile debugWithParamsFile = GetDebugScript("Debug W&ith Params [Test].ps1"); @@ -453,7 +453,7 @@ await debugService.SetLineBreakpointsAsync( } [Fact] - public async Task DebuggerFindsParseableButInvalidSimpleBreakpointConditions() + public async Task DebuggerFindsParsableButInvalidSimpleBreakpointConditions() { BreakpointDetails[] breakpoints = await debugService.SetLineBreakpointsAsync( diff --git a/test/PowerShellEditorServices.Test/Language/CompletionHandlerTests.cs b/test/PowerShellEditorServices.Test/Language/CompletionHandlerTests.cs index 4ba77bf1f..fc1590d35 100644 --- a/test/PowerShellEditorServices.Test/Language/CompletionHandlerTests.cs +++ b/test/PowerShellEditorServices.Test/Language/CompletionHandlerTests.cs @@ -109,6 +109,7 @@ public async Task CompletesVariableInFile() public async Task CompletesAttributeValue() { (_, IEnumerable results) = await GetCompletionResultsAsync(CompleteAttributeValue.SourceDetails).ConfigureAwait(true); + Assert.Equal(3, results.Count()); Assert.Collection(results.OrderBy(c => c.SortText), actual => Assert.Equal(actual with { Data = null }, CompleteAttributeValue.ExpectedCompletion1), actual => Assert.Equal(actual with { Data = null }, CompleteAttributeValue.ExpectedCompletion2), diff --git a/test/PowerShellEditorServices.Test/Session/PsesInternalHostTests.cs b/test/PowerShellEditorServices.Test/Session/PsesInternalHostTests.cs index 179da5f8c..c85aa0d2f 100644 --- a/test/PowerShellEditorServices.Test/Session/PsesInternalHostTests.cs +++ b/test/PowerShellEditorServices.Test/Session/PsesInternalHostTests.cs @@ -137,7 +137,7 @@ public async Task CanResolveAndLoadProfilesForHostId() new PSCommand().AddScript("Assert-ProfileLoaded"), CancellationToken.None).ConfigureAwait(true); - Assert.Collection(profileLoaded, (p) => Assert.True(p)); + Assert.Collection(profileLoaded, Assert.True); } [Fact] @@ -150,7 +150,8 @@ public async Task CanHandleNoProfiles() await psesHost.ExecuteDelegateAsync( "LoadProfiles", executionOptions: null, - (pwsh, _) => { + (pwsh, _) => + { pwsh.LoadProfiles(emptyProfilePaths); Assert.Empty(pwsh.Commands.Commands); }, @@ -162,7 +163,7 @@ public async Task CanLoadPSReadLine() { // NOTE: This is slightly more complicated than one would expect because we explicitly // need it to run on the pipeline thread otherwise Windows complains about the the - // thread's appartment state not matching. + // thread's apartment state not matching. Assert.True(await psesHost.ExecuteDelegateAsync( nameof(psesHost.TryLoadPSReadLine), executionOptions: null, diff --git a/test/PowerShellEditorServices.Test/Session/WorkspaceTests.cs b/test/PowerShellEditorServices.Test/Session/WorkspaceTests.cs index 6d07530ca..5342ee7e6 100644 --- a/test/PowerShellEditorServices.Test/Session/WorkspaceTests.cs +++ b/test/PowerShellEditorServices.Test/Session/WorkspaceTests.cs @@ -12,6 +12,7 @@ namespace Microsoft.PowerShell.EditorServices.Test.Session { + [Trait("Category", "Workspace")] public class WorkspaceTests { private static readonly Lazy s_lazyDriveLetter = new(() => Path.GetFullPath("\\").Substring(0, 1)); @@ -21,7 +22,6 @@ public class WorkspaceTests : string.Empty; [Fact] - [Trait("Category", "Workspace")] public void CanResolveWorkspaceRelativePath() { string workspacePath = TestUtilities.NormalizePath("c:/Test/Workspace/"); @@ -64,29 +64,26 @@ internal static List ExecuteEnumeratePSFiles( string[] excludeGlobs, string[] includeGlobs, int maxDepth, - bool ignoreReparsePoints - ) + bool ignoreReparsePoints) { - IEnumerable result = workspace.EnumeratePSFiles( + List fileList = new(workspace.EnumeratePSFiles( excludeGlobs: excludeGlobs, includeGlobs: includeGlobs, maxDepth: maxDepth, ignoreReparsePoints: ignoreReparsePoints - ); - List fileList = new(); - fileList.AddRange(result); - // Assume order is not important from EnumeratePSFiles and sort the array so we can use deterministic asserts - fileList.Sort(); + )); + // Assume order is not important from EnumeratePSFiles and sort the array so we can use + // deterministic asserts + fileList.Sort(); return fileList; } [Fact] - [Trait("Category", "Workspace")] public void CanRecurseDirectoryTree() { WorkspaceService workspace = FixturesWorkspace(); - List fileList = ExecuteEnumeratePSFiles( + List actual = ExecuteEnumeratePSFiles( workspace: workspace, excludeGlobs: s_defaultExcludeGlobs, includeGlobs: s_defaultIncludeGlobs, @@ -94,65 +91,58 @@ public void CanRecurseDirectoryTree() ignoreReparsePoints: s_defaultIgnoreReparsePoints ); - if (!RuntimeInformation.FrameworkDescription.StartsWith(".NET Framework")) + List expected = new() { - // .Net Core doesn't appear to use the same three letter pattern matching rule although the docs - // suggest it should be find the '.ps1xml' files because we search for the pattern '*.ps1' - // ref https://docs.microsoft.com/en-us/dotnet/api/system.io.directory.getfiles?view=netcore-2.1#System_IO_Directory_GetFiles_System_String_System_String_System_IO_EnumerationOptions_ - Assert.Equal(4, fileList.Count); - Assert.Equal(Path.Combine(workspace.WorkspacePath, "nested", "donotfind.ps1"), fileList[0]); - Assert.Equal(Path.Combine(workspace.WorkspacePath, "nested", "nestedmodule.psd1"), fileList[1]); - Assert.Equal(Path.Combine(workspace.WorkspacePath, "nested", "nestedmodule.psm1"), fileList[2]); - Assert.Equal(Path.Combine(workspace.WorkspacePath, "rootfile.ps1"), fileList[3]); - } - else + Path.Combine(workspace.WorkspacePath, "nested", "donotfind.ps1"), + Path.Combine(workspace.WorkspacePath, "nested", "nestedmodule.psd1"), + Path.Combine(workspace.WorkspacePath, "nested", "nestedmodule.psm1"), + Path.Combine(workspace.WorkspacePath, "rootfile.ps1") + }; + + // .NET Core doesn't appear to use the same three letter pattern matching rule although the docs + // suggest it should be find the '.ps1xml' files because we search for the pattern '*.ps1' + // ref https://docs.microsoft.com/en-us/dotnet/api/system.io.directory.getfiles?view=netcore-2.1#System_IO_Directory_GetFiles_System_String_System_String_System_IO_EnumerationOptions_ + if (RuntimeInformation.FrameworkDescription.StartsWith(".NET Framework")) { - Assert.Equal(5, fileList.Count); - Assert.Equal(Path.Combine(workspace.WorkspacePath, "nested", "donotfind.ps1"), fileList[0]); - Assert.Equal(Path.Combine(workspace.WorkspacePath, "nested", "nestedmodule.psd1"), fileList[1]); - Assert.Equal(Path.Combine(workspace.WorkspacePath, "nested", "nestedmodule.psm1"), fileList[2]); - Assert.Equal(Path.Combine(workspace.WorkspacePath, "other", "other.ps1xml"), fileList[3]); - Assert.Equal(Path.Combine(workspace.WorkspacePath, "rootfile.ps1"), fileList[4]); + expected.Insert(3, Path.Combine(workspace.WorkspacePath, "other", "other.ps1xml")); } + + Assert.Equal(expected, actual); } [Fact] - [Trait("Category", "Workspace")] public void CanRecurseDirectoryTreeWithLimit() { WorkspaceService workspace = FixturesWorkspace(); - List fileList = ExecuteEnumeratePSFiles( + List actual = ExecuteEnumeratePSFiles( workspace: workspace, excludeGlobs: s_defaultExcludeGlobs, includeGlobs: s_defaultIncludeGlobs, maxDepth: 1, ignoreReparsePoints: s_defaultIgnoreReparsePoints ); - - Assert.Single(fileList); - Assert.Equal(Path.Combine(workspace.WorkspacePath, "rootfile.ps1"), fileList[0]); + Assert.Equal(new[] { Path.Combine(workspace.WorkspacePath, "rootfile.ps1") }, actual); } [Fact] - [Trait("Category", "Workspace")] public void CanRecurseDirectoryTreeWithGlobs() { WorkspaceService workspace = FixturesWorkspace(); - List fileList = ExecuteEnumeratePSFiles( + List actual = ExecuteEnumeratePSFiles( workspace: workspace, - excludeGlobs: new[] { "**/donotfind*" }, // Exclude any files starting with donotfind + excludeGlobs: new[] { "**/donotfind*" }, // Exclude any files starting with donotfind includeGlobs: new[] { "**/*.ps1", "**/*.psd1" }, // Only include PS1 and PSD1 files maxDepth: s_defaultMaxDepth, ignoreReparsePoints: s_defaultIgnoreReparsePoints ); - Assert.Equal(2, fileList.Count); - Assert.Equal(Path.Combine(workspace.WorkspacePath, "nested", "nestedmodule.psd1"), fileList[0]); - Assert.Equal(Path.Combine(workspace.WorkspacePath, "rootfile.ps1"), fileList[1]); + Assert.Equal(new[] { + Path.Combine(workspace.WorkspacePath, "nested", "nestedmodule.psd1"), + Path.Combine(workspace.WorkspacePath, "rootfile.ps1") + }, actual); } [Fact] - [Trait("Category", "Workspace")] public void CanDetermineIsPathInMemory() { string tempDir = Path.GetTempPath(); @@ -161,32 +151,29 @@ public void CanDetermineIsPathInMemory() const string shortUriForm = "git:/c%3A/Users/Keith/GitHub/dahlbyk/posh-git/src/PoshGitTypes.ps1?%7B%22path%22%3A%22c%3A%5C%5CUsers%5C%5CKeith%5C%5CGitHub%5C%5Cdahlbyk%5C%5Cposh-git%5C%5Csrc%5C%5CPoshGitTypes.ps1%22%2C%22ref%22%3A%22~%22%7D"; const string longUriForm = "gitlens-git:c%3A%5CUsers%5CKeith%5CGitHub%5Cdahlbyk%5Cposh-git%5Csrc%5CPoshGitTypes%3Ae0022701.ps1?%7B%22fileName%22%3A%22src%2FPoshGitTypes.ps1%22%2C%22repoPath%22%3A%22c%3A%2FUsers%2FKeith%2FGitHub%2Fdahlbyk%2Fposh-git%22%2C%22sha%22%3A%22e0022701fa12e0bc22d0458673d6443c942b974a%22%7D"; - var testCases = new[] { - // Test short file absolute paths - new { IsInMemory = false, Path = shortDirPath }, - new { IsInMemory = false, Path = shortFilePath }, - new { IsInMemory = false, Path = new Uri(shortDirPath).ToString() }, - new { IsInMemory = false, Path = new Uri(shortFilePath).ToString() }, - - // Test short file relative paths - not sure we'll ever get these but just in case - new { IsInMemory = false, Path = "foo.ps1" }, - new { IsInMemory = false, Path = Path.Combine(new [] { "..", "foo.ps1" }) }, - + string[] inMemoryPaths = new[] { // Test short non-file paths - new { IsInMemory = true, Path = "untitled:untitled-1" }, - new { IsInMemory = true, Path = shortUriForm }, - new { IsInMemory = true, Path = "inmemory://foo.ps1" }, + "untitled:untitled-1", + shortUriForm, + "inmemory://foo.ps1", + // Test long non-file path + longUriForm + }; - // Test long non-file path - known to have crashed PSES - new { IsInMemory = true, Path = longUriForm }, + Assert.All(inMemoryPaths, (p) => Assert.True(WorkspaceService.IsPathInMemory(p))); + + string[] notInMemoryPaths = new[] { + // Test short file absolute paths + shortDirPath, + shortFilePath, + new Uri(shortDirPath).ToString(), + new Uri(shortFilePath).ToString(), + // Test short file relative paths + "foo.ps1", + Path.Combine(new[] { "..", "foo.ps1" }) }; - foreach (var testCase in testCases) - { - Assert.True( - WorkspaceService.IsPathInMemory(testCase.Path) == testCase.IsInMemory, - $"Testing path {testCase.Path}"); - } + Assert.All(notInMemoryPaths, (p) => Assert.False(WorkspaceService.IsPathInMemory(p))); } } } From b17e5a3399ab4d2a65ed17d4ca5d5990e6ec0cf6 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Wed, 29 Jun 2022 11:49:18 -0700 Subject: [PATCH 2/8] Fixes 'processses' typo --- .../PowerShell/Handlers/IGetPSHostProcessesHandler.cs | 4 ++-- .../PowerShell/Handlers/PSHostProcessAndRunspaceHandlers.cs | 4 ++-- .../LanguageServerProtocolMessageTests.cs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/PowerShellEditorServices/Services/PowerShell/Handlers/IGetPSHostProcessesHandler.cs b/src/PowerShellEditorServices/Services/PowerShell/Handlers/IGetPSHostProcessesHandler.cs index 896564d1e..0772f4338 100644 --- a/src/PowerShellEditorServices/Services/PowerShell/Handlers/IGetPSHostProcessesHandler.cs +++ b/src/PowerShellEditorServices/Services/PowerShell/Handlers/IGetPSHostProcessesHandler.cs @@ -7,9 +7,9 @@ namespace Microsoft.PowerShell.EditorServices.Handlers { [Serial, Method("powerShell/getPSHostProcesses")] - internal interface IGetPSHostProcessesHandler : IJsonRpcRequestHandler { } + internal interface IGetPSHostProcessesHandler : IJsonRpcRequestHandler { } - internal class GetPSHostProcesssesParams : IRequest { } + internal class GetPSHostProcessesParams : IRequest { } internal class PSHostProcessResponse { diff --git a/src/PowerShellEditorServices/Services/PowerShell/Handlers/PSHostProcessAndRunspaceHandlers.cs b/src/PowerShellEditorServices/Services/PowerShell/Handlers/PSHostProcessAndRunspaceHandlers.cs index f288a9584..ed0b37b04 100644 --- a/src/PowerShellEditorServices/Services/PowerShell/Handlers/PSHostProcessAndRunspaceHandlers.cs +++ b/src/PowerShellEditorServices/Services/PowerShell/Handlers/PSHostProcessAndRunspaceHandlers.cs @@ -9,8 +9,8 @@ namespace Microsoft.PowerShell.EditorServices.Handlers { - using Microsoft.PowerShell.EditorServices.Services.PowerShell; using System.Management.Automation; + using Microsoft.PowerShell.EditorServices.Services.PowerShell; internal class PSHostProcessAndRunspaceHandlers : IGetPSHostProcessesHandler, IGetRunspaceHandler { @@ -23,7 +23,7 @@ public PSHostProcessAndRunspaceHandlers(ILoggerFactory factory, IInternalPowerSh _executionService = executionService; } - public Task Handle(GetPSHostProcesssesParams request, CancellationToken cancellationToken) + public Task Handle(GetPSHostProcessesParams request, CancellationToken cancellationToken) { List psHostProcesses = new(); diff --git a/test/PowerShellEditorServices.Test.E2E/LanguageServerProtocolMessageTests.cs b/test/PowerShellEditorServices.Test.E2E/LanguageServerProtocolMessageTests.cs index 2ecc29ebc..1ed6f3733 100644 --- a/test/PowerShellEditorServices.Test.E2E/LanguageServerProtocolMessageTests.cs +++ b/test/PowerShellEditorServices.Test.E2E/LanguageServerProtocolMessageTests.cs @@ -576,7 +576,7 @@ public async Task CanSendPowerShellGetPSHostProcessesRequestAsync() await PsesLanguageClient .SendRequest( "powerShell/getPSHostProcesses", - new GetPSHostProcesssesParams()) + new GetPSHostProcessesParams()) .Returning(CancellationToken.None).ConfigureAwait(true); } finally From e4f91300c0d677c9100b2c852a39284ae5074ba8 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Wed, 29 Jun 2022 12:23:54 -0700 Subject: [PATCH 3/8] Ignore intentional sync aways in `Dispose` methods --- .../Server/PsesServiceCollectionExtensions.cs | 3 ++- .../DebugAdapter/Handlers/LaunchAndAttachHandler.cs | 2 +- .../Services/PowerShell/Context/PowerShellContextFrame.cs | 2 +- .../PowerShell/Context/PowerShellVersionDetails.cs | 6 +++--- .../Processes/ServerProcess.cs | 8 ++++++-- .../Debugging/DebugServiceTests.cs | 2 ++ .../Extensions/ExtensionCommandTests.cs | 2 ++ .../Language/CompletionHandlerTests.cs | 2 ++ .../Language/SymbolsServiceTests.cs | 2 ++ .../Session/PsesInternalHostTests.cs | 2 ++ 10 files changed, 23 insertions(+), 8 deletions(-) diff --git a/src/PowerShellEditorServices/Server/PsesServiceCollectionExtensions.cs b/src/PowerShellEditorServices/Server/PsesServiceCollectionExtensions.cs index c552515de..3c02510b9 100644 --- a/src/PowerShellEditorServices/Server/PsesServiceCollectionExtensions.cs +++ b/src/PowerShellEditorServices/Server/PsesServiceCollectionExtensions.cs @@ -48,8 +48,9 @@ public static IServiceCollection AddPsesLanguageServices( // is ready, it will be available. NOTE: We cannot await this because it // uses a lazy initialization to avoid a race with the dependency injection // framework, see the EditorObject class for that! +#pragma warning disable VSTHRD110 extensionService.InitializeAsync(); - +#pragma warning restore VSTHRD110 return extensionService; }) .AddSingleton(); diff --git a/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/LaunchAndAttachHandler.cs b/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/LaunchAndAttachHandler.cs index 4b7cd9acc..a93d4bb02 100644 --- a/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/LaunchAndAttachHandler.cs +++ b/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/LaunchAndAttachHandler.cs @@ -453,7 +453,7 @@ await _executionService.ExecutePSCommandAsync( // PSES sends the initialized event at the end of the Launch/Attach handler // The way that the Omnisharp server works is that this OnStarted handler runs after OnInitialized - // (after the Initialize DAP response is sent to the client) but before the _Initalized_ DAP event + // (after the Initialize DAP response is sent to the client) but before the _Initialized_ DAP event // gets sent to the client. Because of the way PSES handles breakpoints, // we can't send the Initialized event until _after_ we finish the Launch/Attach handler. // The flow above depicts this. To achieve this, we wait until _debugStateService.ServerStarted diff --git a/src/PowerShellEditorServices/Services/PowerShell/Context/PowerShellContextFrame.cs b/src/PowerShellEditorServices/Services/PowerShell/Context/PowerShellContextFrame.cs index d837fb5d2..f75027ba8 100644 --- a/src/PowerShellEditorServices/Services/PowerShell/Context/PowerShellContextFrame.cs +++ b/src/PowerShellEditorServices/Services/PowerShell/Context/PowerShellContextFrame.cs @@ -79,7 +79,7 @@ protected virtual void Dispose(bool disposing) public void Dispose() { // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method - Dispose(disposing: true); + Dispose(true); GC.SuppressFinalize(this); } diff --git a/src/PowerShellEditorServices/Services/PowerShell/Context/PowerShellVersionDetails.cs b/src/PowerShellEditorServices/Services/PowerShell/Context/PowerShellVersionDetails.cs index 957a032f8..4bbdd47e1 100644 --- a/src/PowerShellEditorServices/Services/PowerShell/Context/PowerShellVersionDetails.cs +++ b/src/PowerShellEditorServices/Services/PowerShell/Context/PowerShellVersionDetails.cs @@ -1,11 +1,11 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using Microsoft.Extensions.Logging; -using Microsoft.PowerShell.EditorServices.Services.PowerShell.Utility; using System; using System.Collections; using System.Linq; +using Microsoft.Extensions.Logging; +using Microsoft.PowerShell.EditorServices.Services.PowerShell.Utility; namespace Microsoft.PowerShell.EditorServices.Services.PowerShell.Context { @@ -91,7 +91,7 @@ public PowerShellVersionDetails( /// Gets the PowerShell version details for the given runspace. /// /// An ILogger implementation used for writing log messages. - /// The PowerShell instance for which to to get the version. + /// The PowerShell instance for which to get the version. /// A new PowerShellVersionDetails instance. public static PowerShellVersionDetails GetVersionDetails(ILogger logger, PowerShell pwsh) { diff --git a/test/PowerShellEditorServices.Test.E2E/Processes/ServerProcess.cs b/test/PowerShellEditorServices.Test.E2E/Processes/ServerProcess.cs index 020ee8779..8d744dc11 100644 --- a/test/PowerShellEditorServices.Test.E2E/Processes/ServerProcess.cs +++ b/test/PowerShellEditorServices.Test.E2E/Processes/ServerProcess.cs @@ -43,7 +43,7 @@ protected ServerProcess(ILoggerFactory loggerFactory) } /// - /// Finaliser for . + /// Finalizer for . /// ~ServerProcess() { @@ -53,7 +53,11 @@ protected ServerProcess(ILoggerFactory loggerFactory) /// /// Dispose of resources being used by the launcher. /// - public void Dispose() => Dispose(true); + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } /// /// Dispose of resources being used by the launcher. diff --git a/test/PowerShellEditorServices.Test/Debugging/DebugServiceTests.cs b/test/PowerShellEditorServices.Test/Debugging/DebugServiceTests.cs index 38f3b6231..74a233fb6 100644 --- a/test/PowerShellEditorServices.Test/Debugging/DebugServiceTests.cs +++ b/test/PowerShellEditorServices.Test/Debugging/DebugServiceTests.cs @@ -63,7 +63,9 @@ public void Dispose() { debugService.Abort(); debuggerStoppedQueue.Dispose(); +#pragma warning disable VSTHRD002 psesHost.StopAsync().Wait(); +#pragma warning restore VSTHRD002 GC.SuppressFinalize(this); } diff --git a/test/PowerShellEditorServices.Test/Extensions/ExtensionCommandTests.cs b/test/PowerShellEditorServices.Test/Extensions/ExtensionCommandTests.cs index 1f7998ec5..2ae739c26 100644 --- a/test/PowerShellEditorServices.Test/Extensions/ExtensionCommandTests.cs +++ b/test/PowerShellEditorServices.Test/Extensions/ExtensionCommandTests.cs @@ -39,7 +39,9 @@ public ExtensionCommandTests() public void Dispose() { +#pragma warning disable VSTHRD002 psesHost.StopAsync().Wait(); +#pragma warning restore VSTHRD002 GC.SuppressFinalize(this); } diff --git a/test/PowerShellEditorServices.Test/Language/CompletionHandlerTests.cs b/test/PowerShellEditorServices.Test/Language/CompletionHandlerTests.cs index fc1590d35..21d8cce5b 100644 --- a/test/PowerShellEditorServices.Test/Language/CompletionHandlerTests.cs +++ b/test/PowerShellEditorServices.Test/Language/CompletionHandlerTests.cs @@ -35,7 +35,9 @@ public CompletionHandlerTests() public void Dispose() { +#pragma warning disable VSTHRD002 psesHost.StopAsync().Wait(); +#pragma warning restore VSTHRD002 GC.SuppressFinalize(this); } diff --git a/test/PowerShellEditorServices.Test/Language/SymbolsServiceTests.cs b/test/PowerShellEditorServices.Test/Language/SymbolsServiceTests.cs index 06879666d..959a9b1fa 100644 --- a/test/PowerShellEditorServices.Test/Language/SymbolsServiceTests.cs +++ b/test/PowerShellEditorServices.Test/Language/SymbolsServiceTests.cs @@ -46,7 +46,9 @@ public SymbolsServiceTests() public void Dispose() { +#pragma warning disable VSTHRD002 psesHost.StopAsync().GetAwaiter().GetResult(); +#pragma warning restore VSTHRD002 CommandHelpers.s_cmdletToAliasCache.Clear(); CommandHelpers.s_aliasToCmdletCache.Clear(); GC.SuppressFinalize(this); diff --git a/test/PowerShellEditorServices.Test/Session/PsesInternalHostTests.cs b/test/PowerShellEditorServices.Test/Session/PsesInternalHostTests.cs index c85aa0d2f..e07b504eb 100644 --- a/test/PowerShellEditorServices.Test/Session/PsesInternalHostTests.cs +++ b/test/PowerShellEditorServices.Test/Session/PsesInternalHostTests.cs @@ -27,7 +27,9 @@ public class PsesInternalHostTests : IDisposable public void Dispose() { +#pragma warning disable VSTHRD002 psesHost.StopAsync().Wait(); +#pragma warning restore VSTHRD002 GC.SuppressFinalize(this); } From 65456bd862cee289cbdf98f99dab64dedf48da9b Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Wed, 29 Jun 2022 11:18:16 -0700 Subject: [PATCH 4/8] Remove arbitrary timeouts from tests Our CI can be quite slow, and these were just arbitrary timeouts. If the tests succeed, they should never be hit. Let's instead give them as much time as possible. CI will eventually kill the run if it hangs, and locally devs can do the same. This should help improve CI stability. Also fixes the disposal bug in the tests. --- .../PowerShell/Host/PsesInternalHost.cs | 7 +- .../PowerShell/Utility/CancellationContext.cs | 13 ++- .../DebugAdapterClientExtensions.cs | 5 +- .../DebugAdapterProtocolMessageTests.cs | 88 +++++++++---------- .../LSPTestsFixures.cs | 13 +-- .../LanguageServerProtocolMessageTests.cs | 12 ++- .../Debugging/DebugServiceTests.cs | 4 +- .../Session/PsesInternalHostTests.cs | 3 +- 8 files changed, 68 insertions(+), 77 deletions(-) diff --git a/src/PowerShellEditorServices/Services/PowerShell/Host/PsesInternalHost.cs b/src/PowerShellEditorServices/Services/PowerShell/Host/PsesInternalHost.cs index 828cbf890..97ee2edd7 100644 --- a/src/PowerShellEditorServices/Services/PowerShell/Host/PsesInternalHost.cs +++ b/src/PowerShellEditorServices/Services/PowerShell/Host/PsesInternalHost.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System; @@ -291,11 +291,6 @@ public void TriggerShutdown() if (Interlocked.Exchange(ref _shuttingDown, 1) == 0) { _cancellationContext.CancelCurrentTaskStack(); - // NOTE: This is mostly for sanity's sake, as during debugging of tests I became - // concerned that the repeated creation and disposal of the host was not also - // joining and disposing this thread, leaving the tests in a weird state. Because - // the tasks have been canceled, we should be able to join this thread. - _pipelineThread.Join(); } } diff --git a/src/PowerShellEditorServices/Services/PowerShell/Utility/CancellationContext.cs b/src/PowerShellEditorServices/Services/PowerShell/Utility/CancellationContext.cs index 10dba79a3..24d121ba0 100644 --- a/src/PowerShellEditorServices/Services/PowerShell/Utility/CancellationContext.cs +++ b/src/PowerShellEditorServices/Services/PowerShell/Utility/CancellationContext.cs @@ -103,7 +103,18 @@ internal CancellationScope( public CancellationToken CancellationToken => _cancellationSource.Token; - public void Cancel() => _cancellationSource.Cancel(); + public void Cancel() + { + try + { + _cancellationSource.Cancel(); + } + catch (ObjectDisposedException) + { + // We don't want this race condition to cause flaky tests. + // TODO: Find out the cause of the race! + } + } public bool IsIdleScope { get; } diff --git a/test/PowerShellEditorServices.Test.E2E/DebugAdapterClientExtensions.cs b/test/PowerShellEditorServices.Test.E2E/DebugAdapterClientExtensions.cs index 65ac1d20c..3ae9748d1 100644 --- a/test/PowerShellEditorServices.Test.E2E/DebugAdapterClientExtensions.cs +++ b/test/PowerShellEditorServices.Test.E2E/DebugAdapterClientExtensions.cs @@ -6,7 +6,6 @@ using Microsoft.PowerShell.EditorServices.Handlers; using OmniSharp.Extensions.DebugAdapter.Client; using OmniSharp.Extensions.DebugAdapter.Protocol.Requests; -using System.Threading; namespace PowerShellEditorServices.Test.E2E { @@ -29,9 +28,7 @@ public static async Task LaunchScript(this DebugAdapterClient debugAdapterClient } // This will check to see if we received the Initialized event from the server. - await Task.Run( - async () => await started.Task.ConfigureAwait(true), - new CancellationTokenSource(2000).Token).ConfigureAwait(true); + await started.Task.ConfigureAwait(true); } } } diff --git a/test/PowerShellEditorServices.Test.E2E/DebugAdapterProtocolMessageTests.cs b/test/PowerShellEditorServices.Test.E2E/DebugAdapterProtocolMessageTests.cs index 14e250823..f80f08f49 100644 --- a/test/PowerShellEditorServices.Test.E2E/DebugAdapterProtocolMessageTests.cs +++ b/test/PowerShellEditorServices.Test.E2E/DebugAdapterProtocolMessageTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System; @@ -39,7 +39,7 @@ public async Task InitializeAsync() { LoggerFactory factory = new(); _psesProcess = new PsesStdioProcess(factory, true); - await _psesProcess.Start().ConfigureAwait(false); + await _psesProcess.Start().ConfigureAwait(true); TaskCompletionSource initialized = new(); @@ -90,26 +90,21 @@ public async Task InitializeAsync() // that gets completed when we receive the response to Initialize // This tells us that we are ready to send messages to PSES... but are not stuck waiting for // Initialized. - PsesDebugAdapterClient.Initialize(CancellationToken.None).ConfigureAwait(false); - await initialized.Task.ConfigureAwait(false); +#pragma warning disable CS4014 + PsesDebugAdapterClient.Initialize(CancellationToken.None).ConfigureAwait(true); +#pragma warning restore CS4014 + await initialized.Task.ConfigureAwait(true); } public async Task DisposeAsync() { - try + await PsesDebugAdapterClient.RequestDisconnect(new DisconnectArguments { - await PsesDebugAdapterClient.RequestDisconnect(new DisconnectArguments - { - Restart = false, - TerminateDebuggee = true - }).ConfigureAwait(false); - await _psesProcess.Stop().ConfigureAwait(false); - PsesDebugAdapterClient?.Dispose(); - } - catch (ObjectDisposedException) - { - // Language client has a disposal bug in it - } + Restart = false, + TerminateDebuggee = true + }).ConfigureAwait(true); + await _psesProcess.Stop().ConfigureAwait(true); + PsesDebugAdapterClient?.Dispose(); } private static string NewTestFile(string script, bool isPester = false) @@ -147,7 +142,16 @@ private string GenerateScriptFromLoggingStatements(params string[] logStatements return builder.ToString(); } - private static string[] GetLog() => File.ReadLines(s_testOutputPath).ToArray(); + private static async Task GetLog() + { + while (!File.Exists(s_testOutputPath)) + { + await Task.Delay(1000).ConfigureAwait(true); + } + // Sleep one more time after the file exists so whatever is writing can finish. + await Task.Delay(1000).ConfigureAwait(true); + return File.ReadLines(s_testOutputPath).ToArray(); + } [Fact] public void CanInitializeWithCorrectServerSettings() @@ -165,14 +169,12 @@ public async Task CanLaunchScriptWithNoBreakpointsAsync() { string filePath = NewTestFile(GenerateScriptFromLoggingStatements("works")); - await PsesDebugAdapterClient.LaunchScript(filePath, Started).ConfigureAwait(false); + await PsesDebugAdapterClient.LaunchScript(filePath, Started).ConfigureAwait(true); - ConfigurationDoneResponse configDoneResponse = await PsesDebugAdapterClient.RequestConfigurationDone(new ConfigurationDoneArguments()).ConfigureAwait(false); + ConfigurationDoneResponse configDoneResponse = await PsesDebugAdapterClient.RequestConfigurationDone(new ConfigurationDoneArguments()).ConfigureAwait(true); Assert.NotNull(configDoneResponse); - await Task.Delay(2000).ConfigureAwait(false); - - string[] log = GetLog(); - Assert.Equal("works", log[0]); + Assert.Collection(await GetLog().ConfigureAwait(true), + (i) => Assert.Equal("works", i)); } [SkippableFact] @@ -188,7 +190,7 @@ public async Task CanSetBreakpointsAsync() "after breakpoint" )); - await PsesDebugAdapterClient.LaunchScript(filePath, Started).ConfigureAwait(false); + await PsesDebugAdapterClient.LaunchScript(filePath, Started).ConfigureAwait(true); // {"command":"setBreakpoints","arguments":{"source":{"name":"dfsdfg.ps1","path":"/Users/tyleonha/Code/PowerShell/Misc/foo/dfsdfg.ps1"},"lines":[2],"breakpoints":[{"line":2}],"sourceModified":false},"type":"request","seq":3} SetBreakpointsResponse setBreakpointsResponse = await PsesDebugAdapterClient.SetBreakpoints(new SetBreakpointsArguments @@ -196,29 +198,24 @@ public async Task CanSetBreakpointsAsync() Source = new Source { Name = Path.GetFileName(filePath), Path = filePath }, Breakpoints = new SourceBreakpoint[] { new SourceBreakpoint { Line = 2 } }, SourceModified = false, - }).ConfigureAwait(false); + }).ConfigureAwait(true); Breakpoint breakpoint = setBreakpointsResponse.Breakpoints.First(); Assert.True(breakpoint.Verified); Assert.Equal(filePath, breakpoint.Source.Path, ignoreCase: s_isWindows); Assert.Equal(2, breakpoint.Line); - ConfigurationDoneResponse configDoneResponse = await PsesDebugAdapterClient.RequestConfigurationDone(new ConfigurationDoneArguments()).ConfigureAwait(false); + ConfigurationDoneResponse configDoneResponse = await PsesDebugAdapterClient.RequestConfigurationDone(new ConfigurationDoneArguments()).ConfigureAwait(true); Assert.NotNull(configDoneResponse); - await Task.Delay(2000).ConfigureAwait(false); - - string[] log = GetLog(); - Assert.Single(log, (i) => i == "before breakpoint"); + Assert.Collection(await GetLog().ConfigureAwait(true), + (i) => Assert.Equal("before breakpoint", i)); + File.Delete(s_testOutputPath); ContinueResponse continueResponse = await PsesDebugAdapterClient.RequestContinue( new ContinueArguments { ThreadId = 1 }).ConfigureAwait(true); Assert.NotNull(continueResponse); - await Task.Delay(2000).ConfigureAwait(false); - - log = GetLog(); - Assert.Collection(log, - (i) => Assert.Equal("before breakpoint", i), + Assert.Collection(await GetLog().ConfigureAwait(true), (i) => Assert.Equal("at breakpoint", i), (i) => Assert.Equal("after breakpoint", i)); } @@ -247,24 +244,24 @@ public async Task CanStepPastSystemWindowsForms() "Write-Host $form" })); - await PsesDebugAdapterClient.LaunchScript(filePath, Started).ConfigureAwait(false); + await PsesDebugAdapterClient.LaunchScript(filePath, Started).ConfigureAwait(true); SetFunctionBreakpointsResponse setBreakpointsResponse = await PsesDebugAdapterClient.SetFunctionBreakpoints( new SetFunctionBreakpointsArguments { Breakpoints = new FunctionBreakpoint[] { new FunctionBreakpoint { Name = "Write-Host", } } - }).ConfigureAwait(false); + }).ConfigureAwait(true); Breakpoint breakpoint = setBreakpointsResponse.Breakpoints.First(); Assert.True(breakpoint.Verified); - ConfigurationDoneResponse configDoneResponse = await PsesDebugAdapterClient.RequestConfigurationDone(new ConfigurationDoneArguments()).ConfigureAwait(false); + ConfigurationDoneResponse configDoneResponse = await PsesDebugAdapterClient.RequestConfigurationDone(new ConfigurationDoneArguments()).ConfigureAwait(true); Assert.NotNull(configDoneResponse); - await Task.Delay(2000).ConfigureAwait(false); + await Task.Delay(5000).ConfigureAwait(true); VariablesResponse variablesResponse = await PsesDebugAdapterClient.RequestVariables( - new VariablesArguments { VariablesReference = 1 }).ConfigureAwait(false); + new VariablesArguments { VariablesReference = 1 }).ConfigureAwait(true); Variable form = variablesResponse.Variables.FirstOrDefault(v => v.Name == "$form"); Assert.NotNull(form); @@ -286,13 +283,12 @@ public async Task CanLaunchScriptWithCommentedLastLineAsync() // PsesLaunchRequestArguments.Script, which is then assigned to // DebugStateService.ScriptToLaunch in that handler, and finally used by the // ConfigurationDoneHandler in LaunchScriptAsync. - await PsesDebugAdapterClient.LaunchScript(script, Started).ConfigureAwait(false); + await PsesDebugAdapterClient.LaunchScript(script, Started).ConfigureAwait(true); - ConfigurationDoneResponse configDoneResponse = await PsesDebugAdapterClient.RequestConfigurationDone(new ConfigurationDoneArguments()).ConfigureAwait(false); + ConfigurationDoneResponse configDoneResponse = await PsesDebugAdapterClient.RequestConfigurationDone(new ConfigurationDoneArguments()).ConfigureAwait(true); Assert.NotNull(configDoneResponse); - await Task.Delay(2000).ConfigureAwait(false); - - Assert.Collection(GetLog(), (i) => Assert.Equal("a log statement", i)); + Assert.Collection(await GetLog().ConfigureAwait(true), + (i) => Assert.Equal("a log statement", i)); } } } diff --git a/test/PowerShellEditorServices.Test.E2E/LSPTestsFixures.cs b/test/PowerShellEditorServices.Test.E2E/LSPTestsFixures.cs index 12e29ee07..c31333d89 100644 --- a/test/PowerShellEditorServices.Test.E2E/LSPTestsFixures.cs +++ b/test/PowerShellEditorServices.Test.E2E/LSPTestsFixures.cs @@ -95,16 +95,9 @@ public async Task InitializeAsync() public async Task DisposeAsync() { - try - { - await PsesLanguageClient.Shutdown().ConfigureAwait(false); - await _psesProcess.Stop().ConfigureAwait(false); - PsesLanguageClient?.Dispose(); - } - catch (ObjectDisposedException) - { - // Language client has a disposal bug in it - } + await PsesLanguageClient.Shutdown().ConfigureAwait(false); + await _psesProcess.Stop().ConfigureAwait(false); + PsesLanguageClient?.Dispose(); } } } diff --git a/test/PowerShellEditorServices.Test.E2E/LanguageServerProtocolMessageTests.cs b/test/PowerShellEditorServices.Test.E2E/LanguageServerProtocolMessageTests.cs index 1ed6f3733..43ca01a13 100644 --- a/test/PowerShellEditorServices.Test.E2E/LanguageServerProtocolMessageTests.cs +++ b/test/PowerShellEditorServices.Test.E2E/LanguageServerProtocolMessageTests.cs @@ -12,6 +12,10 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.PowerShell.EditorServices.Handlers; +using Microsoft.PowerShell.EditorServices.Logging; +using Microsoft.PowerShell.EditorServices.Services.Configuration; +using Microsoft.PowerShell.EditorServices.Services.PowerShell; +using Microsoft.PowerShell.EditorServices.Services.Template; using Newtonsoft.Json.Linq; using OmniSharp.Extensions.LanguageServer.Protocol; using OmniSharp.Extensions.LanguageServer.Protocol.Client; @@ -21,10 +25,6 @@ using Xunit; using Xunit.Abstractions; using Range = OmniSharp.Extensions.LanguageServer.Protocol.Models.Range; -using Microsoft.PowerShell.EditorServices.Logging; -using Microsoft.PowerShell.EditorServices.Services.Configuration; -using Microsoft.PowerShell.EditorServices.Services.PowerShell; -using Microsoft.PowerShell.EditorServices.Services.Template; namespace PowerShellEditorServices.Test.E2E { @@ -1149,8 +1149,6 @@ await PsesLanguageClient [Fact] public async Task CanSendEvaluateRequestAsync() { - using CancellationTokenSource cancellationSource = new(millisecondsDelay: 5000); - EvaluateResponseBody evaluateResponseBody = await PsesLanguageClient .SendRequest( @@ -1159,7 +1157,7 @@ await PsesLanguageClient { Expression = "Get-ChildItem" }) - .Returning(cancellationSource.Token).ConfigureAwait(true); + .Returning(CancellationToken.None).ConfigureAwait(true); // These always gets returned so this test really just makes sure we get _any_ response. Assert.Equal("", evaluateResponseBody.Result); diff --git a/test/PowerShellEditorServices.Test/Debugging/DebugServiceTests.cs b/test/PowerShellEditorServices.Test/Debugging/DebugServiceTests.cs index 74a233fb6..cb018ae5e 100644 --- a/test/PowerShellEditorServices.Test/Debugging/DebugServiceTests.cs +++ b/test/PowerShellEditorServices.Test/Debugging/DebugServiceTests.cs @@ -103,7 +103,7 @@ private Task ExecutePowerShellCommand(string command, params string[] args) private void AssertDebuggerPaused() { - DebuggerStoppedEventArgs eventArgs = debuggerStoppedQueue.Take(new CancellationTokenSource(5000).Token); + DebuggerStoppedEventArgs eventArgs = debuggerStoppedQueue.Take(CancellationToken.None); Assert.Empty(eventArgs.OriginalEvent.Breakpoints); } @@ -112,7 +112,7 @@ private void AssertDebuggerStopped( int lineNumber = -1, CommandBreakpointDetails commandBreakpointDetails = default) { - DebuggerStoppedEventArgs eventArgs = debuggerStoppedQueue.Take(new CancellationTokenSource(5000).Token); + DebuggerStoppedEventArgs eventArgs = debuggerStoppedQueue.Take(CancellationToken.None); Assert.True(psesHost.DebugContext.IsStopped); diff --git a/test/PowerShellEditorServices.Test/Session/PsesInternalHostTests.cs b/test/PowerShellEditorServices.Test/Session/PsesInternalHostTests.cs index e07b504eb..7412ac59d 100644 --- a/test/PowerShellEditorServices.Test/Session/PsesInternalHostTests.cs +++ b/test/PowerShellEditorServices.Test/Session/PsesInternalHostTests.cs @@ -86,11 +86,12 @@ public async Task CanQueueParallelPSCommands() [Fact] public async Task CanCancelExecutionWithToken() { + using CancellationTokenSource cancellationSource = new(millisecondsDelay: 1000); await Assert.ThrowsAsync(() => { return psesHost.ExecutePSCommandAsync( new PSCommand().AddScript("Start-Sleep 10"), - new CancellationTokenSource(1000).Token); + cancellationSource.Token); }).ConfigureAwait(true); } From d29dc6da6c7a0dc5e0ae538b3ac93579907fb7ce Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Thu, 30 Jun 2022 16:05:11 -0700 Subject: [PATCH 5/8] Set `ServerStarted` with `TrySetResult` since unit tests might do it twice --- .../Services/DebugAdapter/DebugEventHandlerService.cs | 2 +- .../Services/DebugAdapter/Handlers/LaunchAndAttachHandler.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/PowerShellEditorServices/Services/DebugAdapter/DebugEventHandlerService.cs b/src/PowerShellEditorServices/Services/DebugAdapter/DebugEventHandlerService.cs index 69ddb1209..e3237a3b0 100644 --- a/src/PowerShellEditorServices/Services/DebugAdapter/DebugEventHandlerService.cs +++ b/src/PowerShellEditorServices/Services/DebugAdapter/DebugEventHandlerService.cs @@ -101,7 +101,7 @@ private void OnRunspaceChanged(object sender, RunspaceChangedEventArgs e) // Sends the InitializedEvent so that the debugger will continue // sending configuration requests _debugStateService.WaitingForAttach = false; - _debugStateService.ServerStarted.SetResult(true); + _debugStateService.ServerStarted.TrySetResult(true); } return; diff --git a/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/LaunchAndAttachHandler.cs b/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/LaunchAndAttachHandler.cs index a93d4bb02..580b7cc89 100644 --- a/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/LaunchAndAttachHandler.cs +++ b/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/LaunchAndAttachHandler.cs @@ -210,7 +210,7 @@ public async Task Handle(PsesLaunchRequestArguments request, Can // Sends the InitializedEvent so that the debugger will continue // sending configuration requests - _debugStateService.ServerStarted.SetResult(true); + _debugStateService.ServerStarted.TrySetResult(true); return new LaunchResponse(); } @@ -440,7 +440,7 @@ await _executionService.ExecutePSCommandAsync( if (runspaceVersion.Version.Major >= 7) { - _debugStateService.ServerStarted.SetResult(true); + _debugStateService.ServerStarted.TrySetResult(true); } return new AttachResponse(); } From 9480cd1dabdfc9e5284a1cc324b25229ed8ec038 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Fri, 1 Jul 2022 11:56:49 -0700 Subject: [PATCH 6/8] Improve skipped test messages --- .../DebugAdapterProtocolMessageTests.cs | 11 ++++--- .../LanguageServerProtocolMessageTests.cs | 31 +++++++------------ 2 files changed, 18 insertions(+), 24 deletions(-) diff --git a/test/PowerShellEditorServices.Test.E2E/DebugAdapterProtocolMessageTests.cs b/test/PowerShellEditorServices.Test.E2E/DebugAdapterProtocolMessageTests.cs index f80f08f49..b151b57d0 100644 --- a/test/PowerShellEditorServices.Test.E2E/DebugAdapterProtocolMessageTests.cs +++ b/test/PowerShellEditorServices.Test.E2E/DebugAdapterProtocolMessageTests.cs @@ -180,9 +180,8 @@ public async Task CanLaunchScriptWithNoBreakpointsAsync() [SkippableFact] public async Task CanSetBreakpointsAsync() { - Skip.If( - PsesStdioProcess.RunningInConstrainedLanguageMode, - "You can't set breakpoints in ConstrainedLanguage mode."); + Skip.If(PsesStdioProcess.RunningInConstrainedLanguageMode, + "Breakpoints can't be set in Constrained Language Mode."); string filePath = NewTestFile(GenerateScriptFromLoggingStatements( "before breakpoint", @@ -234,8 +233,10 @@ public async Task CanSetBreakpointsAsync() [SkippableFact] public async Task CanStepPastSystemWindowsForms() { - Skip.IfNot(PsesStdioProcess.IsWindowsPowerShell); - Skip.If(PsesStdioProcess.RunningInConstrainedLanguageMode); + Skip.IfNot(PsesStdioProcess.IsWindowsPowerShell, + "Windows Forms requires Windows PowerShell."); + Skip.If(PsesStdioProcess.RunningInConstrainedLanguageMode, + "Breakpoints can't be set in Constrained Language Mode."); string filePath = NewTestFile(string.Join(Environment.NewLine, new[] { diff --git a/test/PowerShellEditorServices.Test.E2E/LanguageServerProtocolMessageTests.cs b/test/PowerShellEditorServices.Test.E2E/LanguageServerProtocolMessageTests.cs index 43ca01a13..bcfd49de9 100644 --- a/test/PowerShellEditorServices.Test.E2E/LanguageServerProtocolMessageTests.cs +++ b/test/PowerShellEditorServices.Test.E2E/LanguageServerProtocolMessageTests.cs @@ -154,8 +154,7 @@ function CanSendWorkspaceSymbolRequest { [SkippableFact] public async Task CanReceiveDiagnosticsFromFileOpenAsync() { - Skip.If( - PsesStdioProcess.RunningInConstrainedLanguageMode && PsesStdioProcess.IsWindowsPowerShell, + Skip.If(PsesStdioProcess.RunningInConstrainedLanguageMode && PsesStdioProcess.IsWindowsPowerShell, "Windows PowerShell doesn't trust PSScriptAnalyzer by default so it won't load."); NewTestFile("$a = 4"); @@ -177,8 +176,7 @@ public async Task WontReceiveDiagnosticsFromFileOpenThatIsNotPowerShellAsync() [SkippableFact] public async Task CanReceiveDiagnosticsFromFileChangedAsync() { - Skip.If( - PsesStdioProcess.RunningInConstrainedLanguageMode && PsesStdioProcess.IsWindowsPowerShell, + Skip.If(PsesStdioProcess.RunningInConstrainedLanguageMode && PsesStdioProcess.IsWindowsPowerShell, "Windows PowerShell doesn't trust PSScriptAnalyzer by default so it won't load."); string filePath = NewTestFile("$a = 4"); @@ -229,8 +227,7 @@ public async Task CanReceiveDiagnosticsFromFileChangedAsync() [SkippableFact] public async Task CanReceiveDiagnosticsFromConfigurationChangeAsync() { - Skip.If( - PsesStdioProcess.RunningInConstrainedLanguageMode && PsesStdioProcess.IsWindowsPowerShell, + Skip.If(PsesStdioProcess.RunningInConstrainedLanguageMode && PsesStdioProcess.IsWindowsPowerShell, "Windows PowerShell doesn't trust PSScriptAnalyzer by default so it won't load."); NewTestFile("gci | % { $_ }"); @@ -330,8 +327,7 @@ await PsesLanguageClient [SkippableFact] public async Task CanSendFormattingRequestAsync() { - Skip.If( - PsesStdioProcess.RunningInConstrainedLanguageMode && PsesStdioProcess.IsWindowsPowerShell, + Skip.If(PsesStdioProcess.RunningInConstrainedLanguageMode && PsesStdioProcess.IsWindowsPowerShell, "Windows PowerShell doesn't trust PSScriptAnalyzer by default so it won't load."); string scriptPath = NewTestFile(@" @@ -367,8 +363,7 @@ public async Task CanSendFormattingRequestAsync() [SkippableFact] public async Task CanSendRangeFormattingRequestAsync() { - Skip.If( - PsesStdioProcess.RunningInConstrainedLanguageMode && PsesStdioProcess.IsWindowsPowerShell, + Skip.If(PsesStdioProcess.RunningInConstrainedLanguageMode && PsesStdioProcess.IsWindowsPowerShell, "Windows PowerShell doesn't trust PSScriptAnalyzer by default so it won't load."); string scriptPath = NewTestFile(@" @@ -891,8 +886,7 @@ function CanSendReferencesCodeLensRequest { [SkippableFact] public async Task CanSendCodeActionRequestAsync() { - Skip.If( - PsesStdioProcess.RunningInConstrainedLanguageMode && PsesStdioProcess.IsWindowsPowerShell, + Skip.If(PsesStdioProcess.RunningInConstrainedLanguageMode && PsesStdioProcess.IsWindowsPowerShell, "Windows PowerShell doesn't trust PSScriptAnalyzer by default so it won't load."); string filePath = NewTestFile("gci"); @@ -971,7 +965,7 @@ public async Task CanSendCompletionAndCompletionResolveRequestAsync() Assert.Contains("Writes customized output to a host", updatedCompletionItem.Documentation.String); } - [SkippableFact(Skip = "This test is too flaky right now.")] + [SkippableFact(Skip = "Completion for Expand-SlowArchive is flaky.")] public async Task CanSendCompletionResolveWithModulePrefixRequestAsync() { await PsesLanguageClient @@ -1090,7 +1084,8 @@ await PsesLanguageClient [SkippableFact] public async Task CanSendGetProjectTemplatesRequestAsync() { - Skip.If(PsesStdioProcess.RunningInConstrainedLanguageMode, "Plaster doesn't work in ConstrainedLanguage mode."); + Skip.If(PsesStdioProcess.RunningInConstrainedLanguageMode, + "Plaster doesn't work in Constrained Language Mode."); GetProjectTemplatesResponse getProjectTemplatesResponse = await PsesLanguageClient @@ -1109,8 +1104,7 @@ await PsesLanguageClient [SkippableFact] public async Task CanSendGetCommentHelpRequestAsync() { - Skip.If( - PsesStdioProcess.RunningInConstrainedLanguageMode && PsesStdioProcess.IsWindowsPowerShell, + Skip.If(PsesStdioProcess.RunningInConstrainedLanguageMode && PsesStdioProcess.IsWindowsPowerShell, "Windows PowerShell doesn't trust PSScriptAnalyzer by default so it won't load."); string scriptPath = NewTestFile(@" @@ -1180,9 +1174,8 @@ await PsesLanguageClient [SkippableFact] public async Task CanSendExpandAliasRequestAsync() { - Skip.If( - PsesStdioProcess.RunningInConstrainedLanguageMode, - "This feature currently doesn't support ConstrainedLanguage Mode."); + Skip.If(PsesStdioProcess.RunningInConstrainedLanguageMode, + "The expand alias request doesn't work in Constrained Language Mode."); ExpandAliasResult expandAliasResult = await PsesLanguageClient From b972fb24312670be988b5ca9b3d937b7006c3bea Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Fri, 1 Jul 2022 11:58:14 -0700 Subject: [PATCH 7/8] Quiet build/test output and format build script --- PowerShellEditorServices.build.ps1 | 81 +++++++++++++++--------------- 1 file changed, 41 insertions(+), 40 deletions(-) diff --git a/PowerShellEditorServices.build.ps1 b/PowerShellEditorServices.build.ps1 index 6bde9dd8a..bb7fe352a 100644 --- a/PowerShellEditorServices.build.ps1 +++ b/PowerShellEditorServices.build.ps1 @@ -11,12 +11,13 @@ param( [string]$DefaultModuleRepository = "PSGallery", + [string[]]$VerbosityArgs = @("--verbosity", "quiet", "--nologo"), + # See: https://docs.microsoft.com/en-us/dotnet/core/testing/selective-unit-tests [string]$TestFilter = '', # See: https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-test - # E.g. use @("--logger", "console;verbosity=detailed") for detailed console output instead - [string[]]$TestArgs = @("--logger", "trx") + [string[]]$TestArgs = $VerbosityArgs + @("--logger", "console;verbosity=normal", "--logger", "trx") ) #Requires -Modules @{ModuleName="InvokeBuild"; ModuleVersion="5.0.0"} @@ -38,9 +39,9 @@ $script:BuildInfoPath = [System.IO.Path]::Combine($PSScriptRoot, "src", "PowerSh $script:PsesCommonProps = [xml](Get-Content -Raw "$PSScriptRoot/PowerShellEditorServices.Common.props") $script:NetRuntime = @{ - PS7 = 'netcoreapp3.1' - PS72 = 'net6.0' - Desktop = 'net462' + PS7 = 'netcoreapp3.1' + PS72 = 'net6.0' + Desktop = 'net462' Standard = 'netstandard2.0' } @@ -54,31 +55,31 @@ if (Get-Command git -ErrorAction SilentlyContinue) { git update-index --assume-unchanged "$PSScriptRoot/src/PowerShellEditorServices.Hosting/BuildInfo.cs" } -task FindDotNet { - assert (Get-Command dotnet -ErrorAction SilentlyContinue) "dotnet not found, please install it: https://aka.ms/dotnet-cli" +Task FindDotNet { + Assert (Get-Command dotnet -ErrorAction SilentlyContinue) "dotnet not found, please install it: https://aka.ms/dotnet-cli" # Strip out semantic version metadata so it can be cast to `Version` $existingVersion, $null = (dotnet --version) -split '-' - assert ([Version]$existingVersion -ge [Version]("6.0")) ".NET SDK 6.0 or higher is required, please update it: https://aka.ms/dotnet-cli" + Assert ([Version]$existingVersion -ge [Version]("6.0")) ".NET SDK 6.0 or higher is required, please update it: https://aka.ms/dotnet-cli" # Anywhere other than on a Mac with an M1 processor, we additionally # need the .NET 3.1 runtime for our netcoreapp3.1 framework. if (-not $script:IsAppleM1 -and -not $script:IsArm64) { $runtimes = dotnet --list-runtimes - assert ($runtimes -match "Microsoft.NETCore.App 3.1") ".NET Runtime 3.1 required but not found!" + Assert ($runtimes -match "Microsoft.NETCore.App 3.1") ".NET Runtime 3.1 required but not found!" } Write-Host "Using dotnet v$(dotnet --version) at path $((Get-Command dotnet).Source)" -ForegroundColor Green } -task BinClean { +Task BinClean { Remove-Item $PSScriptRoot\.tmp -Recurse -Force -ErrorAction Ignore Remove-Item $PSScriptRoot\module\PowerShellEditorServices\bin -Recurse -Force -ErrorAction Ignore Remove-Item $PSScriptRoot\module\PowerShellEditorServices.VSCode\bin -Recurse -Force -ErrorAction Ignore } -task Clean FindDotNet, BinClean, { - exec { & dotnet clean } +Task Clean FindDotNet, BinClean, { + Exec { & dotnet clean $VerbosityArgs } Get-ChildItem -Recurse $PSScriptRoot\src\*.nupkg | Remove-Item -Force -ErrorAction Ignore Get-ChildItem $PSScriptRoot\PowerShellEditorServices*.zip | Remove-Item -Force -ErrorAction Ignore Get-ChildItem $PSScriptRoot\module\PowerShellEditorServices\Commands\en-US\*-help.xml | Remove-Item -Force -ErrorAction Ignore @@ -93,7 +94,7 @@ task Clean FindDotNet, BinClean, { } } -task CreateBuildInfo { +Task CreateBuildInfo { $buildVersion = "" $buildOrigin = "Development" $buildCommit = git rev-parse HEAD @@ -147,33 +148,33 @@ namespace Microsoft.PowerShell.EditorServices.Hosting "@ if (Compare-Object $buildInfoContents.Split([Environment]::NewLine) (Get-Content $script:BuildInfoPath)) { - Write-Host "Updating Build Info" + Write-Host "Updating build info." Set-Content -LiteralPath $script:BuildInfoPath -Value $buildInfoContents -Force } } -task SetupHelpForTests { +Task SetupHelpForTests { if (-not (Get-Help Write-Host).Examples) { - Write-Host "Updating help for tests" + Write-Host "Updating help for tests." Update-Help -Module Microsoft.PowerShell.Utility -Force -Scope CurrentUser } } Task Build FindDotNet, CreateBuildInfo, { - exec { & dotnet restore } - exec { & dotnet publish -c $Configuration .\src\PowerShellEditorServices\PowerShellEditorServices.csproj -f $script:NetRuntime.Standard } - exec { & dotnet publish -c $Configuration .\src\PowerShellEditorServices.Hosting\PowerShellEditorServices.Hosting.csproj -f $script:NetRuntime.PS7 } + Exec { & dotnet restore $VerbosityArgs } + Exec { & dotnet publish $VerbosityArgs -c $Configuration .\src\PowerShellEditorServices\PowerShellEditorServices.csproj -f $script:NetRuntime.Standard } + Exec { & dotnet publish $VerbosityArgs -c $Configuration .\src\PowerShellEditorServices.Hosting\PowerShellEditorServices.Hosting.csproj -f $script:NetRuntime.PS7 } if (-not $script:IsNix) { - exec { & dotnet publish -c $Configuration .\src\PowerShellEditorServices.Hosting\PowerShellEditorServices.Hosting.csproj -f $script:NetRuntime.Desktop } + Exec { & dotnet publish $VerbosityArgs -c $Configuration .\src\PowerShellEditorServices.Hosting\PowerShellEditorServices.Hosting.csproj -f $script:NetRuntime.Desktop } } # Build PowerShellEditorServices.VSCode module - exec { & dotnet publish -c $Configuration .\src\PowerShellEditorServices.VSCode\PowerShellEditorServices.VSCode.csproj -f $script:NetRuntime.Standard } + Exec { & dotnet publish $VerbosityArgs -c $Configuration .\src\PowerShellEditorServices.VSCode\PowerShellEditorServices.VSCode.csproj -f $script:NetRuntime.Standard } } -task Test TestServer, TestE2E +Task Test TestServer, TestE2E -task TestServer TestServerWinPS, TestServerPS7, TestServerPS72 +Task TestServer TestServerWinPS, TestServerPS7, TestServerPS72 Task TestServerWinPS -If (-not $script:IsNix) Build, SetupHelpForTests, { Set-Location .\test\PowerShellEditorServices.Test\ @@ -181,43 +182,43 @@ Task TestServerWinPS -If (-not $script:IsNix) Build, SetupHelpForTests, { # that is debuggable! If architecture is added, the assembly path gets an # additional folder, necesstiating fixes to find the commands definition # file and test files. - exec { & dotnet $script:dotnetTestArgs $script:NetRuntime.Desktop } + Exec { & dotnet $script:dotnetTestArgs $script:NetRuntime.Desktop } } -task TestServerPS7 -If (-not $script:IsAppleM1 -and -not $script:IsArm64) Build, SetupHelpForTests, { +Task TestServerPS7 -If (-not $script:IsAppleM1 -and -not $script:IsArm64) Build, SetupHelpForTests, { Set-Location .\test\PowerShellEditorServices.Test\ - exec { & dotnet $script:dotnetTestArgs $script:NetRuntime.PS7 } + Exec { & dotnet $script:dotnetTestArgs $script:NetRuntime.PS7 } } -task TestServerPS72 Build, SetupHelpForTests, { +Task TestServerPS72 Build, SetupHelpForTests, { Set-Location .\test\PowerShellEditorServices.Test\ - exec { & dotnet $script:dotnetTestArgs $script:NetRuntime.PS72 } + Exec { & dotnet $script:dotnetTestArgs $script:NetRuntime.PS72 } } -task TestE2E Build, SetupHelpForTests, { +Task TestE2E Build, SetupHelpForTests, { Set-Location .\test\PowerShellEditorServices.Test.E2E\ $env:PWSH_EXE_NAME = if ($IsCoreCLR) { "pwsh" } else { "powershell" } $NetRuntime = if ($IsAppleM1 -or $script:IsArm64) { $script:NetRuntime.PS72 } else { $script:NetRuntime.PS7 } - exec { & dotnet $script:dotnetTestArgs $NetRuntime } + Exec { & dotnet $script:dotnetTestArgs $NetRuntime } - # Run E2E tests in ConstrainedLanguage mode. if (!$script:IsNix) { if (-not [Security.Principal.WindowsIdentity]::GetCurrent().Owner.IsWellKnown("BuiltInAdministratorsSid")) { - Write-Warning 'Skipping E2E CLM tests as they must be ran in an elevated process.' + Write-Warning "Skipping Constrained Language Mode tests as they must be ran in an elevated process." return } try { + Write-Host "Running end-to-end tests in Constrained Language Mode." [System.Environment]::SetEnvironmentVariable("__PSLockdownPolicy", "0x80000007", [System.EnvironmentVariableTarget]::Machine); - exec { & dotnet $script:dotnetTestArgs $script:NetRuntime.PS7 } + Exec { & dotnet $script:dotnetTestArgs $script:NetRuntime.PS7 } } finally { [System.Environment]::SetEnvironmentVariable("__PSLockdownPolicy", $null, [System.EnvironmentVariableTarget]::Machine); } } } -task LayoutModule -After Build { +Task LayoutModule -After Build { $modulesDir = "$PSScriptRoot/module" $psesVSCodeBinOutputPath = "$modulesDir/PowerShellEditorServices.VSCode/bin" $psesOutputPath = "$modulesDir/PowerShellEditorServices" @@ -226,8 +227,8 @@ task LayoutModule -After Build { $psesCoreHostPath = "$psesBinOutputPath/Core" $psesDeskHostPath = "$psesBinOutputPath/Desktop" - foreach ($dir in $psesDepsPath,$psesCoreHostPath,$psesDeskHostPath,$psesVSCodeBinOutputPath) { - New-Item -Force -Path $dir -ItemType Directory + foreach ($dir in $psesDepsPath, $psesCoreHostPath, $psesDeskHostPath, $psesVSCodeBinOutputPath) { + New-Item -Force -Path $dir -ItemType Directory | Out-Null } # Copy Third Party Notices.txt to module folder @@ -310,7 +311,7 @@ task RestorePsesModules -After Build { # Save each module in the modules.json file foreach ($moduleName in $moduleInfos.Keys) { if (Test-Path -Path (Join-Path -Path $submodulePath -ChildPath $moduleName)) { - Write-Host "`tModule '${moduleName}' already detected. Skipping" + Write-Host "`tModule '${moduleName}' already detected, skipping!" continue } @@ -331,9 +332,9 @@ task RestorePsesModules -After Build { } Task BuildCmdletHelp -After LayoutModule { - New-ExternalHelp -Path $PSScriptRoot\module\docs -OutputPath $PSScriptRoot\module\PowerShellEditorServices\Commands\en-US -Force - New-ExternalHelp -Path $PSScriptRoot\module\PowerShellEditorServices.VSCode\docs -OutputPath $PSScriptRoot\module\PowerShellEditorServices.VSCode\en-US -Force + New-ExternalHelp -Path $PSScriptRoot\module\docs -OutputPath $PSScriptRoot\module\PowerShellEditorServices\Commands\en-US -Force | Out-Null + New-ExternalHelp -Path $PSScriptRoot\module\PowerShellEditorServices.VSCode\docs -OutputPath $PSScriptRoot\module\PowerShellEditorServices.VSCode\en-US -Force | Out-Null } # The default task is to run the entire CI build -task . Clean, Build, Test +Task . Clean, Build, Test From 80ab5a47cfb748bbbbb0468f2e622fdf21e7b117 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Fri, 1 Jul 2022 12:18:40 -0700 Subject: [PATCH 8/8] Fix flaky `CompletesAttributeValue` test --- .../Completion/CompleteAttributeValue.cs | 3 --- .../Language/CompletionHandlerTests.cs | 12 +++++++----- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/test/PowerShellEditorServices.Test.Shared/Completion/CompleteAttributeValue.cs b/test/PowerShellEditorServices.Test.Shared/Completion/CompleteAttributeValue.cs index d38c430a4..629e50aec 100644 --- a/test/PowerShellEditorServices.Test.Shared/Completion/CompleteAttributeValue.cs +++ b/test/PowerShellEditorServices.Test.Shared/Completion/CompleteAttributeValue.cs @@ -25,7 +25,6 @@ internal static class CompleteAttributeValue FilterText = "ValueFromPipeline", InsertText = "ValueFromPipeline", Label = "ValueFromPipeline", - SortText = "0001ValueFromPipeline", TextEdit = new TextEdit { NewText = "ValueFromPipeline", @@ -44,7 +43,6 @@ internal static class CompleteAttributeValue FilterText = "ValueFromPipelineByPropertyName", InsertText = "ValueFromPipelineByPropertyName", Label = "ValueFromPipelineByPropertyName", - SortText = "0002ValueFromPipelineByPropertyName", TextEdit = new TextEdit { NewText = "ValueFromPipelineByPropertyName", @@ -63,7 +61,6 @@ internal static class CompleteAttributeValue FilterText = "ValueFromRemainingArguments", InsertText = "ValueFromRemainingArguments", Label = "ValueFromRemainingArguments", - SortText = "0003ValueFromRemainingArguments", TextEdit = new TextEdit { NewText = "ValueFromRemainingArguments", diff --git a/test/PowerShellEditorServices.Test/Language/CompletionHandlerTests.cs b/test/PowerShellEditorServices.Test/Language/CompletionHandlerTests.cs index 21d8cce5b..3ddedb89f 100644 --- a/test/PowerShellEditorServices.Test/Language/CompletionHandlerTests.cs +++ b/test/PowerShellEditorServices.Test/Language/CompletionHandlerTests.cs @@ -111,11 +111,13 @@ public async Task CompletesVariableInFile() public async Task CompletesAttributeValue() { (_, IEnumerable results) = await GetCompletionResultsAsync(CompleteAttributeValue.SourceDetails).ConfigureAwait(true); - Assert.Equal(3, results.Count()); - Assert.Collection(results.OrderBy(c => c.SortText), - actual => Assert.Equal(actual with { Data = null }, CompleteAttributeValue.ExpectedCompletion1), - actual => Assert.Equal(actual with { Data = null }, CompleteAttributeValue.ExpectedCompletion2), - actual => Assert.Equal(actual with { Data = null }, CompleteAttributeValue.ExpectedCompletion3)); + // NOTE: Since the completions come through un-ordered from PowerShell, their SortText + // (which has an index prepended from the original order) will mis-match our assumed + // order; hence we ignore it. + Assert.Collection(results.OrderBy(c => c.Label), + actual => Assert.Equal(actual with { Data = null, SortText = null }, CompleteAttributeValue.ExpectedCompletion1), + actual => Assert.Equal(actual with { Data = null, SortText = null }, CompleteAttributeValue.ExpectedCompletion2), + actual => Assert.Equal(actual with { Data = null, SortText = null }, CompleteAttributeValue.ExpectedCompletion3)); } [Fact]