CodeFixVerifierHelper.cs 5.3 KB
Newer Older
1 2 3 4
// 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.

5 6
#nullable enable

7
using System;
8
using System.Collections.Generic;
9
using System.Linq;
10
using System.Text;
11
using Microsoft.CodeAnalysis.Diagnostics;
12 13
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
14 15
using Xunit;

16 17 18 19 20 21 22
#if CODE_STYLE
using OptionSet = Microsoft.CodeAnalysis.Diagnostics.AnalyzerConfigOptions;
using Microsoft.CodeAnalysis.Internal.Options;
#else
using Microsoft.CodeAnalysis.Options;
#endif

23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
namespace Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions
{
    internal static class CodeFixVerifierHelper
    {
        public static void VerifyStandardProperties(DiagnosticAnalyzer analyzer, bool verifyHelpLink = false)
        {
            VerifyMessageTitle(analyzer);
            VerifyMessageDescription(analyzer);

            if (verifyHelpLink)
            {
                VerifyMessageHelpLinkUri(analyzer);
            }
        }

        private static void VerifyMessageTitle(DiagnosticAnalyzer analyzer)
        {
            foreach (var descriptor in analyzer.SupportedDiagnostics)
            {
                if (descriptor.CustomTags.Contains(WellKnownDiagnosticTags.NotConfigurable))
                {
                    // The title only displayed for rule configuration
                    continue;
                }

                Assert.NotEqual("", descriptor.Title?.ToString() ?? "");
            }
        }

        private static void VerifyMessageDescription(DiagnosticAnalyzer analyzer)
        {
            foreach (var descriptor in analyzer.SupportedDiagnostics)
            {
                if (ShouldSkipMessageDescriptionVerification(descriptor))
                {
                    continue;
                }

                Assert.NotEqual("", descriptor.MessageFormat?.ToString() ?? "");
            }

            return;

            // Local function
            static bool ShouldSkipMessageDescriptionVerification(DiagnosticDescriptor descriptor)
            {
                if (descriptor.CustomTags.Contains(WellKnownDiagnosticTags.NotConfigurable))
                {
                    if (!descriptor.IsEnabledByDefault || descriptor.DefaultSeverity == DiagnosticSeverity.Hidden)
                    {
                        // The message only displayed if either enabled and not hidden, or configurable
                        return true;
                    }
                }

                return false;
            }
        }

        private static void VerifyMessageHelpLinkUri(DiagnosticAnalyzer analyzer)
        {
            foreach (var descriptor in analyzer.SupportedDiagnostics)
            {
                Assert.NotEqual("", descriptor.HelpLinkUri ?? "");
            }
        }
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127

        public static (SourceText? analyzerConfig, IEnumerable<KeyValuePair<OptionKey, object?>> options) ConvertOptionsToAnalyzerConfig(string defaultFileExtension, OptionsCollection options)
        {
            if (options.Count == 0)
            {
                return (null, options);
            }

            var optionSet = new OptionSetWrapper(options.ToDictionary<KeyValuePair<OptionKey, object?>, OptionKey, object?>(option => option.Key, option => option.Value));
            var remainingOptions = new List<KeyValuePair<OptionKey, object?>>();

            var analyzerConfig = new StringBuilder();
            analyzerConfig.AppendLine("root = true");
            analyzerConfig.AppendLine();
            analyzerConfig.AppendLine($"[*.{defaultFileExtension}]");
            foreach (var (key, value) in options)
            {
                var editorConfigStorageLocation = key.Option.StorageLocations.OfType<IEditorConfigStorageLocation2>().FirstOrDefault();
                if (editorConfigStorageLocation is null)
                {
                    remainingOptions.Add(KeyValuePairUtil.Create<OptionKey, object?>(key, value));
                    continue;
                }

                analyzerConfig.AppendLine(editorConfigStorageLocation.GetEditorConfigString(value, optionSet));
            }

            return (SourceText.From(analyzerConfig.ToString(), Encoding.UTF8), remainingOptions);
        }

        private sealed class OptionSetWrapper : OptionSet
        {
            private readonly Dictionary<OptionKey, object?> _options;

            public OptionSetWrapper(Dictionary<OptionKey, object?> options)
            {
                _options = options;
            }

128 129 130 131 132 133
#if CODE_STYLE
            public override bool TryGetValue(string key, out string value)
            {
                throw new NotImplementedException();
            }
#else
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
            public override object? GetOption(OptionKey optionKey)
            {
                if (!_options.TryGetValue(optionKey, out var value))
                {
                    value = optionKey.Option.DefaultValue;
                }

                return value;
            }

            public override OptionSet WithChangedOption(OptionKey optionAndLanguage, object? value)
                => throw new NotSupportedException();

            internal override IEnumerable<OptionKey> GetChangedOptions(OptionSet optionSet)
                => SpecializedCollections.EmptyEnumerable<OptionKey>();
149

150
#endif
151
        }
152 153
    }
}