From 21c978ab5e952e4ae543c8af617088dd3110366e Mon Sep 17 00:00:00 2001 From: Ryan Brandenburg Date: Thu, 5 May 2022 16:29:37 -0700 Subject: [PATCH] Check Assembly load safely in Integration tests (#6353) * Wait after File open * Include assemblyLocation in error message * Wait for Extension to load to check coloration * Prevent unwanted references * Debug upgrade * Don't load assemblies --- eng/Versions.props | 2 +- ...osoft.CodeAnalysis.Razor.Workspaces.csproj | 4 +- .../Properties/AssemblyInfo.cs | 4 +- .../Language/TestFile.cs | 25 +-- .../NullableAttributes.cs | 149 ++++++++++++++++++ .../Properties/AssemblyInfo.cs | 4 +- .../AbstractRazorEditorTest.cs | 55 ++++++- .../CodeFoldingTests.cs | 1 - .../Formatting/FormatDocumentTests.cs | 1 - .../GoToDefinitionTests.cs | 1 - .../GoToImplementationTests.cs | 1 - ...VisualStudio.Razor.IntegrationTests.csproj | 11 +- .../OnTypeFormattingTests.cs | 1 - 13 files changed, 212 insertions(+), 47 deletions(-) create mode 100644 src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common/NullableAttributes.cs diff --git a/eng/Versions.props b/eng/Versions.props index 3429531704..907318d77d 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -95,7 +95,7 @@ $(Tooling_MicrosoftCodeAnalysisTestingVersion) $(Tooling_MicrosoftCodeAnalysisTestingVersion) $(MicrosoftVisualStudioPackagesVersion) - 0.1.135-beta + 0.1.137-beta $(MicrosoftVisualStudioExtensibilityTestingXunitVersion) $(MicrosoftVisualStudioPackagesVersion) $(MicrosoftVisualStudioPackagesVersion) diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Microsoft.CodeAnalysis.Razor.Workspaces.csproj b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Microsoft.CodeAnalysis.Razor.Workspaces.csproj index 7e863f0648..5fbbebc52c 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Microsoft.CodeAnalysis.Razor.Workspaces.csproj +++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Microsoft.CodeAnalysis.Razor.Workspaces.csproj @@ -13,8 +13,8 @@ - - + + diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Properties/AssemblyInfo.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Properties/AssemblyInfo.cs index 45180066b2..bd8ff1df84 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Properties/AssemblyInfo.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Properties/AssemblyInfo.cs @@ -1,9 +1,8 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT license. See License.txt in the project root for license information. -#nullable disable - using System.Runtime.CompilerServices; + [assembly: InternalsVisibleTo("Microsoft.AspNetCore.Razor.OmniSharpPlugin.StrongNamed, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] [assembly: InternalsVisibleTo("Microsoft.AspNetCore.Razor.LanguageServer, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] [assembly: InternalsVisibleTo("Microsoft.AspNetCore.Razor.Test.Common, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] @@ -28,6 +27,5 @@ using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("Microsoft.VisualStudio.LanguageServices.Razor, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] [assembly: InternalsVisibleTo("Microsoft.VisualStudio.LanguageServices.Razor.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] [assembly: InternalsVisibleTo("Microsoft.VisualStudio.RazorExtension, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] -[assembly: InternalsVisibleTo("Microsoft.VisualStudio.Razor.IntegrationTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] [assembly: InternalsVisibleTo("Microsoft.Ide.Web, PublicKey=0024000004800000940000000602000000240000525341310004000001000100675da410943cdcf89a2bbd3716e451b3c35c0de9278a874e06d143dbc861f7b4d21771131177e413290078b98615421b2bb9ac25c14021c4e2c7b967407b5ea96417317ff8bdb1ef34e0d63f5965bdf92841bdaae505987af712a2e1951b2ff76a16d211e0d5ae2c444f55dbd0a3c0f5bed051af0cf7bae49114c4e0c527c4ed")] [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common/Language/TestFile.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common/Language/TestFile.cs index 64d92d0f9c..08a5717651 100644 --- a/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common/Language/TestFile.cs +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common/Language/TestFile.cs @@ -1,15 +1,12 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT license. See License.txt in the project root for license information. -#nullable disable - using System; using System.Globalization; using System.IO; using System.Reflection; using System.Threading; using System.Threading.Tasks; -using Xunit; namespace Microsoft.AspNetCore.Razor.Language { @@ -40,7 +37,7 @@ namespace Microsoft.AspNetCore.Razor.Language var stream = Assembly.GetManifestResourceStream(ResourceName); if (stream is null) { - Assert.True(false, string.Format(CultureInfo.InvariantCulture, "Manifest resource: {0} not found", ResourceName)); + throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Manifest resource: {0} not found", ResourceName)); } return stream; @@ -87,25 +84,5 @@ namespace Microsoft.AspNetCore.Razor.Language // since all the unit tests rely on the assumption that the files will have \r\n endings. return contents.Replace("\r", "").Replace("\n", "\r\n"); } - - /// - /// Saves the file to the specified path. - /// - public void Save(string filePath) - { - var directory = Path.GetDirectoryName(filePath); - if (!Directory.Exists(directory)) - { - Directory.CreateDirectory(directory); - } - - using (var outStream = File.Create(filePath)) - { - using (var inStream = OpenRead()) - { - inStream.CopyTo(outStream); - } - } - } } } diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common/NullableAttributes.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common/NullableAttributes.cs new file mode 100644 index 0000000000..da5eb7b791 --- /dev/null +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common/NullableAttributes.cs @@ -0,0 +1,149 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT license. See License.txt in the project root for license information. + +// This was copied from https://github.com/dotnet/runtime/blob/39b9607807f29e48cae4652cd74735182b31182e/src/libraries/System.Private.CoreLib/src/System/Diagnostics/CodeAnalysis/NullableAttributes.cs +// and updated to have the scope of the attributes be internal. +namespace System.Diagnostics.CodeAnalysis +{ +#if !NETCOREAPP + + /// Specifies that null is allowed as an input even if the corresponding type disallows it. + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] + internal sealed class AllowNullAttribute : Attribute { } + + /// Specifies that null is disallowed as an input even if the corresponding type allows it. + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] + internal sealed class DisallowNullAttribute : Attribute { } + + /// Specifies that an output may be null even if the corresponding type disallows it. + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)] + internal sealed class MaybeNullAttribute : Attribute { } + + /// Specifies that an output will not be null even if the corresponding type allows it. + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)] + internal sealed class NotNullAttribute : Attribute { } + + /// Specifies that when a method returns , the parameter may be null even if the corresponding type disallows it. + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] + internal sealed class MaybeNullWhenAttribute : Attribute + { + /// Initializes the attribute with the specified return value condition. + /// + /// The return value condition. If the method returns this value, the associated parameter may be null. + /// + public MaybeNullWhenAttribute(bool returnValue) => ReturnValue = returnValue; + + /// Gets the return value condition. + public bool ReturnValue { get; } + } + + /// Specifies that when a method returns , the parameter will not be null even if the corresponding type allows it. + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] + internal sealed class NotNullWhenAttribute : Attribute + { + /// Initializes the attribute with the specified return value condition. + /// + /// The return value condition. If the method returns this value, the associated parameter will not be null. + /// + public NotNullWhenAttribute(bool returnValue) => ReturnValue = returnValue; + + /// Gets the return value condition. + public bool ReturnValue { get; } + } + + /// Specifies that the output will be non-null if the named parameter is non-null. + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, AllowMultiple = true, Inherited = false)] + internal sealed class NotNullIfNotNullAttribute : Attribute + { + /// Initializes the attribute with the associated parameter name. + /// + /// The associated parameter name. The output will be non-null if the argument to the parameter specified is non-null. + /// + public NotNullIfNotNullAttribute(string parameterName) => ParameterName = parameterName; + + /// Gets the associated parameter name. + public string ParameterName { get; } + } + + /// Applied to a method that will never return under any circumstance. + [AttributeUsage(AttributeTargets.Method, Inherited = false)] + internal sealed class DoesNotReturnAttribute : Attribute { } + + /// Specifies that the method will not return if the associated Boolean parameter is passed the specified value. + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] + internal sealed class DoesNotReturnIfAttribute : Attribute + { + /// Initializes the attribute with the specified parameter value. + /// + /// The condition parameter value. Code after the method will be considered unreachable by diagnostics if the argument to + /// the associated parameter matches this value. + /// + public DoesNotReturnIfAttribute(bool parameterValue) => ParameterValue = parameterValue; + + /// Gets the condition parameter value. + public bool ParameterValue { get; } + } + +#endif + +#if !NETCOREAPP || NETCOREAPP3_1 + + /// Specifies that the method or property will ensure that the listed field and property members have not-null values. + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] + internal sealed class MemberNotNullAttribute : Attribute + { + /// Initializes the attribute with a field or property member. + /// + /// The field or property member that is promised to be not-null. + /// + public MemberNotNullAttribute(string member) => Members = new[] { member }; + + /// Initializes the attribute with the list of field and property members. + /// + /// The list of field and property members that are promised to be not-null. + /// + public MemberNotNullAttribute(params string[] members) => Members = members; + + /// Gets field or property member names. + public string[] Members { get; } + } + + /// Specifies that the method or property will ensure that the listed field and property members have not-null values when returning with the specified return value condition. + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] + internal sealed class MemberNotNullWhenAttribute : Attribute + { + /// Initializes the attribute with the specified return value condition and a field or property member. + /// + /// The return value condition. If the method returns this value, the associated parameter will not be null. + /// + /// + /// The field or property member that is promised to be not-null. + /// + public MemberNotNullWhenAttribute(bool returnValue, string member) + { + ReturnValue = returnValue; + Members = new[] { member }; + } + + /// Initializes the attribute with the specified return value condition and list of field and property members. + /// + /// The return value condition. If the method returns this value, the associated parameter will not be null. + /// + /// + /// The list of field and property members that are promised to be not-null. + /// + public MemberNotNullWhenAttribute(bool returnValue, params string[] members) + { + ReturnValue = returnValue; + Members = members; + } + + /// Gets the return value condition. + public bool ReturnValue { get; } + + /// Gets field or property member names. + public string[] Members { get; } + } + +#endif +} diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common/Properties/AssemblyInfo.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common/Properties/AssemblyInfo.cs index dd1929fa55..7c349b9e37 100644 --- a/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common/Properties/AssemblyInfo.cs +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common/Properties/AssemblyInfo.cs @@ -1,9 +1,8 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT license. See License.txt in the project root for license information. -#nullable disable - using System.Runtime.CompilerServices; + [assembly: InternalsVisibleTo("Microsoft.VisualStudio.Editor.Razor.Test.Common, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] [assembly: InternalsVisibleTo("Microsoft.AspNetCore.Razor.LanguageServer.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] [assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.Razor.Extensions.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] @@ -15,4 +14,5 @@ using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("Microsoft.VisualStudio.Editor.Razor.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] [assembly: InternalsVisibleTo("Microsoft.VisualStudio.LanguageServices.Razor.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] [assembly: InternalsVisibleTo("Microsoft.VisualStudio.LanguageServerClient.Razor.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] +[assembly: InternalsVisibleTo("Microsoft.VisualStudio.Razor.IntegrationTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] diff --git a/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/AbstractRazorEditorTest.cs b/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/AbstractRazorEditorTest.cs index 09b1f5fdf4..3108f26e1f 100644 --- a/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/AbstractRazorEditorTest.cs +++ b/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/AbstractRazorEditorTest.cs @@ -4,10 +4,10 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Reflection; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Host; using Microsoft.Internal.VisualStudio.Shell.Embeddable.Feedback; using Microsoft.VisualStudio.ComponentModelHost; using Microsoft.VisualStudio.Razor.IntegrationTests.InProcess; @@ -94,7 +94,13 @@ Welcome to your new app. // 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); - EnsureExtensionInstalled(); + // 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 EnsureExtensionInstalledAsync(HangMitigatingCancellationToken); + try { await TestServices.Editor.WaitForClassificationAsync(HangMitigatingCancellationToken, expectedClassification: RazorComponentElementClassification, count: 3); @@ -164,7 +170,9 @@ Welcome to your new app. { // 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 { @@ -194,16 +202,51 @@ Welcome to your new app. } } - private static void EnsureExtensionInstalled() + private async Task EnsureExtensionInstalledAsync(CancellationToken cancellationToken) { - var assembly = Assembly.Load(new AssemblyName("Microsoft.VisualStudio.RazorExtension")); - var version = assembly.GetName().Version; + const string AssemblyName = "Microsoft.AspNetCore.Razor.LanguageServer"; + using var semaphore = new SemaphoreSlim(1); + await semaphore.WaitAsync(cancellationToken); + + AppDomain.CurrentDomain.AssemblyLoad += CurrentDomain_AssemblyLoad; var localAppData = Environment.GetEnvironmentVariable("LocalAppData"); + Assembly? assembly = null; + try + { + var assemblies = AppDomain.CurrentDomain.GetAssemblies(); + assembly = assemblies.FirstOrDefault((assembly) => assembly.GetName().Name.Equals(AssemblyName)); + if (assembly is null) + { + await semaphore.WaitAsync(cancellationToken); + } + + semaphore.Release(); + } + finally + { + AppDomain.CurrentDomain.AssemblyLoad -= CurrentDomain_AssemblyLoad; + } + + if (assembly is null) + { + throw new NotImplementedException($"Integration test did not load extension"); + } + + var version = assembly.GetName().Version; if (!version.Equals(new Version(42, 42, 42, 42)) || !assembly.Location.StartsWith(localAppData, StringComparison.OrdinalIgnoreCase)) { - throw new NotImplementedException("Integration test not running against Experimental Extension"); + throw new NotImplementedException($"Integration test not running against Experimental Extension {assembly.Location}"); + } + + void CurrentDomain_AssemblyLoad(object sender, AssemblyLoadEventArgs args) + { + if (args.LoadedAssembly.GetName().Name.Equals(AssemblyName, StringComparison.Ordinal)) + { + assembly = args.LoadedAssembly; + semaphore.Release(); + } } } diff --git a/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/CodeFoldingTests.cs b/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/CodeFoldingTests.cs index c411e47cd7..2a9a7ad0df 100644 --- a/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/CodeFoldingTests.cs +++ b/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/CodeFoldingTests.cs @@ -5,7 +5,6 @@ using System.Collections.Immutable; using System.Linq; using System.Text; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Host; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Text.Outlining; 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 0df1114b2f..3f3a2d8ab0 100644 --- a/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/Formatting/FormatDocumentTests.cs +++ b/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/Formatting/FormatDocumentTests.cs @@ -7,7 +7,6 @@ using System.IO; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Razor.Language; -using Microsoft.CodeAnalysis.Host; using Xunit; namespace Microsoft.VisualStudio.Razor.IntegrationTests diff --git a/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/GoToDefinitionTests.cs b/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/GoToDefinitionTests.cs index 4befc966a2..e9fd0b2570 100644 --- a/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/GoToDefinitionTests.cs +++ b/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/GoToDefinitionTests.cs @@ -2,7 +2,6 @@ // Licensed under the MIT license. See License.txt in the project root for license information. using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Host; using Xunit; namespace Microsoft.VisualStudio.Razor.IntegrationTests diff --git a/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/GoToImplementationTests.cs b/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/GoToImplementationTests.cs index 0d3909463b..20b0dd9f04 100644 --- a/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/GoToImplementationTests.cs +++ b/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/GoToImplementationTests.cs @@ -2,7 +2,6 @@ // Licensed under the MIT license. See License.txt in the project root for license information. using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Host; using Xunit; namespace Microsoft.VisualStudio.Razor.IntegrationTests diff --git a/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/Microsoft.VisualStudio.Razor.IntegrationTests.csproj b/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/Microsoft.VisualStudio.Razor.IntegrationTests.csproj index 2e4fe3f343..a2892e9b25 100644 --- a/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/Microsoft.VisualStudio.Razor.IntegrationTests.csproj +++ b/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/Microsoft.VisualStudio.Razor.IntegrationTests.csproj @@ -15,11 +15,14 @@ - - - + + All + false + False + + - + diff --git a/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/OnTypeFormattingTests.cs b/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/OnTypeFormattingTests.cs index d6e09cecea..21cce047d5 100644 --- a/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/OnTypeFormattingTests.cs +++ b/src/Razor/test/Microsoft.VisualStudio.Razor.IntegrationTests/OnTypeFormattingTests.cs @@ -2,7 +2,6 @@ // Licensed under the MIT license. See License.txt in the project root for license information. using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Host; using Xunit; namespace Microsoft.VisualStudio.Razor.IntegrationTests