/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

syntax = "proto3";

package luci.resultdb.v1;

import "google/protobuf/timestamp.proto";
import "google/api/field_behavior.proto";
import "google/protobuf/field_mask.proto";
import public "tools/tradefederation/core/proto/resultdb/artifact.proto";
import public "tools/tradefederation/core/proto/resultdb/common.proto";
import public "tools/tradefederation/core/proto/resultdb/instruction.proto";
import public "tools/tradefederation/core/proto/resultdb/invocation.proto";
import public "tools/tradefederation/core/proto/resultdb/predicate.proto";
import public "tools/tradefederation/core/proto/resultdb/root_invocation.proto";
import public "tools/tradefederation/core/proto/resultdb/test_aggregation.proto";
import public "tools/tradefederation/core/proto/resultdb/test_result.proto";
import public "tools/tradefederation/core/proto/resultdb/test_variant.proto";
import public "tools/tradefederation/core/proto/resultdb/test_verdict.proto";
import public "tools/tradefederation/core/proto/resultdb/test_metadata.proto";
import public "tools/tradefederation/core/proto/resultdb/work_unit.proto";

option go_package = "go.chromium.org/luci/resultdb/proto/v1;resultpb";
option java_package = "com.android.resultdb.proto";
option java_multiple_files = true;

// Service to read test results.
//
// Use of LUCI is subject to the Google [Terms of Service](https://policies.google.com/terms)
// and [Privacy Policy](https://policies.google.com/privacy).
service ResultDB {
  // == Root invocations and work units ========================================
  // Retrieves a root invocation.
  rpc GetRootInvocation(GetRootInvocationRequest) returns (RootInvocation) {};

  // Retrieves a work unit.
  rpc GetWorkUnit(GetWorkUnitRequest) returns (WorkUnit) {};

  // Retrieves a list of work units.
  rpc BatchGetWorkUnits(BatchGetWorkUnitsRequest) returns (BatchGetWorkUnitsResponse) {};

  // == Invocations ============================================================
  // Retrieves an invocation.
  rpc GetInvocation(GetInvocationRequest) returns (Invocation) {};

  // Retrieve names of all root invocations for a given invocation.
  rpc QueryRootInvocationNames(QueryRootInvocationNamesRequest) returns (QueryRootInvocationNamesResponse) {};

  // == Test results ===========================================================

  // Retrieves a test result.
  rpc GetTestResult(GetTestResultRequest) returns (TestResult) {};

  // Retrieves test results for a parent invocation.
  //
  // Note: response does not contain test results of included invocations.
  // Use QueryTestResults instead.
  rpc ListTestResults(ListTestResultsRequest)
      returns (ListTestResultsResponse) {};

  // Retrieves a test exoneration.
  rpc GetTestExoneration(GetTestExonerationRequest) returns (TestExoneration) {
  };

  // Retrieves test exonerations for a parent invocation.
  //
  // Note: response does not contain test results of included invocations.
  // Use QueryTestExonerations instead.
  rpc ListTestExonerations(ListTestExonerationsRequest)
      returns (ListTestExonerationsResponse) {};

  // Retrieves test results from an invocation, recursively.
  // Supports invocation inclusions.
  // Supports advanced filtering.
  // Examples: go/resultdb-rpc#querytestresults
  rpc QueryTestResults(QueryTestResultsRequest)
      returns (QueryTestResultsResponse) {};

  // Retrieves test exonerations from an invocation.
  // Supports invocation inclusions.
  // Supports advanced filtering.
  rpc QueryTestExonerations(QueryTestExonerationsRequest)
      returns (QueryTestExonerationsResponse) {};

  // Retrieves the test result statistics of an invocation.
  // Currently supports total number of test results belong to the invocation,
  // directly and indirectly.
  rpc QueryTestResultStatistics(QueryTestResultStatisticsRequest)
      returns (QueryTestResultStatisticsResponse) {};

  // Calculate new test variants by running the difference between the tests
  // run in the given invocation against the submitted test history for the
  // source.
  rpc QueryNewTestVariants(QueryNewTestVariantsRequest)
      returns (QueryNewTestVariantsResponse) {};

  // Retrieves test verdicts for a test run. A test run comprises only
  // the test results from a single invocation and not its included
  // invocations.
  //
  // Useful to incrementally ingest test results for an export root as its
  // individual constituent invocations finalize, in conjunction with
  // the invocations-ready-for-export pub/sub.
  //
  // Compared to the ListTestResults RPC, this RPC ensures all results
  // for a test variant are returned together, which is useful when
  // ingesting results into analyses that treat retried test results
  // in a given test run differently to the first retry.
  //
  // To use, the caller must have `resultdb.testResults.list` permission
  // on the queried invocation.
  rpc QueryRunTestVerdicts(QueryRunTestVerdictsRequest) returns (QueryRunTestVerdictsResponse) {};

  // Retrieves test verdicts from an invocation, recursively.
  // Supports invocation inclusions.
  // TODO(meiring): Update guidance usage to prefer QueryTestVerdicts
  // for use cases where staleness is acceptable once we stablise that RPC.
  rpc QueryTestVariants(QueryTestVariantsRequest) returns (QueryTestVariantsResponse) {};

  // Retrieves test variants from a single invocation, matching the specified
  // test IDs and hashes.
  rpc BatchGetTestVariants(BatchGetTestVariantsRequest) returns (BatchGetTestVariantsResponse) {};

  // Retrieves test metadata from a LUCI project, matching the predicate.
  rpc QueryTestMetadata(QueryTestMetadataRequest) returns (QueryTestMetadataResponse) {};

  // Retrieves an instruction for step or test result.
  // If the instruction contains placeholders, they will not be replaced.
  // The callers of this RPC are responsible to populate the placeholders with real data.
  rpc GetInstruction(GetInstructionRequest) returns (Instruction) {};

  // Retrieves the instruction and the dependency chain for all targets.
  // A maximum depth can be specified for the maximum number of dependency nodes to be returned.
  // If an error occurs while traversing a chain (e.g. circular dependency, permission, not found...),
  // the chain will stop and the rpc will return whatever it has found so far.
  rpc QueryInstruction(QueryInstructionRequest) returns (QueryInstructionResponse) {};

  // UNSTABLE (until launched): This RPC is in development and API may change without notice.
  //
  // Retrieves test aggregations for a single root invocation.
  //
  // Unlike QueryTestResults and QueryTestVariants which are guaranteed to read all test results
  // uploaded to Recorder service up to the time of query, this RPC only guarantees eventual
  // consistency (i.e. there may be a lag). Once an invocation is finalized, data is
  // guaranteed to be consistent.
  rpc QueryTestAggregations(QueryTestAggregationsRequest) returns (QueryTestAggregationsResponse) {};

  // UNSTABLE (until launched): This RPC is in development and API may change without notice.
  // Retrieves test verdicts for a single root invocation.
  //
  // Unlike QueryTestResults and QueryTestVariants which are guaranteed to read all test results
  // uploaded to Recorder service up to the time of query, this RPC only guarantees eventual
  // consistency (i.e. there may be a lag). Once an invocation is finalized, data is
  // guaranteed to be consistent.
  rpc QueryTestVerdicts(QueryTestVerdictsRequest) returns (QueryTestVerdictsResponse) {};

  // == Artifacts =============================================================

  // Retrieves an artifact.
  rpc GetArtifact(GetArtifactRequest) returns (Artifact) {};

  // Retrieves artifacts for a parent invocation/testResult.
  //
  // Note: if the parent is an invocation, the response does not contain
  // artifacts of included invocations. Use QueryArtifacts instead.
  rpc ListArtifacts(ListArtifactsRequest) returns (ListArtifactsResponse) {};

  // Retrieves artifacts from an invocation, recursively.
  // Can retrieve artifacts of test results included in the invocation
  // directly or indirectly.
  // Supports invocation inclusions.
  rpc QueryArtifacts(QueryArtifactsRequest) returns (QueryArtifactsResponse) {};

  // Lists the artifact contents as a list of log lines and
  // performs best effort extraction of log information
  // such as severity and timestamp for each line.
  //
  // Currently supports artifacts with content types: [text/plain,]
  rpc ListArtifactLines(ListArtifactLinesRequest) returns (ListArtifactLinesResponse) {};

  // Retrieves the line ranges in the given failure that do not usually appear
  // in logs from passes.
  //
  // Lines are normalized before comparison to remove numbers, dates, tmp file
  // paths, etc.
  //
  // Due to missed normalizations, sampling error or other reasons, this may
  // not eliminate all lines that appear in passes.
  rpc QueryArtifactFailureOnlyLines(QueryArtifactFailureOnlyLinesRequest) returns (QueryArtifactFailureOnlyLinesResponse) {};

  // == Log Search =============================================================

  // Queries result level artifacts that matches a search_string. Support regex or exact match.
  // Results are grouped by test_id, variant_hash, artifact_id.
  // Within each group, artifacts are sorted by recency and at most 3 are returned.
  // To obtain more matching artifacts of a particular group, uses QueryTestVariantArtifacts.
  rpc QueryTestVariantArtifactGroups(QueryTestVariantArtifactGroupsRequest) returns (
    QueryTestVariantArtifactGroupsResponse) {}

  // Queries test result artifacts that match a search_string for given test_id, variant_hash and artifact_id.
  rpc QueryTestVariantArtifacts(QueryTestVariantArtifactsRequest) returns (
    QueryTestVariantArtifactsResponse) {}

  // Queries invocation level artifacts that matches a search_string. Support regex or exact match.
  // Results are grouped by variant_union_hash, artifact_id.
  // Within each group, artifacts are sorted by recency and at most 3 are returned.
  // To obtain more matching artifacts of a particular group, uses QueryInvocationVariantArtifacts.
  rpc QueryInvocationVariantArtifactGroups(QueryInvocationVariantArtifactGroupsRequest) returns (
    QueryInvocationVariantArtifactGroupsResponse) {}

  // Queries invocation artifacts that match a search_string for given variant_union_hash and artifact_id.
  rpc QueryInvocationVariantArtifacts(QueryInvocationVariantArtifactsRequest) returns (
    QueryInvocationVariantArtifactsResponse) {}
}

