AbstractDiagnosticProviderBasedUserDiagnosticTest.cs 10.2 KB
Newer Older
J
Jonathon Marolf 已提交
1 2 3
// 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.
4

5
using System.Collections.Concurrent;
6 7 8 9
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading.Tasks;
10
using Microsoft.CodeAnalysis.CodeActions;
11 12 13
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces;
C
chborl 已提交
14
using Microsoft.CodeAnalysis.Text;
15
using Microsoft.CodeAnalysis.UnitTests.Diagnostics;
16
using Roslyn.Test.Utilities;
17 18 19
using Roslyn.Utilities;
using Xunit;

20 21 22 23 24 25
#if CODE_STYLE
using Microsoft.CodeAnalysis.Internal.Options;
#else
using Microsoft.CodeAnalysis.Options;
#endif

26 27
namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics
{
28
    public abstract partial class AbstractDiagnosticProviderBasedUserDiagnosticTest : AbstractUserDiagnosticTest
29
    {
30 31
        private readonly ConcurrentDictionary<Workspace, (DiagnosticAnalyzer, CodeFixProvider)> _analyzerAndFixerMap =
            new ConcurrentDictionary<Workspace, (DiagnosticAnalyzer, CodeFixProvider)>();
32

33
        internal abstract (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace);
34

35
        internal virtual (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace, TestParameters parameters)
36
            => CreateDiagnosticProviderAndFixer(workspace);
37

38
        private (DiagnosticAnalyzer, CodeFixProvider) GetOrCreateDiagnosticProviderAndFixer(
39
            Workspace workspace, TestParameters parameters)
40
        {
41
            return parameters.fixProviderData == null
42
                ? _analyzerAndFixerMap.GetOrAdd(workspace, CreateDiagnosticProviderAndFixer)
43
                : CreateDiagnosticProviderAndFixer(workspace, parameters);
44 45
        }

46 47 48 49 50 51 52 53 54 55 56 57 58
        internal virtual 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;
        }

59 60 61 62 63
        [Fact]
        public void TestSupportedDiagnosticsMessageTitle()
        {
            using (var workspace = new AdhocWorkspace())
            {
64
                var diagnosticAnalyzer = CreateDiagnosticProviderAndFixer(workspace).Item1;
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
                if (diagnosticAnalyzer == null)
                {
                    return;
                }

                foreach (var descriptor in diagnosticAnalyzer.SupportedDiagnostics)
                {
                    if (descriptor.CustomTags.Contains(WellKnownDiagnosticTags.NotConfigurable))
                    {
                        // The title only displayed for rule configuration
                        continue;
                    }

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

        [Fact]
        public void TestSupportedDiagnosticsMessageDescription()
        {
            using (var workspace = new AdhocWorkspace())
            {
88
                var diagnosticAnalyzer = CreateDiagnosticProviderAndFixer(workspace).Item1;
89 90 91 92 93 94 95
                if (diagnosticAnalyzer == null)
                {
                    return;
                }

                foreach (var descriptor in diagnosticAnalyzer.SupportedDiagnostics)
                {
96
                    if (ShouldSkipMessageDescriptionVerification(descriptor))
97
                    {
98
                        continue;
99 100 101 102 103 104 105 106 107 108 109 110
                    }

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

        [Fact(Skip = "https://github.com/dotnet/roslyn/issues/26717")]
        public void TestSupportedDiagnosticsMessageHelpLinkUri()
        {
            using (var workspace = new AdhocWorkspace())
            {
111
                var diagnosticAnalyzer = CreateDiagnosticProviderAndFixer(workspace).Item1;
112 113 114 115 116 117 118 119 120 121 122 123
                if (diagnosticAnalyzer == null)
                {
                    return;
                }

                foreach (var descriptor in diagnosticAnalyzer.SupportedDiagnostics)
                {
                    Assert.NotEqual("", descriptor.HelpLinkUri ?? "");
                }
            }
        }

124
        internal async override Task<IEnumerable<Diagnostic>> GetDiagnosticsAsync(
125
            TestWorkspace workspace, TestParameters parameters)
126
        {
127
            var providerAndFixer = GetOrCreateDiagnosticProviderAndFixer(workspace, parameters);
128 129

            var provider = providerAndFixer.Item1;
C
CyrusNajmabadi 已提交
130
            var document = GetDocumentAndSelectSpan(workspace, out var span);
131
            var allDiagnostics = await DiagnosticProviderTestUtilities.GetAllDiagnosticsAsync(provider, document, span);
132 133
            AssertNoAnalyzerExceptionDiagnostics(allDiagnostics);
            return allDiagnostics;
134 135
        }

136
        internal override async Task<(ImmutableArray<Diagnostic>, ImmutableArray<CodeAction>, CodeAction actionToInvoke)> GetDiagnosticAndFixesAsync(
137
            TestWorkspace workspace, TestParameters parameters)
138
        {
139
            var providerAndFixer = GetOrCreateDiagnosticProviderAndFixer(workspace, parameters);
140 141 142

            var provider = providerAndFixer.Item1;
            string annotation = null;
C
CyrusNajmabadi 已提交
143
            if (!TryGetDocumentAndSelectSpan(workspace, out var document, out var span))
144 145 146 147
            {
                document = GetDocumentAndAnnotatedSpan(workspace, out annotation, out span);
            }

M
Manish Vasani 已提交
148
            var testDriver = new TestDiagnosticAnalyzerDriver(document.Project, provider);
149 150
            var filterSpan = parameters.includeDiagnosticsOutsideSelection ? (TextSpan?)null : span;
            var diagnostics = (await testDriver.GetAllDiagnosticsAsync(document, filterSpan)).ToImmutableArray();
M
Manish Vasani 已提交
151
            AssertNoAnalyzerExceptionDiagnostics(diagnostics);
152

M
Manish Vasani 已提交
153
            var fixer = providerAndFixer.Item2;
C
chborl 已提交
154 155
            if (fixer == null)
            {
156
                return (diagnostics, ImmutableArray<CodeAction>.Empty, null);
C
chborl 已提交
157
            }
158

M
Manish Vasani 已提交
159 160
            var ids = new HashSet<string>(fixer.FixableDiagnosticIds);
            var dxs = diagnostics.Where(d => ids.Contains(d.Id)).ToList();
161
            var (resultDiagnostics, codeActions, actionToInvoke) = await GetDiagnosticAndFixesAsync(
162
                dxs, fixer, testDriver, document, span, annotation, parameters.index);
163 164 165 166 167

            // If we are also testing non-fixable diagnostics,
            // then the result diagnostics need to include all diagnostics,
            // not just the fixable ones returned from GetDiagnosticAndFixesAsync.
            if (parameters.retainNonFixableDiagnostics)
168 169 170 171 172
            {
                resultDiagnostics = diagnostics;
            }

            return (resultDiagnostics, codeActions, actionToInvoke);
173
        }
174

C
chborl 已提交
175
        protected async Task TestDiagnosticInfoAsync(
176 177 178
            string initialMarkup,
            IDictionary<OptionKey, object> options,
            string diagnosticId,
C
chborl 已提交
179 180
            DiagnosticSeverity diagnosticSeverity,
            LocalizableString diagnosticMessage = null)
181
        {
C
chborl 已提交
182 183
            await TestDiagnosticInfoAsync(initialMarkup, null, null, options, diagnosticId, diagnosticSeverity, diagnosticMessage);
            await TestDiagnosticInfoAsync(initialMarkup, GetScriptOptions(), null, options, diagnosticId, diagnosticSeverity, diagnosticMessage);
184 185
        }

C
chborl 已提交
186
        protected async Task TestDiagnosticInfoAsync(
187 188 189 190 191
            string initialMarkup,
            ParseOptions parseOptions,
            CompilationOptions compilationOptions,
            IDictionary<OptionKey, object> options,
            string diagnosticId,
C
chborl 已提交
192 193
            DiagnosticSeverity diagnosticSeverity,
            LocalizableString diagnosticMessage = null)
194
        {
195
            var testOptions = new TestParameters(parseOptions, compilationOptions, options);
C
CyrusNajmabadi 已提交
196
            using (var workspace = CreateWorkspaceFromOptions(initialMarkup, testOptions))
197
            {
C
Cyrus Najmabadi 已提交
198 199
                var diagnostics = (await GetDiagnosticsAsync(workspace, testOptions)).ToImmutableArray();
                diagnostics = diagnostics.WhereAsArray(d => d.Id == diagnosticId);
C
chborl 已提交
200
                Assert.Equal(1, diagnostics.Count());
C
chborl 已提交
201 202 203 204 205 206

                var hostDocument = workspace.Documents.Single(d => d.SelectedSpans.Any());
                var expected = hostDocument.SelectedSpans.Single();
                var actual = diagnostics.Single().Location.SourceSpan;
                Assert.Equal(expected, actual);

207
                Assert.Equal(diagnosticSeverity, diagnostics.Single().Severity);
C
chborl 已提交
208 209 210 211 212

                if (diagnosticMessage != null)
                {
                    Assert.Equal(diagnosticMessage, diagnostics.Single().GetMessage());
                }
213 214 215
            }
        }

216
#pragma warning disable CS1574 // XML comment has cref attribute that could not be resolved
217 218 219 220
        /// <summary>
        /// The internal method <see cref="AnalyzerExecutor.IsAnalyzerExceptionDiagnostic(Diagnostic)"/> does
        /// essentially this, but due to linked files between projects, this project cannot have internals visible
        /// access to the Microsoft.CodeAnalysis project without the cascading effect of many extern aliases, so it
221
        /// is re-implemented here in a way that is potentially overly aggressive with the knowledge that if this method
222 223 224
        /// starts failing on non-analyzer exception diagnostics, it can be appropriately tuned or re-evaluated.
        /// </summary>
        private void AssertNoAnalyzerExceptionDiagnostics(IEnumerable<Diagnostic> diagnostics)
225
#pragma warning restore CS1574 // XML comment has cref attribute that could not be resolved
226
        {
227
            var analyzerExceptionDiagnostics = diagnostics.Where(diag => diag.Descriptor.CustomTags.Contains(WellKnownDiagnosticTags.AnalyzerException));
228
            AssertEx.Empty(analyzerExceptionDiagnostics, "Found analyzer exception diagnostics");
229
        }
230
    }
S
Sam Harwell 已提交
231
}