Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Verify text before other properties #966

Merged
merged 3 commits into from
Feb 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Collections.Immutable;
using System.Linq;
using System.Reflection;
using System.Runtime.ExceptionServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
Expand Down Expand Up @@ -195,17 +196,18 @@ protected virtual ImmutableArray<CodeAction> FilterCodeActions(ImmutableArray<Co
/// <param name="verifier">The verifier to use for test assertions.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that the task will observe.</param>
/// <returns>A <see cref="Project"/> with the changes from the <see cref="CodeAction"/>.</returns>
protected async Task<Project> ApplyCodeActionAsync(Project project, CodeAction codeAction, IVerifier verifier, CancellationToken cancellationToken)
protected async Task<(Project updatedProject, ExceptionDispatchInfo? validationError)> ApplyCodeActionAsync(Project project, CodeAction codeAction, IVerifier verifier, CancellationToken cancellationToken)
{
var operations = await codeAction.GetOperationsAsync(cancellationToken).ConfigureAwait(false);
var solution = operations.OfType<ApplyChangesOperation>().Single().ChangedSolution;
var changedProject = solution.GetProject(project.Id);
ExceptionDispatchInfo? validationError = null;
if (changedProject != project)
{
project = await RecreateProjectDocumentsAsync(changedProject, verifier, cancellationToken).ConfigureAwait(false);
(project, validationError) = await RecreateProjectDocumentsAsync(changedProject, verifier, cancellationToken).ConfigureAwait(false);
}

return project;
return (project, validationError);
}

/// <summary>
Expand All @@ -215,15 +217,16 @@ protected async Task<Project> ApplyCodeActionAsync(Project project, CodeAction c
/// <param name="verifier">The verifier to use for test assertions.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/>.</param>
/// <returns>The updated <see cref="Project"/>.</returns>
private async Task<Project> RecreateProjectDocumentsAsync(Project project, IVerifier verifier, CancellationToken cancellationToken)
private async Task<(Project updatedProject, ExceptionDispatchInfo? validationError)> RecreateProjectDocumentsAsync(Project project, IVerifier verifier, CancellationToken cancellationToken)
{
ExceptionDispatchInfo? validationError = null;
foreach (var documentId in project.DocumentIds)
{
var document = project.GetDocument(documentId);
var initialTree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
document = await RecreateDocumentAsync(document, cancellationToken).ConfigureAwait(false);
var recreatedTree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
if (CodeActionValidationMode != CodeActionValidationMode.None)
if (CodeActionValidationMode != CodeActionValidationMode.None && validationError is null)
{
try
{
Expand All @@ -236,23 +239,32 @@ await recreatedTree.GetRootAsync(cancellationToken).ConfigureAwait(false),
await initialTree.GetRootAsync(cancellationToken).ConfigureAwait(false),
checkTrivia: CodeActionValidationMode == CodeActionValidationMode.Full);
}
catch
catch (Exception genericError)
{
// Try to revalidate the tree with a better message
var renderedInitialTree = TreeToString(await initialTree.GetRootAsync(cancellationToken).ConfigureAwait(false), CodeActionValidationMode);
var renderedRecreatedTree = TreeToString(await recreatedTree.GetRootAsync(cancellationToken).ConfigureAwait(false), CodeActionValidationMode);
verifier.EqualOrDiff(renderedRecreatedTree, renderedInitialTree);

// This is not expected to be hit, but it will be hit if the validation failure occurred in a
// portion of the tree not captured by the rendered form from TreeToString.
throw;
try
{
verifier.EqualOrDiff(renderedRecreatedTree, renderedInitialTree);
}
catch (Exception specificError)
{
validationError = ExceptionDispatchInfo.Capture(specificError);
}
finally
{
// This is not expected to be hit, but it will be hit if the validation failure occurred in
// a portion of the tree not captured by the rendered form from TreeToString.
validationError ??= ExceptionDispatchInfo.Capture(genericError);
}
}
}

project = document.Project;
}

return project;
return (project, validationError);
}

private static async Task<Document> RecreateDocumentAsync(Document document, CancellationToken cancellationToken)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Microsoft.CodeAnalysis.Testing.AnalyzerTest<TVerifier>.XmlReferences.get -> Syst
Microsoft.CodeAnalysis.Testing.AnalyzerVerifier<TAnalyzer, TTest, TVerifier>
Microsoft.CodeAnalysis.Testing.AnalyzerVerifier<TAnalyzer, TTest, TVerifier>.AnalyzerVerifier() -> void
Microsoft.CodeAnalysis.Testing.CodeActionTest<TVerifier>
Microsoft.CodeAnalysis.Testing.CodeActionTest<TVerifier>.ApplyCodeActionAsync(Microsoft.CodeAnalysis.Project project, Microsoft.CodeAnalysis.CodeActions.CodeAction codeAction, Microsoft.CodeAnalysis.Testing.IVerifier verifier, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<Microsoft.CodeAnalysis.Project>
Microsoft.CodeAnalysis.Testing.CodeActionTest<TVerifier>.ApplyCodeActionAsync(Microsoft.CodeAnalysis.Project project, Microsoft.CodeAnalysis.CodeActions.CodeAction codeAction, Microsoft.CodeAnalysis.Testing.IVerifier verifier, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<(Microsoft.CodeAnalysis.Project updatedProject, System.Runtime.ExceptionServices.ExceptionDispatchInfo validationError)>
Microsoft.CodeAnalysis.Testing.CodeActionTest<TVerifier>.CodeActionEquivalenceKey.get -> string
Microsoft.CodeAnalysis.Testing.CodeActionTest<TVerifier>.CodeActionEquivalenceKey.set -> void
Microsoft.CodeAnalysis.Testing.CodeActionTest<TVerifier>.CodeActionIndex.get -> int?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,25 @@ private async Task<ImmutableArray<MetadataReference>> ResolveCoreAsync(string la

PackageReaderBase packageReader;
var installedPath = GetInstalledPath(localPathResolver, globalPathResolver, packageToInstall);
if (installedPath is { })
{
packageReader = new PackageFolderReader(installedPath);
if (Path.GetDirectoryName(installedPath) == temporaryPackagesFolder)
{
// Delete the folder if it's in the temporary path and the package reader cannot read the
// nuspec file.
try
{
_ = packageReader.GetNuspecFile();
}
catch (PackagingException)
{
Directory.Delete(installedPath, recursive: true);
installedPath = null;
}
}
}

if (installedPath is null)
{
var downloadResource = await availablePackages[packageToInstall].Source.GetResourceAsync<DownloadResource>(cancellationToken);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -265,13 +265,14 @@ protected override async Task RunImplAsync(CancellationToken cancellationToken)

if (CodeFixExpected())
{
// Verify code fix output before verifying the diagnostics of the fixed state
await VerifyFixAsync(testState, fixedState, batchFixedState, Verify, cancellationToken).ConfigureAwait(false);

await VerifyDiagnosticsAsync(new EvaluatedProjectState(fixedState, ReferenceAssemblies), fixedState.AdditionalProjects.Values.Select(additionalProject => new EvaluatedProjectState(additionalProject, ReferenceAssemblies)).ToImmutableArray(), fixedState.ExpectedDiagnostics.ToArray(), Verify.PushContext("Diagnostics of fixed state"), cancellationToken).ConfigureAwait(false);
if (allowFixAll && CodeActionExpected(BatchFixedState))
{
await VerifyDiagnosticsAsync(new EvaluatedProjectState(batchFixedState, ReferenceAssemblies), batchFixedState.AdditionalProjects.Values.Select(additionalProject => new EvaluatedProjectState(additionalProject, ReferenceAssemblies)).ToImmutableArray(), batchFixedState.ExpectedDiagnostics.ToArray(), Verify.PushContext("Diagnostics of batch fixed state"), cancellationToken).ConfigureAwait(false);
}

await VerifyFixAsync(testState, fixedState, batchFixedState, Verify, cancellationToken).ConfigureAwait(false);
}
}

Expand Down Expand Up @@ -534,6 +535,7 @@ private async Task VerifyProjectAsync(ProjectState newState, Project project, IV

var previousDiagnostics = ImmutableArray.Create<(Project project, Diagnostic diagnostic)>();

ExceptionDispatchInfo? firstValidationError = null;
var currentIteration = -1;
bool done;
do
Expand All @@ -557,7 +559,7 @@ private async Task VerifyProjectAsync(ProjectState newState, Project project, IV
}
catch (Exception ex)
{
return (project, ExceptionDispatchInfo.Capture(ex));
return (project, firstValidationError ?? ExceptionDispatchInfo.Capture(ex));
}

previousDiagnostics = analyzerDiagnostics;
Expand Down Expand Up @@ -599,7 +601,8 @@ private async Task VerifyProjectAsync(ProjectState newState, Project project, IV
anyActions = true;

var originalProjectId = project.Id;
var fixedProject = await ApplyCodeActionAsync(fixableDocument.Project, actionToApply, verifier, cancellationToken).ConfigureAwait(false);
var (fixedProject, currentError) = await ApplyCodeActionAsync(fixableDocument.Project, actionToApply, verifier, cancellationToken).ConfigureAwait(false);
firstValidationError ??= currentError;
if (fixedProject != fixableDocument.Project)
{
done = false;
Expand Down Expand Up @@ -637,10 +640,10 @@ private async Task VerifyProjectAsync(ProjectState newState, Project project, IV
}
catch (Exception ex)
{
return (project, ExceptionDispatchInfo.Capture(ex));
return (project, firstValidationError ?? ExceptionDispatchInfo.Capture(ex));
}

return (project, null);
return (project, firstValidationError ?? null);
}

private Task<(Project project, ExceptionDispatchInfo? iterationCountFailure)> FixAllAnalyzerDiagnosticsInDocumentAsync(ImmutableArray<DiagnosticAnalyzer> analyzers, ImmutableArray<CodeFixProvider> codeFixProviders, int? codeFixIndex, string? codeFixEquivalenceKey, Action<CodeAction, IVerifier>? codeActionVerifier, Project project, int numberOfIterations, IVerifier verifier, CancellationToken cancellationToken)
Expand Down Expand Up @@ -675,6 +678,7 @@ private async Task VerifyProjectAsync(ProjectState newState, Project project, IV

var previousDiagnostics = ImmutableArray.Create<(Project project, Diagnostic diagnostic)>();

ExceptionDispatchInfo? firstValidationError = null;
var currentIteration = -1;
bool done;
do
Expand All @@ -698,7 +702,7 @@ private async Task VerifyProjectAsync(ProjectState newState, Project project, IV
}
catch (Exception ex)
{
return (project, ExceptionDispatchInfo.Capture(ex));
return (project, firstValidationError ?? ExceptionDispatchInfo.Capture(ex));
}

var fixableDiagnostics = analyzerDiagnostics
Expand Down Expand Up @@ -771,7 +775,8 @@ private async Task VerifyProjectAsync(ProjectState newState, Project project, IV
}

var originalProjectId = project.Id;
var fixedProject = await ApplyCodeActionAsync(fixableDocument.Project, action, verifier, cancellationToken).ConfigureAwait(false);
var (fixedProject, currentError) = await ApplyCodeActionAsync(fixableDocument.Project, action, verifier, cancellationToken).ConfigureAwait(false);
firstValidationError ??= currentError;
if (fixedProject != fixableDocument.Project)
{
done = false;
Expand All @@ -798,10 +803,10 @@ private async Task VerifyProjectAsync(ProjectState newState, Project project, IV
}
catch (Exception ex)
{
return (project, ExceptionDispatchInfo.Capture(ex));
return (project, firstValidationError ?? ExceptionDispatchInfo.Capture(ex));
}

return (project, null);
return (project, firstValidationError);
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@ protected override async Task RunImplAsync(CancellationToken cancellationToken)

if (CodeActionExpected())
{
await VerifyDiagnosticsAsync(new EvaluatedProjectState(fixedState, ReferenceAssemblies), fixedState.AdditionalProjects.Values.Select(additionalProject => new EvaluatedProjectState(additionalProject, ReferenceAssemblies)).ToImmutableArray(), FilterTriggerSpanResults(fixedState.ExpectedDiagnostics).ToArray(), Verify.PushContext("Diagnostics of fixed state"), cancellationToken).ConfigureAwait(false);
await VerifyRefactoringAsync(testState, fixedState, GetTriggerSpanResult(testState.ExpectedDiagnostics), Verify, cancellationToken).ConfigureAwait(false);
await VerifyDiagnosticsAsync(new EvaluatedProjectState(fixedState, ReferenceAssemblies), fixedState.AdditionalProjects.Values.Select(additionalProject => new EvaluatedProjectState(additionalProject, ReferenceAssemblies)).ToImmutableArray(), FilterTriggerSpanResults(fixedState.ExpectedDiagnostics).ToArray(), Verify.PushContext("Diagnostics of fixed state"), cancellationToken).ConfigureAwait(false);
}

static IEnumerable<DiagnosticResult> FilterTriggerSpanResults(IEnumerable<DiagnosticResult> expected)
Expand Down Expand Up @@ -227,6 +227,7 @@ private async Task VerifyProjectAsync(ProjectState newState, Project project, IV
numberOfIterations = -numberOfIterations;
}

ExceptionDispatchInfo? firstValidationError = null;
var currentIteration = -1;
bool done;
do
Expand All @@ -239,7 +240,7 @@ private async Task VerifyProjectAsync(ProjectState newState, Project project, IV
}
catch (Exception ex)
{
return (project, ExceptionDispatchInfo.Capture(ex));
return (project, firstValidationError ?? ExceptionDispatchInfo.Capture(ex));
}

done = true;
Expand All @@ -262,7 +263,8 @@ private async Task VerifyProjectAsync(ProjectState newState, Project project, IV
anyActions = true;

var originalProjectId = project.Id;
var fixedProject = await ApplyCodeActionAsync(triggerDocument.Project, actionToApply, verifier, cancellationToken).ConfigureAwait(false);
var (fixedProject, currentError) = await ApplyCodeActionAsync(triggerDocument.Project, actionToApply, verifier, cancellationToken).ConfigureAwait(false);
firstValidationError ??= currentError;
if (fixedProject != triggerDocument.Project)
{
done = false;
Expand Down Expand Up @@ -294,10 +296,10 @@ private async Task VerifyProjectAsync(ProjectState newState, Project project, IV
}
catch (Exception ex)
{
return (project, ExceptionDispatchInfo.Capture(ex));
return (project, firstValidationError ?? ExceptionDispatchInfo.Capture(ex));
}

return (project, null);
return (project, firstValidationError);

async Task<Location> GetTriggerLocationAsync()
{
Expand Down