// A request message for the GetRootInvocation RPC.
message GetRootInvocationRequest {
  // The name of the root invocation to read, see RootInvocation.name.
  string name = 1 [ (google.api.field_behavior) = REQUIRED ];
}

// A request message for the GetWorkUnit RPC.
message GetWorkUnitRequest {
  // The name of the work unit to read, see WorkUnit.name.
  string name = 1 [ (google.api.field_behavior) = REQUIRED ];

  // The view to apply to the returned work unit. Defaults to BASIC.
  WorkUnitView view = 2;
}

// A request message for the BatchGetWorkUnits RPC.
//
// As per google.aip.dev/231, this request will be handled atomically:
// either it will succeed in reading all nominated work units or it will fail.
message BatchGetWorkUnitsRequest {
  // The parent root invocation shared by all work units being retrieved.
  // Format: rootInvocations/{root_invocation_id}
  // The parent of all work units specified in `names` must match this
  // field.
  // Required.
  string parent = 1 [ (google.api.field_behavior) = REQUIRED ];

  // The names of the work units to retrieve.
  // A maximum of 500 work units can be retrieved in a batch.
  // Format: rootInvocations/{root_invocation_id}/workUnits/{work_unit_id}
  repeated string names = 2 [ (google.api.field_behavior) = REQUIRED ];

  // The view to apply to returned work units. Using the view BASIC is
  // strongly recommended as response size limits could be easily exceeded
  // if FULL is used (as each work unit's extended properties may be up to
  // ~2MB). Defaults to BASIC.
  WorkUnitView view = 3;
}

// WorkUnitView represents the set of work unit fields to be retrieved.
// See https://google.aip.dev/157.
enum WorkUnitView {
  // The default / unset value. The API will default to the basic view.
  WORK_UNIT_VIEW_UNSPECIFIED = 0;

  // Include basic information about the work unit. Excludes
  // extended_properties.
  WORK_UNIT_VIEW_BASIC = 1;

  // Include all fields, including extended_properties.
  WORK_UNIT_VIEW_FULL = 2;
}

// A response message for the BatchGetWorkUnits RPC.
message BatchGetWorkUnitsResponse {
  // The work units requested. These will appear in the same order as
  // names in the request, i.e. work_units[i] corresponds to names[i].
  repeated WorkUnit work_units = 1;
}

// A request message for GetInvocation RPC.
message GetInvocationRequest {
  // The name of the invocation to request, see Invocation.name.
  string name = 1 [ (google.api.field_behavior) = REQUIRED ];
}

// A request message for QueryRootInvocationNames RPC.
message QueryRootInvocationNamesRequest {
  // The name of the invocation to request, see Invocation.name.
  string name = 1 [ (google.api.field_behavior) = REQUIRED ];
}

// A response message for QueryRootInvocationNames RPC.
message QueryRootInvocationNamesResponse {
  // The name of all root invocations to the invocation in the request.
  // A root either
  //   - has is_export_root = true, or
  //   - has no parent invocation.
  //
  // If the invocation to request is already a root,
  // the root_invocation_names will only contains the invocation in the request
  // (i.e. a invocation can be its own root).
  //
  // root_invocation_names can be empty when no root is found (in the presence of cycles).
  repeated string root_invocation_names = 1;
}


