blob: bf8ddf0d02cfaf914115ffa78bfb10ae433b470e [file] [log] [blame] [view]
JSON Reporter Protocol
======================
The test runner supports a JSON reporter which provides a machine-readable
representation of the test runner's progress. This reporter is intended for use
by IDEs and other tools to present a custom view of the test runner's operation
without needing to parse output intended for humans.
Note that the test runner is highly asynchronous, and users of this protocol
shouldn't make assumptions about the ordering of events beyond what's explicitly
specified in this document. It's possible for events from multiple tests to be
intertwined, for a single test to emit an error after it completed successfully,
and so on.
## Usage
Pass the `--reporter json` command-line flag to the test runner to activate the
JSON reporter.
pub run test --reporter json <path-to-test-file>
The JSON stream will be emitted via standard output. It will be a stream of JSON
objects, separated by newlines.
See `json_reporter.schema.json` for a formal description of the protocol schema.
See `test/runner/json_reporter_test.dart` for some sample output.
## Compatibility
The protocol emitted by the JSON reporter is considered part of the public API
of the `test` package, and is subject to its [semantic versioning][semver]
restrictions. In particular:
[semver]: https://www.dartlang.org/tools/pub/versioning.html#semantic-versions
* No new feature will be added to the protocol without increasing the test
package's minor version number.
* No breaking change will be made to the protocol without increasing the test
package's major version number.
The following changes are not considered breaking. This is not necessarily a
comprehensive list.
* Adding a new attribute to an existing object.
* Adding a new type of any object with a `type` parameter.
* Adding new test state values.
## Reading this Document
Each major type of JSON object used by the protocol is described by a class.
Classes have names which are referred to in this document, but are not used as
part of the protocol. Classes have typed attributes, which refer to the types
and names of attributes in the JSON objects. If an attribute's type is another
class, that refers to a nested object. The special type `List<...>` indicates a
JSON list of the given type.
Classes can "extend" one another, meaning that the subclass has all the
attributes of the superclass. Concrete subclasses can be distinguished by the
specific value of their `type` attribute. Classes may be abstract, indicating
that only their subclasses will ever be used.
## Events
### Event
```
abstract class Event {
// The type of the event.
//
// This is always one of the subclass types listed below.
String type;
// The time (in milliseconds) that has elapsed since the test runner started.
int time;
}
```
This is the root class of the protocol. All root-level objects emitted by the
JSON reporter will be subclasses of `Event`.
### StartEvent
```
class StartEvent extends Event {
String type = "start";
// The version of the JSON reporter protocol being used.
//
// This is a semantic version, but it reflects only the version of the
// protocol—it's not identical to the version of the test runner itself.
String protocolVersion;
// The version of the test runner being used.
String runnerVersion;
}
```
A single start event is emitted before any other events. It indicates that the
test runner has started running.
### GroupEvent
```
class GroupEvent extends Event {
String type = "group";
/// Metadata about the group.
Group group;
}
```
A group event is emitted before any `TestStartEvent`s for tests in a given
group. This is the only event that contains the full metadata about a group;
future events will refer to the group by its opaque ID.
This includes the implicit group at the root of each suite, which has a `null`
name. However, it does *not* include implicit groups for the virtual suites
generated to represent loading test files.
The group should be considered skipped if `group.metadata.skip` is `true`. When
a group is skipped, a single `TestStartEvent` will be emitted for a test within
that group that will also be skipped.
### TestStartEvent
```
class TestStartEvent extends Event {
String type = "testStart";
// Metadata about the test that started.
Test test;
}
```
An event emitted when a test begins running. This is the only event that
contains the full metadata about a test; future events will refer to the test by
its opaque ID.
The test should be considered skipped if `test.metadata.skip` is `true`.
### PrintEvent
```
class PrintEvent extends Event {
String type = "print";
// The ID of the test that printed a message.
int testID;
// The message that was printed.
String message;
}
```
A `PrintEvent` indicates that a test called `print()` and wishes to display
output.
### ErrorEvent
```
class ErrorEvent extends Event {
String type = "error";
// The ID of the test that experienced the error.
int testID;
// The result of calling toString() on the error object.
String error;
// The error's stack trace, in the stack_trace package format.
String stackTrace;
// Whether the error was a TestFailure.
bool isFailure;
}
```
A `ErrorEvent` indicates that a test encountered an uncaught error. Note
that this may happen even after the test has completed, in which case it should
be considered to have failed.
If a test is asynchronous, it may encounter multiple errors, which will result
in multiple `ErrorEvent`s.
### TestDoneEvent
```
class TestDoneEvent extends Event {
String type = "testDone";
// The ID of the test that completed.
int testID;
// The result of the test.
String result;
// Whether the test's result should be hidden.
bool hidden;
}
```
An event emitted when a test completes. The `result` attribute indicates the
result of the test:
* `"success"` if the test had no errors.
* `"failure"` if the test had a `TestFailure` but no other errors.
* `"error"` if the test had an error other than a `TestFailure`.
If the test encountered an error, the `TestDoneEvent` will be emitted after the
corresponding `ErrorEvent`.
The `hidden` attribute indicates that the test's result should be hidden and not
counted towards the total number of tests run for the suite. This is true for
virtual tests created for loading test suites, `setUpAll()`, and
`tearDownAll()`. Only successful tests will be hidden.
Note that it's possible for a test to encounter an error after completing. In
that case, it should be considered to have failed, but no additional
`TestDoneEvent` will be emitted. If a previously-hidden test encounters an
error after completing, it should be made visible.
### DoneEvent
```
class DoneEvent extends Event {
String type = "done";
// Whether all tests succeeded (or were skipped).
bool success;
}
```
An event indicating the result of the entire test run. This will be the final
event emitted by the reporter.
## Other Classes
### Test
```
class Test {
// An opaque ID for the test.
int id;
// The name of the test, including prefixes from any containing groups.
String name;
// The IDs of groups containing this test, in order from outermost to
// innermost.
List<int> groupIDs;
// The test's metadata, including metadata from any containing groups.
Metadata metadata;
}
```
A single test case. The test's ID is unique in the context of this test run.
It's used elsewhere in the protocol to refer to this test without including its
full representation.
Most tests will have at least one group ID, representing the implicit root
group. However, some may not; these should be treated as having no group
metadata.
### Group
```
class Group {
// An opaque ID for the group.
int id;
// The name of the group, including prefixes from any containing groups.
String? name;
// The ID of the group's parent group, unless it's the root group.
int? parentID;
// The group's metadata, including metadata from any containing groups.
Metadata metadata;
}
```
A group containing test cases. The group's ID is unique in the context of this
test run. It's used elsewhere in the protocol to refer to this test without
including its full representation.
The implicit group at the root of each test suite has `null` `name` and
`parentID` attributes.
### Metadata
```
class Metadata {
// Whether the test case will be skipped by the test runner.
bool skip;
// The reason the test case is skipped, if the user provided it.
String? skipReason;
}
```
The metadata attached to a test by a user.