|
// 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.
#nullable disable
using System;
using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CodeRefactorings;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Shared.Utilities;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.VisualStudio.Composition;
using Roslyn.Test.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.Editor.UnitTests.CodeFixes;
[UseExportProvider]
public class ExtensionOrderingTests
{
private static ExportProvider ExportProvider => EditorTestCompositions.EditorFeatures.ExportProviderFactory.CreateExportProvider();
[Fact]
public void TestNoCyclesInFixProviders()
{
// This test will fail if a cycle is detected in the ordering of our code fix providers.
// If this test fails, you can break the cycle by inspecting and fixing up the contents of
// any [ExtensionOrder()] attributes present on our code fix providers.
var providers = ExportProvider.GetExports<CodeFixProvider, CodeChangeProviderMetadata>();
var providersPerLanguage = providers.ToPerLanguageMapWithMultipleLanguages();
var csharpProviders = providersPerLanguage[LanguageNames.CSharp];
// ExtensionOrderer.TestAccessor.CheckForCycles() will throw ArgumentException if cycle is detected.
ExtensionOrderer.TestAccessor.CheckForCycles(csharpProviders);
// ExtensionOrderer.Order() will not throw even if cycle is detected. However, it will
// break the cycle and the resulting order will end up being unpredictable.
var actualOrder = ExtensionOrderer.Order(csharpProviders).ToArray();
Assert.True(actualOrder.Length > 0);
Assert.True(actualOrder.IndexOf(p => p.Metadata.Name == PredefinedCodeFixProviderNames.AddImport) <
actualOrder.IndexOf(p => p.Metadata.Name == PredefinedCodeFixProviderNames.FullyQualify));
var vbProviders = providersPerLanguage[LanguageNames.VisualBasic];
ExtensionOrderer.TestAccessor.CheckForCycles(vbProviders);
actualOrder = [.. ExtensionOrderer.Order(vbProviders)];
Assert.True(actualOrder.Length > 0);
Assert.True(actualOrder.IndexOf(p => p.Metadata.Name == PredefinedCodeFixProviderNames.AddImport) <
actualOrder.IndexOf(p => p.Metadata.Name == PredefinedCodeFixProviderNames.FullyQualify));
}
[Fact]
public void TestNoCyclesInSuppressionProviders()
{
// This test will fail if a cycle is detected in the ordering of our suppression fix providers.
// If this test fails, you can break the cycle by inspecting and fixing up the contents of
// any [ExtensionOrder()] attributes present on our suppression fix providers.
var providers = ExportProvider.GetExports<IConfigurationFixProvider, CodeChangeProviderMetadata>();
var providersPerLanguage = providers.ToPerLanguageMapWithMultipleLanguages();
TestCore(LanguageNames.CSharp);
TestCore(LanguageNames.VisualBasic);
return;
// Local functions.
void TestCore(string language)
{
var providers = providersPerLanguage[language];
// ExtensionOrderer.TestAccessor.CheckForCycles() will throw ArgumentException if cycle is detected.
ExtensionOrderer.TestAccessor.CheckForCycles(providers);
// ExtensionOrderer.Order() will not throw even if cycle is detected. However, it will
// break the cycle and the resulting order will end up being unpredictable.
var actualOrder = ExtensionOrderer.Order(providers).ToArray();
Assert.Equal(3, actualOrder.Length);
Assert.Equal(PredefinedConfigurationFixProviderNames.Suppression, actualOrder[0].Metadata.Name);
Assert.Equal(PredefinedConfigurationFixProviderNames.ConfigureCodeStyleOption, actualOrder[1].Metadata.Name);
Assert.Equal(PredefinedConfigurationFixProviderNames.ConfigureSeverity, actualOrder[2].Metadata.Name);
}
}
[Fact]
public void TestNoCyclesInRefactoringProviders()
{
// This test will fail if a cycle is detected in the ordering of our code refactoring providers.
// If this test fails, you can break the cycle by inspecting and fixing up the contents of
// any [ExtensionOrder()] attributes present on our code refactoring providers.
var providers = ExportProvider.GetExports<CodeRefactoringProvider, CodeChangeProviderMetadata>();
var providersPerLanguage = providers.ToPerLanguageMapWithMultipleLanguages();
var csharpProviders = providersPerLanguage[LanguageNames.CSharp];
// ExtensionOrderer.TestAccessor.CheckForCycles() will throw ArgumentException if cycle is detected.
ExtensionOrderer.TestAccessor.CheckForCycles(csharpProviders);
// ExtensionOrderer.Order() will not throw even if cycle is detected. However, it will
// break the cycle and the resulting order will end up being unpredictable.
var actualOrder = ExtensionOrderer.Order(csharpProviders).ToArray();
Assert.True(actualOrder.Length > 0);
var vbProviders = providersPerLanguage[LanguageNames.VisualBasic];
ExtensionOrderer.TestAccessor.CheckForCycles(vbProviders);
actualOrder = [.. ExtensionOrderer.Order(vbProviders)];
Assert.True(actualOrder.Length > 0);
}
[Theory, WorkItem("https://devdiv.visualstudio.com/DevDiv/_queries/edit/1599579")]
[InlineData(LanguageNames.CSharp)]
[InlineData(LanguageNames.VisualBasic)]
public void TestCodeFixServiceOrderIsCorrect(string language)
{
// This test will fail if a cycle is detected in the ordering of our code fix providers.
// If this test fails, you can break the cycle by inspecting and fixing up the contents of
// any [ExtensionOrder()] attributes present on our code fix providers.
var providers = ExportProvider.GetExports<CodeFixProvider, CodeChangeProviderMetadata>();
var providersPerLanguage = providers.ToPerLanguageMapWithMultipleLanguages();
var langProviders = providersPerLanguage[language];
// ExtensionOrderer.TestAccessor.CheckForCycles() will throw ArgumentException if cycle is detected.
ExtensionOrderer.TestAccessor.CheckForCycles(langProviders);
// ExtensionOrderer.Order() will not throw even if cycle is detected. However, it will
// break the cycle and the resulting order will end up being unpredictable.
var expectedOrder = ExtensionOrderer.Order(langProviders).Select(lazy => lazy.Value).ToImmutableArray();
var codeFixService = (CodeFixService)ExportProvider.GetExportedValue<ICodeFixService>();
var codeFixPriorityMap = codeFixService.GetTestAccessor().GetFixerPriorityPerLanguageMap(services: null!)[language].Value;
Assert.True(codeFixPriorityMap.Count > 0);
var actualOrder = codeFixPriorityMap.OrderBy(kvp => kvp.Value).Select(kvp => kvp.Key).ToImmutableArray();
// Ok, now go through and ensure that all the items in teh CodeFixProvider are ordered as the
// ExtensionOrderer would order them.
var currentIndex = expectedOrder.IndexOf(actualOrder[0]);
Assert.True(currentIndex >= 0);
for (var i = 1; i < actualOrder.Length; i++)
{
var nextCodeFixProvider = actualOrder[i];
var nextIndex = expectedOrder.IndexOf(nextCodeFixProvider);
Assert.True(nextIndex > currentIndex);
currentIndex = nextIndex;
}
}
}
|