// A request message for GetTestResult RPC.
message GetTestResultRequest {
  // The name of the test result to request, see TestResult.name.
  string name = 1 [ (google.api.field_behavior) = REQUIRED ];
}

// A request message for ListTestResults RPC.
message ListTestResultsRequest {
  // Name of the invocation, e.g. "invocations/{id}".
  string invocation = 1 [ (google.api.field_behavior) = REQUIRED ];

  // The maximum number of test results to return.
  //
  // The service may return fewer than this value.
  // If unspecified, at most 100 test results will be returned.
  // The maximum value is 1000; values above 1000 will be coerced to 1000.
  int32 page_size = 2;

  // A page token, received from a previous `ListTestResults` call.
  // Provide this to retrieve the subsequent page.
  //
  // When paginating, all other parameters provided to `ListTestResults` MUST
  // match the call that provided the page token.
  string page_token = 3;

  // Fields to include in the response.
  // If not set, the default mask is used where summary_html and tags are
  // excluded.
  // Test result names will always be included even if "name" is not a part of
  // the mask.
  google.protobuf.FieldMask read_mask = 4;
}

// A response message for ListTestResults RPC.
message ListTestResultsResponse {
  // The test results from the specified invocation.
  repeated TestResult test_results = 1;

  // A token, which can be sent as `page_token` to retrieve the next page.
  // If this field is omitted, there were no subsequent pages at the time of
  // request.
  // If the invocation is not finalized, more results may appear later.
  string next_page_token = 2;
}

// A request message for GetTestExoneration RPC.
message GetTestExonerationRequest {
  // The name of the test exoneration to request, see TestExoneration.name.
  string name = 1;
}

// A request message for ListTestExonerations RPC.
message ListTestExonerationsRequest {
  // Name of the invocation, e.g. "invocations/{id}".
  string invocation = 1 [ (google.api.field_behavior) = REQUIRED ];

  // The maximum number of test exonerations to return.
  //
  // The service may return fewer than this value.
  // If unspecified, at most 100 test exonerations will be returned.
  // The maximum value is 1000; values above 1000 will be coerced to 1000.
  int32 page_size = 2;

  // A page token, received from a previous `ListTestExonerations` call.
  // Provide this to retrieve the subsequent page.
  //
  // When paginating, all other parameters provided to `ListTestExonerations`
  // MUST match the call that provided the page token.
  string page_token = 3;
}

// A response message for ListTestExonerations RPC.
message ListTestExonerationsResponse {
  // The test exonerations from the specified invocation.
  repeated TestExoneration test_exonerations = 1;

  // A token, which can be sent as `page_token` to retrieve the next page.
  // If this field is omitted, there were no subsequent pages at the time of
  // request.
  // If the invocation is not finalized, more results may appear later.
  string next_page_token = 2;
}

// A request message for QueryTestResults RPC.
message QueryTestResultsRequest {
  // Retrieve test results included in these invocations, directly or indirectly
  // (via Invocation.included_invocations).
  //
  // Specifying multiple invocations is equivalent to querying one invocation
  // that includes these.
  repeated string invocations = 1;

  // A test result in the response must satisfy this predicate.
  TestResultPredicate predicate = 2;

  // The maximum number of test results to return.
  //
  // The service may return fewer than this value.
  // If unspecified, at most 100 test results will be returned.
  // The maximum value is 1000; values above 1000 will be coerced to 1000.
  int32 page_size = 4;

  // A page token, received from a previous `QueryTestResults` call.
  // Provide this to retrieve the subsequent page.
  //
  // When paginating, all other parameters provided to `QueryTestResults` MUST
  // match the call that provided the page token.
  string page_token = 5;

  // Fields to include in the response.
  // If not set, the default mask is used where summary_html, properties and tags are
  // excluded.
  // Test result names will always be included even if "name" is not a part of
  // the mask.
  google.protobuf.FieldMask read_mask = 6;
}

// A response message for QueryTestResults RPC.
message QueryTestResultsResponse {
  // Matched test results.
  // Ordered by parent invocation ID, test ID and result ID.
  repeated TestResult test_results = 1;

  // A token, which can be sent as `page_token` to retrieve the next page.
  // If this field is omitted, there were no subsequent pages at the time of
  // request.
  string next_page_token = 2;
}

// A request message for QueryTestExonerations RPC.
message QueryTestExonerationsRequest {
  // Retrieve test exonerations included in these invocations, directly or
  // indirectly (via Invocation.included_invocations).
  //
  // As of February 2025, a maximum of one invocation may be specified.
  repeated string invocations = 1;

  // A test exoneration in the response must satisfy this predicate.
  TestExonerationPredicate predicate = 2
      [ (google.api.field_behavior) = REQUIRED ];

  // The maximum number of test exonerations to return.
  //
  // The service may return fewer than this value.
  // If unspecified, at most 100 test exonerations will be returned.
  // The maximum value is 1000; values above 1000 will be coerced to 1000.
  int32 page_size = 4;

  // A page token, received from a previous `QueryTestExonerations` call.
  // Provide this to retrieve the subsequent page.
  //
  // When paginating, all other parameters provided to `QueryTestExonerations`
  // MUST match the call that provided the page token.
  string page_token = 5;
}

// A response message for QueryTestExonerations RPC.
message QueryTestExonerationsResponse {
  // The test exonerations matching the predicate.
  // Ordered by parent invocation ID, test ID and exoneration ID.
  repeated TestExoneration test_exonerations = 1;

  // A token, which can be sent as `page_token` to retrieve the next page.
  // If this field is omitted, there were no subsequent pages at the time of
  // request.
  string next_page_token = 2;
}

// A request message for QueryTestResultStatistics RPC.
message QueryTestResultStatisticsRequest {
  // Retrieve statistics of test result belong to these invocations,
  // directly or indirectly (via Invocation.included_invocations).
  //
  // Specifying multiple invocations is equivalent to requesting one invocation
  // that includes these.
  repeated string invocations = 1;
}

// A response message for QueryTestResultStatistics RPC.
message QueryTestResultStatisticsResponse {
  // Total number of test results.
  int64 total_test_results = 1;
}

// A request message for GetArtifact RPC.
message GetArtifactRequest {
  // The name of the artifact to request, see Artifact.name.
  string name = 1 [ (google.api.field_behavior) = REQUIRED ];
}

