Skip to content
This repository was archived by the owner on Apr 8, 2019. It is now read-only.

Commit 63b39da

Browse files
committed
Update test framework to support custom validation functions
1 parent b49b767 commit 63b39da

File tree

1 file changed

+157
-19
lines changed

1 file changed

+157
-19
lines changed

PublicApiAnalyzer/PublicApiAnalyzer.Test/Verifiers/CodeFixVerifier.cs

Lines changed: 157 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ public CodeFixVerifier()
3535
this.UseTabs = DefaultUseTabs;
3636
}
3737

38+
protected delegate Task VerifyCodeFixAsync(Project project, bool fixAll, CancellationToken cancellationToken);
39+
3840
/// <summary>
3941
/// Gets or sets the value of the <see cref="FormattingOptions.IndentationSize"/> to apply to the test
4042
/// workspace.
@@ -92,6 +94,27 @@ public bool UseTabs
9294
return this.VerifyCSharpFixAsync(new[] { oldSource }, new[] { newSource }, batchNewSources, oldFileNames, newFileNames, codeFixIndex, allowNewCompilerDiagnostics, numberOfIncrementalIterations, numberOfFixAllIterations, cancellationToken);
9395
}
9496

97+
/// <summary>
98+
/// Called to test a C# code fix when applied on the input source as a string.
99+
/// </summary>
100+
/// <param name="oldSource">A class in the form of a string before the code fix was applied to it.</param>
101+
/// <param name="verifyFixedProjectAsync">A validation function to verify the results of the code fix.</param>
102+
/// <param name="oldFileName">The name of the file in the project before the code fix was applied.</param>
103+
/// <param name="codeFixIndex">Index determining which code fix to apply if there are multiple.</param>
104+
/// <param name="allowNewCompilerDiagnostics">A value indicating whether or not the test will fail if the code fix introduces other warnings after being applied.</param>
105+
/// <param name="numberOfIncrementalIterations">The number of iterations the incremental fixer will be called.
106+
/// If this value is less than 0, the negated value is treated as an upper limit as opposed to an exact
107+
/// value.</param>
108+
/// <param name="numberOfFixAllIterations">The number of iterations the Fix All fixer will be called. If this
109+
/// value is less than 0, the negated value is treated as an upper limit as opposed to an exact value.</param>
110+
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that the task will observe.</param>
111+
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
112+
protected Task VerifyCSharpFixAsync(string oldSource, VerifyCodeFixAsync verifyFixedProjectAsync, string oldFileName = null, int? codeFixIndex = null, bool allowNewCompilerDiagnostics = false, int numberOfIncrementalIterations = DefaultNumberOfIncrementalIterations, int numberOfFixAllIterations = 1, CancellationToken cancellationToken = default(CancellationToken))
113+
{
114+
var oldFileNames = oldFileName == null ? null : new[] { oldFileName };
115+
return this.VerifyCSharpFixAsync(new[] { oldSource }, verifyFixedProjectAsync, oldFileNames, codeFixIndex, allowNewCompilerDiagnostics, numberOfIncrementalIterations, numberOfFixAllIterations, cancellationToken);
116+
}
117+
95118
/// <summary>
96119
/// Called to test a C# code fix when applied on the input source as a string.
97120
/// </summary>
@@ -156,6 +179,76 @@ public bool UseTabs
156179
}
157180
}
158181

