From b732d61e05010861435b3b82d1adc20ae1a5d406 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Sat, 20 Feb 2016 20:37:00 -0800 Subject: [PATCH] Provide a way for 'code action' tests to pass data to their CodeFixProvider constructor. This is useful for testing purposes so we can pass mocks to the specific CodeFixProvider constructor. For example 'add nuget reference' will pass mocks for the nuget search and install services in its tests. Also provide a way for code action operations to state if they should be invoked during tests. Lots of tests just want to examine the state returned by certain operations (like SolutionChangedOperation). However, for mutating operations, some tests need the operation to actually apply so that they can check the results of the operation. --- .../AbstractCodeActionOrUserDiagnosticTest.cs | 71 +++++++++++++------ .../CodeActions/AbstractCodeActionTest.cs | 3 +- ...agnosticProviderBasedUserDiagnosticTest.cs | 23 ++++-- .../AbstractSuppressionDiagnosticTest.cs | 6 +- .../Diagnostics/AbstractUserDiagnosticTest.cs | 34 +++++---- .../Operations/CodeActionOperation.cs | 9 +++ 6 files changed, 101 insertions(+), 45 deletions(-) diff --git a/src/EditorFeatures/Test/CodeActions/AbstractCodeActionOrUserDiagnosticTest.cs b/src/EditorFeatures/Test/CodeActions/AbstractCodeActionOrUserDiagnosticTest.cs index 7484e23fa86..c662426e7f4 100644 --- a/src/EditorFeatures/Test/CodeActions/AbstractCodeActionOrUserDiagnosticTest.cs +++ b/src/EditorFeatures/Test/CodeActions/AbstractCodeActionOrUserDiagnosticTest.cs @@ -72,53 +72,59 @@ protected void ApplyOptionsToWorkspace(Workspace workspace, IDictionary options = null, - string fixAllActionEquivalenceKey = null) + string fixAllActionEquivalenceKey = null, + object fixProviderData = null) { - await TestMissingAsync(initialMarkup, parseOptions: null, options: options, fixAllActionEquivalenceKey: fixAllActionEquivalenceKey); - await TestMissingAsync(initialMarkup, parseOptions: GetScriptOptions(), options: options, fixAllActionEquivalenceKey: fixAllActionEquivalenceKey); + await TestMissingAsync(initialMarkup, parseOptions: null, options: options, fixAllActionEquivalenceKey: fixAllActionEquivalenceKey, fixProviderData: fixProviderData); + await TestMissingAsync(initialMarkup, parseOptions: GetScriptOptions(), options: options, fixAllActionEquivalenceKey: fixAllActionEquivalenceKey, fixProviderData: fixProviderData); } protected Task TestMissingAsync( string initialMarkup, ParseOptions parseOptions, IDictionary options = null, - string fixAllActionEquivalenceKey = null) + string fixAllActionEquivalenceKey = null, + object fixProviderData = null) { - return TestMissingAsync(initialMarkup, parseOptions, compilationOptions: null, options: options, fixAllActionEquivalenceKey: fixAllActionEquivalenceKey); + return TestMissingAsync(initialMarkup, parseOptions, compilationOptions: null, options: options, fixAllActionEquivalenceKey: fixAllActionEquivalenceKey, fixProviderData: fixProviderData); } protected async Task TestMissingAsync( string initialMarkup, ParseOptions parseOptions, CompilationOptions compilationOptions, IDictionary options = null, - string fixAllActionEquivalenceKey = null) + string fixAllActionEquivalenceKey = null, + object fixProviderData = null) { using (var workspace = await CreateWorkspaceFromFileAsync(initialMarkup, parseOptions, compilationOptions)) { ApplyOptionsToWorkspace(workspace, options); - var actions = await GetCodeActionsAsync(workspace, fixAllActionEquivalenceKey); + var actions = await GetCodeActionsAsync(workspace, fixAllActionEquivalenceKey, fixProviderData); Assert.True(actions == null || actions.Count == 0); } } - protected async Task> GetCodeActionsAsync(TestWorkspace workspace, string fixAllActionEquivalenceKey) + protected async Task> GetCodeActionsAsync( + TestWorkspace workspace, string fixAllActionEquivalenceKey, object fixProviderData = null) { - return MassageActions(await GetCodeActionsWorkerAsync(workspace, fixAllActionEquivalenceKey)); + return MassageActions(await GetCodeActionsWorkerAsync(workspace, fixAllActionEquivalenceKey, fixProviderData)); } - protected abstract Task> GetCodeActionsWorkerAsync(TestWorkspace workspace, string fixAllActionEquivalenceKey); + protected abstract Task> GetCodeActionsWorkerAsync( + TestWorkspace workspace, string fixAllActionEquivalenceKey, object fixProviderData = null); protected async Task TestSmartTagTextAsync( string initialMarkup, string displayText, int index = 0, ParseOptions parseOptions = null, - CompilationOptions compilationOptions = null) + CompilationOptions compilationOptions = null, + object fixProviderData = null) { using (var workspace = await CreateWorkspaceFromFileAsync(initialMarkup, parseOptions, compilationOptions)) { - var actions = await GetCodeActionsAsync(workspace, fixAllActionEquivalenceKey: null); + var actions = await GetCodeActionsAsync(workspace, fixAllActionEquivalenceKey: null, fixProviderData: fixProviderData); Assert.Equal(displayText, actions.ElementAt(index).Title); } } @@ -157,10 +163,11 @@ protected async Task> GetCodeActionsAsync(TestWorkspace worksp string initialMarkup, string expectedMarkup, int index = 0, bool compareTokens = true, IDictionary options = null, - string fixAllActionEquivalenceKey = null) + string fixAllActionEquivalenceKey = null, + object fixProviderData = null) { - await TestAsync(initialMarkup, expectedMarkup, null, index, compareTokens, options, fixAllActionEquivalenceKey); - await TestAsync(initialMarkup, expectedMarkup, GetScriptOptions(), index, compareTokens, options, fixAllActionEquivalenceKey); + await TestAsync(initialMarkup, expectedMarkup, null, index, compareTokens, options, fixAllActionEquivalenceKey, fixProviderData); + await TestAsync(initialMarkup, expectedMarkup, GetScriptOptions(), index, compareTokens, options, fixAllActionEquivalenceKey, fixProviderData); } protected Task TestAsync( @@ -168,9 +175,10 @@ protected async Task> GetCodeActionsAsync(TestWorkspace worksp ParseOptions parseOptions, int index = 0, bool compareTokens = true, IDictionary options = null, - string fixAllActionEquivalenceKey = null) + string fixAllActionEquivalenceKey = null, + object fixProviderData = null) { - return TestAsync(initialMarkup, expectedMarkup, parseOptions, null, index, compareTokens, options, fixAllActionEquivalenceKey); + return TestAsync(initialMarkup, expectedMarkup, parseOptions, null, index, compareTokens, options, fixAllActionEquivalenceKey, fixProviderData); } protected async Task TestAsync( @@ -178,7 +186,8 @@ protected async Task> GetCodeActionsAsync(TestWorkspace worksp ParseOptions parseOptions, CompilationOptions compilationOptions, int index = 0, bool compareTokens = true, IDictionary options = null, - string fixAllActionEquivalenceKey = null) + string fixAllActionEquivalenceKey = null, + object fixProviderData = null) { string expected; IDictionary> spanMap; @@ -194,7 +203,7 @@ protected async Task> GetCodeActionsAsync(TestWorkspace worksp { ApplyOptionsToWorkspace(workspace, options); - var actions = await GetCodeActionsAsync(workspace, fixAllActionEquivalenceKey); + var actions = await GetCodeActionsAsync(workspace, fixAllActionEquivalenceKey, fixProviderData); await TestActionsAsync( workspace, expected, index, actions, @@ -323,11 +332,27 @@ protected static async Task> VerifyInputsAndGet TestWorkspace workspace, IEnumerable operations) { - var applyChangesOperation = operations.OfType().First(); - var oldSolution = workspace.CurrentSolution; - var newSolution = applyChangesOperation.ChangedSolution; + Tuple result = null; + foreach (var operation in operations) + { + if (operation is ApplyChangesOperation && result == null) + { + var oldSolution = workspace.CurrentSolution; + var newSolution = ((ApplyChangesOperation)operation).ChangedSolution; + result = Tuple.Create(oldSolution, newSolution); + } + else if (operation.ApplyDuringTests) + { + operation.Apply(workspace, CancellationToken.None); + } + } - return Tuple.Create(oldSolution, newSolution); + if (result == null) + { + throw new InvalidOperationException("No ApplyChangesOperation found"); + } + + return result; } protected virtual IList MassageActions(IList actions) diff --git a/src/EditorFeatures/Test/CodeActions/AbstractCodeActionTest.cs b/src/EditorFeatures/Test/CodeActions/AbstractCodeActionTest.cs index b01b7c5aca1..0eed5e80631 100644 --- a/src/EditorFeatures/Test/CodeActions/AbstractCodeActionTest.cs +++ b/src/EditorFeatures/Test/CodeActions/AbstractCodeActionTest.cs @@ -25,7 +25,8 @@ public abstract class AbstractCodeActionTest : AbstractCodeActionOrUserDiagnosti { protected abstract object CreateCodeRefactoringProvider(Workspace workspace); - protected override async Task> GetCodeActionsWorkerAsync(TestWorkspace workspace, string fixAllActionEquivalenceKey) + protected override async Task> GetCodeActionsWorkerAsync( + TestWorkspace workspace, string fixAllActionEquivalenceKey, object fixProviderData) { return (await GetCodeRefactoringAsync(workspace))?.Actions?.ToList(); } diff --git a/src/EditorFeatures/Test/Diagnostics/AbstractDiagnosticProviderBasedUserDiagnosticTest.cs b/src/EditorFeatures/Test/Diagnostics/AbstractDiagnosticProviderBasedUserDiagnosticTest.cs index c94a8b69862..942db3412aa 100644 --- a/src/EditorFeatures/Test/Diagnostics/AbstractDiagnosticProviderBasedUserDiagnosticTest.cs +++ b/src/EditorFeatures/Test/Diagnostics/AbstractDiagnosticProviderBasedUserDiagnosticTest.cs @@ -25,14 +25,24 @@ public abstract class AbstractDiagnosticProviderBasedUserDiagnosticTest : Abstra internal abstract Tuple CreateDiagnosticProviderAndFixer(Workspace workspace); - private Tuple GetOrCreateDiagnosticProviderAndFixer(Workspace workspace) + internal virtual Tuple CreateDiagnosticProviderAndFixer( + Workspace workspace, object fixProviderData) { - return _analyzerAndFixerMap.GetOrAdd(workspace, CreateDiagnosticProviderAndFixer); + return CreateDiagnosticProviderAndFixer(workspace); } - internal async override Task> GetDiagnosticsAsync(TestWorkspace workspace) + private Tuple GetOrCreateDiagnosticProviderAndFixer( + Workspace workspace, object fixProviderData) { - var providerAndFixer = GetOrCreateDiagnosticProviderAndFixer(workspace); + return fixProviderData == null + ? _analyzerAndFixerMap.GetOrAdd(workspace, CreateDiagnosticProviderAndFixer) + : CreateDiagnosticProviderAndFixer(workspace, fixProviderData); + } + + internal async override Task> GetDiagnosticsAsync( + TestWorkspace workspace, object fixProviderData = null) + { + var providerAndFixer = GetOrCreateDiagnosticProviderAndFixer(workspace, fixProviderData); var provider = providerAndFixer.Item1; TextSpan span; @@ -42,9 +52,10 @@ internal async override Task> GetDiagnosticsAsync(TestWo return allDiagnostics; } - internal override async Task>> GetDiagnosticAndFixesAsync(TestWorkspace workspace, string fixAllActionId) + internal override async Task>> GetDiagnosticAndFixesAsync( + TestWorkspace workspace, string fixAllActionId, object fixProviderData) { - var providerAndFixer = GetOrCreateDiagnosticProviderAndFixer(workspace); + var providerAndFixer = GetOrCreateDiagnosticProviderAndFixer(workspace, fixProviderData); var provider = providerAndFixer.Item1; Document document; diff --git a/src/EditorFeatures/Test/Diagnostics/AbstractSuppressionDiagnosticTest.cs b/src/EditorFeatures/Test/Diagnostics/AbstractSuppressionDiagnosticTest.cs index 95ee7f3de34..a7437f8b964 100644 --- a/src/EditorFeatures/Test/Diagnostics/AbstractSuppressionDiagnosticTest.cs +++ b/src/EditorFeatures/Test/Diagnostics/AbstractSuppressionDiagnosticTest.cs @@ -53,7 +53,8 @@ private ImmutableArray FilterDiagnostics(IEnumerable dia return diagnostics.ToImmutableArray(); } - internal override async Task> GetDiagnosticsAsync(TestWorkspace workspace) + internal override async Task> GetDiagnosticsAsync( + TestWorkspace workspace, object fixProviderData) { var providerAndFixer = CreateDiagnosticProviderAndFixer(workspace); @@ -64,7 +65,8 @@ internal override async Task> GetDiagnosticsAsync(TestWo return FilterDiagnostics(diagnostics); } - internal override async Task>> GetDiagnosticAndFixesAsync(TestWorkspace workspace, string fixAllActionId) + internal override async Task>> GetDiagnosticAndFixesAsync( + TestWorkspace workspace, string fixAllActionId, object fixProviderData) { var providerAndFixer = CreateDiagnosticProviderAndFixer(workspace); diff --git a/src/EditorFeatures/Test/Diagnostics/AbstractUserDiagnosticTest.cs b/src/EditorFeatures/Test/Diagnostics/AbstractUserDiagnosticTest.cs index 372438a1b50..90bf6959a94 100644 --- a/src/EditorFeatures/Test/Diagnostics/AbstractUserDiagnosticTest.cs +++ b/src/EditorFeatures/Test/Diagnostics/AbstractUserDiagnosticTest.cs @@ -27,18 +27,21 @@ namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics { public abstract class AbstractUserDiagnosticTest : AbstractCodeActionOrUserDiagnosticTest { - internal abstract Task>> GetDiagnosticAndFixesAsync(TestWorkspace workspace, string fixAllActionEquivalenceKey); - internal abstract Task> GetDiagnosticsAsync(TestWorkspace workspace); + internal abstract Task>> GetDiagnosticAndFixesAsync( + TestWorkspace workspace, string fixAllActionEquivalenceKey, object fixProviderData); + internal abstract Task> GetDiagnosticsAsync(TestWorkspace workspace, object fixProviderData); - protected override async Task> GetCodeActionsWorkerAsync(TestWorkspace workspace, string fixAllActionEquivalenceKey) + protected override async Task> GetCodeActionsWorkerAsync( + TestWorkspace workspace, string fixAllActionEquivalenceKey, object fixProviderData) { - var diagnostics = await GetDiagnosticAndFixAsync(workspace, fixAllActionEquivalenceKey); + var diagnostics = await GetDiagnosticAndFixAsync(workspace, fixAllActionEquivalenceKey, fixProviderData); return diagnostics?.Item2?.Fixes.Select(f => f.Action).ToList(); } - internal async Task> GetDiagnosticAndFixAsync(TestWorkspace workspace, string fixAllActionEquivalenceKey = null) + internal async Task> GetDiagnosticAndFixAsync( + TestWorkspace workspace, string fixAllActionEquivalenceKey = null, object fixProviderData = null) { - return (await GetDiagnosticAndFixesAsync(workspace, fixAllActionEquivalenceKey)).FirstOrDefault(); + return (await GetDiagnosticAndFixesAsync(workspace, fixAllActionEquivalenceKey, fixProviderData)).FirstOrDefault(); } protected Document GetDocumentAndSelectSpan(TestWorkspace workspace, out TextSpan span) @@ -224,11 +227,13 @@ protected async Task TestEquivalenceKeyAsync(string initialMarkup, string equiva protected async Task TestActionCountInAllFixesAsync( string initialMarkup, int count, - ParseOptions parseOptions = null, CompilationOptions compilationOptions = null) + ParseOptions parseOptions = null, + CompilationOptions compilationOptions = null, + object fixProviderData = null) { using (var workspace = await CreateWorkspaceFromFileAsync(initialMarkup, parseOptions, compilationOptions)) { - var diagnosticAndFix = await GetDiagnosticAndFixesAsync(workspace, null); + var diagnosticAndFix = await GetDiagnosticAndFixesAsync(workspace, fixAllActionEquivalenceKey: null, fixProviderData: fixProviderData); var diagnosticCount = diagnosticAndFix.Select(x => x.Item2.Fixes.Count()).Sum(); Assert.Equal(count, diagnosticCount); @@ -238,8 +243,11 @@ protected async Task TestEquivalenceKeyAsync(string initialMarkup, string equiva protected async Task TestSpansAsync( string initialMarkup, string expectedMarkup, int index = 0, - ParseOptions parseOptions = null, CompilationOptions compilationOptions = null, - string diagnosticId = null, string fixAllActionEquivalenceId = null) + ParseOptions parseOptions = null, + CompilationOptions compilationOptions = null, + string diagnosticId = null, + string fixAllActionEquivalenceId = null, + object fixProviderData = null) { IList spansList; string unused; @@ -251,13 +259,13 @@ protected async Task TestEquivalenceKeyAsync(string initialMarkup, string equiva ISet actualTextSpans; if (diagnosticId == null) { - var diagnosticsAndFixes = await GetDiagnosticAndFixesAsync(workspace, fixAllActionEquivalenceId); + var diagnosticsAndFixes = await GetDiagnosticAndFixesAsync(workspace, fixAllActionEquivalenceId, fixProviderData); var diagnostics = diagnosticsAndFixes.Select(t => t.Item1); actualTextSpans = diagnostics.Select(d => d.Location.SourceSpan).ToSet(); } else { - var diagnostics = await GetDiagnosticsAsync(workspace); + var diagnostics = await GetDiagnosticsAsync(workspace, fixProviderData); actualTextSpans = diagnostics.Where(d => d.Id == diagnosticId).Select(d => d.Location.SourceSpan).ToSet(); } @@ -437,7 +445,7 @@ protected async Task TestEquivalenceKeyAsync(string initialMarkup, string equiva testState.TestProjectManagementService.SetDefaultNamespace( defaultNamespace: defaultNamespace); - var diagnosticsAndFixes = await GetDiagnosticAndFixesAsync(testState.Workspace, null); + var diagnosticsAndFixes = await GetDiagnosticAndFixesAsync(testState.Workspace, fixAllActionEquivalenceKey: null, fixProviderData: null); var generateTypeDiagFixes = diagnosticsAndFixes.SingleOrDefault(df => GenerateTypeTestState.FixIds.Contains(df.Item1.Id)); if (isMissing) diff --git a/src/Workspaces/Core/Portable/CodeActions/Operations/CodeActionOperation.cs b/src/Workspaces/Core/Portable/CodeActions/Operations/CodeActionOperation.cs index 7788d5d4f5e..b70cf06d9d1 100644 --- a/src/Workspaces/Core/Portable/CodeActions/Operations/CodeActionOperation.cs +++ b/src/Workspaces/Core/Portable/CodeActions/Operations/CodeActionOperation.cs @@ -1,6 +1,8 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Threading; +using System.Threading.Tasks; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CodeActions { @@ -24,5 +26,12 @@ public virtual string Title public virtual void Apply(Workspace workspace, CancellationToken cancellationToken) { } + + /// + /// Operations may make all sorts of changes that may not be appropriate during testing + /// (like popping up UI). So, by default, we don't apply them unless the operation asks + /// for that to happen. + /// + internal virtual bool ApplyDuringTests => false; } } -- GitLab