// A request message for ListArtifacts RPC.
message ListArtifactsRequest {
  // Name of the parent, e.g. an invocation (see Invocation.name) or
  // a test result (see TestResult.name).
  string parent = 1 [ (google.api.field_behavior) = REQUIRED ];

  // The maximum number of artifacts to return.
  //
  // The service may return fewer than this value.
  // If unspecified, at most 100 artifacts will be returned.
  // The maximum value is 1000; values above 1000 will be coerced to 1000.
  int32 page_size = 2;

  // A page token, received from a previous `ListArtifacts` call.
  // Provide this to retrieve the subsequent page.
  //
  // When paginating, all other parameters provided to `ListArtifacts` MUST
  // match the call that provided the page token.
  string page_token = 3;
}

// A response message for ListArtifacts RPC.
message ListArtifactsResponse {
  // The artifacts from the specified parent.
  repeated Artifact artifacts = 1;

  // A token, which can be sent as `page_token` to retrieve the next page.
  // If this field is omitted, there were no subsequent pages at the time of
  // request.
  // If the invocation is not finalized, more results may appear later.
  string next_page_token = 2;
}

// A request message for QueryArtifacts RPC.
message QueryArtifactsRequest {
  // Retrieve artifacts included in these invocations, directly or indirectly
  // (via Invocation.included_invocations and via contained test results).
  //
  // Specifying multiple invocations is equivalent to querying one invocation
  // that includes these.
  repeated string invocations = 1;

  // An artifact in the response must satisfy this predicate.
  ArtifactPredicate predicate = 2;

  // The maximum number of artifacts to return.
  //
  // The service may return fewer than this value.
  // If unspecified, at most 100 artifacts will be returned.
  // The maximum value is 1000; values above 1000 will be coerced to 1000.
  int32 page_size = 4;

  // A page token, received from a previous `QueryArtifacts` call.
  // Provide this to retrieve the subsequent page.
  //
  // When paginating, all other parameters provided to `QueryArtifacts` MUST
  // match the call that provided the page token.
  string page_token = 5;

  // Fields to include in the response.
  // If not set, the default mask is used where all fields are included.
  //
  // In particular, the fetch_url field can slow down the request or in some cases introduce
  // quota errors, so it is encouraged to not read the fetch_url field unless necessary.
  google.protobuf.FieldMask read_mask = 6;
}

// A response message for QueryArtifacts RPC.
message QueryArtifactsResponse {
  // Matched artifacts.
  // First invocation-level artifacts, then test-result-level artifacts
  // ordered by parent invocation ID, test ID and artifact ID.
  repeated Artifact artifacts = 1;

  // A token, which can be sent as `page_token` to retrieve the next page.
  // If this field is omitted, there were no subsequent pages at the time of
  // request.
  string next_page_token = 2;
}

// A request message for ListArtifactLines RPC.
message ListArtifactLinesRequest {
  // Retrieve log lines for this artifact.
  // Format:
  // - For invocation-level artifacts:
  //   "invocations/{INVOCATION_ID}/artifacts/{ARTIFACT_ID}".
  // - For test-result-level artifacts:
  //   "invocations/{INVOCATION_ID}/tests/{URL_ESCAPED_TEST_ID}/results/{RESULT_ID}/artifacts/{ARTIFACT_ID}".
  string parent = 1;

  // The maximum number of log lines to return.
  //
  // The service may return fewer than this value.
  // The response size will be truncated to 10MB
  // if the size of matching lines is larger than 10MB.
  // If unspecified or is equal to 0, a max of 10MB of lines will be returned.
  // Negative page sizes are not allowed.
  int32 page_size = 2;
}

// A response for ListArtifactLines RPC
message ListArtifactLinesResponse {

  // The log lines in the artifact.
  repeated ArtifactLine lines = 1;
}

// A request for the QueryArtifactFailureOnlyLines RPC.
message QueryArtifactFailureOnlyLinesRequest {
  // Retrieve log lines for this artifact.
  // Invocation level artifacts are not yet supported.
  // Format:
  // - For test-result-level artifacts:
  //   "invocations/{INVOCATION_ID}/tests/{URL_ESCAPED_TEST_ID}/results/{RESULT_ID}/artifacts/{ARTIFACT_ID}".
  string parent = 1;

  // If set to true, the content of the log lines will be returned in the
  // response.  If left missing or false, only the line range indexes will
  // be returned.
  bool include_content = 2;

  // The maximum number of line ranges to return.
  //
  // The service may return fewer than this value.
  // If unspecified, at most 1000 line ranges will be returned.
  // The maximum value is 10,000; values above 10,000 will be coerced to 10,000.
  int32 page_size = 4;

  // A page token, received from a previous `QueryArtifactFailureOnlyLines` call.
  // Provide this to retrieve the subsequent page.
  //
  // When paginating, all other parameters provided to `QueryArtifactFailureOnlyLines` MUST
  // match the call that provided the page token.
  string page_token = 5;
}

// A response for the QueryArtifactFailureOnlyLines RPC.
message QueryArtifactFailureOnlyLinesResponse {
  // A representation of a range of lines in an artifact, where lines are
  // represented by their index.  The first line is line 0.
  message LineRange {
    // The line index of the start of the line range.  The start is inclusive,
    // i.e. the start line is included in the range.
    int32 start = 1;
    // The line index of the end of the line range.  The end is exclusive, i.e.
    // the end line is not included in the range.
    int32 end = 2;
    // The content of the lines in the range.
    // Only included if include_content in the request is set to true.
    repeated string lines = 3;
  }

  // Line ranges [start, end) in the requested artifact that do not typically
  // appear versions of the artifact from passing test results.
  // Line ranges are returned in sorted ascending order.
  repeated LineRange failure_only_line_ranges = 1;

  // A token, which can be sent as `page_token` to retrieve the next page.
  // If this field is omitted, there were no subsequent pages at the time of
  // request.
  string next_page_token = 2;
}

