From e716bf658ba4963ac147b3e1b7ec04f05ac4faa7 Mon Sep 17 00:00:00 2001 From: Ryan Brandenburg Date: Fri, 13 May 2022 13:36:06 -0700 Subject: [PATCH] Add new feedback collection items (#6357) * Add new feedback collection items * Collect BinLogs for integration tests * Log the Hive directory * Ensure TextView has correct ContentType * Ensure LSP Editor is Enabled * Force Creation of experimental hive --- azure-pipelines.yml | 52 +++-- eng/pipelines/test-integration-job.yml | 42 ++++ ...alStudioWindowsLSPEditorFeatureDetector.cs | 22 +- ...lStudio.RazorExtension.Dependencies.csproj | 3 +- ...crosoft.VisualStudio.RazorExtension.csproj | 1 - .../RazorDeployment/RazorDeployment.csproj | 3 +- ...udioWindowsLSPEditorFeatureDetectorTest.cs | 35 ++- .../AbstractEditorTest.cs | 6 +- .../AbstractIntegrationTest.cs | 6 + .../AbstractRazorEditorTest.cs | 216 +++--------------- .../BreakpointSpanTests.cs | 30 +-- .../CodeFoldingTests.cs | 22 +- .../FeedbackLogging.cs | 192 ++++++++++++++++ .../Formatting/FormatDocumentTests.cs | 14 +- .../GoToDefinitionTests.cs | 36 +-- .../GoToImplementationTests.cs | 18 +- .../OnEnterRulesTests.cs | 32 +-- .../OnTypeFormattingTests.cs | 10 +- .../ProjectTests.cs | 4 +- .../RazorCodeActionsTests.cs | 18 +- .../RazorProjectConstants.cs | 54 +++++ .../Semantic/RazorSemanticTokensTests.cs | 22 +- 22 files changed, 514 insertions(+), 324 deletions(-) create mode 100644 src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/FeedbackLogging.cs create mode 100644 src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/RazorProjectConstants.cs diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 7d04ae4727..21e73ed2b6 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -197,6 +197,15 @@ stages: name: Build displayName: Build condition: succeeded() + - task: PublishBuildArtifacts@1 + displayName: Upload Build BinLog + condition: always() + continueOnError: true + inputs: + pathtoPublish: artifacts/log/$(_BuildConfig)/Build.binlog + artifactName: $(Agent.Os)_$(Agent.JobName) BuildBinLog + artifactType: Container + parallel: true - script: eng\cibuild.cmd -configuration $(_BuildConfig) -msbuildEngine vs @@ -213,6 +222,15 @@ stages: name: Build_Vsix displayName: Build and Deploy Vsix condition: succeeded() + - task: PublishBuildArtifacts@1 + displayName: Upload Build VSIX BinLog + condition: always() + continueOnError: true + inputs: + pathtoPublish: artifacts/log/$(_BuildConfig)/Build.binlog + artifactName: $(Agent.Os)_$(Agent.JobName) BuildVSIXBinLog + artifactType: Container + parallel: true - script: eng\CIBuild.cmd -configuration $(_BuildConfig) -prepareMachine @@ -222,23 +240,15 @@ stages: name: Run_Tests displayName: Run Unit and Integration tests condition: succeeded() - # - task: PublishBuildArtifacts@1 - # displayName: Update Integration Tests Data - # condition: always() - # continueOnError: true - # inputs: - # pathtoPublish: artifacts/log/$(_BuildConfig)/Screenshots/ - # artifactName: $(Agent.Os)_$(Agent.JobName) IntegrationTestsData $(_BuildConfig) - # artifactType: Container - # parallel: true - # Run VSCode functional tests - # - powershell: | - # . ../../../../activate.ps1 - # yarn test -- --ci --configuration $(_BuildConfig) --no-restore - # deactivate - # workingDirectory: $(Build.SourcesDirectory)/src/Razor/test/VSCode.FunctionalTest - # displayName: Run VSCode Tests - # condition: and(succeeded(), ne(variables['_BuildConfig'], 'Release')) # Temporary: Don't run on Release + - task: PublishBuildArtifacts@1 + displayName: Upload Run tests BinLog + condition: always() + continueOnError: true + inputs: + pathtoPublish: artifacts/log/$(_BuildConfig)/Build.binlog + artifactName: $(Agent.Os)_$(Agent.JobName) RunTestsBinLog + artifactType: Container + parallel: true - powershell: ./eng/scripts/FinishDumpCollectionForHangingBuilds.ps1 artifacts/log/$(_BuildConfig) displayName: Finish background dump collection continueOnError: true @@ -252,14 +262,6 @@ stages: workingDirectory: $(Build.SourcesDirectory)/src/Razor/src/Microsoft.AspNetCore.Razor.VSCode.BlazorWasmDebuggingExtension failOnStderr: true condition: and(succeeded(), eq(variables['_BuildConfig'], 'Release')) - # - task: PublishTestResults@2 - # displayName: Publish Integration Test Results - # condition: always() - # continueOnError: true - # inputs: - # testResultsFormat: 'VSTest' - # testResultsFiles: '*.trx' - # searchFolder: '$(Build.SourcesDirectory)/artifacts/TestResults/$(_BuildConfig)' - task: PublishBuildArtifacts@1 displayName: Upload Test Results condition: always() diff --git a/eng/pipelines/test-integration-job.yml b/eng/pipelines/test-integration-job.yml index ffcc373d31..42df28a1d5 100644 --- a/eng/pipelines/test-integration-job.yml +++ b/eng/pipelines/test-integration-job.yml @@ -15,6 +15,18 @@ steps: inputs: versionSpec: 10.x + # We explicitly pass the VS install directory in azure-pipelines-integration-dartlab.yml + - powershell: | + & "C:\\Test\\VisualStudio\\Common7\\IDE\\devenv.exe" /rootsuffix RoslynDev /updateConfiguration + if(Test-Path -Path $env:LocalAppData\Microsoft\VisualStudio\17.0*RoslynDev) + { + Write-Host "The hive 'RoslynDev' exists" + } + else{ + throw "Failed to create hive" + } + displayName: Create and Verify hive + - script: eng\cibuild.cmd -configuration ${{ parameters.configuration }} -msbuildEngine vs @@ -27,6 +39,16 @@ steps: displayName: Build condition: succeeded() + - task: PublishBuildArtifacts@1 + displayName: Upload Build BinLog + condition: always() + continueOnError: true + inputs: + pathtoPublish: artifacts/log/${{ parameters.configuration }}/Build.binlog + artifactName: $(Agent.Os)_$(Agent.JobName) BuildBinLog + artifactType: Container + parallel: true + - script: eng\cibuild.cmd -configuration ${{ parameters.configuration }} -msbuildEngine vs @@ -37,6 +59,16 @@ steps: displayName: Build and Deploy VSIX condition: succeeded() + - task: PublishBuildArtifacts@1 + displayName: Upload Build VSIX BinLog + condition: always() + continueOnError: true + inputs: + pathtoPublish: artifacts/log/${{ parameters.configuration }}/Build.binlog + artifactName: $(Agent.Os)_$(Agent.JobName) BuildVSIXBinLog + artifactType: Container + parallel: true + - script: eng\cibuild.cmd -configuration ${{ parameters.configuration }} -msbuildEngine vs @@ -47,6 +79,16 @@ steps: displayName: Run Integration Tests condition: succeeded() + - task: PublishBuildArtifacts@1 + displayName: Upload Run tests BinLog + condition: always() + continueOnError: true + inputs: + pathtoPublish: artifacts/log/${{ parameters.configuration }}/Build.binlog + artifactName: $(Agent.Os)_$(Agent.JobName) RunTestsBinLog + artifactType: Container + parallel: true + - task: PublishTestResults@2 displayName: Publish xUnit Test Results inputs: diff --git a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/VisualStudioWindowsLSPEditorFeatureDetector.cs b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/VisualStudioWindowsLSPEditorFeatureDetector.cs index be7cefa530..75338be21b 100644 --- a/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/VisualStudioWindowsLSPEditorFeatureDetector.cs +++ b/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/VisualStudioWindowsLSPEditorFeatureDetector.cs @@ -3,6 +3,7 @@ using System; using System.Composition; +using Microsoft.CodeAnalysis.Razor; using Microsoft.CodeAnalysis.Razor.Workspaces; using Microsoft.Internal.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Editor.Razor; @@ -28,8 +29,10 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor private readonly Lazy _vsUIShellOpenDocument; private readonly Lazy _useLegacyEditor; + private readonly RazorLogger _logger; + [ImportingConstructor] - public VisualStudioWindowsLSPEditorFeatureDetector(AggregateProjectCapabilityResolver projectCapabilityResolver) + public VisualStudioWindowsLSPEditorFeatureDetector(AggregateProjectCapabilityResolver projectCapabilityResolver, RazorLogger logger) { _projectCapabilityResolver = projectCapabilityResolver; _vsUIShellOpenDocument = new Lazy(() => @@ -55,6 +58,8 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor var useLegacyEditor = settingsManager.GetValueOrDefault(UseLegacyASPNETCoreEditorSetting); return useLegacyEditor; }); + + _logger = logger; } [Obsolete("Test constructor")] @@ -66,13 +71,16 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor public override bool IsLSPEditorAvailable(string documentMoniker, object hierarchy) { + _logger.LogVerbose("Checking if LSP Editor is available"); if (documentMoniker is null) { + _logger.LogWarning($"LSP Editor not available because {nameof(documentMoniker)} is null"); return false; } if (!IsLSPEditorAvailable()) { + _logger.LogVerbose($"Using Legacy editor because the option was set to true"); return false; } @@ -80,6 +88,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor if (!ProjectSupportsLSPEditor(documentMoniker, ivsHierarchy)) { // Current project hierarchy doesn't support the LSP Razor editor + _logger.LogVerbose($"Using Legacy editor because the current project does not support LSP Editor"); return false; } @@ -105,6 +114,15 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor hierarchy = uiHierarchy; if (!ErrorHandler.Succeeded(hr) || hierarchy is null) { + if (!ErrorHandler.Succeeded(hr)) + { + _logger.LogWarning($"Project does not support LSP Editor beccause {nameof(_vsUIShellOpenDocument.Value.IsDocumentInAProject)} failed with exit code {hr}"); + } + else if (hierarchy is null) + { + _logger.LogWarning($"Project does not support LSP Editor because {nameof(hierarchy)} is null"); + } + return false; } } @@ -114,6 +132,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor // those types of scenarios for the new .NET Core Razor editor. if (_projectCapabilityResolver.HasCapability(documentMoniker, hierarchy, LegacyRazorEditorCapability)) { + _logger.LogVerbose($"Project does not support LSP Editor because '{documentMoniker}' has Capability {LegacyRazorEditorCapability}"); // CPS project that requires the legacy editor return false; } @@ -124,6 +143,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor return true; } + _logger.LogVerbose($"Project {documentMoniker} does not support LSP Editor because it does not have the {DotNetCoreCSharpCapability} capability."); // Not a C# .NET Core project. This typically happens for legacy Razor scenarios return false; } diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension.Dependencies/Microsoft.VisualStudio.RazorExtension.Dependencies.csproj b/src/Razor/src/Microsoft.VisualStudio.RazorExtension.Dependencies/Microsoft.VisualStudio.RazorExtension.Dependencies.csproj index 25de145ce2..82e5c24611 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension.Dependencies/Microsoft.VisualStudio.RazorExtension.Dependencies.csproj +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension.Dependencies/Microsoft.VisualStudio.RazorExtension.Dependencies.csproj @@ -7,10 +7,9 @@ In general this project should not be deployed (F5'd) by itself as it only includes extra dependencies + codebases that may not be present in public VS installs. --> - + net472 - RoslynDev false diff --git a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/Microsoft.VisualStudio.RazorExtension.csproj b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/Microsoft.VisualStudio.RazorExtension.csproj index 4b686047cc..b3906b8ae6 100644 --- a/src/Razor/src/Microsoft.VisualStudio.RazorExtension/Microsoft.VisualStudio.RazorExtension.csproj +++ b/src/Razor/src/Microsoft.VisualStudio.RazorExtension/Microsoft.VisualStudio.RazorExtension.csproj @@ -2,7 +2,6 @@ net472 - RoslynDev diff --git a/src/Razor/src/RazorDeployment/RazorDeployment.csproj b/src/Razor/src/RazorDeployment/RazorDeployment.csproj index 241753f203..432e8d9263 100644 --- a/src/Razor/src/RazorDeployment/RazorDeployment.csproj +++ b/src/Razor/src/RazorDeployment/RazorDeployment.csproj @@ -5,10 +5,9 @@ It ensures that a single F5 command will build and deploy the independent MS.VS.RazorExtension vsix and MS.VS.RazorExtension.Dependencies vsix. We rely on launch settings to run devenv with the appropriate hive that the vsixes have been deployed to. --> - + net472 - RoslynDev diff --git a/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/VisualStudioWindowsLSPEditorFeatureDetectorTest.cs b/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/VisualStudioWindowsLSPEditorFeatureDetectorTest.cs index ed0f63d54f..1a46c1996b 100644 --- a/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/VisualStudioWindowsLSPEditorFeatureDetectorTest.cs +++ b/src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/VisualStudioWindowsLSPEditorFeatureDetectorTest.cs @@ -3,7 +3,9 @@ #nullable disable +using Microsoft.CodeAnalysis.Razor; using Microsoft.VisualStudio.Shell.Interop; +using Moq; using Xunit; namespace Microsoft.VisualStudio.LanguageServices.Razor @@ -14,7 +16,8 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor public void IsLSPEditorAvailable_ProjectSupported_ReturnsTrue() { // Arrange - var featureDetector = new TestLSPEditorFeatureDetector() + var logger = GetRazorLogger(); + var featureDetector = new TestLSPEditorFeatureDetector(logger) { ProjectSupportsLSPEditorValue = true, }; @@ -30,7 +33,8 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor public void IsLSPEditorAvailable_LegacyEditorEnabled_ReturnsFalse() { // Arrange - var featureDetector = new TestLSPEditorFeatureDetector() + var logger = GetRazorLogger(); + var featureDetector = new TestLSPEditorFeatureDetector(logger) { UseLegacyEditor = true, ProjectSupportsLSPEditorValue = true, @@ -47,7 +51,8 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor public void IsLSPEditorAvailable_IsVSRemoteClient_ReturnsTrue() { // Arrange - var featureDetector = new TestLSPEditorFeatureDetector() + var logger = GetRazorLogger(); + var featureDetector = new TestLSPEditorFeatureDetector(logger) { IsVSRemoteClientValue = true, ProjectSupportsLSPEditorValue = true, @@ -64,7 +69,8 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor public void IsLSPEditorAvailable_UnsupportedProject_ReturnsFalse() { // Arrange - var featureDetector = new TestLSPEditorFeatureDetector() + var logger = GetRazorLogger(); + var featureDetector = new TestLSPEditorFeatureDetector(logger) { ProjectSupportsLSPEditorValue = false, }; @@ -80,7 +86,8 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor public void IsRemoteClient_VSRemoteClient_ReturnsTrue() { // Arrange - var featureDetector = new TestLSPEditorFeatureDetector() + var logger = GetRazorLogger(); + var featureDetector = new TestLSPEditorFeatureDetector(logger) { IsVSRemoteClientValue = true, }; @@ -96,7 +103,8 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor public void IsRemoteClient_LiveShareGuest_ReturnsTrue() { // Arrange - var featureDetector = new TestLSPEditorFeatureDetector() + var logger = GetRazorLogger(); + var featureDetector = new TestLSPEditorFeatureDetector(logger) { IsLiveShareGuestValue = true, }; @@ -112,7 +120,8 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor public void IsRemoteClient_UnknownEnvironment_ReturnsFalse() { // Arrange - var featureDetector = new TestLSPEditorFeatureDetector(); + var logger = GetRazorLogger(); + var featureDetector = new TestLSPEditorFeatureDetector(logger); // Act var result = featureDetector.IsRemoteClient(); @@ -121,9 +130,21 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor Assert.False(result); } + private static RazorLogger GetRazorLogger() + { + var mock = new Mock(MockBehavior.Strict); + mock.Setup(l => l.LogVerbose(It.IsAny())); + + return mock.Object; + } + #pragma warning disable CS0618 // Type or member is obsolete (Test constructor) private class TestLSPEditorFeatureDetector : VisualStudioWindowsLSPEditorFeatureDetector { + public TestLSPEditorFeatureDetector(RazorLogger logger) : base(projectCapabilityResolver: null, logger) + { + } + public bool UseLegacyEditor { get; set; } public bool IsLiveShareGuestValue { get; set; } diff --git a/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/AbstractEditorTest.cs b/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/AbstractEditorTest.cs index 591fdef8ac..a253b91764 100644 --- a/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/AbstractEditorTest.cs +++ b/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/AbstractEditorTest.cs @@ -38,9 +38,9 @@ namespace Microsoft.VisualStudio.Razor.IntegrationTests RazorDebug.AssertNotNull(_projectTemplate); RazorDebug.AssertNotNull(_projectName); - await TestServices.SolutionExplorer.CreateSolutionAsync(_solutionName, HangMitigatingCancellationToken); - await TestServices.SolutionExplorer.AddProjectAsync(_projectName, _projectTemplate, LanguageName, HangMitigatingCancellationToken); - await TestServices.SolutionExplorer.RestoreNuGetPackagesAsync(ProjectName, HangMitigatingCancellationToken); + await TestServices.SolutionExplorer.CreateSolutionAsync(_solutionName, ControlledHangMitigatingCancellationToken); + await TestServices.SolutionExplorer.AddProjectAsync(_projectName, _projectTemplate, LanguageName, ControlledHangMitigatingCancellationToken); + await TestServices.SolutionExplorer.RestoreNuGetPackagesAsync(ProjectName, ControlledHangMitigatingCancellationToken); } } } diff --git a/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/AbstractIntegrationTest.cs b/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/AbstractIntegrationTest.cs index cb2d3c27c9..e7774a2f26 100644 --- a/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/AbstractIntegrationTest.cs +++ b/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/AbstractIntegrationTest.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. See License.txt in the project root for license information. using System; +using System.Threading; using Microsoft.VisualStudio.Extensibility.Testing; using Xunit; using Xunit.Sdk; @@ -30,6 +31,11 @@ namespace Microsoft.VisualStudio.Razor.IntegrationTests protected const string ProjectName = "TestProj"; protected const string SolutionName = "TestSolution"; + private readonly static TimeSpan s_shortHangMitigatingTimeout = new(hours: 0, minutes: 1, seconds: 0); + private readonly CancellationTokenSource _shortHangMitigatingCancellationTokenSource = new(s_shortHangMitigatingTimeout); + + protected CancellationToken ControlledHangMitigatingCancellationToken => HangMitigatingCancellationToken; + public override async Task InitializeAsync() { await base.InitializeAsync(); diff --git a/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/AbstractRazorEditorTest.cs b/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/AbstractRazorEditorTest.cs index 3108f26e1f..3aec8d685a 100644 --- a/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/AbstractRazorEditorTest.cs +++ b/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/AbstractRazorEditorTest.cs @@ -2,204 +2,76 @@ // Licensed under the MIT license. See License.txt in the project root for license information. using System; -using System.Collections.Generic; -using System.IO; using System.Linq; using System.Reflection; using System.Threading; using System.Threading.Tasks; -using Microsoft.Internal.VisualStudio.Shell.Embeddable.Feedback; -using Microsoft.VisualStudio.ComponentModelHost; +using Microsoft.Internal.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Razor.IntegrationTests.InProcess; +using Microsoft.VisualStudio.Settings; using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.TestTools.UnitTesting; using Xunit.Harness; namespace Microsoft.VisualStudio.Razor.IntegrationTests { public abstract class AbstractRazorEditorTest : AbstractEditorTest { - internal const string BlazorProjectName = "BlazorProject"; - - private static readonly string s_pagesDir = Path.Combine("Pages"); - private static readonly string s_sharedDir = Path.Combine("Shared"); - internal static readonly string FetchDataRazorFile = Path.Combine(s_pagesDir, "FetchData.razor"); - internal static readonly string CounterRazorFile = Path.Combine(s_pagesDir, "Counter.razor"); - internal static readonly string IndexRazorFile = Path.Combine(s_pagesDir, "Index.razor"); - internal static readonly string ModifiedIndexRazorFile = Path.Combine(s_pagesDir, "ModifiedIndex.razor"); - internal static readonly string SemanticTokensFile = Path.Combine(s_pagesDir, "SemanticTokens.razor"); - internal static readonly string MainLayoutFile = Path.Combine(s_sharedDir, "MainLayout.razor"); - internal static readonly string ErrorCshtmlFile = Path.Combine(s_pagesDir, "Error.cshtml"); - internal static readonly string ImportsRazorFile = "_Imports.razor"; - - internal static readonly string IndexPageContent = @"@page ""/"" - -Index - -

Hello, world!

- -Welcome to your new app. - -"; - - internal static readonly string MainLayoutContent = @"@inherits LayoutComponentBase - -BlazorApp - -
-
- -
- -
-
- About -
- -
- @Body -
-
-
-"; - + private const string LegacyRazorEditorFeatureFlag = "Razor.LSP.LegacyEditor"; + private const string UseLegacyASPNETCoreEditorSetting = "TextEditor.HTML.Specific.UseLegacyASPNETCoreRazorEditor"; private const string RazorComponentElementClassification = "RazorComponentElement"; - private const string RazorOutputLogId = "RazorOutputLog"; - private const string LogHubLogId = "RazorLogHub"; protected override string LanguageName => LanguageNames.Razor; - private static bool s_customLoggersAdded = false; - public override async Task InitializeAsync() { await base.InitializeAsync(); - // Add custom logs on failure if they haven't already been. - if (!s_customLoggersAdded) - { - DataCollectionService.RegisterCustomLogger(RazorOutputPaneLogger, RazorOutputLogId, "log"); - DataCollectionService.RegisterCustomLogger(RazorLogHubLogger, LogHubLogId, "zip"); + VisualStudioLogging.AddCustomLoggers(); - s_customLoggersAdded = true; - } + await TestServices.SolutionExplorer.CreateSolutionAsync("BlazorSolution", ControlledHangMitigatingCancellationToken); + await TestServices.SolutionExplorer.AddProjectAsync("BlazorProject", WellKnownProjectTemplates.BlazorProject, groupId: WellKnownProjectTemplates.GroupIdentifiers.Server, templateId: null, LanguageName, ControlledHangMitigatingCancellationToken); + await TestServices.SolutionExplorer.RestoreNuGetPackagesAsync(ControlledHangMitigatingCancellationToken); + await TestServices.Workspace.WaitForProjectSystemAsync(ControlledHangMitigatingCancellationToken); - await TestServices.SolutionExplorer.CreateSolutionAsync("BlazorSolution", HangMitigatingCancellationToken); - await TestServices.SolutionExplorer.AddProjectAsync("BlazorProject", WellKnownProjectTemplates.BlazorProject, groupId: WellKnownProjectTemplates.GroupIdentifiers.Server, templateId: null, LanguageName, HangMitigatingCancellationToken); - await TestServices.SolutionExplorer.RestoreNuGetPackagesAsync(HangMitigatingCancellationToken); - await TestServices.Workspace.WaitForProjectSystemAsync(HangMitigatingCancellationToken); - - await TestServices.Workspace.WaitForAsyncOperationsAsync(FeatureAttribute.LanguageServer, HangMitigatingCancellationToken); + await TestServices.Workspace.WaitForAsyncOperationsAsync(FeatureAttribute.LanguageServer, ControlledHangMitigatingCancellationToken); // We open the Index.razor file, and wait for 3 RazorComponentElement's to be classified, as that // way we know the LSP server is up, running, and has processed both local and library-sourced Components - await TestServices.SolutionExplorer.AddFileAsync(BlazorProjectName, ModifiedIndexRazorFile, IndexPageContent, open: true, HangMitigatingCancellationToken); + await TestServices.SolutionExplorer.AddFileAsync(RazorProjectConstants.BlazorProjectName, RazorProjectConstants.ModifiedIndexRazorFile, RazorProjectConstants.IndexPageContent, open: true, ControlledHangMitigatingCancellationToken); // Razor extension doesn't launch until a razor file is opened, so wait for it to equalize - await TestServices.Workspace.WaitForAsyncOperationsAsync(FeatureAttribute.LanguageServer, HangMitigatingCancellationToken); - await TestServices.Workspace.WaitForAsyncOperationsAsync(FeatureAttribute.Workspace, HangMitigatingCancellationToken); - await TestServices.Workspace.WaitForProjectSystemAsync(HangMitigatingCancellationToken); + await TestServices.Workspace.WaitForAsyncOperationsAsync(FeatureAttribute.LanguageServer, ControlledHangMitigatingCancellationToken); + await TestServices.Workspace.WaitForAsyncOperationsAsync(FeatureAttribute.Workspace, ControlledHangMitigatingCancellationToken); + await TestServices.Workspace.WaitForProjectSystemAsync(ControlledHangMitigatingCancellationToken); - await EnsureExtensionInstalledAsync(HangMitigatingCancellationToken); + EnsureLSPEditorEnabled(); + await EnsureTextViewRolesAsync(ControlledHangMitigatingCancellationToken); + await EnsureExtensionInstalledAsync(ControlledHangMitigatingCancellationToken); - try - { - await TestServices.Editor.WaitForClassificationAsync(HangMitigatingCancellationToken, expectedClassification: RazorComponentElementClassification, count: 3); - } - catch (OperationCanceledException) - { - // DataCollectionService does not fire in the case that errors or exceptions are thrown during Initialization. - // Let's capture some of the things we care about most manually. - var logHubFilePath = CreateLogFileName(LogHubLogId, "zip"); - RazorLogHubLogger(logHubFilePath); - var outputPaneFilePath = CreateLogFileName(RazorOutputLogId, "log"); - RazorOutputPaneLogger(outputPaneFilePath); - throw; - } + await TestServices.Editor.WaitForClassificationAsync(ControlledHangMitigatingCancellationToken, expectedClassification: RazorComponentElementClassification, count: 3); // Close the file we opened, just in case, so the test can start with a clean slate - await TestServices.Editor.CloseDocumentWindowAsync(HangMitigatingCancellationToken); + await TestServices.Editor.CloseDocumentWindowAsync(ControlledHangMitigatingCancellationToken); + } - static void RazorLogHubLogger(string filePath) - { - var componentModel = GlobalServiceProvider.ServiceProvider.GetService(); - if (componentModel is null) - { - // Unable to get componentModel - return; - } + private static void EnsureLSPEditorEnabled() + { + var settingsManager = (ISettingsManager)ServiceProvider.GlobalProvider.GetService(typeof(SVsSettingsPersistenceManager)); + Assumes.Present(settingsManager); + var featureFlags = (IVsFeatureFlags)AsyncPackage.GetGlobalService(typeof(SVsFeatureFlags)); + var legacyEditorFeatureFlagEnabled = featureFlags.IsFeatureEnabled(LegacyRazorEditorFeatureFlag, defaultValue: false); + Assert.AreEqual(false, legacyEditorFeatureFlagEnabled, "Expected Legacy Editor Feature Flag to be disabled, but it was enabled"); - var feedbackFileProviders = componentModel.GetExtensions(); + var useLegacyEditor = settingsManager.GetValueOrDefault(UseLegacyASPNETCoreEditorSetting); + Assert.AreEqual(false, useLegacyEditor, "Expected the Legacy Razor Editor to be disabled, but it was enabled"); + } - // Collect all the file names first since they can kick of file creation events that might need extra time to resolve. - var files = new List(); - foreach (var feedbackFileProvider in feedbackFileProviders) - { - files.AddRange(feedbackFileProvider.GetFiles()); - } - - _ = CollectLogHubAsync(files, filePath); - } - - static async Task CollectLogHubAsync(IEnumerable files, string destination) - { - // What's important in this weird threading stuff is ensuring we vacate the thread RazorLogHubLogger was called on - // because if we don't it ends up blocking the thread that creates the zip file we need. - await ThreadHelper.JoinableTaskFactory.RunAsync(async () => - { - foreach (var file in files) - { - var name = Path.GetFileName(file); - - // Only caputre loghub - if (name.Contains("LogHub") && Path.GetExtension(file) == ".zip") - { - await Task.Run(() => - { - WaitForFileExistsAsync(file); - if (File.Exists(file)) - { - File.Copy(file, destination); - } - }); - } - } - }); - } - - static void RazorOutputPaneLogger(string filePath) - { - // JoinableTaskFactory.Run isn't an option because we might be disposing already. - // Don't use ThreadHelper.JoinableTaskFactory in test methods, but it's correct here. -#pragma warning disable VSTHRD103 // Call async methods when in an async method - ThreadHelper.JoinableTaskFactory.Run(async () => -#pragma warning restore VSTHRD103 // Call async methods when in an async method - { - try - { - var testServices = await Extensibility.Testing.TestServices.CreateAsync(ThreadHelper.JoinableTaskFactory); - var paneContent = await testServices.Output.GetRazorOutputPaneContentAsync(CancellationToken.None); - File.WriteAllText(filePath, paneContent); - } - catch (Exception) - { - // Eat any errors so we don't block further collection - } - }); - } - - static void WaitForFileExistsAsync(string file) - { - const int MaxRetries = 50; - var retries = 0; - while (!File.Exists(file) && retries < MaxRetries) - { - retries++; - // Free your thread - Thread.Yield(); - // Wait a bit - Thread.Sleep(100); - } - } + private async Task EnsureTextViewRolesAsync(CancellationToken cancellationToken) + { + var textView = await TestServices.Editor.GetActiveTextViewAsync(cancellationToken); + var contentType = textView.TextSnapshot.ContentType; + Assert.AreEqual("Razor", contentType.TypeName); } private async Task EnsureExtensionInstalledAsync(CancellationToken cancellationToken) @@ -249,21 +121,5 @@ Welcome to your new app. } } } - - // We use reflection to get at a couple of the internals of DataCollectionService so that we use the propper LogDirectory. - private static string CreateLogFileName(string logId, string extension) - { - var dataCollectionServiceType = typeof(DataCollectionService); - var getLogDirectoryMethod = dataCollectionServiceType.GetMethod("GetLogDirectory", BindingFlags.Static | BindingFlags.NonPublic); - var logDirectory = getLogDirectoryMethod.Invoke(obj: null, new object[] { }); - - var createLogFileNameMethod = dataCollectionServiceType.GetMethod("CreateLogFileName", BindingFlags.Static | BindingFlags.NonPublic); - var timestamp = DateTimeOffset.UtcNow; - var testName = "TestInitialization"; - var errorId = "InitializationError"; - var @params = new object[] { logDirectory, timestamp, testName, errorId, logId, extension }; - var logFileName = (string)createLogFileNameMethod.Invoke(obj: null, @params); - return logFileName; - } } } diff --git a/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/BreakpointSpanTests.cs b/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/BreakpointSpanTests.cs index 5423393f5f..97f93bd9f3 100644 --- a/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/BreakpointSpanTests.cs +++ b/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/BreakpointSpanTests.cs @@ -12,23 +12,23 @@ namespace Microsoft.VisualStudio.Razor.IntegrationTests public async Task SetBreakpoint_FirstCharacter_SpanAdjusts() { // Open the file - await TestServices.SolutionExplorer.OpenFileAsync(BlazorProjectName, CounterRazorFile, HangMitigatingCancellationToken); + await TestServices.SolutionExplorer.OpenFileAsync(RazorProjectConstants.BlazorProjectName, RazorProjectConstants.CounterRazorFile, ControlledHangMitigatingCancellationToken); // Wait for classifications to indicate Razor LSP is up and running - await TestServices.Editor.WaitForClassificationAsync(HangMitigatingCancellationToken); - await TestServices.Editor.SetTextAsync("

@{ var abc = 123; }

", HangMitigatingCancellationToken); + await TestServices.Editor.WaitForClassificationAsync(ControlledHangMitigatingCancellationToken); + await TestServices.Editor.SetTextAsync("

@{ var abc = 123; }

", ControlledHangMitigatingCancellationToken); // Act - await TestServices.Debugger.SetBreakpointAsync(CounterRazorFile, line: 1, character: 1, HangMitigatingCancellationToken); + await TestServices.Debugger.SetBreakpointAsync(RazorProjectConstants.CounterRazorFile, line: 1, character: 1, ControlledHangMitigatingCancellationToken); // Assert - await TestServices.Debugger.VerifyBreakpointAsync(CounterRazorFile, line: 1, character: 7, HangMitigatingCancellationToken); + await TestServices.Debugger.VerifyBreakpointAsync(RazorProjectConstants.CounterRazorFile, line: 1, character: 7, ControlledHangMitigatingCancellationToken); } [IdeFact] public async Task SetBreakpoint_FirstCharacter_InvalidLine() { - var version = await TestServices.Shell.GetVersionAsync(HangMitigatingCancellationToken); + var version = await TestServices.Shell.GetVersionAsync(ControlledHangMitigatingCancellationToken); if (version < new System.Version(17, 3, 32412, 127)) { // Functionality under test was added in v17.3-Preview1 (17.3.32412.127) so this test will @@ -39,16 +39,16 @@ namespace Microsoft.VisualStudio.Razor.IntegrationTests } // Open the file - await TestServices.SolutionExplorer.OpenFileAsync(BlazorProjectName, CounterRazorFile, HangMitigatingCancellationToken); + await TestServices.SolutionExplorer.OpenFileAsync(RazorProjectConstants.BlazorProjectName, RazorProjectConstants.CounterRazorFile, ControlledHangMitigatingCancellationToken); // Wait for classifications to indicate Razor LSP is up and running - await TestServices.Editor.WaitForClassificationAsync(HangMitigatingCancellationToken); + await TestServices.Editor.WaitForClassificationAsync(ControlledHangMitigatingCancellationToken); await TestServices.Editor.SetTextAsync(@"

@{ var abc = 123; -}

", HangMitigatingCancellationToken); +}

", ControlledHangMitigatingCancellationToken); // Act - var result = await TestServices.Debugger.SetBreakpointAsync(CounterRazorFile, line: 1, character: 1, HangMitigatingCancellationToken); + var result = await TestServices.Debugger.SetBreakpointAsync(RazorProjectConstants.CounterRazorFile, line: 1, character: 1, ControlledHangMitigatingCancellationToken); // Assert Assert.False(result); @@ -58,19 +58,19 @@ namespace Microsoft.VisualStudio.Razor.IntegrationTests public async Task SetBreakpoint_FirstCharacter_ValidLine() { // Open the file - await TestServices.SolutionExplorer.OpenFileAsync(BlazorProjectName, CounterRazorFile, HangMitigatingCancellationToken); + await TestServices.SolutionExplorer.OpenFileAsync(RazorProjectConstants.BlazorProjectName, RazorProjectConstants.CounterRazorFile, ControlledHangMitigatingCancellationToken); // Wait for classifications to indicate Razor LSP is up and running - await TestServices.Editor.WaitForClassificationAsync(HangMitigatingCancellationToken); + await TestServices.Editor.WaitForClassificationAsync(ControlledHangMitigatingCancellationToken); await TestServices.Editor.SetTextAsync(@"

@{ var abc = 123; -}

", HangMitigatingCancellationToken); +}

", ControlledHangMitigatingCancellationToken); // Act - await TestServices.Debugger.SetBreakpointAsync(CounterRazorFile, line: 2, character: 1, HangMitigatingCancellationToken); + await TestServices.Debugger.SetBreakpointAsync(RazorProjectConstants.CounterRazorFile, line: 2, character: 1, ControlledHangMitigatingCancellationToken); // Assert - await TestServices.Debugger.VerifyBreakpointAsync(CounterRazorFile, line: 2, character: 4, HangMitigatingCancellationToken); + await TestServices.Debugger.VerifyBreakpointAsync(RazorProjectConstants.CounterRazorFile, line: 2, character: 4, ControlledHangMitigatingCancellationToken); } } } diff --git a/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/CodeFoldingTests.cs b/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/CodeFoldingTests.cs index 2a9a7ad0df..c560e6f3cb 100644 --- a/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/CodeFoldingTests.cs +++ b/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/CodeFoldingTests.cs @@ -22,7 +22,7 @@ namespace Microsoft.VisualStudio.Razor.IntegrationTests private async Task AssertFoldableBlocksAsync(params string[] blockTexts) { - var textView = await TestServices.Editor.GetActiveTextViewAsync(HangMitigatingCancellationToken); + var textView = await TestServices.Editor.GetActiveTextViewAsync(ControlledHangMitigatingCancellationToken); var text = textView.TextBuffer.CurrentSnapshot.GetText(); var foldableSpans = blockTexts.Select(blockText => @@ -43,8 +43,8 @@ namespace Microsoft.VisualStudio.Razor.IntegrationTests var outlines = new ICollapsible[0]; while (tries++ < MaxTries) { - textView = await TestServices.Editor.GetActiveTextViewAsync(HangMitigatingCancellationToken); - outlines = await TestServices.Editor.GetOutlineRegionsAsync(textView, HangMitigatingCancellationToken); + textView = await TestServices.Editor.GetActiveTextViewAsync(ControlledHangMitigatingCancellationToken); + outlines = await TestServices.Editor.GetOutlineRegionsAsync(textView, ControlledHangMitigatingCancellationToken); (missingLines, var extraLines) = GetOutlineDiff(outlines, foldableSpans, textView); if (missingLines.Length == 0) @@ -122,7 +122,7 @@ namespace Microsoft.VisualStudio.Razor.IntegrationTests public async Task CodeFolding_CodeBlock() { await TestServices.SolutionExplorer.AddFileAsync( - BlazorProjectName, + RazorProjectConstants.BlazorProjectName, "Test.razor", @" @page ""/Test"" @@ -140,7 +140,7 @@ namespace Microsoft.VisualStudio.Razor.IntegrationTests } }", open: true, - HangMitigatingCancellationToken); + ControlledHangMitigatingCancellationToken); await AssertFoldableBlocksAsync( @"@code { @@ -161,7 +161,7 @@ namespace Microsoft.VisualStudio.Razor.IntegrationTests public async Task CodeFolding_IfBlock() { await TestServices.SolutionExplorer.AddFileAsync( - BlazorProjectName, + RazorProjectConstants.BlazorProjectName, "Test.razor", @" @page ""/Test"" @@ -183,7 +183,7 @@ namespace Microsoft.VisualStudio.Razor.IntegrationTests } ", open: true, - HangMitigatingCancellationToken); + ControlledHangMitigatingCancellationToken); await AssertFoldableBlocksAsync( @"@if(true) @@ -206,7 +206,7 @@ namespace Microsoft.VisualStudio.Razor.IntegrationTests public async Task CodeFolding_ForEach() { await TestServices.SolutionExplorer.AddFileAsync( - BlazorProjectName, + RazorProjectConstants.BlazorProjectName, "Test.razor", @" @page ""/Test"" @@ -225,7 +225,7 @@ namespace Microsoft.VisualStudio.Razor.IntegrationTests } ", open: true, - HangMitigatingCancellationToken); + ControlledHangMitigatingCancellationToken); await AssertFoldableBlocksAsync( @"@foreach (var s in GetStuff()) @@ -241,7 +241,7 @@ namespace Microsoft.VisualStudio.Razor.IntegrationTests public async Task CodeFolding_CodeBlock_Region() { await TestServices.SolutionExplorer.AddFileAsync( - BlazorProjectName, + RazorProjectConstants.BlazorProjectName, "Test.razor", @" @page ""/Test"" @@ -258,7 +258,7 @@ namespace Microsoft.VisualStudio.Razor.IntegrationTests } ", open: true, - HangMitigatingCancellationToken); + ControlledHangMitigatingCancellationToken); await AssertFoldableBlocksAsync( @"#region Methods diff --git a/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/FeedbackLogging.cs b/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/FeedbackLogging.cs new file mode 100644 index 0000000000..42b6bbd2ee --- /dev/null +++ b/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/FeedbackLogging.cs @@ -0,0 +1,192 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT license. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Internal.VisualStudio.Shell.Embeddable.Feedback; +using Microsoft.VisualStudio.ComponentModelHost; +using Microsoft.VisualStudio.Shell; +using Xunit.Harness; + +namespace Microsoft.VisualStudio.Razor.IntegrationTests +{ + internal static class VisualStudioLogging + { + private static bool s_customLoggersAdded = false; + + public const string RazorOutputLogId = "RazorOutputLog"; + public const string LogHubLogId = "RazorLogHub"; + public const string ServiceHubLogId = "ServiceHubLog"; + public const string ComponentModelCacheId = "ComponentModelCache"; + public const string ExtensionDirectoryId = "ExtensionDirectory"; + + private static readonly object s_lockObj = new(); + + public static void AddCustomLoggers() + { + lock (s_lockObj) + { + // Add custom logs on failure if they haven't already been. + if (!s_customLoggersAdded) + { + DataCollectionService.RegisterCustomLogger(RazorOutputPaneLogger, RazorOutputLogId, "log"); + DataCollectionService.RegisterCustomLogger(RazorLogHubLogger, LogHubLogId, "zip"); + DataCollectionService.RegisterCustomLogger(RazorServiceHubLogger, ServiceHubLogId, "zip"); + DataCollectionService.RegisterCustomLogger(RazorComponentModelCacheLogger, ComponentModelCacheId, "zip"); + DataCollectionService.RegisterCustomLogger(RazorExtensionExplorerLogger, ExtensionDirectoryId, "txt"); + + s_customLoggersAdded = true; + } + } + } + + private static void RazorLogHubLogger(string filePath) + { + FeedbackLoggerInternal(filePath, "LogHub"); + } + + private static void RazorServiceHubLogger(string filePath) + { + FeedbackLoggerInternal(filePath, "ServiceHubLogs"); + } + + private static void RazorComponentModelCacheLogger(string filePath) + { + FeedbackLoggerInternal(filePath, "ComponentModelCache"); + } + + private static void FeedbackLoggerInternal(string filePath, string expectedFilePart) + { + var componentModel = GlobalServiceProvider.ServiceProvider.GetService(); + if (componentModel is null) + { + // Unable to get componentModel + return; + } + + var feedbackFileProviders = componentModel.GetExtensions(); + + // Collect all the file names first since they can kick of file creation events that might need extra time to resolve. + var files = new List(); + foreach (var feedbackFileProvider in feedbackFileProviders) + { + files.AddRange(feedbackFileProvider.GetFiles()); + } + + _ = CollectFeedbackItemsAsync(files, filePath, expectedFilePart); + } + + private static void RazorExtensionExplorerLogger(string filePath) + { + var hiveDirectories = GetHiveDirectories(); + var fileBuilder = new StringBuilder(); + if (hiveDirectories.Count() != 1) + { + fileBuilder.Append("Expected 1 hive but found "); + fileBuilder.AppendLine(hiveDirectories.Count().ToString()); + } + + foreach (var hiveDirectory in hiveDirectories) + { + var extensionsDir = Path.Combine(hiveDirectory, "Extensions"); + var compatListFile = Path.Combine(extensionsDir, "CompatibilityList.xml"); + if (File.Exists(compatListFile)) + { + var compatListContent = File.ReadAllText(compatListFile); + fileBuilder.AppendLine("CompatListContents:"); + fileBuilder.AppendLine(compatListContent); + } + else + { + fileBuilder.AppendLine("Missing CompatList file"); + } + + var microsoftDir = Path.Combine(extensionsDir, "Microsoft"); + var msExtensionFiles = Directory.EnumerateFiles(microsoftDir, "*", SearchOption.AllDirectories); + foreach (var msExtensionFile in msExtensionFiles) + { + fileBuilder.Append(" "); + fileBuilder.AppendLine(msExtensionFile); + } + } + + File.WriteAllText(filePath, fileBuilder.ToString()); + } + + private static IEnumerable GetHiveDirectories() + { + var localAppData = Environment.GetEnvironmentVariable("LocalAppData"); + var vsLocalDir = Path.Combine(localAppData, "Microsoft", "VisualStudio"); + var directories = Directory.GetDirectories(vsLocalDir, "17*RoslynDev", SearchOption.TopDirectoryOnly); + var hiveDirectories = directories.Where(d => !d.Contains("$")); + + return hiveDirectories; + } + + private static void RazorOutputPaneLogger(string filePath) + { + // JoinableTaskFactory.Run isn't an option because we might be disposing already. + // Don't use ThreadHelper.JoinableTaskFactory in test methods, but it's correct here. +#pragma warning disable VSTHRD103 // Call async methods when in an async method + ThreadHelper.JoinableTaskFactory.Run(async () => +#pragma warning restore VSTHRD103 // Call async methods when in an async method + { + try + { + var testServices = await Extensibility.Testing.TestServices.CreateAsync(ThreadHelper.JoinableTaskFactory); + var paneContent = await testServices.Output.GetRazorOutputPaneContentAsync(CancellationToken.None); + File.WriteAllText(filePath, paneContent); + } + catch (Exception) + { + // Eat any errors so we don't block further collection + } + }); + } + + private static async Task CollectFeedbackItemsAsync(IEnumerable files, string destination, string expectedFilePart) + { + // What's important in this weird threading stuff is ensuring we vacate the thread RazorLogHubLogger was called on + // because if we don't it ends up blocking the thread that creates the zip file we need. + await ThreadHelper.JoinableTaskFactory.RunAsync(async () => + { + foreach (var file in files) + { + var name = Path.GetFileName(file); + + // Only caputre loghub + if (name.Contains(expectedFilePart) && Path.GetExtension(file) == ".zip") + { + await Task.Run(() => + { + WaitForFileExists(file); + if (File.Exists(file)) + { + File.Copy(file, destination); + } + }); + } + } + }); + } + + private static void WaitForFileExists(string file) + { + const int MaxRetries = 50; + var retries = 0; + while (!File.Exists(file) && retries < MaxRetries) + { + retries++; + // Free your thread + Thread.Yield(); + // Wait a bit + Thread.Sleep(100); + } + } + } +} diff --git a/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/Formatting/FormatDocumentTests.cs b/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/Formatting/FormatDocumentTests.cs index 3f3a2d8ab0..1810f5b8aa 100644 --- a/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/Formatting/FormatDocumentTests.cs +++ b/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/Formatting/FormatDocumentTests.cs @@ -57,31 +57,31 @@ namespace Microsoft.VisualStudio.Razor.IntegrationTests // Open the file if (testFileName.EndsWith(".razor", StringComparison.OrdinalIgnoreCase)) { - await TestServices.SolutionExplorer.OpenFileAsync(BlazorProjectName, CounterRazorFile, HangMitigatingCancellationToken); + await TestServices.SolutionExplorer.OpenFileAsync(RazorProjectConstants.BlazorProjectName, RazorProjectConstants.CounterRazorFile, ControlledHangMitigatingCancellationToken); } else { - await TestServices.SolutionExplorer.OpenFileAsync(BlazorProjectName, ErrorCshtmlFile, HangMitigatingCancellationToken); + await TestServices.SolutionExplorer.OpenFileAsync(RazorProjectConstants.BlazorProjectName, RazorProjectConstants.ErrorCshtmlFile, ControlledHangMitigatingCancellationToken); } - await TestServices.Editor.SetTextAsync(input, HangMitigatingCancellationToken); + await TestServices.Editor.SetTextAsync(input, ControlledHangMitigatingCancellationToken); // Wait for the document to settle if (testFileName == "FormatAndUndo.cshtml") { // This doesn't have anything to outline so we'll wait for semantic colors - await TestServices.Editor.WaitForClassificationAsync(HangMitigatingCancellationToken, "method name"); + await TestServices.Editor.WaitForClassificationAsync(ControlledHangMitigatingCancellationToken, "method name"); } else { - await TestServices.Editor.WaitForOutlineRegionsAsync(HangMitigatingCancellationToken); + await TestServices.Editor.WaitForOutlineRegionsAsync(ControlledHangMitigatingCancellationToken); } // Act - await TestServices.Editor.InvokeFormatDocumentAsync(HangMitigatingCancellationToken); + await TestServices.Editor.InvokeFormatDocumentAsync(ControlledHangMitigatingCancellationToken); // Assert - var actual = await TestServices.Editor.WaitForTextChangeAsync(input, HangMitigatingCancellationToken); + var actual = await TestServices.Editor.WaitForTextChangeAsync(input, ControlledHangMitigatingCancellationToken); if (!TryGetResource(expectedResourceName, out var expected)) { diff --git a/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/GoToDefinitionTests.cs b/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/GoToDefinitionTests.cs index e9fd0b2570..929cf944d4 100644 --- a/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/GoToDefinitionTests.cs +++ b/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/GoToDefinitionTests.cs @@ -12,63 +12,63 @@ namespace Microsoft.VisualStudio.Razor.IntegrationTests public async Task GoToDefinition_MethodInSameFile() { // Open the file - await TestServices.SolutionExplorer.OpenFileAsync(BlazorProjectName, CounterRazorFile, HangMitigatingCancellationToken); + await TestServices.SolutionExplorer.OpenFileAsync(RazorProjectConstants.BlazorProjectName, RazorProjectConstants.CounterRazorFile, ControlledHangMitigatingCancellationToken); - await TestServices.Editor.PlaceCaretAsync("IncrementCount", charsOffset: -1, HangMitigatingCancellationToken); + await TestServices.Editor.PlaceCaretAsync("IncrementCount", charsOffset: -1, ControlledHangMitigatingCancellationToken); // Act - await TestServices.Editor.InvokeGoToDefinitionAsync(HangMitigatingCancellationToken); + await TestServices.Editor.InvokeGoToDefinitionAsync(ControlledHangMitigatingCancellationToken); // Assert - await TestServices.Editor.WaitForCurrentLineTextAsync("private void IncrementCount()", HangMitigatingCancellationToken); + await TestServices.Editor.WaitForCurrentLineTextAsync("private void IncrementCount()", ControlledHangMitigatingCancellationToken); } [IdeFact] public async Task GoToDefinition_CSharpClass() { // Open the file - await TestServices.SolutionExplorer.OpenFileAsync(BlazorProjectName, IndexRazorFile, HangMitigatingCancellationToken); + await TestServices.SolutionExplorer.OpenFileAsync(RazorProjectConstants.BlazorProjectName, RazorProjectConstants.IndexRazorFile, ControlledHangMitigatingCancellationToken); // Change text to refer back to Program class - await TestServices.Editor.SetTextAsync(@" "); // Act - await TestServices.Editor.PlaceCaretAsync(">", charsOffset: 1, HangMitigatingCancellationToken); + await TestServices.Editor.PlaceCaretAsync(">", charsOffset: 1, ControlledHangMitigatingCancellationToken); TestServices.Input.Send("{ENTER}"); TestServices.Input.Send("A"); @@ -25,7 +25,7 @@ public class OnEnterRulesTests : AbstractRazorEditorTest -", HangMitigatingCancellationToken); +", ControlledHangMitigatingCancellationToken); } [IdeFact] @@ -37,7 +37,7 @@ public class OnEnterRulesTests : AbstractRazorEditorTest "); // Act - await TestServices.Editor.PlaceCaretAsync("", charsOffset: 1, HangMitigatingCancellationToken); + await TestServices.Editor.PlaceCaretAsync("", charsOffset: 1, ControlledHangMitigatingCancellationToken); TestServices.Input.Send("{ENTER}"); TestServices.Input.Send("A"); @@ -45,7 +45,7 @@ public class OnEnterRulesTests : AbstractRazorEditorTest await TestServices.Editor.VerifyTextContainsAsync(@" A -", HangMitigatingCancellationToken); +", ControlledHangMitigatingCancellationToken); } [IdeFact] @@ -57,7 +57,7 @@ A "); // Act - await TestServices.Editor.PlaceCaretAsync("button", charsOffset: 1, HangMitigatingCancellationToken); + await TestServices.Editor.PlaceCaretAsync("button", charsOffset: 1, ControlledHangMitigatingCancellationToken); TestServices.Input.Send("{ENTER}"); @@ -65,7 +65,7 @@ A await TestServices.Editor.VerifyTextContainsAsync(@" -", HangMitigatingCancellationToken); +", ControlledHangMitigatingCancellationToken); } [IdeFact] @@ -77,14 +77,14 @@ A "); // Act - await TestServices.Editor.PlaceCaretAsync("stuff", charsOffset: 1, HangMitigatingCancellationToken); + await TestServices.Editor.PlaceCaretAsync("stuff", charsOffset: 1, ControlledHangMitigatingCancellationToken); TestServices.Input.Send("{ENTER}"); // Assert await TestServices.Editor.VerifyTextContainsAsync(@" -", HangMitigatingCancellationToken); +", ControlledHangMitigatingCancellationToken); } [IdeFact] @@ -96,14 +96,14 @@ A "); // Act - await TestServices.Editor.PlaceCaretAsync("button", charsOffset: 1, HangMitigatingCancellationToken); + await TestServices.Editor.PlaceCaretAsync("button", charsOffset: 1, ControlledHangMitigatingCancellationToken); TestServices.Input.Send("{ENTER}"); // Assert await TestServices.Editor.VerifyTextContainsAsync(@" -", HangMitigatingCancellationToken); +", ControlledHangMitigatingCancellationToken); } [IdeFact] @@ -115,14 +115,14 @@ A "); // Act - await TestServices.Editor.PlaceCaretAsync("@onclick='thing'", charsOffset: 1, HangMitigatingCancellationToken); + await TestServices.Editor.PlaceCaretAsync("@onclick='thing'", charsOffset: 1, ControlledHangMitigatingCancellationToken); TestServices.Input.Send("{ENTER}"); // Assert await TestServices.Editor.VerifyTextContainsAsync(@" -", HangMitigatingCancellationToken); +", ControlledHangMitigatingCancellationToken); } [IdeFact] @@ -134,21 +134,21 @@ A "); // Act - await TestServices.Editor.PlaceCaretAsync("button", charsOffset: 1, HangMitigatingCancellationToken); + await TestServices.Editor.PlaceCaretAsync("button", charsOffset: 1, ControlledHangMitigatingCancellationToken); TestServices.Input.Send("{ENTER}"); // Assert await TestServices.Editor.VerifyTextContainsAsync(@"