-
Notifications
You must be signed in to change notification settings - Fork 4k
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
Add external access to stacktrace APIs for Unit Testing #58082
Conversation
|
||
if (result.ParsedFrames.Length > 1) | ||
{ | ||
throw new InvalidOperationException(); |
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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]
?
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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.)
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm... probably :)
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
There was a problem hiding this 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) |
There was a problem hiding this comment.
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?
There was a problem hiding this 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) |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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>(); |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
make this internal.
There was a problem hiding this comment.
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?
src/Features/Core/Portable/ExternalAccess/UnitTesting/API/UnitTestingStackTraceService.cs
Outdated
Show resolved
Hide resolved
src/Features/Core/Portable/ExternalAccess/UnitTesting/API/UnitTestingStackTraceService.cs
Outdated
Show resolved
Hide resolved
src/Features/Core/Portable/ExternalAccess/UnitTesting/API/UnitTestingStackTraceService.cs
Outdated
Show resolved
Hide resolved
src/Features/Core/Portable/ExternalAccess/UnitTesting/API/UnitTestingStackTraceService.cs
Outdated
Show resolved
Hide resolved
src/Features/Core/Portable/ExternalAccess/UnitTesting/API/UnitTestingStackTraceService.cs
Outdated
Show resolved
Hide resolved
src/Features/Core/Portable/ExternalAccess/UnitTesting/API/UnitTestingStackTraceService.cs
Outdated
Show resolved
Hide resolved
src/Features/Core/Portable/ExternalAccess/UnitTesting/API/UnitTestingStackTraceService.cs
Outdated
Show resolved
Hide resolved
src/Features/Core/Portable/ExternalAccess/UnitTesting/API/UnitTestingStackTraceService.cs
Outdated
Show resolved
Hide resolved
src/Features/Core/Portable/ExternalAccess/UnitTesting/API/UnitTestingStackTraceService.cs
Outdated
Show resolved
Hide resolved
@sharwell thanks for the detailed review. I think I followed the correct pattern this time. PTAL |
@sharwell PTAL |
There was a problem hiding this 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
src/Features/Core/Portable/ExternalAccess/UnitTesting/UnitTestingDefinitionItemWrapper.cs
Outdated
Show resolved
Hide resolved
src/Features/Core/Portable/ExternalAccess/UnitTesting/UnitTestingParsedFrameWrapper.cs
Outdated
Show resolved
Hide resolved
Ping @sharwell |
Changes were made that were requested. If there's anything else please let me know and I'll do a follow up PR
Adds an API for single line use with navigation either based on symbol (
TryFindMethodDefinitionAsync
) or document name/line number (if available)