|
| 1 | +# JSON schema |
| 2 | + |
| 3 | +<!-- |
| 4 | +This source file is part of the Swift.org open source project |
| 5 | +
|
| 6 | +Copyright (c) 2024 Apple Inc. and the Swift project authors |
| 7 | +Licensed under Apache License v2.0 with Runtime Library Exception |
| 8 | +
|
| 9 | +See https://swift.org/LICENSE.txt for license information |
| 10 | +See https://swift.org/CONTRIBUTORS.txt for Swift project authors |
| 11 | +--> |
| 12 | + |
| 13 | +This document outlines the JSON schemas used by the testing library for its ABI |
| 14 | +entry point and for the `--experimental-event-stream-output` command-line |
| 15 | +argument. For more information about the ABI entry point, see the documentation |
| 16 | +for [ABI.EntryPoint_v0](https://github.com/search?q=repo%3Aapple%2Fswift-testing%EntryPoint_v0&type=code). |
| 17 | + |
| 18 | +> [!WARNING] |
| 19 | +> This JSON schema is still being developed and is subject to any and all |
| 20 | +> changes including removal from the package. |
| 21 | +
|
| 22 | +## Modified Backus-Naur form |
| 23 | + |
| 24 | +This schema is expressed using a modified Backus-Naur syntax. `{`, `}`, `:`, and |
| 25 | +`,` represent their corresponding JSON tokens. `\n` represents an ASCII newline |
| 26 | +character. |
| 27 | + |
| 28 | +The order of keys in JSON objects is not normative. Whitespace in this schema is |
| 29 | +not normative; it is present to help the reader understand the content of the |
| 30 | +various JSON objects in the schema. The event stream is output using the JSON |
| 31 | +Lines format and does not include newline characters (except **one** at the end |
| 32 | +of the `<output-record-line>` rule.) |
| 33 | + |
| 34 | +Trailing commas in JSON objects and arrays are only to be included where |
| 35 | +syntactically valid. |
| 36 | + |
| 37 | +### Common data types |
| 38 | + |
| 39 | +`<string>` and `<number>` are defined as in JSON. `<array:T>` represents an |
| 40 | +array (also defined as in JSON) whose elements all follow rule `<T>`. |
| 41 | + |
| 42 | +``` |
| 43 | +<bool> ::= true | false ; as in JSON |
| 44 | +
|
| 45 | +<source-location> ::= { |
| 46 | + "fileID": <string>, ; the Swift file ID of the file |
| 47 | + "line": <number>, |
| 48 | + "column": <number>, |
| 49 | +} |
| 50 | +
|
| 51 | +<instant> ::= { |
| 52 | + "absolute": <number>, ; floating-point seconds since system-defined epoch |
| 53 | + "since1970": <number>, ; floating-point seconds since 1970-01-01 00:00:00 UT |
| 54 | +} |
| 55 | +
|
| 56 | +<version> ::= "version": 0 ; will be incremented as the format changes |
| 57 | +``` |
| 58 | + |
| 59 | +<!-- |
| 60 | +TODO: implement input/configuration |
| 61 | +
|
| 62 | +### Configuration |
| 63 | +
|
| 64 | +A single configuration is passed into the testing library prior to running any |
| 65 | +tests and, as the name suggests, configures the test run. The configuration is |
| 66 | +encoded as a single [JSON Lines](https://jsonlines.org) value. |
| 67 | +
|
| 68 | +``` |
| 69 | +<configuration-record> ::= { |
| 70 | + <version>, |
| 71 | + "kind": "configuration", |
| 72 | + "payload": <configuration> |
| 73 | +} |
| 74 | +
|
| 75 | +<configuration> ::= { |
| 76 | + ["verbosity": <number>,] ; 0 is the default; higher means more verbose output |
| 77 | + ; while negative values mean quieter output. |
| 78 | + ["filters": <array:test-filter>,] ; how to filter the tests in the test run |
| 79 | + ["parallel": <bool>,] ; whether to enable parallel testing (on by default) |
| 80 | + ; more TBD |
| 81 | +} |
| 82 | +
|
| 83 | +<test-filter> ::= <test-filter-tag> | <test-filter-id> |
| 84 | +
|
| 85 | +<test-filter-action> ::= "include" | "exclude" |
| 86 | +
|
| 87 | +<test-filter-tag> ::= { |
| 88 | + "action": <test-filter-action>, |
| 89 | + "tags": <array:string>, ; the names of tags to include |
| 90 | + "operator": <test-filter-tag-operator> ; how to combine the values in "tags" |
| 91 | +} |
| 92 | +
|
| 93 | +<test-filter-tag-operator> ::= "any" | "all" |
| 94 | +
|
| 95 | +<test-filter-id> ::= { |
| 96 | + "action": <test-filter-action>, |
| 97 | + "id": <test-id> ; the ID of the test to filter in/out |
| 98 | +} |
| 99 | +``` |
| 100 | +--> |
| 101 | + |
| 102 | +### Streams |
| 103 | + |
| 104 | +A stream consists of a sequence of values encoded as [JSON Lines](https://jsonlines.org). |
| 105 | +A single instance of `<output-stream>` is defined per test process and can be |
| 106 | +accessed by passing `--experimental-event-stream-output` to the test executable |
| 107 | +created by `swift build --build-tests`. |
| 108 | + |
| 109 | +``` |
| 110 | +<output-stream> ::= <output-record>\n | <output-record>\n <output-stream> |
| 111 | +``` |
| 112 | + |
| 113 | +### Records |
| 114 | + |
| 115 | +Records represent the values produced on a stream. Each record is encoded on a |
| 116 | +single line and can be decoded independently of other lines. If a decoder |
| 117 | +encounters a record whose `"kind"` field is unrecognized, the decoder should |
| 118 | +ignore that line. |
| 119 | + |
| 120 | +``` |
| 121 | +<output-record> ::= <test-record> | <event-record> |
| 122 | +
|
| 123 | +<test-record> ::= { |
| 124 | + <version>, |
| 125 | + "kind": "test", |
| 126 | + "payload": <test> |
| 127 | +} |
| 128 | +
|
| 129 | +<event-record> ::= { |
| 130 | + <version>, |
| 131 | + "kind": "event", |
| 132 | + "payload": <event> |
| 133 | +} |
| 134 | +``` |
| 135 | + |
| 136 | +### Tests |
| 137 | + |
| 138 | +Test records represent individual test functions and test suites. Test records |
| 139 | +are passed through the record stream **before** most events. |
| 140 | + |
| 141 | +<!-- |
| 142 | +If a test record represents a parameterized test function whose inputs are |
| 143 | +enumerable and can be independently replayed, the test record will include an |
| 144 | +additional `"testCases"` field describing the individual test cases. |
| 145 | +--> |
| 146 | + |
| 147 | +``` |
| 148 | +<test> ::= <test-suite> | <test-function> |
| 149 | +
|
| 150 | +<test-suite> ::= { |
| 151 | + "kind": "suite", |
| 152 | + "name": <string>, ; the unformatted, unqualified type name |
| 153 | + ["displayName": <string>,] ; the user-supplied custom display name |
| 154 | + "sourceLocation": <source-location>, ; where the test suite is defined |
| 155 | + "id": <test-id>, |
| 156 | +} |
| 157 | +
|
| 158 | +<test-function> ::= { |
| 159 | + "kind": "function", |
| 160 | + "name": <string>, ; the unformatted function name |
| 161 | + ["displayName": <string>,] ; the user-supplied custom display name |
| 162 | + "sourceLocation": <source-location>, ; where the test is defined |
| 163 | + "id": <test-id>, |
| 164 | + "isParameterized": <bool> ; is this a parameterized test function or not? |
| 165 | +} |
| 166 | +
|
| 167 | +<test-id> ::= <string> ; an opaque string representing the test case |
| 168 | +``` |
| 169 | + |
| 170 | +<!-- |
| 171 | + TODO: define a round-trippable format for a test case ID |
| 172 | + ["testCases": <array:test-case>] ; if "isParameterized": true and the inputs |
| 173 | + ; are enumerable, all test case IDs, |
| 174 | + ; otherwise not present |
| 175 | +
|
| 176 | +<test-case> ::= { |
| 177 | + "id": <string>, ; an opaque string representing the test case |
| 178 | + "displayName": <string> ; a string representing the corresponding Swift value |
| 179 | +} |
| 180 | +``` |
| 181 | +--> |
| 182 | + |
| 183 | +### Events |
| 184 | + |
| 185 | +Event records represent things that can happen during testing. They include |
| 186 | +information about the event such as when it occurred and where in the test |
| 187 | +source it occurred. They also include a `"messages"` field that contains |
| 188 | +sufficient information to display the event in a human-readable format. |
| 189 | + |
| 190 | +``` |
| 191 | +<event> ::= { |
| 192 | + "kind": <event-kind>, |
| 193 | + "instant": <instant>, ; when the event occurred |
| 194 | + ["issue": <issue>,] ; the recorded issue (if "kind" is "issueRecorded") |
| 195 | + "messages": <array:message>, |
| 196 | + ["testID": <test-id>,] |
| 197 | +} |
| 198 | +
|
| 199 | +<event-kind> ::= "runStarted" | "testStarted" | "testCaseStarted" | |
| 200 | + "issueRecorded" | "testCaseEnded" | "testEnded" | "testSkipped" | |
| 201 | + "runEnded" ; additional event kinds may be added in the future |
| 202 | +
|
| 203 | +<issue> ::= { |
| 204 | + "isKnown": <bool>, ; is this a known issue or not? |
| 205 | + ["sourceLocation": <source-location>,] ; where the issue occurred, if known |
| 206 | +} |
| 207 | +
|
| 208 | +<message> ::= { |
| 209 | + "symbol": <message-symbol>, |
| 210 | + "text": <string>, ; the human-readable text of this message |
| 211 | +} |
| 212 | +
|
| 213 | +<message-symbol> ::= "default" | "skip" | "pass" | "passWithKnownIssue" | "fail" |
| 214 | + "difference" | "warning" | "details" |
| 215 | +``` |
| 216 | + |
| 217 | +<!-- |
| 218 | + ["testID": <test-id>, |
| 219 | + ["testCase": <test-case>]] |
| 220 | +--> |
0 commit comments