/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/EditorFeatures/Test/Options/GlobalOptionsTests.cs
199 строк
10 KB
Cyrus Najmabadi
Remove feature option from DevNext
25 мар 2025, 22:55
25 мар 2025, 22:55
d7ffa84
Код
Авторство
О чём код?
// 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.Collections.Immutable; using System.Composition; using System.Linq; using System.Reflection; using System.Runtime.Serialization; using Microsoft.CodeAnalysis.AddImport; using Microsoft.CodeAnalysis.BraceMatching; using Microsoft.CodeAnalysis.Classification; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.DocumentationComments; using Microsoft.CodeAnalysis.DocumentHighlighting; using Microsoft.CodeAnalysis.Editor; using Microsoft.CodeAnalysis.Editor.UnitTests; using Microsoft.CodeAnalysis.FindUsages; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.ImplementType; using Microsoft.CodeAnalysis.InlineHints; using Microsoft.CodeAnalysis.MetadataAsSource; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Structure; using Microsoft.CodeAnalysis.SymbolSearch; using Microsoft.CodeAnalysis.Test.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.UnitTests; [UseExportProvider] public sealed class GlobalOptionsTests { [Export(typeof(IGlobalOptionService)), Shared, PartNotDiscoverable] internal sealed class TestGlobalOptions : IGlobalOptionService { public readonly List<OptionKey2> AccessedOptionKeys = []; [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] public TestGlobalOptions() { } private void OnOptionAccessed(OptionKey2 key) { AccessedOptionKeys.Add(key); } bool IOptionsReader.TryGetOption<T>(OptionKey2 optionKey, out T value) { value = GetOption<T>(optionKey); return true; } public T GetOption<T>(Option2<T> option) => GetOption<T>(new OptionKey2(option)); public T GetOption<T>(PerLanguageOption2<T> option, string languageName) => GetOption<T>(new OptionKey2(option, languageName)); public T GetOption<T>(OptionKey2 optionKey) { OnOptionAccessed(optionKey); return (T)OptionsTestHelpers.GetDifferentValue(typeof(T), optionKey.Option.DefaultValue)!; } #region Unused public ImmutableArray<object?> GetOptions(ImmutableArray<OptionKey2> optionKeys) => throw new NotImplementedException(); public bool RefreshOption(OptionKey2 optionKey, object? newValue) => throw new NotImplementedException(); public void SetGlobalOption<T>(Option2<T> option, T value) => throw new NotImplementedException(); public void SetGlobalOption<T>(PerLanguageOption2<T> option, string language, T value) => throw new NotImplementedException(); public void SetGlobalOption(OptionKey2 optionKey, object? value) => throw new NotImplementedException(); public bool SetGlobalOptions(ImmutableArray<KeyValuePair<OptionKey2, object?>> options) => throw new NotImplementedException(); public void AddOptionChangedHandler(object target, WeakEventHandler<OptionChangedEventArgs> handler) => throw new NotImplementedException(); public void RemoveOptionChangedHandler(object target, WeakEventHandler<OptionChangedEventArgs> handler) => throw new NotImplementedException(); public StructuredAnalyzerConfigOptions GetAnalyzerConfigOptions(string language) => throw new NotImplementedException(); #endregion } private static void VerifyDataMembersHaveNonDefaultValues(object options, object defaultOptions, string? language = null) { Assert.Equal(options.GetType(), defaultOptions.GetType()); Recurse(options.GetType(), options, defaultOptions, language); static void Recurse(Type type, object options, object defaultOptions, string? language) { foreach (var property in type.GetProperties(BindingFlags.Instance | BindingFlags.Public)) { if (property.GetCustomAttributes<DataMemberAttribute>().Any()) { // value initialized from global options: var value = property.GetValue(options); // default value for the option -- may be different then default(T): var defaultValue = property.GetValue(defaultOptions); if (OptionDefinition.IsSupportedOptionType(property.PropertyType)) { // Skip validation of ReloadChangedAnalyzerReferences. The test options store returns 'true' // for 'null' (which the option uses to mean 'try the feature flag'). Which is also equivalent // to the default for this option. if (IsStoredInGlobalOptions(property, language)) { Assert.False(Equals(value, defaultValue), $"{type.FullName}.{property.Name} not initialized from global options"); } } else { var propertyType = Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType; if (propertyType != property.PropertyType) { var getValueOrDefault = property.PropertyType.GetMethod("GetValueOrDefault", []); value = getValueOrDefault.Invoke(value, []); defaultValue = getValueOrDefault.Invoke(defaultValue, []); } Recurse(propertyType, value, defaultValue, language); } } } } } private static TestWorkspace CreateWorkspace(out TestGlobalOptions globalOptions) { var composition = EditorTestCompositions.LanguageServerProtocolEditorFeatures. AddExcludedPartTypes(typeof(GlobalOptionService)). AddParts(typeof(TestGlobalOptions)); var workspace = new TestWorkspace(composition: composition); globalOptions = Assert.IsType<TestGlobalOptions>(workspace.ExportProvider.GetExportedValue<IGlobalOptionService>()); return workspace; } /// <summary> /// Properties for options not stored in global options. /// </summary> private static bool IsStoredInGlobalOptions(PropertyInfo property, string? language) => !(property.DeclaringType == typeof(AddImportPlacementOptions) && property.Name == nameof(AddImportPlacementOptions.AllowInHiddenRegions) || property.DeclaringType == typeof(AddImportPlacementOptions) && property.Name == nameof(AddImportPlacementOptions.UsingDirectivePlacement) && language == LanguageNames.VisualBasic || property.DeclaringType == typeof(DocumentFormattingOptions) && property.Name == nameof(DocumentFormattingOptions.FileHeaderTemplate) || property.DeclaringType == typeof(DocumentFormattingOptions) && property.Name == nameof(DocumentFormattingOptions.InsertFinalNewLine) || property.DeclaringType == typeof(ClassificationOptions) && property.Name == nameof(ClassificationOptions.FrozenPartialSemantics) || property.DeclaringType == typeof(HighlightingOptions) && property.Name == nameof(HighlightingOptions.FrozenPartialSemantics) || property.DeclaringType == typeof(BlockStructureOptions) && property.Name == nameof(BlockStructureOptions.IsMetadataAsSource) || property.DeclaringType == typeof(WorkspaceConfigurationOptions) && property.Name == nameof(WorkspaceConfigurationOptions.SourceGeneratorExecution)); /// <summary> /// Our mock <see cref="IGlobalOptionService"/> implementation returns a non-default value for each option it reads. /// Option objects initialized from this service thus should have all their data properties initialized to /// non-default values. We then enumerate these properties via reflection and compare each property value with the /// default instance of the respective options type. /// </summary> [Theory] [InlineData(LanguageNames.CSharp)] [InlineData(LanguageNames.VisualBasic)] public void ReadingOptionsFromGlobalOptions(string language) { using var workspace = CreateWorkspace(out var globalOptions); var languageServices = workspace.Services.SolutionServices.GetLanguageServices(language); VerifyDataMembersHaveNonDefaultValues(globalOptions.GetBraceMatchingOptions(language), BraceMatchingOptions.Default, language); VerifyDataMembersHaveNonDefaultValues(globalOptions.GetFindUsagesOptions(language), FindUsagesOptions.Default, language); VerifyDataMembersHaveNonDefaultValues(globalOptions.GetInlineHintsOptions(language), InlineHintsOptions.Default, language); VerifyDataMembersHaveNonDefaultValues(globalOptions.GetAutoFormattingOptions(language), AutoFormattingOptions.Default, language); VerifyDataMembersHaveNonDefaultValues(globalOptions.GetBlockStructureOptions(language, isMetadataAsSource: false), BlockStructureOptions.Default, language); VerifyDataMembersHaveNonDefaultValues(globalOptions.GetDocumentationCommentOptions(globalOptions.GetLineFormattingOptions(language), language), DocumentationCommentOptions.Default, language); VerifyDataMembersHaveNonDefaultValues(globalOptions.GetImplementTypeOptions(language), ImplementTypeOptions.Default, language); VerifyDataMembersHaveNonDefaultValues(globalOptions.GetMetadataAsSourceOptions(), MetadataAsSourceOptions.Default, language); VerifyDataMembersHaveNonDefaultValues(globalOptions.GetMemberDisplayOptions(language), MemberDisplayOptions.Default, language); VerifyDataMembersHaveNonDefaultValues(globalOptions.GetSymbolSearchOptions(language), SymbolSearchOptions.Default, language); VerifyDataMembersHaveNonDefaultValues(globalOptions.GetWorkspaceConfigurationOptions(), WorkspaceConfigurationOptions.Default); } }