// A request message for QueryTestVariants RPC.
// Next id: 10.
message QueryTestVariantsRequest {
  // Retrieve test verdicts included in these invocations, directly or indirectly
  // (via Invocation.included_invocations).
  //
  // As of April 2024, a maximum of one invocation may be specified.
  repeated string invocations = 2;

  // A test variant must satisfy this predicate.
  TestVariantPredicate predicate = 6;

  // The maximum number of test results to be included in a test variant.
  //
  // If a test variant has more results than the limit, the remaining results
  // will not be returned.
  // If unspecified, at most 10 results will be included in a test variant.
  // The maximum value is 100; values above 100 will be coerced to 100.
  int32 result_limit = 8;

  // The maximum number of test variants to return.
  //
  // The service may return fewer than this value.
  // If unspecified, at most 100 test variants will be returned.
  // The maximum value is 10,000; values above 10,000 will be coerced to 10,000.
  int32 page_size = 4;

  // A page token, received from a previous `QueryTestVariants` call.
  // Provide this to retrieve the subsequent page.
  //
  // When paginating, all other parameters provided to `QueryTestVariants` MUST
  // match the call that provided the page token.
  string page_token = 5;

  // Fields to include in the response.
  // If not set, the default mask is used where all fields are included.
  //
  // The following fields in results.*.result will NEVER be included even when
  // specified:
  // * test_id
  // * variant_hash
  // * variant
  // * test_metadata
  // Those values can be found in the parent test variant objects.
  //
  // The following fields will ALWAYS be included even when NOT specified:
  // * test_id
  // * variant_hash
  // * status
  google.protobuf.FieldMask read_mask = 7;

  // The ordering of test verdicts in the response.
  //
  // See https://google.aip.dev/132 for syntax.
  //
  // For performance reasons, only two sort orders are supported:
  // - verdict status v1 ascending (order is UNEXPECTED, UNEXPECTEDLY_SKIPPED,
  //   FLAKY, EXONERATED, EXPECTED).
  //   This is the default. It can be explicitly requested by "status" or "status asc".
  // - effective verdict status v2 ascending (by verdict status v2 including any status overrides,
  //   order is FAILED, EXECUTION_ERRORED, PRECLUDED, FLAKY, EXONERATED, SKIPPED & PASSED).
  //   It can be requested by "status_v2_effective" or "status_v2_effective asc"
  //   where status_v2_effective is a virtual field representing the above sort order.
  string order_by = 9;

  // An AIP-160 style filter to select test variants to return.
  //
  // Filtering supports a subset of [AIP-160 filtering](https://google.aip.dev/160).
  //
  // All values are case-sensitive.
  //
  // A bare value is searched for in the column test_id. E.g. ninja.
  //
  // You can use AND, OR and NOT (case sensitive) logical operators, along
  // with grouping. '-' is equivalent to NOT. Multiple bare values are
  // considered to be AND separated.  E.g. These are equivalent:
  // hello world
  // and:
  // hello AND world
  //
  // More examples:
  // a OR b
  // a AND NOT(b or -c)
  //
  // You can filter particular columns with '=', '!=' and ':' (has) operators.
  // The right hand side of the operator must be a simple value. E.g:
  // test_id:telemetry
  // -variant.os:mac
  //
  // Supported columns to search on:
  // - test_id
  // - variant_hash
  // - variant
  string filter = 10;
}

// A response message for QueryTestVariants RPC.
message QueryTestVariantsResponse {
  // Matched test variants.
  // Ordered by TestVariantStatus, test_id, then variant_hash
  repeated TestVariant test_variants = 1;

  // A token, which can be sent as `page_token` to retrieve the next page.
  // If this field is omitted, there were no subsequent pages at the time of
  // request.
  string next_page_token = 2;

  // The code sources tested by the returned test variants. The sources are keyed
  // by an ID which allows them to be cross-referenced from TestVariant.sources_id.
  //
  // The sources are returned via this map instead of directly on the TestVariant
  // to avoid excessive response size. Each source message could be up to a few
  // kilobytes and there are usually no more than a handful of different sources
  // tested in an invocation, so deduplicating them here reduces response size.
  map<string, Sources> sources = 3;
}

// Request message for QueryRunTestVerdicts RPC.
message QueryRunTestVerdictsRequest {
  // Retrieve test verdicts for the test run represented by this invocation.
  // Format: invocations/{INVOCATION_ID}.
  string invocation = 1;

  // The maximum number of test results to be included in a test variant.
  //
  // If a test variant has more results than the limit, the remaining results
  // will not be returned.
  // If unspecified, at most 10 results will be included in a test variant.
  // The maximum value is 100; values above 100 will be coerced to 100.
  int32 result_limit = 2;

  // The maximum number of test variants to return.
  //
  // The service may return fewer than this value.
  // If unspecified, at most 1000 test variants will be returned.
  // The maximum value is 10,000; values above 10,000 will be coerced to 10,000.
  // Page sizes smaller than the maximum may be returned at the server's
  // discretion, for example, due to keep the response size tractable for
  // the server.
  int32 page_size = 3;

  // A page token, received from a previous `QueryRunTestVerdicts` call.
  // Provide this to retrieve the subsequent page.
  //
  // When paginating, all other parameters provided to
  // `QueryRunTestVerdicts` MUST match the call that provided the page
  // token.
  string page_token = 4;
}

// A response message for QueryRunTestVerdicts RPC.
message QueryRunTestVerdictsResponse {
  // Test verdicts for the run.
  //
  // Ordered by test_id, then variant_hash.
  repeated RunTestVerdict run_test_verdicts = 1;

  // A token, which can be sent as `page_token` to retrieve the next page.
  // If this field is omitted, there were no subsequent pages at the time of
  // request.
  string next_page_token = 2;
}

// A request message for BatchGetTestVariants RPC.
message BatchGetTestVariantsRequest {
  message TestVariantIdentifier {
    // Option 1: Structured test identifier.

    // The structured test identifier. See the comment on
    // TestResult.test_id_structured for full documentation.
    //
    // Either this should be specified, or both test_id and variant_hash below.
    TestIdentifier test_id_structured = 3;

    // Option 2: Flat test identifier and variant.

    // The unique identifier of the test in a LUCI project. See the comment on
    // TestResult.test_id for full documentation.
    string test_id = 1 [ (google.api.field_behavior) = REQUIRED ];

    // Hash of the variant. See the comment on TestResult.variant_hash for full
    // documentation.
    string variant_hash = 2 [ (google.api.field_behavior) = REQUIRED ];
  }

  // Name of the invocation that the test variants are in.
  string invocation = 1;

  // A list of test variants to query. Size is limited to 500.
  // Any request for more than 500 variants will return an error.
  repeated TestVariantIdentifier test_variants = 2;

  // The maximum number of test results to be included in a test variant.
  //
  // If a test variant has more results than the limit, the remaining results
  // will not be returned.
  // If unspecified, at most 10 results will be included in a test variant.
  // The maximum value is 100; values above 100 will be coerced to 100.
  int32 result_limit = 3;
}

