Skip to content

Commit 16c2d48

Browse files
authored
Revise some of the docs, add information about upgrading (#876)
I was reviewing some of the docs and noticed a few things that could be improved. The changes to the bottom section of the README stem from thinking about other kinds of changes that we must reasonably make from time to time that could prevent a user from easily being able to upgrade to a newer version of the suite. I figured being very explicit about this in this document is best for clarity and transparency. This also adds a section to the README that describes the machinery of the thing and also includes a sequence diagram to show what's going on inside the test runner. And it adds a section in other docs that describe the process for upgrading to a new release of the conformance suite.
1 parent 7d90745 commit 16c2d48

File tree

3 files changed

+152
-20
lines changed

3 files changed

+152
-20
lines changed

README.md

+93-2
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,81 @@ started with any of these tasks, you'll want to read one or more of these guides
3333
* [Testing Client Implementations](./docs/testing_clients.md)
3434
* [Authoring New Test Cases](./docs/authoring_test_cases.md)
3535

36+
## How it works
37+
38+
The tests are data-driven: all test cases are defined in YAML files in this repo. These files
39+
get embedded in the test runner so that the single self-contained executable contains all of
40+
the test case data.
41+
42+
The test runner first processes your configuration and uses that to select which test cases
43+
are relevant. Even if a test case is known to fail, it will still be executed to make sure it
44+
is still failing (and report the fact if the test actually passes).
45+
46+
It then groups all of the test cases by the server configuration needed. So test cases that
47+
will use TLS and the Connect protocol are in a different group from test cases that do _not_
48+
use TLS and use the gRPC protocol.
49+
50+
It then begins running the tests.
51+
52+
```mermaid
53+
sequenceDiagram
54+
actor user
55+
create participant test runner
56+
user ->> test runner: run conformance suite
57+
58+
create participant client
59+
test runner -->> client: start process
60+
61+
rect rgb(255,250,240)
62+
loop for each server config
63+
create participant server
64+
test runner -->> server: start process
65+
test runner ->> server: send config via stdin
66+
server ->> test runner: send result via stdout
67+
68+
rect rgb(240,255,240)
69+
loop for each test case
70+
test runner ->>+ client: send RPC details via stdin
71+
client ->>+ server: invoke RPC, send request(s)
72+
server ->> server: process RPC
73+
server ->>- client: send response(s)
74+
client ->>- test runner: send RPC results via stdout
75+
test runner ->> test runner: assess RPC results
76+
end
77+
end
78+
79+
destroy server
80+
test runner --x server: terminate
81+
end
82+
end
83+
84+
destroy client
85+
test runner --x client: terminate
86+
87+
destroy test runner
88+
test runner ->> user: report results
89+
```
90+
91+
It first starts a client process (either a client under test, if in client mode, or a
92+
reference client).
93+
94+
For each server configuration, it starts a server process (either a server under test, if
95+
in server mode, or a reference server). It sends the server configuration details by writing
96+
them to the process's _stdin_. When the server is listening on the network and ready to
97+
accept RPCs, it sends the details to the test runner by writing to its _stdout_.
98+
99+
For each test case that applies to this server configuration, it adds details to the test
100+
case data with the server's address, so the client will know how to reach it. It then
101+
sends the test case data to the client by writing them to the process's _stdin_. The
102+
client then invokes the RPC. It reports the RPC results to the test runner by writing
103+
them to its _stdout_.
104+
105+
The test runner decides whether the test case was successful or not by comparing the
106+
RPC results against expected results.
107+
108+
After all tests have been run and all child processes stopped, it reports the
109+
results.
110+
36111
## Testing your implementation
37112

38113
### Setup
@@ -135,7 +210,7 @@ This will build the necessary binaries and run tests of the following implementa
135210
confirm interoperability with official gRPC implementations.
136211

137212
Both of the above clients are tested against both the Connect reference server and the gRPC server.
138-
The servers are tested against the Connect reference client and the gRPC client. And since the gRPC
213+
Both servers are tested against the Connect reference client and the gRPC client. And since the gRPC
139214
client does not support gRPC-Web, the servers are also tested against the official gRPC-Web JS client.
140215

141216
## Status: Stable
@@ -149,7 +224,21 @@ formats, or the Protobuf messages used by clients and servers under test in the
149224
Note, however, that we reserve the right to rename, remove, or re-organize
150225
individual test cases, which may impact the "known failing" and "known flaky"
151226
configurations for an implementation under test. We will document these changes
152-
in the [release notes](https://github.com/connectrpc/conformance/releases).
227+
in the [release notes].
228+
229+
We also intend to occasionally add new test cases, and occasionally these
230+
additions may also necessitate updates to the Protobuf schemas (such as new
231+
request or response fields). The Protobuf changes will remain compatible, so
232+
your programs will continue to compile, but actually passing new/updated test
233+
cases may require updates to your program, to incorporate the new fields into
234+
the behavior of the client or server under test. These kinds of changes will
235+
also be documented in the releases notes.
236+
237+
New test cases in a release could also reveal previously undetected conformance
238+
issues which may require fixes to the implementations you are testing. So while
239+
we aim for backwards-compatibility and making it easy to upgrade to new releases
240+
of the conformance suite, it is expected that some releases may incur some effort
241+
to adopt. (See [the docs][upgrading] for more details.)
153242

154243
## Ecosystem
155244

@@ -197,3 +286,5 @@ Offered under the [Apache 2 license][license].
197286
[docs]: https://connectrpc.com
198287
[license]: https://github.com/connectrpc/conformance/blob/main/LICENSE
199288
[protobuf-es]: https://github.com/bufbuild/protobuf-es
289+
[release notes]: https://github.com/connectrpc/conformance/releases
290+
[upgrading]: ./docs/configuring_and_running_tests.md#upgrading

docs/configuring_and_running_tests.md

+52-12
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ A single case is defined by the following properties:
153153

154154
A single set of features is expanded into one or more (usually many more) config cases.
155155
For example, if the features support HTTP 1.1 and HTTP/2, all three protocols, all
156-
stream types, identity and gzip encoding, and TLS, that results in 2*3*5*2*2 = 120
156+
stream types, identity and gzip encoding, and TLS, that results in 2×3×5×2×2 = 120
157157
combinations. Some of those combinations may not be valid (such as full-duplex
158158
bidirectional streams over HTTP 1.1, or gRPC over HTTP 1.1), so the total number of
159159
config cases would be close to 120 but not quite.
@@ -208,9 +208,9 @@ Let's dissect this line-by-line:
208208
anything that _looks_ like an option, is actually a positional argument.
209209
* `./path/to/client/program --some-flag-for-client-program`: The positional arguments
210210
represent the command to invoke in order to run the client under test. The first
211-
token must be the path to the executable. Any other arguments are passed to that
212-
executable as arguments. So in this case, `--some-flag-for-client-program` is an
213-
option that our client under test understands.
211+
token must be the path to the executable. Any subsequent arguments are passed as
212+
arguments to that executable. So in this case, `--some-flag-for-client-program` is
213+
an option that our client under test understands.
214214

215215
Common reasons to pass arguments to the client or server under test are:
216216
1. To control verbosity of log output. When troubleshooting an implementation, it
@@ -331,9 +331,9 @@ If you provide a `-v` option to the test runner, it will print some other messag
331331
running:
332332
```text
333333
Computed 44 config case permutations.
334-
Loaded 1 known failing test cases/patterns.
335-
Loaded 8 test suites, 97 test case templates.
336-
Computed 602 test case permutations across 10 server configurations.
334+
Loaded 8 test suite(s), 97 test case template(s).
335+
Loaded 1 known failing test case pattern(s) that match 4 test case permutation(s).
336+
Computed 602 test case permutation(s) across 10 server configuration(s).
337337
Running 47 tests with reference server for server config {HTTP_VERSION_1, PROTOCOL_CONNECT, TLS:false}...
338338
Running 47 tests with reference server for server config {HTTP_VERSION_1, PROTOCOL_CONNECT, TLS:true}...
339339
Running 46 tests with reference server for server config {HTTP_VERSION_1, PROTOCOL_GRPC_WEB, TLS:false}...
@@ -349,11 +349,12 @@ Running 46 tests with reference server (grpc) for server config {HTTP_VERSION_2,
349349
Running 46 tests with reference server (grpc) for server config {HTTP_VERSION_2, PROTOCOL_GRPC_WEB, TLS:false}...
350350
```
351351
This shows a summary of the config as it is loaded and processed, telling us the total number of
352-
[config cases](#config-cases) that apply to the current configuration (44) and the number of patterns that
353-
identify "known failing" cases. It shows us the total number of test suites (8) and the total number of test
354-
cases across those suites (97). The next line shows us that it has used the 44 relevant config cases and 97
355-
test case templates to compute a total of 602 [test case permutations](#test-case-permutations). This means
356-
that the client under test will be invoking 602 RPCs.
352+
[config cases](#config-cases) that apply to the current configuration (44), the total number of test suites (8),
353+
and the total number of test cases across those suites (97). It then shows the number of patterns
354+
provided to identify "known failing" cases (1), and the number of test cases that matched the "known
355+
failing" patterns (4). The next line shows us that it has used the 44 relevant config cases and 97
356+
test case templates to compute a total of 602 [test case permutations](#test-case-permutations). This
357+
means that the client under test will be invoking 602 RPCs.
357358

358359
The remaining lines in the example output above are printed as each test server is started. Each server config
359360
represents a different RPC server, started with the given configuration (since we are running the tests using
@@ -447,6 +448,11 @@ flaky test cases, use `--known-flaky` (instead of `--skip`). Use of `--run` or `
447448
configurations is discouraged. It should instead be possibly to correctly filter the set of tests
448449
to run just based on config YAML files.
449450

451+
One reason one might need to use `--skip` in a CI configuration is if a bug in the implementation
452+
under test causes the client or server to crash or to deadlock. Since such bugs could prevent the
453+
conformance suite from ever completing successfully (even if such tests are marked as "known
454+
failing"), it may be necessary to temporarily skip them in CI until those bugs are fixed.
455+
450456
## Configuring CI
451457

452458
The easiest way to run conformance tests as part of CI is to do so from a container that has the
@@ -500,6 +506,40 @@ If you have multiple test programs, such as both a client and a server, or even
500506
different sets of arguments, you should name the relevant config YAML and known-failing files so
501507
it is clear to which invocation they apply.
502508

509+
## Upgrading
510+
511+
When a new version of the conformance suite is released, ideally, you could simply update
512+
the version number you are using and everything just works. We aim for
513+
backwards-compatibility between releases to maximize the chances of this ideal outcome. But
514+
there are a number of things that can happen in a release that make the process a little
515+
more laborious:
516+
517+
* As a matter of hygiene/maintenance, we may rename and re-organize test suites and test
518+
cases. This means that any test case patterns that are part of your configuration (like
519+
known-failing files) may need to be updated. We don't expect this to happen often, but
520+
when it does, we will include information in the release notes to aid in updating your
521+
configuration.
522+
* The new version may contain new/updated test cases that require some changes in the
523+
behavior/logic of your implementations under test. This might be for testing new
524+
functionality that requires new fields in the conformance protocol messages. Without
525+
changes in your client or server under test, the new test cases will likely fail.
526+
* The new version may contain new/updated test cases that reveal previously undetected
527+
conformance failures.
528+
529+
To minimize disruption when upgrading, we recommend a process that looks like so:
530+
1. Update to the new release of the conformance suite.
531+
2. Update test case patterns (like in known-failing configurations) if necessary to match
532+
any changes to test case names and organization.
533+
3. Update/add known-failing configurations for any new failures resulting from new/updated
534+
test cases.
535+
4. **Commit/merge the upgrade.**
536+
5. File bugs for the new failures.
537+
6. As the bugs are fixed, update the known-failing configurations as you go.
538+
539+
By simply marking all new failures as "known failing" and filing bugs for them, it should
540+
allow you to upgrade to a new release quickly. You can then decide on the urgency of fixing
541+
the new failures and prioritize accordingly.
542+
503543
[config-proto]: https://buf.build/connectrpc/conformance/docs/main:connectrpc.conformance.v1#connectrpc.conformance.v1.Config
504544
[configcase-proto]: https://buf.build/connectrpc/conformance/docs/main:connectrpc.conformance.v1#connectrpc.conformance.v1.ConfigCase
505545
[connect-protocol]: https://connectrpc.com/docs/protocol/

docs/testing_clients.md

+7-6
Original file line numberDiff line numberDiff line change
@@ -199,9 +199,9 @@ ignored (and the headers and/or trailers treated as empty).
199199
### Cancellation
200200

201201
The `ClientCompatRequest` can contain instructions for the client program to cancel the
202-
RPC before it has completed. When the RPC is canceled based on these instructions, the
203-
rest of the invocation logic should proceed as if it had _not_ been canceled. This way,
204-
the client program exercises the client implementation's cancellation handling, and how
202+
RPC before it has completed. Ideally, when the RPC is canceled based on these instructions,
203+
the rest of the invocation logic should proceed as if it had _not_ been canceled. This way,
204+
the client program can exercise the client implementation's cancellation handling, and how
205205
it impacts subsequent operations for the call. This allows the conformance suite to verify
206206
that asynchronous cancellations are handled correctly by the implementation and result in
207207
proper notification of the cancellation to the code that is consuming the RPC results.
@@ -241,6 +241,7 @@ invoke the method using the given request headers,
241241
delay the indicated number of milliseconds
242242
cancel the RPC (but do not return)
243243
}
244+
receive the response
244245
245246
if the operation fails {
246247
abort, returning a result that describes the error and any
@@ -252,9 +253,9 @@ construct a result using the payload and any available headers
252253
```
253254

254255
_*_ Note: some client APIs will provide a blocking operation for unary RPCs,
255-
which doesn't return until the RPC is complete. For these cases, you must
256-
arrange for the RPC to be canceled asynchronously after the indicated
257-
number of milliseconds, and then invoke the unary operation.
256+
which doesn't return until the RPC response is received. For these cases,
257+
you must arrange for the RPC to be canceled asynchronously after the indicated
258+
number of milliseconds, and then invoke the blocking operation.
258259

259260
#### Client stream
260261

0 commit comments

Comments
 (0)