-
Notifications
You must be signed in to change notification settings - Fork 29.8k
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
Separate subtest stdout from TAP output #45529
Comments
I'm not convinced that this is a spec violation. TAP exists on stdout, which is also where most test authors are likely to dump their debugging logs. I imagine this is also why the spec doesn't define any harness behavior other than NOT treating the test as a failure. It would be nice to take any invalid lines and format them as a diagnostic or part of the YAML block. I believe the TAP parser PR does the former.
As far as I know, this is a common thing with TAP-first tools. I know it's possible with node-tap. I've also seen mention of this from other ecosystems. For example, ctap's
This is definitely something that we can improve. I believe the TAP parser PR helps out here. We also want to do something similar with stderr.
In addition to the Cons you listed, this would only work in the context of the Node.js CLI runner. In theory, you should be able to mix and match TAP tools, including existing parsers from npm and even other languages. Spawning Node with an IPC channel also changes the process - for example, by defining
I agree with this. I think this ties in with the "Ambiguity between stdout from different subtests within a file" point. I don't think this is something that can be done with 100% accuracy though.
I don't think we should do this. Again, the more things we tweak, the more likely we are to surprise people and need to document edge cases. My personal opinion is that we should try to capture as much non-TAP stdout and stderr as we can from the tests and show them as diagnostics/YAML, but the rest of the concerns here are not really a big deal (In years of working on projects using TAP I've personally never ran into an issue related to tests logging things to stdout). |
Taking a step back, a few questions that I may have incorrectly assumed the answers to that could help me understand your positions better: What are the design goals of Why was TAP selected for |
I think those questions are mostly covered in the issue suggesting adding a test runner to core: #40954 Speaking only for myself here: I want a simple, fast test runner that is specifically designed for Node code (not browsers or any other environment). I also don't want to install a ton of dependencies for something that I need on literally every project because the npm registry is hard for me to trust. Also, adding tooling like a test runner is becoming increasingly common for JS runtimes (see Deno and Bun for example). I don't know if I would have picked TAP myself if others hadn't brought it up, but it is used widely (including in Node already), is pretty simple, and does the job well enough. EDIT: I should clarify that I trust the npm registry itself. It's the module authors that I don't trust. |
I'm just sort of lurking here but, what a nice response |
Agreed, thanks for taking the time @cjihrig and thanks for linking that issue. I hadn't stumbled upon that yet and it's really useful for understanding the design decision process behind FWIW, my motivations for getting involved are very similar. Working on a little passion project, I couldn't stomach the literal hundreds of dependencies that a vanilla Working with the Assuming the IPC message approach is a dead-end, I'd like to talk about how the TAP frictions can be alleviated. Did you have any thoughts on my proposal to use a JSON subset of YAML for the nested docs in At some point (around when this stops being marked as experimental), it would be good to write a spec that extends say TAP 14 and additionally specifies the contents/structure of the embedded YAML doc. |
I haven't given a lot of thought to parsing YAML. The TAP spec doesn't currently call for any YAML parsing, and I think we can get pretty far without needing to parse it. For example, we want to add reporter support. In core we could log the YAML as is, regex out specific fields that we need, or possibly support parsing some subset of YAML that we need. Userland reporters would be free to npm install anything they want/need. Maybe @MoLow or @manekinekko have additional thoughts there?
I'm not sure about that. It will make the YAML easier to parse at the cost of making it more difficult to read.
I doubt that we will write any spec, but once the test runner is stable we can discuss treating the TAP output according to semver or some other versioning scheme (similar to what we do with diagnostic reports already). |
Actually, we already look for specific keys in the YAML block (in the TAP parser). We can add more rules to this logic. node/lib/internal/test_runner/tap_parser.js Lines 899 to 922 in d385106
|
now that both TAP parser and reporters landed - closing this as not planned |
What is the problem this feature will solve?
Currently, there is no separation between test TAP output coming from
node:test
and arbitrary user stdout written from the test.This is problematic in cases like:
This test will either output invalid tap by writing
not ok
before theTAP version 14
line or output invalid TAP by writingnot ok
after the TAP version line and therefore reporting the results of more tests than the plan allows for.This is clearly a contrived example but more generally, this introduces the following problems:
Spec compliance
The TAP version 13 contains wording around this
The TAP version 14 spec comments on this as well:
My interpretation of these two specs is that any test that writes to std out falls in one of two buckets
In any case, I would expect that a language core test runner should output valid TAP by default when users write to stdout.
Accidental control flow via console.log
console.log
ing or otherwise writing string likeok
,not ok
to stdout will impact harness's ability to interpret test results. Hopefully we can agree this is undesirable.Ambiguity between stdout from different subtests within a file
This is secondary to this feature request but it would be quite nice if in addition to solving 1 and 2 above, the stdout from each subtest could be cleanly separated by
node:test
. This would enable richer userland implementations on top ofrun
to do common test runner things like only show logs written from a failing subtest.What is the feature you are proposing to solve the problem?
De-interleaved stdout output from TAP output of the node test runner tests. See proposals below
What alternatives have you considered?
Some ideas:
Use a separate channel to communicate test result output
Here's a diagram representing how it works today:
Each box is a fully fledged OS process. Running a single test directly with
node ./path/to/file.test.js
outputs TAP just like running a set of tests withnode --test
does. This is a pleasing application of composite design and this behavior should be preserved through any changes.The proposal here is that an additional channel is introduced:
This solution solves problems 1 and 2 but not 3. Assuming separate processes are still used, an implementation would likely involve using an IPC channel between the head program and the test executor program. The executor program would detect the presence of an IPC channel and post messages to that IPC channel instead of stdout when it is present. Since the IPC channel messages are an implementation detail they do not have to be TAP. JSON seems like a natural choice but any encoding scheme is possible.
A toy implementation of this design showing the messaging channel mechanism and how to cleanly vary between it and direct stdout: https://github.com/kmannislands/node-test-executor-proposal
Pros:
ok
/not ok
in UTF-8.run
implementation significantly - Obviates the TAP parser inrun
, which accounts for a few thousand lines of relatively complex code. Also assuming IPC channels, test messages will arrive and be processed in full removing a lot of the current complexity around handling results serialized as TAP strings to streaming stdout.TAPStream
where users are exposed to quite a bit of implementation detail about TAP. This strikes me as a leaky abstraction--as a user I would expect the return value of run to be likeTestResultStream
and emit messages that make sense in the domain of a test runner rather than expose me to internal detail of TAP via a TAP text stream or TAP lexer tokens.Cons:
Introduces variance in behavior between running a test directly with
node
and running viarun
ornode:test
Users can still potentially write to the process's IPC channel, introducing a similar problem to before albeit to a lesser extent since IPC comminication is much more niche in node than
console.log
. This could be remediated via a special encoding of messages on the IPC channel or probably something else more elegant like a custom additional IPC channel fornode:test
since this is a core implementation.Also diverges from TAP spec on a
should
That being said, it's a
should
and when pursuing this design, I don't think the TAP spec necessarily applies.Include stdout either in the yaml diagnostic block or as diagnostic/comment lines
Not sure the best way to implement something like this in the node core but the idea could potentially solve all 3 problems mentioned above while preserving the high-level architecture of node test.
The proposal here is that test std out writes are intercepted and either written to the TAP output as comments/diagnostics or in the body of the yaml diagnostic.
So the desired outcome is something like the contrived example above would output TAP like:
or
Pros:
node:test
run
node:test
!Cons:
Redirect test stdout to stderr
This is the simplest option to implement. The proposal here is that when running a test,
process.stdout
is changed by side-effect of callingtest
ordescribe
to referenceprocess.stderr
instead. The reference tostdout
should be held on to and only accessible bynode:test
this ensuring that onlynode:test
can write to it.The text was updated successfully, but these errors were encountered: