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

Add external access to stacktrace APIs for Unit Testing #58082

Merged
merged 9 commits into from
Jan 27, 2022

Conversation

ryzngard
Copy link
Contributor

@ryzngard ryzngard commented Dec 2, 2021

Adds an API for single line use with navigation either based on symbol (TryFindMethodDefinitionAsync) or document name/line number (if available)


if (result.ParsedFrames.Length > 1)
{
throw new InvalidOperationException();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not expose an API to parse an entire stack trace and return IEnumerable<Frame> (in addition to the current per-frame API)?

I haven't looked into exactly how we will consume this on UT side. But for completeness it would be nice to have an API that supports parsing an entire trace (especially since the internal API supports this).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whatever works best for the UT team. We can do either. This was my guess at "best" way to do it so you handle line split and we parse a single line. If you want the whole trace to be handled by us then IEnumerable makes sense

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its not a huge issue but I think handling the whole trace in here may be better since that doesn't preclude passing in individual lines.

In other words, with an API that returns IEnumerable, I can still pass in individual lines and call .Single on the retuned enumerable on our side. However, the opposite is not possible (i.e. I can never pass in an entire stack trace if the API only supports passing one at a time).

throw new InvalidOperationException();
}

return new Frame(result.ParsedFrames.Single(), _stackTraceService);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Since this is an array can we say result.ParsedFrames[0]?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Depends on #58082 (comment)

If we do one result, then does multiple make sense or is that an error?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes if we support only a single API here that returns multiple this is a moot point.

{
private readonly IStackTraceExplorerService _stackTraceService;

public UnitTestingStackTraceService(HostWorkspaceServices services)
Copy link
Contributor

@shyamnamboodiripad shyamnamboodiripad Dec 4, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be changed to accept Workspace instead? This can then be used to access Workspace.Services here. The Workspace can also be threaded down to Frame and Definition below to avoid the need for caller to pass it when calling Definition.TryNavigateToAsync below.

(You could probably move the call to Workspace.Services to happen inside the Frame constructor.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, this was based off another item. Whatever is easiest we can accomodate

private readonly ParsedFrame _parsedFrame;
private readonly IStackTraceExplorerService _stackTraceService;

public Frame(ParsedFrame parsedFrame, IStackTraceExplorerService service)
Copy link
Contributor

@shyamnamboodiripad shyamnamboodiripad Dec 4, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this constructor be internal instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm... probably :)

Copy link
Contributor

@shyamnamboodiripad shyamnamboodiripad Dec 4, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I was thinking that the constructor needn't be exposed to UT. But I guess that is not possible since this is not a nested struct. So its ok to ignore this (and the below) comment.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually wait :) this is a nested struct - so it would be nice to make this private since nothing else needs to construct it :) @ryzngard

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They can't be private because then the API couldn't construct them. The only other option is to make an interface and have the implementing type private. Not sure it's worth the trouble

Copy link
Contributor

@shyamnamboodiripad shyamnamboodiripad left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM modulo comments 👍🏾 Thanks much for exposing the APIs!!

{
private readonly FindUsages.DefinitionItem _definition;

public Definition(FindUsages.DefinitionItem definition)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this constructor be internal instead?

Copy link
Member

@vritant24 vritant24 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

_definition = definition;
}

public async Task<bool> TryNavigateToAsync(Workspace workspace, bool showInPreviewTab, bool activateTab, CancellationToken cancellationToken)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we eliminate the Workspace parameter here since it is already passed in the constructor to UnitTestingStackTraceService above?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems reasonable. As a rule I don't like holding onto data for people that might change, but I think workspace reference is safe here.

private readonly IStackTraceExplorerService _stackTraceService;

public UnitTestingStackTraceService(Workspace workspace)
=> _stackTraceService = workspace.Services.GetRequiredService<IStackTraceExplorerService>();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

consider: Store the Workspace in the field and pass it down to the Frame constructor below. Then you could only fetch stackTraceService inside Frame (since its only used inside that struct).

private readonly IStackTraceExplorerService _stackTraceService;
private readonly Workspace _workspace;

public Frame(ParsedFrame parsedFrame, IStackTraceExplorerService service, Workspace workspace)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make this internal.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the enclosing type is internal does that really matter?

@ryzngard ryzngard requested a review from sharwell January 5, 2022 00:38
@ryzngard
Copy link
Contributor Author

ryzngard commented Jan 5, 2022

@sharwell thanks for the detailed review. I think I followed the correct pattern this time. PTAL

@ryzngard
Copy link
Contributor Author

@sharwell PTAL

Copy link
Member

@sharwell sharwell left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly correct, just two types need to move to Api namespace

@ryzngard ryzngard enabled auto-merge (squash) January 20, 2022 01:43
@ryzngard
Copy link
Contributor Author

Ping @sharwell

@ryzngard ryzngard dismissed sharwell’s stale review January 27, 2022 18:13

Changes were made that were requested. If there's anything else please let me know and I'll do a follow up PR

@ryzngard ryzngard merged commit c805b93 into dotnet:main Jan 27, 2022
@ghost ghost added this to the Next milestone Jan 27, 2022
@ryzngard ryzngard deleted the features/unit_testing_stacktrace_api branch January 27, 2022 18:14
@RikkiGibson RikkiGibson modified the milestones: Next, 17.2.P1 Feb 4, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

8 participants