/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/ExportProviderBuilderTests.cs
175 строк
7 KB
David Barbet
feedback
11 июн 2026, 01:55
11 июн 2026, 01:55
f9b6575
Код
Авторство
О чём код?
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using Basic.Reference.Assemblies; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Text; using Roslyn.Test.Utilities; using Xunit.Abstractions; namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests; /// <summary> /// Use <see cref="AbstractLanguageServerMefHost"/> as we are testing the MEF composition and caching logic directly. /// </summary> public sealed class ExportProviderBuilderTests(ITestOutputHelper testOutputHelper) : AbstractLanguageServerMefHost(testOutputHelper) { [Fact] public async Task MefCompositionIsCached() { await using var testServer = await CreateLanguageServerAsync(serverConfiguration: ServerConfigurationWithoutDevKit); await AssertCacheWriteWasAttemptedAsync(); AssertCachedCompositionCountEquals(expectedCount: 1); } [Fact] public async Task MefCompositionIsReused() { await using var testServer = await CreateLanguageServerAsync(serverConfiguration: ServerConfigurationWithoutDevKit); await AssertCacheWriteWasAttemptedAsync(); // Second test server with the same set of assemblies. await using var testServer2 = await CreateLanguageServerAsync(serverConfiguration: ServerConfigurationWithoutDevKit); AssertNoCacheWriteWasAttempted(); AssertCachedCompositionCountEquals(expectedCount: 1); } [Fact] public async Task MultipleMefCompositionsAreCached() { await using var testServer = await CreateLanguageServerAsync(serverConfiguration: ServerConfigurationWithoutDevKit); await AssertCacheWriteWasAttemptedAsync(); // Second test server with a different set of assemblies. await using var testServer2 = await CreateLanguageServerAsync(serverConfiguration: DefaultServerConfiguration); await AssertCacheWriteWasAttemptedAsync(); AssertCachedCompositionCountEquals(expectedCount: 2); } [Theory, WorkItem("https://github.com/microsoft/vscode-dotnettools/issues/1686")] // gen-delims [InlineData("#"), InlineData(":"), InlineData("?"), InlineData("["), InlineData("]"), InlineData("@"), // sub-delims InlineData("!"), InlineData("$"), InlineData("&"), InlineData("'"), InlineData("("), InlineData(")"), InlineData("*"), InlineData("+"), InlineData(","), InlineData(";"), InlineData("=")] public async Task CanFindCodeBaseWhenReservedCharactersInPath(string reservedCharacter) { // Test that given an unescaped code base URI (as vs-mef gives us), we can resolve the file path even if it contains reserved characters. // Certain characters aren't valid file paths on different file systems and so can't be in the path. if (ExecutionConditionUtil.IsWindows && reservedCharacter is "*" or ":" or "?") { return; } var dllPath = GenerateDll(reservedCharacter, out var assemblyName); await using var testServer = await CreateLanguageServerAsync( serverConfiguration: DefaultServerConfiguration with { ExtensionAssemblyPaths = [dllPath] }); var assemblies = AppDomain.CurrentDomain.GetAssemblies(); var assembly = Assert.Single(assemblies, a => a.GetName().Name == assemblyName); var type = Assert.Single(assembly.GetTypes(), t => t.FullName?.Contains("ExportedType") == true); var values = testServer.ExportProvider.GetExportedValues(type, contractName: null); Assert.Single(values); } private string GenerateDll(string reservedCharacter, out string assemblyName) { var directory = TempRoot.CreateDirectory(); CSharpCompilationOptions options = new(OutputKind.DynamicallyLinkedLibrary); // Create a dll that exports and imports a mef type to ensure that MEF attempts to load and create a MEF graph // using this assembly. var source = """ namespace MyTestExportNamespace { [System.ComponentModel.Composition.Export(typeof(ExportedType))] public class ExportedType { } public class ImportType { [System.ComponentModel.Composition.ImportingConstructorAttribute] public ImportType(ExportedType t) { } } } """; // Generate an assembly name associated with the character we're testing - this ensures // that if multiple of these tests run in the same process we're making sure that the correct expected assembly is loaded. assemblyName = "MyAssembly" + reservedCharacter.GetHashCode(); #pragma warning disable RS0030 // Do not use banned APIs - intentionally using System.ComponentModel.Composition to verify mef construction. var compilation = CSharpCompilation.Create( assemblyName, syntaxTrees: [SyntaxFactory.ParseSyntaxTree(SourceText.From(source, encoding: null, SourceHashAlgorithms.Default))], references: [ NetStandard20.References.mscorlib, NetStandard20.References.netstandard, NetStandard20.References.SystemRuntime, MetadataReference.CreateFromFile(typeof(System.ComponentModel.Composition.ExportAttribute).Assembly.Location) ], options: options); #pragma warning restore RS0030 // Do not use banned APIs // Write a dll to a subdir with a reserved character in the name. var tempSubDir = directory.CreateDirectory(reservedCharacter); var tempFile = tempSubDir.CreateFile($"{assemblyName}.dll"); var dllData = compilation.EmitToStream(); tempFile.WriteAllBytes(dllData.ToArray()); // Mark the file as read only to prevent mutations. var fileInfo = new FileInfo(tempFile.Path); fileInfo.IsReadOnly = true; return tempFile.Path; } private async Task AssertCacheWriteWasAttemptedAsync() { var cacheWriteTask = LanguageServerExportProviderBuilder.TestAccessor.GetCacheWriteTask(); Assert.NotNull(cacheWriteTask); await cacheWriteTask; } private void AssertNoCacheWriteWasAttempted() { var cacheWriteTask2 = LanguageServerExportProviderBuilder.TestAccessor.GetCacheWriteTask(); Assert.Null(cacheWriteTask2); } private void AssertCachedCompositionCountEquals(int expectedCount) { var mefCompositions = Directory.EnumerateFiles(MefCacheDirectory.Path, "*.mef-composition", SearchOption.AllDirectories); Assert.Equal(expectedCount, mefCompositions.Count()); } }