182+
/// <summary>
183+
/// Called to test a C# code fix when applied on the input source as a string.
184+
/// </summary>
185+
/// <param name="oldSources">An array of sources in the form of strings before the code fix was applied to them.</param>
186+
/// <param name="verifyFixedProjectAsync">A validation function to verify the results of the code fix.</param>
187+
/// <param name="oldFileNames">An array of file names in the project before the code fix was applied.</param>
188+
/// <param name="codeFixIndex">Index determining which code fix to apply if there are multiple.</param>
189+
/// <param name="allowNewCompilerDiagnostics">A value indicating whether or not the test will fail if the code fix introduces other warnings after being applied.</param>
190+
/// <param name="numberOfIncrementalIterations">The number of iterations the incremental fixer will be called.
191+
/// If this value is less than 0, the negated value is treated as an upper limit as opposed to an exact
192+
/// value.</param>
193+
/// <param name="numberOfFixAllIterations">The number of iterations the Fix All fixer will be called. If this
194+
/// value is less than 0, the negated value is treated as an upper limit as opposed to an exact value.</param>
195+
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that the task will observe.</param>
196+
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
197+
protected async Task VerifyCSharpFixAsync(string[] oldSources, VerifyCodeFixAsync verifyFixedProjectAsync, string[] oldFileNames = null, int? codeFixIndex = null, bool allowNewCompilerDiagnostics = false, int numberOfIncrementalIterations = DefaultNumberOfIncrementalIterations, int numberOfFixAllIterations = 1, CancellationToken cancellationToken = default(CancellationToken))
198+
{
199+
#pragma warning disable SA1101 // Prefix local calls with this
200+
Func<Project, CancellationToken, Task> verifyAsync = (project, ct) => verifyFixedProjectAsync(project, false, ct);
201+
#pragma warning restore SA1101 // Prefix local calls with this
202+
203+
var t1 = this.VerifyFixInternalAsync(LanguageNames.CSharp, this.GetCSharpDiagnosticAnalyzers().ToImmutableArray(), this.GetCSharpCodeFixProvider(), oldSources, oldFileNames, codeFixIndex, allowNewCompilerDiagnostics, numberOfIncrementalIterations, FixEachAnalyzerDiagnosticAsync, verifyAsync, cancellationToken).ConfigureAwait(false);
204+
205+
var fixAllProvider = this.GetCSharpCodeFixProvider().GetFixAllProvider();
206+
Assert.NotEqual(WellKnownFixAllProviders.BatchFixer, fixAllProvider);
207+
208+
if (fixAllProvider == null)
209+
{
210+
await t1;
211+
}
212+
else
213+
{
214+
if (Debugger.IsAttached)
215+
{
216+
await t1;
217+
}
218+
219+
#pragma warning disable SA1101 // Prefix local calls with this
220+
Func<Project, CancellationToken, Task> verifyFixAllAsync = (project, ct) => verifyFixedProjectAsync(project, true, ct);
221+
#pragma warning restore SA1101 // Prefix local calls with this
222+
223+
var t2 = this.VerifyFixInternalAsync(LanguageNames.CSharp, this.GetCSharpDiagnosticAnalyzers().ToImmutableArray(), this.GetCSharpCodeFixProvider(), oldSources, oldFileNames, codeFixIndex, allowNewCompilerDiagnostics, numberOfFixAllIterations, FixAllAnalyzerDiagnosticsInDocumentAsync, verifyFixAllAsync, cancellationToken).ConfigureAwait(false);
224+
if (Debugger.IsAttached)
225+
{
226+
await t2;
227+
}
228+
229+
var t3 = this.VerifyFixInternalAsync(LanguageNames.CSharp, this.GetCSharpDiagnosticAnalyzers().ToImmutableArray(), this.GetCSharpCodeFixProvider(), oldSources, oldFileNames, codeFixIndex, allowNewCompilerDiagnostics, numberOfFixAllIterations, FixAllAnalyzerDiagnosticsInProjectAsync, verifyFixAllAsync, cancellationToken).ConfigureAwait(false);
230+
if (Debugger.IsAttached)
231+
{
232+
await t3;
233+
}
234+
235+
var t4 = this.VerifyFixInternalAsync(LanguageNames.CSharp, this.GetCSharpDiagnosticAnalyzers().ToImmutableArray(), this.GetCSharpCodeFixProvider(), oldSources, oldFileNames, codeFixIndex, allowNewCompilerDiagnostics, numberOfFixAllIterations, FixAllAnalyzerDiagnosticsInSolutionAsync, verifyFixAllAsync, cancellationToken).ConfigureAwait(false);
236+
if (Debugger.IsAttached)
237+
{
238+
await t4;
239+
}
240+
241+
if (!Debugger.IsAttached)
242+
{
243+
// Allow the operations to run in parallel
244+
await t1;
245+
await t2;
246+
await t3;
247+
await t4;
248+
}
249+
}
250+
}
251+
159252
/// <summary>
160253
/// Called to test a C# fix all provider when applied on the input source as a string.
161254
/// </summary>
@@ -394,14 +487,12 @@ private static bool AreDiagnosticsDifferent(ImmutableArray<Diagnostic> analyzerD
394487
return false;
395488
}
396489

