Skip to content

Conversation

Youssef1313
Copy link
Member

@Youssef1313 Youssef1313 commented Sep 10, 2025

This PR adds a draft documentation for the pipe protocol, which is used when running Microsoft.Testing.Platform test applications with the new MTP-specific dotnet test implementation shipping in .NET 10.

Fixes #6187

This document outlines the protocol used by 'dotnet test' CLI when communicating with Microsoft.Testing.Platform (MTP) applications.

> [!NOTE]
> Through the document, .NET CLI will be referred to for easy interpretation, but it's not necessarily ".NET CLI".
Copy link
Member

Choose a reason for hiding this comment

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

what does this sentence mean? what's the difference between .NET CLI and ".NET CLI"

Copy link
Member Author

Choose a reason for hiding this comment

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

I meant we are using .NET CLI as an example. But it could be whatever other client (or well, from pipe-point of view, server)

Copy link
Member

Choose a reason for hiding this comment

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

the JSON-RPC protocol has the following start

MSTest Runner projects builds into a self-contained executable that can be invoked to run tests. The protocol describes the communication between the client (IDE/CLI/CI) any the MSTest Runner executable (also refered to as the server).
The communication is based on JSON-RPC and describes the RPC messages sent in order to support running of tests.

if we'd use a similar preamble reading these docs side by side would be easier.

otherwise, a similar note could be added. that the document describes the communication between any client (dotnet test) and MTP runner. and that afterwards this document uses .NET CLI when referring to any compatible client

- In this case, should we have a "special" response that means "I didn't understand your request"?
- I think such special response gives us the most flexibility.

## General protocol message format
Copy link
Member

Choose a reason for hiding this comment

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

Should we have under the General flow some reference that the messages are sent using a BinaryFormatter usign the payload defined in this section?

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm not sure how BinaryFormatter is relevant here.

Copy link
Member

Choose a reason for hiding this comment

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

without seeing the reference implementation I'm unsure how the messages are encoded in practice. knowing the Stream/ StreamWriter/ Serializer APIs used to encode the messages would help me understand this better

Handshake is expected to happen in all modes. Help, discovery, and execution.
Today, there is an MTP bug that we don't handshake in help mode. This will be worked around in .NET CLI. A fix needs to be done in MTP.

### Handshake request
Copy link
Member

Choose a reason for hiding this comment

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

It might be nice to align it with the MTP VS protocol spec and write down the direction of the request for brevity. for instance it looks like the request is first sent from the MTP server to the dotnet test CLI. maybe it's written somewhere above and I missed it

- Supported protocol versions (required): The protocol versions that are supported by the test application, separated by semicolons.
- Host type (required): The host type which is handshaking with us.
- Module path (required): The path to the test module, either the assembly dll path, or the actual apphost (exe) path.
- ExecutionId (required): Explained above.
Copy link
Member

Choose a reason for hiding this comment

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

if this is a markdown document it might be better to add hyperlinks here, so that I can click and navigate to the definition quickly


### `ExecutionId`

For the execution of a test app, the test app and all its child processes are uniquely identified by `ExecutionId`. Per **current implementation**, we can already identify the test app and all its child processes by knowing which pipe is receiving the message. But we are still including `ExecutionId` in the protocol, in case we needed it in future due to implementation changes.
Copy link
Member

Choose a reason for hiding this comment

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

so the ExecutionId is a generated GUID by the test runner? are currently messages validated that the ExecutionId does not change?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes. It's a Guid generated by MTP and is the same for child processes as well.
Today's implementation might be missing the validation. The purpose of this doc is to have a well-defined spec, then review the implementation and adjust it as needed.

- SessionUid (required)
- ExecutionId (required)

## Command line options
Copy link
Member

Choose a reason for hiding this comment

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

it might be good to add "message" for consistency


## Command line options

This message is expected to be received only in help mode.
Copy link
Member

Choose a reason for hiding this comment

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

it would be useful to explain what this message means from user's point of view, i.e. that if a user calls dotnet test --help each test application is expected to report its command line options back

- When running in "execution" mode, the absence of these messages mean that the test app doesn't have any tests.
- When running in "help" mode, the test app MUST NOT send these messages.

This message contains the following properties:
Copy link
Member

Choose a reason for hiding this comment

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

are in-progress tests somehow being relayed?

Copy link
Member Author

Choose a reason for hiding this comment

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

A while back, I added an "IDE mode" to the protocol (and I'm realizing now this part isn't documented and probably needs to be documented). With the "IDE" mode, in-progress is reported as "successful" with state denoting "in-progress". In IDE mode, we also send line number and file path. Maybe few more things that I don't recall now.

- What we could do?
- Keep InstanceId for compatibility.
- Add the metadata on test results as "another" way of knowing retries.
- In handshake request, tell .NET CLI that we have the "new" way of knowing retries.
Copy link
Member

Choose a reason for hiding this comment

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

are we considering using the VS server protocol for dotnet test at any point? we already have capabilities there and this would make it easier to include features such as retries to both CLI and VS/VsCode simulatenously

Copy link
Member Author

Choose a reason for hiding this comment

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

I personally would consider the opposite 😄
Any kind of "capability" can potentially be added as a property in handshake request.

Copy link
Member

Choose a reason for hiding this comment

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

the JSON-RPC provides a more general-purpose way of communicating between client and server and comes with out of the box implementations for many programming languages, decreasing the amount of work needed to create various implementations

and there's a couple of features that the JSON-RPC brings out of the box

  • allows server to respond with exceptions to the client (rather than just rely on console stderr)
  • allows client to cancel requests (rather than support cancellation just via Ctrl+C)
  • allows to overlap messages (where now requests cannot be processed in parallel)
  • allows to write a client that logs all RPC messages for debugging purposes (as by default everything is just JSON). if one of the endpoints doesn't know how to decode the binary, debugging through the message is much trickier
  • supports both request/response and notifications data formats
  • if we use well known JSON-RPC libraries we already can get the endianness handling for free
  • all of the serialization logic is already well known/documented since it's just JSON

of course these can be over added to the pipe protocol, but we're effectively reimplementing JSON-RPC with a custom serialization format


- Youssef: Can we get rid of InstanceId completely? I personally don't believe it was the right design.
- If we want to simply track retries, that could simply have been a metadata on test results.
- Having it as a metadata on test results even allows for in-process retries to be reported.
Copy link
Member

Choose a reason for hiding this comment

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

I think the tricky part here is to align the tests with file artifacts.
For instance, if the tests get rerun 3 times, how should these be reported.
And when should a FileArtifact get a different SessionId.

So if a test runner runs test1 twice and collects the coverage once, perhaps there should be a single SessionId, but two different RunIds.
Whereas, if a test runner runs test1 once, collects the coverage and runs it again, perhaps there should be just one RunId, but two different SessionIds.

Copy link
Member Author

Choose a reason for hiding this comment

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

Hmm, for the purpose of dotnet test, it's sufficient to link artifacts to test results via TestNodeUid+SessionUid. But, this might limit us if, in future, we wanted to use the pipe protocol in VS. There is at least an ambiguity when an MSTest test method is folded and it reports multiple results. In this case, we can't link artifacts to individual results.

At the same time, TestNodeUid is supposedly to uniquely identify a "single" test. So, what MSTest does today with folding is already kinda a violation of MTP contract.

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.

Spec dotnet test pipe protocol

2 participants