// A response message for BatchGetTestVariants RPC.
message BatchGetTestVariantsResponse {
  // Test variants matching the requests. Any variants that weren't found are
  // omitted from the response. Clients shouldn't rely on the ordering of this
  // field, as no particular order is guaranteed.
  repeated TestVariant test_variants = 1;

  // The code sources tested by the returned test variants. The sources are keyed
  // by an ID which allows them to be cross-referenced from TestVariant.sources_id.
  //
  // The sources are returned via this map instead of directly on the TestVariant
  // to avoid excessive response size. Each source message could be up to a few
  // kilobytes and there are usually no more than a handful of different sources
  // tested in an invocation, so deduplicating them here reduces response size.
  map<string, Sources> sources = 2;
}

// A request message for QueryTestMetadata RPC.
message QueryTestMetadataRequest {
  // The LUCI Project to query.
  string project = 1 [(google.api.field_behavior) = REQUIRED];

  // Filters to apply to the returned test metadata.
  TestMetadataPredicate predicate = 2;

  // The maximum number of test metadata entries to return.
  //
  // The service may return fewer than this value.
  // If unspecified, at most 1000 test metadata entries will be returned.
  // The maximum value is 100K; values above 100K will be coerced to 100K.
  int32 page_size = 3;

  // A page token, received from a previous `QueryTestMetadata` call.
  // Provide this to retrieve the subsequent page.
  //
  // When paginating, all other parameters provided to `QueryTestMetadata` MUST
  // match the call that provided the page token.
  string page_token = 4;
}

// A response message for QueryTestMetadata RPC.
message QueryTestMetadataResponse {
  // The matched testMetadata.
  repeated TestMetadataDetail testMetadata = 1;

  // A token, which can be sent as `page_token` to retrieve the next page.
  // If this field is omitted, there were no subsequent pages at the time of
  // request.
  string next_page_token = 2;
}

// A request message for QueryNewTestVariants RPC.
// To use this RPC, callers need:
// - resultdb.baselines.get in the realm the <baseline_project>:@project, where
//   baseline_project is the LUCI project that contains the baseline.
// - resultdb.testResults.list in the realm of the invocation which is being
//   queried.
message QueryNewTestVariantsRequest {
  // Name of the invocation, e.g. "invocations/{id}".
  string invocation = 1 [ (google.api.field_behavior) = REQUIRED ];

  // The baseline to compare test variants against, to determine if they are new.
  // e.g. “projects/{project}/baselines/{baseline_id}”.
  // For example, in the project "chromium", the baseline_id may be
  // "try:linux-rel".
  string baseline = 2 [ (google.api.field_behavior) = REQUIRED ];
}

// A response message for QueryNewTestVariants RPC.
message QueryNewTestVariantsResponse {

  // Represents a new test, which contains minimal information to uniquely identify a TestVariant.
  message NewTestVariant {
    // A unique identifier of the test in a LUCI project.
    // Regex: ^[[::print::]]{1,256}$
    //
    // Refer to TestResult.test_id for details.
    string test_id = 1;

    // Hash of the variant.
    // hex(sha256(sorted(''.join('%s:%s\n' for k, v in variant.items())))).
    string variant_hash = 2;
  }

  // Indicates whether the baseline has been populated with at least 72 hours
  // of data and the results can be relied upon.
  bool is_baseline_ready = 1;


  // Test variants that are new, meaning that they have not been part of
  // a submitted run prior.
  repeated NewTestVariant new_test_variants = 2;
}

message GetInstructionRequest {
  // Name of the instruction. The format is:
  // invocations/{invocation_id}/instructions/{instruction_id}
  string name = 1;
}

// Request for QueryInstructionDependencies RPC.
message QueryInstructionRequest {
  // Name of the instruction to query for. The format is:
  // invocations/{invocation_id}/instructions/{instruction_id}
  string name = 1;

  // The maximum depth to traverse the dependency chain. Default is 5.
  // The maximum value we support is 10, value bigger than 10 will be adjusted to 10.
  // Non-positive value will be adjusted to the default.
  int32 dependency_max_depth = 2;
}

// Response for QueryInstructionDependencies RPC.
message QueryInstructionResponse {
  // The instruction.
  Instruction instruction = 1;

  // Dependency chain, one for each target.
  repeated InstructionDependencyChain dependency_chains = 2;
}

// The dependency chain for one target.
message InstructionDependencyChain {
  // Target of the dependency chain.
  InstructionTarget target = 1;

  // Captures information about a dependency.
  message Node {
    // The instruction name that the dependency belongs to.
    // The format is invocations/{invocation_id}/instructions/{instruction_id}.
    // We need this for the UI to resolve the placeholders of the content.
    string instruction_name = 1;

    // Content of the dependency.
    // Placeholders (if existed) will be returned as-is.
    // The caller of this RPC is responsible for resolving the placeholders.
    string content = 2;

    // In case the traversal encounters an error, the error will be returned in this field.
    // If an error is returned, it will only be returned in the last dependency node,
    // after that, the chain will stop.
    string error = 3;

    // The descriptive name of the instruction that the dependency belongs to.
    // In the following cases, the descriptive name will not be set:
    // - If the user does not have the permission to access the instruction for the node, or
    // - If the instruction cannot be found.
    string descriptive_name = 4;
  }

  // List of dependencies.
  // The list will be sorted by the position in the dependency chain.
  // The direct dependency will be at position 0.
  // If the dependency traversing encounters an error, the last node will contain the error.
  repeated Node nodes = 2;
}

message QueryTestVariantArtifactGroupsRequest {
  // The LUCI project (required).
  string project = 1;

  // The search string to search in text artifact content (required).
  // Support regex or exact match.
  ArtifactContentMatcher search_string = 2;

  // The test id matcher to restrict the scope of the search (optional).
  IDMatcher test_id_matcher = 3;

  // The artifact id matcher to restrict the scope of the search (optional).
  IDMatcher artifact_id_matcher = 4;

  // The lower bound of the time range to search in UTC time (exclusive) (required).
  // start_time must not be before 20 July 2024 UTC.
  // start_time must be less than the end time.
  // The duration between start_time and end_time must not be greater than 7 days.
  google.protobuf.Timestamp start_time = 5;

  // The upper bound of the time range to search in UTC time (inclusive) (required).
  google.protobuf.Timestamp end_time = 6;

  // The maximum number of match groups to return. The service may return fewer than
  // this value.
  // If unspecified, at most 100 items will be returned.
  // The maximum value is 100; values above 100 will be coerced to 100.
  int32 page_size = 7;

  // A page token, received from a previous `QueryTestVariantArtifactGroups` call.
  // Provide this to retrieve the subsequent page.
  //
  // When paginating, all other parameters provided to `QueryTestVariantArtifactGroups` must
  // match the call that provided the page token.
  string page_token = 8;
}

