1/*
2 * Copyright (C) 2024 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17syntax = "proto3";
18
19package luci.resultdb.v1;
20
21import "google/api/field_behavior.proto";
22import "google/protobuf/duration.proto";
23import "google/protobuf/struct.proto";
24import "google/protobuf/timestamp.proto";
25import public "tools/tradefederation/core/proto/resultdb/common.proto";
26import public "tools/tradefederation/core/proto/resultdb/test_metadata.proto";
27import public "tools/tradefederation/core/proto/resultdb/failure_reason.proto";
28
29option go_package = "go.chromium.org/luci/resultdb/proto/v1;resultpb";
30option java_package = "com.android.resultdb.proto";
31option java_multiple_files = true;
32
33// A result of a functional test case.
34// Often a single test case is executed multiple times and has multiple results,
35// a single test suite has multiple test cases,
36// and the same test suite can be executed in different variants
37// (OS, GPU, compile flags, etc).
38//
39// This message does not specify the test id.
40// It should be available in the message that embeds this message.
41//
42// Next id: 17.
43message TestResult {
44  reserved 11;  // test_location
45
46  // Can be used to refer to this test result, e.g. in ResultDB.GetTestResult
47  // RPC.
48  // Format:
49  // "invocations/{INVOCATION_ID}/tests/{URL_ESCAPED_TEST_ID}/results/{RESULT_ID}".
50  // where URL_ESCAPED_TEST_ID is test_id escaped with
51  // https://golang.org/pkg/net/url/#PathEscape See also https://aip.dev/122.
52  //
53  // Output only.
54  string name = 1 [
55    (google.api.field_behavior) = OUTPUT_ONLY,
56    (google.api.field_behavior) = IMMUTABLE
57  ];
58
59  // Test id, a unique identifier of the test in a LUCI project.
60  // Regex: ^[[::print::]]{1,512}$
61  //
62  // If two tests have a common test id prefix that ends with a
63  // non-alphanumeric character, they considered a part of a group. Examples:
64  // - "a/b/c"
65  // - "a/b/d"
66  // - "a/b/e:x"
67  // - "a/b/e:y"
68  // - "a/f"
69  // This defines the following groups:
70  // - All items belong to one group because of the common prefix "a/"
71  // - Within that group, the first 4 form a sub-group because of the common
72  //   prefix "a/b/"
73  // - Within that group, "a/b/e:x" and "a/b/e:y" form a sub-group because of
74  //   the common prefix "a/b/e:".
75  // This can be used in UI.
76  // LUCI does not interpret test ids in any other way.
77  string test_id = 2 [(google.api.field_behavior) = IMMUTABLE];
78
79  // Identifies a test result in a given invocation and test id.
80  // Regex: ^[a-z0-9\-_.]{1,32}$
81  string result_id = 3 [
82    (google.api.field_behavior) = IMMUTABLE,
83    (google.api.field_behavior) = REQUIRED
84  ];
85
86  // Description of one specific way of running the test,
87  // e.g. a specific bucket, builder and a test suite.
88  Variant variant = 4 [(google.api.field_behavior) = IMMUTABLE];
89
90  // Whether the result of test case execution is expected.
91  // In a typical Chromium CL, 99%+ of test results are expected.
92  // Users are typically interested only in the unexpected results.
93  //
94  // An unexpected result != test case failure. There are test cases that are
95  // expected to fail/skip/crash. The test harness compares the actual status
96  // with the expected one(s) and this field is the result of the comparison.
97  bool expected = 5 [(google.api.field_behavior) = IMMUTABLE];
98
99  // Machine-readable status of the test case.
100  // MUST NOT be STATUS_UNSPECIFIED.
101  TestStatus status = 6 [(google.api.field_behavior) = IMMUTABLE];
102
103  // Human-readable explanation of the result, in HTML.
104  // MUST be sanitized before rendering in the browser.
105  //
106  // The size of the summary must be equal to or smaller than 4096 bytes in
107  // UTF-8.
108  //
109  // Supports artifact embedding using custom tags:
110  // * <text-artifact> renders contents of an artifact as text.
111  //   Usage:
112  //   * To embed result level artifact: <text-artifact
113  //   artifact-id="<artifact_id>">
114  //   * To embed invocation level artifact: <text-artifact
115  //   artifact-id="<artifact_id>" inv-level>
116  string summary_html = 7 [(google.api.field_behavior) = IMMUTABLE];
117
118  // The point in time when the test case started to execute.
119  google.protobuf.Timestamp start_time = 8
120      [(google.api.field_behavior) = IMMUTABLE];
121
122  // Duration of the test case execution.
123  // MUST be equal to or greater than 0.
124  google.protobuf.Duration duration = 9
125      [(google.api.field_behavior) = IMMUTABLE];
126
127  // Metadata for this test result.
128  // It might describe this particular execution or the test case.
129  // A key can be repeated.
130  repeated StringPair tags = 10 [(google.api.field_behavior) = IMMUTABLE];
131
132  // Hash of the variant.
133  // hex(sha256(sorted(''.join('%s:%s\n' for k, v in variant.items())))).
134  //
135  // Output only.
136  string variant_hash = 12 [
137    (google.api.field_behavior) = OUTPUT_ONLY,
138    (google.api.field_behavior) = IMMUTABLE
139  ];
140
141  // Information about the test at the time of its execution.
142  TestMetadata test_metadata = 13;
143
144  // Information about the test failure. Only present if the test failed.
145  FailureReason failure_reason = 14;
146
147  // Arbitrary JSON object that contains structured, domain-specific properties
148  // of the test result.
149  //
150  // The serialized size must be <= 8 KB.
151  google.protobuf.Struct properties = 15;
152
153  // Whether the test result has been masked so that it includes only metadata.
154  // The metadata fields for a TestResult are:
155  // * name
156  // * test_id
157  // * result_id
158  // * expected
159  // * status
160  // * start_time
161  // * duration
162  // * variant_hash
163  // * failure_reason.primary_error_message (truncated to 140 characters)
164  // * skip_reason
165  //
166  // Output only.
167  bool is_masked = 16 [(google.api.field_behavior) = OUTPUT_ONLY];
168
169  // Reasoning behind a test skip, in machine-readable form.
170  // Used to assist downstream analyses, such as automatic bug-filing.
171  // MUST not be set unless status is SKIP.
172  SkipReason skip_reason = 18;
173}
174
175// Machine-readable status of a test result.
176enum TestStatus {
177  // Status was not specified.
178  // Not to be used in actual test results; serves as a default value for an
179  // unset field.
180  STATUS_UNSPECIFIED = 0;
181
182  // The test case has passed.
183  PASS = 1;
184
185  // The test case has failed.
186  // Suggests that the code under test is incorrect, but it is also possible
187  // that the test is incorrect or it is a flake.
188  FAIL = 2;
189
190  // The test case has crashed during execution.
191  // The outcome is inconclusive: the code under test might or might not be
192  // correct, but the test+code is incorrect.
193  CRASH = 3;
194
195  // The test case has started, but was aborted before finishing.
196  // A common reason: timeout.
197  ABORT = 4;
198
199  // The test case did not execute.
200  // Examples:
201  // - The execution of the collection of test cases, such as a test
202  //   binary, was aborted prematurely and execution of some test cases was
203  //   skipped.
204  // - The test harness configuration specified that the test case MUST be
205  //   skipped.
206  SKIP = 5;
207}
208
209// Machine-readable reason that a test execution was skipped.
210// Only reasons actually used are listed here, if you need a new reason
211// please add it here and send a CL to the OWNERS.
212enum SkipReason {
213  // Skip reason was not specified.
214  // This represents an unset field which should be used for non-skip test
215  // result statuses.  It can also be used if none of the other statuses
216  // apply.
217  SKIP_REASON_UNSPECIFIED = 0;
218
219  // Disabled automatically in response to a test skipping policy that skips
220  // flaky tests.
221  // Used for ChromeOS CQ test filtering.
222  AUTOMATICALLY_DISABLED_FOR_FLAKINESS = 1;
223}
224
225// Indicates the test subject (e.g. a CL) is absolved from blame
226// for an unexpected result of a test variant.
227// For example, the test variant fails both with and without CL, so it is not
228// CL's fault.
229message TestExoneration {
230  // Can be used to refer to this test exoneration, e.g. in
231  // ResultDB.GetTestExoneration RPC.
232  // Format:
233  // invocations/{INVOCATION_ID}/tests/{URL_ESCAPED_TEST_ID}/exonerations/{EXONERATION_ID}.
234  // URL_ESCAPED_TEST_ID is test_variant.test_id escaped with
235  // https://golang.org/pkg/net/url/#PathEscape See also https://aip.dev/122.
236  //
237  // Output only.
238  string name = 1 [
239    (google.api.field_behavior) = OUTPUT_ONLY,
240    (google.api.field_behavior) = IMMUTABLE
241  ];
242
243  // Test identifier, see TestResult.test_id.
244  string test_id = 2;
245
246  // Description of the variant of the test, see Variant type.
247  // Unlike TestResult.extra_variant_pairs, this one must be a full definition
248  // of the variant, i.e. it is not combined with Invocation.base_test_variant.
249  Variant variant = 3;
250
251  // Identifies an exoneration in a given invocation and test id.
252  // It is server-generated.
253  string exoneration_id = 4 [
254    (google.api.field_behavior) = OUTPUT_ONLY,
255    (google.api.field_behavior) = IMMUTABLE
256  ];
257
258  // Reasoning behind the exoneration, in HTML.
259  // MUST be sanitized before rendering in the browser.
260  string explanation_html = 5 [(google.api.field_behavior) = IMMUTABLE];
261
262  // Hash of the variant.
263  // hex(sha256(sorted(''.join('%s:%s\n' for k, v in variant.items())))).
264  string variant_hash = 6 [(google.api.field_behavior) = IMMUTABLE];
265
266  // Reasoning behind the exoneration, in machine-readable form.
267  // Used to assist downstream analyses, such as automatic bug-filing.
268  // This allow detection of e.g. critical tests failing in presubmit,
269  // even if they are being exonerated because they fail on other CLs.
270  ExonerationReason reason = 7 [(google.api.field_behavior) = IMMUTABLE];
271
272  // Whether the test exoneration has been masked so that it includes only
273  // metadata. The metadata fields for a TestExoneration are:
274  // * name
275  // * test_id
276  // * exoneration_id
277  // * variant_hash
278  // * explanation_html
279  // * reason
280  //
281  // Output only.
282  bool is_masked = 8 [(google.api.field_behavior) = OUTPUT_ONLY];
283}
284
285// Reason why a test variant was exonerated.
286enum ExonerationReason {
287  // Reason was not specified.
288  // Not to be used in actual test exonerations; serves as a default value for
289  // an unset field.
290  EXONERATION_REASON_UNSPECIFIED = 0;
291
292  // Similar unexpected results were observed on a mainline branch
293  // (i.e. against a build without unsubmitted changes applied).
294  // (For avoidance of doubt, this includes both flakily and
295  // deterministically occurring unexpected results.)
296  // Applies to unexpected results in presubmit/CQ runs only.
297  OCCURS_ON_MAINLINE = 1;
298
299  // Similar unexpected results were observed in presubmit run(s) for other,
300  // unrelated CL(s). (This is suggestive of the issue being present
301  // on mainline but is not confirmed as there are possible confounding
302  // factors, like how tests are run on CLs vs how tests are run on
303  // mainline branches.)
304  // Applies to unexpected results in presubmit/CQ runs only.
305  OCCURS_ON_OTHER_CLS = 2;
306
307  // The tests are not critical to the test subject (e.g. CL) passing.
308  // This could be because more data is being collected to determine if
309  // the tests are stable enough to be made critical (as is often the
310  // case for experimental test suites).
311  // If information exists indicating the tests are producing unexpected
312  // results, and the tests are not critical for that reason,
313  // prefer more specific reasons OCCURS_ON_MAINLINE or OCCURS_ON_OTHER_CLS.
314  NOT_CRITICAL = 3;
315
316  // The test result was an unexpected pass. (Note that such an exoneration is
317  // not automatically created for unexpected passes, unless the option is
318  // specified to ResultSink or the project manually creates one).
319  UNEXPECTED_PASS = 4;
320}
321