diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/MefCompositionTests.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/MefCompositionTests.cs new file mode 100644 index 0000000000000..3d4b7737108e3 --- /dev/null +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/MefCompositionTests.cs @@ -0,0 +1,65 @@ +// 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 System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using Castle.Core.Internal; +using Microsoft.VisualStudio.Shell.Interop; +using Roslyn.Test.Utilities; +using Roslyn.VisualStudio.IntegrationTests; +using Xunit; + +namespace Roslyn.VisualStudio.NewIntegrationTests +{ + public class MefCompositionTests : AbstractIntegrationTest + { + [IdeFact] + public async Task AssertNoCompositionFailures() + { + // Read the .err file that contains errors; this isn't a great way to do it but we have no + // better option at this point. + var shell = await TestServices.Shell.GetRequiredGlobalServiceAsync(HangMitigatingCancellationToken); + Marshal.ThrowExceptionForHR(shell.GetApplicationDataFolder((uint)__VsApplicationDataFolder.ApplicationDataFolder_LocalSettings, out var applicationDataFolder)); + + var compositionErrorFileLines = File.ReadAllLines(Path.Combine(applicationDataFolder, @"ComponentModelCache\Microsoft.VisualStudio.Default.err")); + var compositionErrors = new List(); + + for (var i = 0; i < compositionErrorFileLines.Length; i++) + { + var line = compositionErrorFileLines[i]; + + if (line.EndsWith("expected exactly 1 export matching constraints:") && + StartsWithApplicableSymbol(line)) + { + var entireError = string.Join(Environment.NewLine, compositionErrorFileLines.Skip(i).TakeWhile(s => !string.IsNullOrWhiteSpace(s))); + compositionErrors.Add(entireError); + } + } + + // Ideally I'd write Assert.Empty(compositionErrors), but since the string gets truncated we'll do this instead + AssertEx.Equal("", string.Join(Environment.NewLine, compositionErrors)); + } + + private static bool StartsWithApplicableSymbol(string s) + { + // ExternalAccess missing exports may mean the langauge isn't installed, or you aren't running + // on fully matched bits; rather than flag them let's keep the noise down. + if (s.StartsWith("Microsoft.CodeAnalysis.ExternalAccess")) + return false; + + // Not actually our code + if (s.StartsWith("Microsoft.CodeAnalysis.Editor.TypeScript")) + return false; + + return s.StartsWith("Microsoft.CodeAnalysis") || + s.StartsWith("Microsoft.VisualStudio.LanguageServices") || + s.StartsWith("Roslyn"); + } + } +}