8000 Third Party Test Runner Resiliency by plioi · Pull Request #302 · fixie/fixie · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Third Party Test Runner Resiliency #302

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

Merged
merged 12 commits into from
Nov 27, 2023
Merged

Conversation

plioi
Copy link
Contributor
@plioi plioi commented Nov 27, 2023

This PR ensures Test Adapter resiliency in the face of faulty third party LaunchProcessWithDebuggerAttached implementations. It is a replacement for the earlier attempts at resiliency removed in #300 and #301.

In the past, I made the mistake of attempting to work around such debugger-attachment issues to the detriment of the project. I added workarounds intended to smooth over third party gaps, in fact dropping end users into bizarre assembly loading issues instead of driving them directly to success. I added workarounds intended to allow expected behavior for as many test runners as possible, only to find later that the Test Adapter was throwing exceptions that left their UX stuck spinning or reporting "inconclusive" results. In each case, in addition to creating false solutions, the complexity in the implementation grew.

#300, #301, and this code change mark a change in the Fixie project's stance on test runner integrations. When the world insists on taking the "move fast and break things" stance, automated testing proponents must meet that world with the opposing stance: "move slow and heal things". That goes for our tests as well as our test frameworks.

To follow my own advice, this repo will no longer be interested in workarounds, when I can instead give the user immediate guidance that unblocks their ability to move slow and heal things, and when I can give the user sufficient evidence to report their (now non-blocking, low-stakes) issue to the correct party.

Background

Fixie provides a VSTest "Test Adapter" primarily in order to take part in the Visual Studio Test Explorer test runner. Any third party test runner can integrate by providing a "test host" process similar to the one provided by VSTest which calls into our Test Adapter to run tests and receive results.

For some test frameworks, that's enough to handle debugging. The test runner (such as an alternative IDE) launches their test host with the debugger attached to that process, loads the test framework's Test Adapter assembly, and calls into it with reflection (e.g. "Run All Tests Please"). If the test framework runs tests in-process, then test method breakpoints will be hit.

To ensure legitimate isolation, though, test frameworks can run the actual test assembly in a dedicated secondary process. For these cases, the Test Adapters need to call back to the test host (e.g. an IDE that just attached a debugger to the test host), asking them to start the process for them under the same debugging session. Doing so is not within the Test Adapter's control. (There are yet more reasons beyond environment and assembly loading isolation that a VSTest-integrated test framework might work with attaching debuggers to external processes, but they don't apply in this case.)

Specifically, any VSTest Test Adapter can make one pivotal request back to the test host: to launch a secondary process with the test host's debugger attached:

Namespace: Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter
Interface: IFrameworkHandle
Member: LaunchProcessWithDebuggerAttached
int LaunchProcessWithDebuggerAttached(
      string filePath,
      string? workingDirectory,
      string? arguments,
      IDictionary<string, string?>? environmentVariables);

VSTest's own test host implements this method, and so debugging Fixie tests works in Test Explorer without user intervention. The secondary process is enlisted in the existing debugging session, and the user has no need to be aware of the process hops involved. 8000

JetBrains' VSTest runner for ReSharper and Rider, unfortunately, throws NotSupportedException from this method.

Prior to this PR, this meant that the Test Adapter would encounter a highly-unanticipated exception at a pivotal moment: while the Test Adapter is establishing a named pipe connection to the test assembly process. Interrupting that communication would then fail with exceptions of its own around the far side of the pipe never connecting, and the JetBrains UI would have little to no information about what went wrong.

Solution

This PR improves our error handling in these situations with the following goals:

  1. First and foremost, the end user is already facing some blocking issue in their own project that they need to debug and resolve. They do not need to be further blocked by their choice of IDE. The first feedback they receive must appear in their normal test runner UI where test results appear, and it must tell them exactly what to do next to debug their test, in their IDE, right now.
  2. Secondary to unblocking the user, attempt to prevent them from submitting their bug report to the wrong organization. Provide actionable feedback about where to submit their feedback and what technical details to include in that feedback.
  3. Fixie's Test Adapter implementation should, ideally, not require any further change if the third party test runner fixes its implementation of the VSTest API. The user should be able to update their IDE once its own issue has been resolved, and the very next attempt to use the debugger on a test should just work. In other words, we'll avoid detecting and making special cases for specific third party runners, unless absolutely necessary, as these would need to be revisited over time and would risk needing to be reintroduced in the face of third party regressions, with Issues being submitted here at each step.

plioi added 12 commits November 21, 2023 17:51
…to take part in the debugger-attached code path.
LaunchProcessWithDebuggerAttached would be silently skipped.

Third-party test runners may or may not have the debugger attached to
the currently running Fixie.TestAdapter process during the discovery
phase. That is their choice and for some test frameworks that may even
be meaningful.

In Fixie's case, debugger-attached runs only make sense when the
secondary test assembly process we want to launch is being launched by
the IFrameworkHandle.LaunchProcessWithDebuggerAttached method. However,
there is no IFrameworkHandle instance during any Discovery phase, so by
here it is null.

If we were to attempt to run the debugger-attachment code during
discovery, where the IFrameworkHandle is null, the process launch
would be silently skipped with the use of `?.`, and the user would
experience errors while the interprocess communication between the
currently running Fixie.TestAdapter process and the *nonexistent*
secondary test assembly process inevitably failed.
…istinguishing when the IFrameworkHandle is null. This change will be improved upon in the next commit by renaming the overloads.
…t clear which code path is intended for each phase of the testing lifecycle.
…ges, to reveal dead code paths during the discovery phase.
…at the returned Process on this code path is never null.
…nitialization expression, enabling the Inline Parameter refactoring in the next commit.
…ion code paths by demonstrating that the test assembly's working directory can be inferred just prior to launching the test assembly process.
…ers with guidance to immediately continue debugging their test, as well as providing evidence to submit to the third party test runner's organization.
@plioi plioi marked this pull request as ready for review November 27, 2023 03:00
@plioi plioi merged commit ce26f4d into main Nov 27, 2023
@plioi plioi deleted the third-party-runner-resiliency branch November 27, 2023 03:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant
0