397-
private async Task VerifyFixInternalAsync(
490+
private async Task<Project> ApplyFixInternalAsync(
398491
string language,
399492
ImmutableArray<DiagnosticAnalyzer> analyzers,
400493
CodeFixProvider codeFixProvider,
401494
string[] oldSources,
402-
string[] newSources,
403495
string[] oldFileNames,
404-
string[] newFileNames,
405496
int? codeFixIndex,
406497
bool allowNewCompilerDiagnostics,
407498
int numberOfIterations,
@@ -414,12 +505,6 @@ private async Task VerifyFixInternalAsync(
414505
Assert.Equal($"{oldSources.Length} old file names", $"{oldFileNames.Length} old file names");
415506
}
416507

417-
if (newFileNames != null)
418-
{
419-
// Make sure the test case is consistent regarding the number of sources and file names after the code fix
420-
Assert.Equal($"{newSources.Length} new file names", $"{newFileNames.Length} new file names");
421-
}
422-
423508
var project = this.CreateProject(oldSources, language, oldFileNames);
424509
var compilerDiagnostics = await GetCompilerDiagnosticsAsync(project, cancellationToken).ConfigureAwait(false);
425510

@@ -447,21 +532,74 @@ private async Task VerifyFixInternalAsync(
447532
Assert.True(false, message.ToString());
448533
}
449534

450-
// After applying all of the code fixes, compare the resulting string to the inputted one
451-
var updatedDocuments = project.Documents.ToArray();
535+
return project;
536+
}
452537

453-
Assert.Equal($"{newSources.Length} documents", $"{updatedDocuments.Length} documents");
538+
private async Task VerifyFixInternalAsync(
539+
string language,
540+
ImmutableArray<DiagnosticAnalyzer> analyzers,
541+
CodeFixProvider codeFixProvider,
542+
string[] oldSources,
543+
string[] newSources,
544+
string[] oldFileNames,
545+
string[] newFileNames,
546+
int? codeFixIndex,
547+
bool allowNewCompilerDiagnostics,
548+
int numberOfIterations,
549+
Func<ImmutableArray<DiagnosticAnalyzer>, CodeFixProvider, int?, Project, int, CancellationToken, Task<Project>> getFixedProject,
550+
CancellationToken cancellationToken)
551+
{
552+
if (oldFileNames != null)
553+
{
554+
// Make sure the test case is consistent regarding the number of sources and file names before the code fix
555+
Assert.Equal($"{oldSources.Length} old file names", $"{oldFileNames.Length} old file names");
556+
}
454557

455-
for (int i = 0; i < updatedDocuments.Length; i++)
558+
if (newFileNames != null)
456559
{
457-
var actual = await GetStringFromDocumentAsync(updatedDocuments[i], cancellationToken).ConfigureAwait(false);
458-
Assert.Equal(newSources[i], actual);
560+
// Make sure the test case is consistent regarding the number of sources and file names after the code fix
561+
Assert.Equal($"{newSources.Length} new file names", $"{newFileNames.Length} new file names");
562+
}
459563

460-
if (newFileNames != null)
564+
// After applying all of the code fixes, compare the resulting string to the inputed one
565+
Func<Project, CancellationToken, Task> verifyFixedProjectAsync =
566+
async (project, ct) =>
461567
{
462-
Assert.Equal(newFileNames[i], updatedDocuments[i].Name);
463-
}
464-
}
568+
var updatedDocuments = project.Documents.ToArray();
569+
570+
Assert.Equal($"{newSources.Length} documents", $"{updatedDocuments.Length} documents");
571+
572+
for (int i = 0; i < updatedDocuments.Length; i++)
573+
{
574+
var actual = await GetStringFromDocumentAsync(updatedDocuments[i], cancellationToken).ConfigureAwait(false);
575+
Assert.Equal(newSources[i], actual);
576+
577+
if (newFileNames != null)
578+
{
579+
Assert.Equal(newFileNames[i], updatedDocuments[i].Name);
580+
}
581+
}
582+
};
583+
584+
await this.VerifyFixInternalAsync(language, analyzers, codeFixProvider, oldSources, oldFileNames, codeFixIndex, allowNewCompilerDiagnostics, numberOfIterations, getFixedProject, verifyFixedProjectAsync, cancellationToken).ConfigureAwait(false);
585+
}
586+
587+
private async Task VerifyFixInternalAsync(
588+
string language,
589+
ImmutableArray<DiagnosticAnalyzer> analyzers,
590+
CodeFixProvider codeFixProvider,
591+
string[] oldSources,
592+
string[] oldFileNames,
593+
int? codeFixIndex,
594+
bool allowNewCompilerDiagnostics,
595+
int numberOfIterations,
596+
Func<ImmutableArray<DiagnosticAnalyzer>, CodeFixProvider, int?, Project, int, CancellationToken, Task<Project>> getFixedProject,
597+
Func<Project, CancellationToken, Task> verifyFixedProjectAsync,
598+
CancellationToken cancellationToken)
599+
{
600+
var project = await this.ApplyFixInternalAsync(language, analyzers, codeFixProvider, oldSources, oldFileNames, codeFixIndex, allowNewCompilerDiagnostics, numberOfIterations, getFixedProject, cancellationToken).ConfigureAwait(false);
601+
602+
await verifyFixedProjectAsync(project, cancellationToken).ConfigureAwait(false);
465603
}
466604

467605
private async Task<Tuple<Solution, ImmutableArray<CodeAction>>> GetOfferedFixesInternalAsync(string language, string source, int? diagnosticIndex, ImmutableArray<DiagnosticAnalyzer> analyzers, CodeFixProvider codeFixProvider, CancellationToken cancellationToken)

0 commit comments

Comments
 (0)