message QueryTestVariantArtifactGroupsResponse {
  // Represents one group of matched artifacts.
  // test_id, variant_hash and artifact_id form the group key.
  message MatchGroup {
    // The LUCI test id.
    string test_id = 1;

    // The hash of the variant.
    string variant_hash = 2;

    // The definition of the variant.
    // Description of one specific way of running the test,
    // e.g. a specific bucket, builder and a test suite.
    Variant variant = 3;

    // ID of the artifact.
    string artifact_id = 4;

    // Artifacts that matches the search for this (test id, variant_hash, artifact id) combination.
    // Return at most 3 items, ordered by partition time DESC, artifact name ASC.
    repeated ArtifactMatchingContent artifacts = 5;

    // The total number of matching artifacts for this (test id, variant_hash, artifact id) combination.
    int32 matching_count = 6;
  }

  // Test variant artifacts are grouped by test_id, variant_hash and artifact_id.
  // Groups are ordered by partition_time of the most recent artifact DESC in the group, test id, variant hash, artifact id.
  repeated MatchGroup groups  = 1;

  // A token, which can be sent as `page_token` to retrieve the next page.
  // If this field is omitted, there are no subsequent pages.
  string next_page_token = 2;
}

message QueryTestVariantArtifactsRequest {
  // The LUCI project (required).
  string project =1;

  // The search string to search in text artifact content (required).
  // Support regex or exact match.
  ArtifactContentMatcher search_string = 2;

  // The test id. (required).
  string test_id = 3;

  // The variant hash (required).
  string variant_hash = 4;

  // The artifact id (required).
  string artifact_id = 5;

  // The lower bound of the time range to search in UTC time (exclusive) (required).
  // start_time must not be before 20 July 2024 UTC.
  // start_time must be less than the end time.
  // The duration between start_time and end_time must not be greater than 7 days.
  google.protobuf.Timestamp start_time = 6;

  // The upper bound of the time range to search in UTC time (inclusive) (required).
  google.protobuf.Timestamp end_time = 7;

  // The maximum number of items to return. The service may return fewer than
  // this value.
  // If unspecified, at most 100 items will be returned.
  // The maximum value is 100; values above 100 will be coerced to 100.
  int32 page_size = 8;

  // A page token, received from a previous `QueryTestVariantArtifacts` call.
  // Provide this to retrieve the subsequent page.
  //
  // When paginating, all other parameters provided to `QueryTestVariantArtifacts` must
  // match the call that provided the page token.
  string page_token = 9;
}

message QueryTestVariantArtifactsResponse {
  // Artifacts that matches the search.
  // Ordered by partition_time DESC, artifact name.
  repeated ArtifactMatchingContent artifacts = 1;

  // A token, which can be sent as `page_token` to retrieve the next page.
  // If this field is omitted, there are no subsequent pages.
  string next_page_token = 2;
}

message QueryInvocationVariantArtifactGroupsRequest {
  // The LUCI project (required).
  string project = 1;

  // The search string to search in text artifact content (required).
  // Support regex or exact match.
  ArtifactContentMatcher search_string = 2;

  // The artifact id matcher to restrict the scope of the search (optional).
  IDMatcher artifact_id_matcher = 3;

  // The lower bound of the time range to search in UTC time (exclusive) (required).
  // start_time must not be before 20 July 2024 UTC.
  // start_time must be less than the end time.
  // The duration between start_time and end_time must not be greater than 7 days.
  google.protobuf.Timestamp start_time = 4;

  // The upper bound of the time range to search in UTC time (inclusive) (required).
  google.protobuf.Timestamp end_time = 5;

  // The maximum number of match groups to return. The service may return fewer than
  // this value.
  // If unspecified, at most 100 items will be returned.
  // The maximum value is 100; values above 100 will be coerced to 100.
  int32 page_size = 6;

  // A page token, received from a previous `QueryInvocationVariantArtifactGroups` call.
  // Provide this to retrieve the subsequent page.
  //
  // When paginating, all other parameters provided to `QueryInvocationVariantArtifactGroups` must
  // match the call that provided the page token.
  string page_token = 7;
}

message QueryInvocationVariantArtifactGroupsResponse {
  // Represents one group of matched artifacts.
  // variant_union_hash and artifact_id form the group key.
  message MatchGroup {
    // Hash of the union of all variants of test results directly included by the invocation.
    string variant_union_hash = 1;

    // Union of all variants of test results directly included by the invocation.
    // Roughly defines a specific way to run an invocation.
    Variant variant_union = 2;

    // ID of the artifact.
    string artifact_id = 3;

    // Artifacts that matches the search for this (variant_union_hash, artifact id) combination.
    // Return at most 3 items, ordered by partition time DESC, artifact name ASC.
    repeated ArtifactMatchingContent artifacts = 4;

    // The total number of matching artifacts for this (variant_union_hash, artifact id) combination.
    int32 matching_count = 5;
  }

  // Invocation variant artifacts  are grouped by variant_union_hash and artifact_id.
  // Ordered by partition_time of the most recent artifact DESC in the group, variant_union_hash, artifact id.
  repeated MatchGroup groups  = 1;

  // A token, which can be sent as `page_token` to retrieve the next page.
  // If this field is omitted, there are no subsequent pages.
  string next_page_token = 2;
}

message QueryInvocationVariantArtifactsRequest {
  // The LUCI project (required).
  string project = 1;

  // The search string to search in text artifact content (required).
  // Support regex or exact match.
  ArtifactContentMatcher search_string = 2;

  // Hash of the union of all variants of test results directly included by the invocation (required).
  string variant_union_hash = 3;

  // The artifact id (required).
  string artifact_id = 4;

  // The lower bound of the time range to search in UTC time (exclusive) (required).
  // start_time must not be before 20 July 2024 UTC.
  // start_time must be less than the end time.
  // The duration between start_time and end_time must not be greater than 7 days.
  google.protobuf.Timestamp start_time = 5;

  // The upper bound of the time range to search in UTC time (inclusive) (required).
  google.protobuf.Timestamp end_time = 6;

  // The maximum number of items to return. The service may return fewer than
  // this value.
  // If unspecified, at most 100 items will be returned.
  // The maximum value is 100; values above 100 will be coerced to 100.
  int32 page_size = 7;

  // A page token, received from a previous `QueryInvocationVariantArtifacts` call.
  // Provide this to retrieve the subsequent page.
  //
  // When paginating, all other parameters provided to `QueryInvocationVariantArtifacts` must
  // match the call that provided the page token.
  string page_token = 8;
}

message QueryInvocationVariantArtifactsResponse {
  // Artifacts that matches the search.
  // Ordered by partition_time DESC, artifact name.
  repeated ArtifactMatchingContent artifacts = 1;

  // A token, which can be sent as `page_token` to retrieve the next page.
  // If this field is omitted, there are no subsequent pages.
  string next_page_token = 2;
}

