From ba13364a1379c7063db48c97e8e40be3c5a78b38 Mon Sep 17 00:00:00 2001 From: Petr Date: Thu, 23 Mar 2023 16:43:43 +0100 Subject: [PATCH 1/4] An attempt to deflake some int tests --- .../BuildProjectTests.cs | 2 + .../InProcess/SolutionExplorerInProcess.cs | 37 +++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/vsintegration/tests/FSharp.Editor.IntegrationTests/BuildProjectTests.cs b/vsintegration/tests/FSharp.Editor.IntegrationTests/BuildProjectTests.cs index c962d2414db..050f36b6a43 100644 --- a/vsintegration/tests/FSharp.Editor.IntegrationTests/BuildProjectTests.cs +++ b/vsintegration/tests/FSharp.Editor.IntegrationTests/BuildProjectTests.cs @@ -24,6 +24,7 @@ module Test await SolutionExplorer.CreateSingleProjectSolutionAsync("Library", template, TestToken); await SolutionExplorer.RestoreNuGetPackagesAsync(TestToken); + await SolutionExplorer.OpenFileAsync("Library", "Library.fs", TestToken); await Editor.SetTextAsync(code, TestToken); var actualBuildSummary = await SolutionExplorer.BuildSolutionAsync(TestToken); @@ -45,6 +46,7 @@ module Test await SolutionExplorer.CreateSingleProjectSolutionAsync("Library", template, TestToken); await SolutionExplorer.RestoreNuGetPackagesAsync(TestToken); + await SolutionExplorer.OpenFileAsync("Library", "Library.fs", TestToken); await Editor.SetTextAsync(code, TestToken); var actualBuildSummary = await SolutionExplorer.BuildSolutionAsync(TestToken); diff --git a/vsintegration/tests/FSharp.Editor.IntegrationTests/InProcess/SolutionExplorerInProcess.cs b/vsintegration/tests/FSharp.Editor.IntegrationTests/InProcess/SolutionExplorerInProcess.cs index e1836606f58..7fb59f76e90 100644 --- a/vsintegration/tests/FSharp.Editor.IntegrationTests/InProcess/SolutionExplorerInProcess.cs +++ b/vsintegration/tests/FSharp.Editor.IntegrationTests/InProcess/SolutionExplorerInProcess.cs @@ -35,6 +35,25 @@ public async Task CreateSolutionAsync(string solutionName, CancellationToken can var solutionPath = CreateTemporaryPath(); await CreateSolutionAsync(solutionPath, solutionName, cancellationToken); } + + public async Task OpenFileAsync(string projectName, string relativeFilePath, CancellationToken cancellationToken) + { + await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + + var filePath = await GetAbsolutePathForProjectRelativeFilePathAsync(projectName, relativeFilePath, cancellationToken); + if (!File.Exists(filePath)) + { + throw new FileNotFoundException(filePath); + } + + VsShellUtilities.OpenDocument(ServiceProvider.GlobalProvider, filePath, VSConstants.LOGVIEWID.Code_guid, out _, out _, out _, out var view); + + // Reliably set focus using NavigateToLineAndColumn + var textManager = await GetRequiredGlobalServiceAsync(cancellationToken); + ErrorHandler.ThrowOnFailure(view.GetBuffer(out var textLines)); + ErrorHandler.ThrowOnFailure(view.GetCaretPos(out var line, out var column)); + ErrorHandler.ThrowOnFailure(textManager.NavigateToLineAndColumn(textLines, VSConstants.LOGVIEWID.Code_guid, line, column, line, column)); + } private async Task CreateSolutionAsync(string solutionPath, string solutionName, CancellationToken cancellationToken) { @@ -188,6 +207,24 @@ private string CreateTemporaryPath() return Path.Combine(Path.GetTempPath(), "fsharp-test", Path.GetRandomFileName()); } + private async Task GetAbsolutePathForProjectRelativeFilePathAsync(string projectName, string relativeFilePath, CancellationToken cancellationToken) + { + await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + + var dte = await GetRequiredGlobalServiceAsync(cancellationToken); + var solution = dte.Solution; + Assumes.Present(solution); + + var project = solution.Projects.Cast().FirstOrDefault(x => x.Name == projectName); + if (project is null) + { + throw new NotImplementedException("Prevent null fallthrough"); + } + + var projectPath = Path.GetDirectoryName(project.FullName); + return Path.Combine(projectPath, relativeFilePath); + } + private async Task GetProjectAsync(string nameOrFileName, CancellationToken cancellationToken) { await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); From 81e0fd0f1cf7207676d09e65fae9b935952b9e8e Mon Sep 17 00:00:00 2001 From: Petr Date: Thu, 23 Mar 2023 18:05:26 +0100 Subject: [PATCH 2/4] Update vsintegration/tests/FSharp.Editor.IntegrationTests/InProcess/SolutionExplorerInProcess.cs Co-authored-by: Sam Harwell --- .../InProcess/SolutionExplorerInProcess.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/vsintegration/tests/FSharp.Editor.IntegrationTests/InProcess/SolutionExplorerInProcess.cs b/vsintegration/tests/FSharp.Editor.IntegrationTests/InProcess/SolutionExplorerInProcess.cs index 7fb59f76e90..4597c3bffb7 100644 --- a/vsintegration/tests/FSharp.Editor.IntegrationTests/InProcess/SolutionExplorerInProcess.cs +++ b/vsintegration/tests/FSharp.Editor.IntegrationTests/InProcess/SolutionExplorerInProcess.cs @@ -215,12 +215,7 @@ private async Task GetAbsolutePathForProjectRelativeFilePathAsync(string var solution = dte.Solution; Assumes.Present(solution); - var project = solution.Projects.Cast().FirstOrDefault(x => x.Name == projectName); - if (project is null) - { - throw new NotImplementedException("Prevent null fallthrough"); - } - + var project = solution.Projects.Cast().First(x => x.Name == projectName); var projectPath = Path.GetDirectoryName(project.FullName); return Path.Combine(projectPath, relativeFilePath); } From 55f7c14c1750b7ae2f54b89c8a907582e5017149 Mon Sep 17 00:00:00 2001 From: Petr Date: Mon, 27 Mar 2023 16:27:20 +0200 Subject: [PATCH 3/4] Adding some other stuff --- azure-pipelines.yml | 17 ++++- eng/FinishDumpCollectionForHangingBuilds.ps1 | 76 ++++++++++++++++++++ 2 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 eng/FinishDumpCollectionForHangingBuilds.ps1 diff --git a/azure-pipelines.yml b/azure-pipelines.yml index f47a73160ee..6057d50d54c 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -326,6 +326,8 @@ stages: variables: - name: XUNIT_LOGS value: $(Build.SourcesDirectory)\artifacts\TestResults\$(_configuration) + - name: __VSNeverShowWhatsNew + value: 1 pool: # The PR build definition sets this variable: # WindowsMachineQueueName=Windows.vs2022.amd64.open @@ -372,6 +374,19 @@ stages: searchFolder: '$(Build.SourcesDirectory)/artifacts/TestResults/$(_configuration)' continueOnError: true condition: ne(variables['_testKind'], 'testFSharpQA') + - task: PublishBuildArtifacts@1 + displayName: Publish Tests BinLog + condition: always() + continueOnError: true + inputs: + PathToPublish: '$(Build.SourcesDirectory)/artifacts/log/$(_configuration)/Build.VisualFSharp.sln.binlog' + ArtifactName: 'Windows $(_configuration) $(_testKind) test binlogs' + ArtifactType: Container + parallel: true + - powershell: ./eng/FinishDumpCollectionForHangingBuilds.ps1 '$(Build.SourcesDirectory)/artifacts/log/$(_configuration)/Build.VisualFSharp.sln.binlog' + displayName: Finish background dump collection + continueOnError: true + condition: always() - task: PublishBuildArtifacts@1 displayName: Publish Test Logs inputs: @@ -379,7 +394,7 @@ stages: ArtifactName: 'Windows $(_configuration) $(_testKind) test logs' publishLocation: Container continueOnError: true - condition: failed() + condition: always() - script: dotnet build $(Build.SourcesDirectory)/eng/DumpPackageRoot/DumpPackageRoot.csproj displayName: Dump NuGet cache contents condition: failed() diff --git a/eng/FinishDumpCollectionForHangingBuilds.ps1 b/eng/FinishDumpCollectionForHangingBuilds.ps1 new file mode 100644 index 00000000000..b6123e49a44 --- /dev/null +++ b/eng/FinishDumpCollectionForHangingBuilds.ps1 @@ -0,0 +1,76 @@ +param( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [string] + $ProcDumpOutputPath +) + +Write-Output "Finishing dump collection for hanging builds."; + +$repoRoot = Resolve-Path "$PSScriptRoot\..\.."; +$ProcDumpOutputPath = Join-Path $repoRoot $ProcDumpOutputPath; + +$sentinelFile = Join-Path $ProcDumpOutputPath "dump-sentinel.txt"; +if ((-not (Test-Path $sentinelFile))) { + Write-Output "No sentinel file available in '$sentinelFile'. " + + "StartDumpCollectionForHangingBuilds.ps1 has not been executed, is not correctly configured or failed before creating the sentinel file."; + return; +} + +Get-Process "procdump" -ErrorAction SilentlyContinue | ForEach-Object { Write-Output "ProcDump with PID $($_.Id) is still running."; }; + +$capturedDumps = Get-ChildItem $ProcDumpOutputPath -Filter *.dmp; +$capturedDumps | ForEach-Object { Write-Output "Found captured dump $_"; }; + +$JobName = (Get-Content $sentinelFile); + +if ($JobName.Count -ne 1) { + if ($JobName.Count -eq 0) { + Write-Warning "No job name found. This is likely an error."; + return; + } + else { + Write-Output "Multiple job names found '$JobName'."; + return; + } +} + +$dumpCollectionJob = Get-Job -Name $JobName -ErrorAction SilentlyContinue; +$registeredJob = Get-ScheduledJob -Name $JobName -ErrorAction SilentlyContinue; + +if ($null -eq $dumpCollectionJob) { + Write-Output "No job found for '$JobName'. It either didn't run or there is an issue with the job definition."; + + if ($null -eq $registeredJob) { + Write-Warning "Couldn't find a scheduled job '$JobName'."; + } + return; +} + +Write-Output "Listing existing jobs"; +Get-Job -Name CaptureDumps* + +Write-Output "Listing existing scheduled jobs"; +Get-ScheduledJob -Name CaptureDumps* + +Write-Output "Displaying job output"; +Receive-Job $dumpCollectionJob; + +Write-Output "Waiting for current job to finish"; +Get-Job -ErrorAction SilentlyContinue | Wait-Job; + +try { + Write-Output "Removing collection job"; + Remove-Job $dumpCollectionJob; +} +catch { + Write-Output "Failed to remove collection job"; +} + +try { + Write-Output "Unregistering scheduled job"; + Unregister-ScheduledJob $registeredJob; +} +catch { + Write-Output "Failed to unregister $JobName"; +} From 1a154e3f0fd55e616e8a06f5ff77dd96c8647186 Mon Sep 17 00:00:00 2001 From: Petr Date: Mon, 27 Mar 2023 18:39:49 +0200 Subject: [PATCH 4/4] up --- azure-pipelines.yml | 6 +- eng/FinishDumpCollectionForHangingBuilds.ps1 | 76 -------------------- 2 files changed, 1 insertion(+), 81 deletions(-) delete mode 100644 eng/FinishDumpCollectionForHangingBuilds.ps1 diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 6057d50d54c..4129dee4eae 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -379,14 +379,10 @@ stages: condition: always() continueOnError: true inputs: - PathToPublish: '$(Build.SourcesDirectory)/artifacts/log/$(_configuration)/Build.VisualFSharp.sln.binlog' + PathToPublish: '$(Build.SourcesDirectory)\artifacts\log/$(_configuration)\Build.VisualFSharp.sln.binlog' ArtifactName: 'Windows $(_configuration) $(_testKind) test binlogs' ArtifactType: Container parallel: true - - powershell: ./eng/FinishDumpCollectionForHangingBuilds.ps1 '$(Build.SourcesDirectory)/artifacts/log/$(_configuration)/Build.VisualFSharp.sln.binlog' - displayName: Finish background dump collection - continueOnError: true - condition: always() - task: PublishBuildArtifacts@1 displayName: Publish Test Logs inputs: diff --git a/eng/FinishDumpCollectionForHangingBuilds.ps1 b/eng/FinishDumpCollectionForHangingBuilds.ps1 deleted file mode 100644 index b6123e49a44..00000000000 --- a/eng/FinishDumpCollectionForHangingBuilds.ps1 +++ /dev/null @@ -1,76 +0,0 @@ -param( - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [string] - $ProcDumpOutputPath -) - -Write-Output "Finishing dump collection for hanging builds."; - -$repoRoot = Resolve-Path "$PSScriptRoot\..\.."; -$ProcDumpOutputPath = Join-Path $repoRoot $ProcDumpOutputPath; - -$sentinelFile = Join-Path $ProcDumpOutputPath "dump-sentinel.txt"; -if ((-not (Test-Path $sentinelFile))) { - Write-Output "No sentinel file available in '$sentinelFile'. " + - "StartDumpCollectionForHangingBuilds.ps1 has not been executed, is not correctly configured or failed before creating the sentinel file."; - return; -} - -Get-Process "procdump" -ErrorAction SilentlyContinue | ForEach-Object { Write-Output "ProcDump with PID $($_.Id) is still running."; }; - -$capturedDumps = Get-ChildItem $ProcDumpOutputPath -Filter *.dmp; -$capturedDumps | ForEach-Object { Write-Output "Found captured dump $_"; }; - -$JobName = (Get-Content $sentinelFile); - -if ($JobName.Count -ne 1) { - if ($JobName.Count -eq 0) { - Write-Warning "No job name found. This is likely an error."; - return; - } - else { - Write-Output "Multiple job names found '$JobName'."; - return; - } -} - -$dumpCollectionJob = Get-Job -Name $JobName -ErrorAction SilentlyContinue; -$registeredJob = Get-ScheduledJob -Name $JobName -ErrorAction SilentlyContinue; - -if ($null -eq $dumpCollectionJob) { - Write-Output "No job found for '$JobName'. It either didn't run or there is an issue with the job definition."; - - if ($null -eq $registeredJob) { - Write-Warning "Couldn't find a scheduled job '$JobName'."; - } - return; -} - -Write-Output "Listing existing jobs"; -Get-Job -Name CaptureDumps* - -Write-Output "Listing existing scheduled jobs"; -Get-ScheduledJob -Name CaptureDumps* - -Write-Output "Displaying job output"; -Receive-Job $dumpCollectionJob; - -Write-Output "Waiting for current job to finish"; -Get-Job -ErrorAction SilentlyContinue | Wait-Job; - -try { - Write-Output "Removing collection job"; - Remove-Job $dumpCollectionJob; -} -catch { - Write-Output "Failed to remove collection job"; -} - -try { - Write-Output "Unregistering scheduled job"; - Unregister-ScheduledJob $registeredJob; -} -catch { - Write-Output "Failed to unregister $JobName"; -}