// Represent a artifact that contains a match.
message ArtifactMatchingContent {
  // Name of the artifact.
  // - For invocation-level artifacts:
  //   "invocations/{INVOCATION_ID}/artifacts/{ARTIFACT_ID}".
  // - For test-result-level artifacts:
  //   "invocations/{INVOCATION_ID}/tests/{URL_ESCAPED_TEST_ID}/results/{RESULT_ID}/artifacts/{ARTIFACT_ID}".
  string name = 1;

  // Partition time of the artifact.
  // This is the creation time of the direct parent invocation.
  google.protobuf.Timestamp partition_time = 2;

  // The test result status, only populated if it is a result level artifact.
  // Deprecated: Use test_status_v2 instead.
  TestStatus test_status = 3 [ deprecated = true ];

  // The test result status (v2), only populated if it is a result level artifact.
  TestResult.Status test_status_v2 = 6;

  // Part of the artifact content that contains the first occurrence of the match.
  // The snippet is at most 10 KiB.
  // Prioritize fiting first match into the 10KiB first, divided the remaining bytes equally
  // to fit content immediately before and after the first match, including at most one more line above and below.
  // If the first match is more than 10KiB, it will be truncated.
  // Ellipsis ("...") are added, if the snippet is truncated.
  string snippet = 4;

  // Represent the byte location of a match in snippet.
  message Match {
    // Start byte index of the match, inclusive.
    int32 start_index = 1;
    // End byte index of the match, exclusive.
    int32 end_index = 2;
  }

  // All non-overlapping matches exists in the snippet from front to end in order (i.e. matches[i].end_index <= matches[i+1].start_index).
  repeated Match matches = 5;
}

// Used to match a artifact content.
message ArtifactContentMatcher {
  oneof matcher {
    // The string is a regex expression. Use regex match to find matching artifact content.
    string regex_contain = 1;

    // Use case insensitive equality match with this string to find matching artifact content.
    string contain = 2;
  }
}

// Used to match IDs (eg. test id, artifact id).
message IDMatcher {
  oneof matcher {
    // The id should has a matching prefix.
    string has_prefix = 1;

    // The id should exactly equal to this string.
    string exact_equal = 2;
  }
}

// A request message for QueryTestAggregations RPC.
// Next ID: 6.
message QueryTestAggregationsRequest {
  // The root invocation to retrieve aggregates for.
  // Format: invocations/{INVOCATION_ID}.
  // N.B. Specifying an invocation that is not an export root will yield an
  // INVALID_ARGUMENT error.
  string invocation = 1 [ (google.api.field_behavior) = REQUIRED ];

  // The level of aggregate to return.
  // All values except CASE are supported. For CASE-level aggregations,
  // see QueryTestVerdicts instead.
  AggregationLevel aggregation_level = 2;

  // The test prefix for which to return aggregates.
  //
  // The test prefix must not be more precise than the requested aggregation_level.
  // For example, if the requested aggregation_level is COARSE, you may not
  // use a test_id_prefix with an aggregation_level of FINE.
  //
  // Example #1: To obtain coarse-level aggregates in a module:
  // - set the requested aggregation_level to COARSE.
  // - set the test_prefix_filter.aggregation_level to MODULE, and
  // - test_prefix_filter.id to the Test ID prefix of that module.
  //
  // Example #2: To obtain the invocation-level aggregate:
  // - set the requested aggregation_level to INVOCATION.
  // - leave the test_prefix_filter unset, or set it to
  //   test_id_prefix.aggregation_level to INVOCATION.
  TestIdentifierPrefix test_prefix_filter = 3;

  // The maximum number of items to return. The service may return fewer than
  // this value.
  // If unspecified, at most 1000 items will be returned.
  // The maximum value is 10000; values above 10000 will be coerced to 10000.
  int32 page_size = 4;

  // A page token, received from a previous `QueryTestAggregations` call.
  // Provide this to retrieve the subsequent page.
  //
  // When paginating, all other parameters provided to `QueryTestAggregationsRequest` must
  // match the call that provided the page token.
  string page_token = 5;
}

// A response message for QueryTestAggregations RPC.
// Next ID: 3.
message QueryTestAggregationsResponse {
  // The test aggregations.
  // Results are sorted by structured test identifier, lexicographically over the tuple
  // (module_name, module_variant_hash, module_scheme, coarse_name, fine_name, case_name).
  //
  // Note: When querying invocations that are not yet finalized, aggregations
  // may be incomplete.
  repeated TestAggregation aggregations = 1;

  // A token, which can be sent as `page_token` to retrieve the next page.
  // If this field is omitted, there are no subsequent pages.
  string next_page_token = 2;
}

// A request message for QueryTestVerdicts RPC.
// Next ID: 5.
message QueryTestVerdictsRequest {
  // The root invocation to retrieve test verdicts for.
  // Format: invocations/{INVOCATION_ID}.
  // N.B. Specifying an invocation that is not an export root will yield an
  // INVALID_ARGUMENT error.
  string invocation = 1;

  // The test prefix for which to return aggregates.
  //
  // The test prefix may have:
  // - No fields set, to return all aggregates in the invocation.
  // - Only module set, to return all aggregates within a module.
  // - Module and coarse name set, to return aggregates with a coarse-level aggregation.
  // - Module, coarse name and fine name set, to return aggregates within a fine-level aggregation.
  //
  // The test prefix should not be more precise than the requested aggregation_level.
  // For example, if asking for a coarse-level aggregation, do not include a fine name in the
  // test prefix filter.
  TestIdentifierPrefix test_prefix_filter = 2;

  // The maximum number of items to return. The service may return fewer than
  // this value.
  // If unspecified, at most 1000 items will be returned.
  // The maximum value is 10000; values above 10000 will be coerced to 10000.
  int32 page_size = 3;

  // A page token, received from a previous `QueryTestVerdicts` call.
  // Provide this to retrieve the subsequent page.
  //
  // When paginating, all other parameters provided to `QueryTestVerdictsRequest` must
  // match the call that provided the page token.
  string page_token = 4;
}

// A response message for QueryTestVerdicts RPC.
// Next ID: 3.
message QueryTestVerdictsResponse {
  // The test verdicts.
  // Results are sorted by structured test identifier, lexicographically over the tuple
  // (module_name, module_scheme, module_variant_hash, coarse_name, fine_name, case_name).
  repeated TestVerdict test_verdicts = 1;

  // A token, which can be sent as `page_token` to retrieve the next page.
  // If this field is omitted, there are no subsequent pages.
  string next_page_token = 2;
}
