go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/lucicfg/starlark/stdlib/internal/luci/lib/resultdb.star (about) 1 # Copyright 2020 The LUCI Authors. 2 # 3 # Licensed under the Apache License, Version 2.0 (the "License"); 4 # you may not use this file except in compliance with the License. 5 # You may obtain a copy of the License at 6 # 7 # http://www.apache.org/licenses/LICENSE-2.0 8 # 9 # Unless required by applicable law or agreed to in writing, software 10 # distributed under the License is distributed on an "AS IS" BASIS, 11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 # See the License for the specific language governing permissions and 13 # limitations under the License. 14 15 """ResultDB integration settings.""" 16 17 load("@stdlib//internal/validate.star", "validate") 18 load( 19 "@stdlib//internal/luci/proto.star", 20 "buildbucket_pb", 21 "predicate_pb", 22 "resultdb_pb", 23 ) 24 25 def _settings(*, enable = False, bq_exports = None, history_options = None): 26 """Specifies how Buildbucket should integrate with ResultDB. 27 28 Args: 29 enable: boolean, whether to enable ResultDB:Buildbucket integration. 30 bq_exports: list of resultdb_pb.BigQueryExport() protos, configurations 31 for exporting specific subsets of test results to a designated BigQuery 32 table, use resultdb.export_test_results(...) to create these. 33 history_options: Configuration for indexing test results from this 34 builder's builds for history queries, use resultdb.history_options(...) 35 to create this value. 36 37 Returns: 38 A populated buildbucket_pb.BuilderConfig.ResultDB() proto. 39 """ 40 return buildbucket_pb.BuilderConfig.ResultDB( 41 enable = validate.bool("enable", enable, default = False, required = False), 42 bq_exports = bq_exports or [], 43 history_options = history_options, 44 ) 45 46 def _history_options(*, by_timestamp = False): 47 """Defines a history indexing configuration. 48 49 Args: 50 by_timestamp: bool, indicates whether the build's test results will be 51 indexed by their creation timestamp for the purposes of retrieving the 52 history of a given set of tests/variants. 53 54 Returns: 55 A populated resultdb_pb.HistoryOptions() proto. 56 """ 57 return resultdb_pb.HistoryOptions( 58 use_invocation_timestamp = by_timestamp, 59 ) 60 61 def _bq_export(bq_table = None): 62 if type(bq_table) == "tuple": 63 if len(bq_table) != 3: 64 fail("Expected tuple of length 3, got %s" % (bq_table,)) 65 project, dataset, table = bq_table 66 elif type(bq_table) == "string": 67 project, dataset, table = validate.string( 68 "bq_table", 69 bq_table, 70 regexp = r"^([^.]+)\.([^.]+)\.([^.]+)$", 71 ).split(".") 72 else: 73 fail("Unsupported bq_table type %s" % type(bq_table)) 74 75 return resultdb_pb.BigQueryExport( 76 project = project, 77 dataset = dataset, 78 table = table, 79 ) 80 81 def _export_test_results( 82 *, 83 bq_table = None, 84 predicate = None): 85 """Defines a mapping between a test results and a BigQuery table for them. 86 87 Args: 88 bq_table: Tuple of `(project, dataset, table)`; OR a string of the form 89 `<project>.<dataset>.<table>` where the parts represent the 90 BigQuery-enabled gcp project, dataset and table to export results. 91 predicate: A predicate_pb.TestResultPredicate() proto. If given, specifies 92 the subset of test results to export to the above table, instead of all. 93 Use resultdb.test_result_predicate(...) to generate this, if needed. 94 95 Returns: 96 A populated resultdb_pb.BigQueryExport() proto. 97 """ 98 ret = _bq_export(bq_table) 99 ret.test_results = resultdb_pb.BigQueryExport.TestResults( 100 predicate = validate.type( 101 "predicate", 102 predicate, 103 predicate_pb.TestResultPredicate(), 104 required = False, 105 ), 106 ) 107 return ret 108 109 def _test_result_predicate( 110 *, 111 test_id_regexp = None, 112 variant = None, 113 variant_contains = False, 114 unexpected_only = False): 115 """Represents a predicate of test results. 116 117 Args: 118 test_id_regexp: string, regular expression that a test result must fully 119 match to be considered covered by this definition. 120 variant: string dict, defines the test variant to match. 121 E.g. `{"test_suite": "not_site_per_process_webkit_layout_tests"}` 122 variant_contains: bool, if true the variant parameter above will cause a 123 match if it's a subset of the test's variant, otherwise it will only 124 match if it's exactly equal. 125 unexpected_only: bool, if true only export test results of test variants 126 that had unexpected results. 127 128 Returns: 129 A populated predicate_pb.TestResultPredicate() proto. 130 """ 131 ret = predicate_pb.TestResultPredicate( 132 test_id_regexp = validate.string( 133 "test_id_regexp", 134 test_id_regexp, 135 required = False, 136 ), 137 ) 138 139 unexpected_only = validate.bool( 140 "unexpected_only", 141 unexpected_only, 142 default = False, 143 required = False, 144 ) 145 if unexpected_only: 146 ret.expectancy = predicate_pb.TestResultPredicate.VARIANTS_WITH_UNEXPECTED_RESULTS 147 else: 148 ret.expectancy = predicate_pb.TestResultPredicate.ALL 149 150 variant_contains = validate.bool( 151 "variant_contains", 152 variant_contains, 153 default = False, 154 required = False, 155 ) 156 variant = validate.str_dict("variant", variant, required = False) 157 if variant: 158 if variant_contains: 159 ret.variant.contains = {"def": variant} 160 else: 161 ret.variant.equals = {"def": variant} 162 163 return ret 164 165 def _export_text_artifacts( 166 *, 167 bq_table = None, 168 predicate = None): 169 """Defines a mapping between text artifacts and a BigQuery table for them. 170 171 Args: 172 bq_table: string of the form `<project>.<dataset>.<table>` 173 where the parts respresent the BigQuery-enabled gcp project, dataset and 174 table to export results. 175 predicate: A predicate_pb.ArtifactPredicate() proto. If given, specifies 176 the subset of text artifacts to export to the above table, instead of all. 177 Use resultdb.artifact_predicate(...) to generate this, if needed. 178 179 Returns: 180 A populated resultdb_pb.BigQueryExport() proto. 181 """ 182 ret = _bq_export(bq_table) 183 ret.text_artifacts = resultdb_pb.BigQueryExport.TextArtifacts( 184 predicate = validate.type( 185 "predicate", 186 predicate, 187 predicate_pb.ArtifactPredicate(), 188 required = False, 189 ), 190 ) 191 return ret 192 193 def _artifact_predicate( 194 *, 195 test_result_predicate = None, 196 included_invocations = None, 197 test_results = None, 198 content_type_regexp = None, 199 artifact_id_regexp = None): 200 """Represents a predicate of text artifacts. 201 202 Args: 203 test_result_predicate: predicate_pb.TestResultPredicate(), a predicate of 204 test results. 205 included_invocations: bool, if true, invocation level artifacts are 206 included. 207 test_results: bool, if true, test result level artifacts are included. 208 content_type_regexp: string, an artifact must have a content type matching 209 this regular expression entirely, i.e. the expression is implicitly 210 wrapped with ^ and $. 211 artifact_id_regexp: string, an artifact must have an ID matching this 212 regular expression entirely, i.e. the expression is implicitly wrapped 213 with ^ and $. 214 215 Returns: 216 A populated predicate_pb.ArtifactPredicate() proto. 217 """ 218 ret = predicate_pb.ArtifactPredicate( 219 test_result_predicate = validate.type( 220 "test_result_predicate", 221 test_result_predicate, 222 predicate_pb.TestResultPredicate(), 223 required = False, 224 ), 225 content_type_regexp = validate.string( 226 "content_type_regexp", 227 content_type_regexp, 228 required = False, 229 ), 230 artifact_id_regexp = validate.string( 231 "artifact_id_regexp", 232 artifact_id_regexp, 233 required = False, 234 ), 235 ) 236 237 included_invocations = validate.bool( 238 "included_invocations", 239 included_invocations, 240 default = None, 241 required = False, 242 ) 243 if included_invocations != None: 244 ret.follow_edges.included_invocations = included_invocations 245 246 test_results = validate.bool( 247 "test_results", 248 test_results, 249 default = None, 250 required = False, 251 ) 252 if test_results != None: 253 ret.follow_edges.test_results = test_results 254 255 return ret 256 257 def _validate_settings(attr, settings): 258 """Validates the type of a ResultDB settings proto. 259 260 Args: 261 attr: field name with settings, for error messages. Required. 262 settings: A proto such as the one returned by resultdb.settings(...). 263 264 Returns: 265 A validated proto, if it's the correct type. 266 """ 267 return validate.type( 268 attr, 269 settings, 270 buildbucket_pb.BuilderConfig.ResultDB(), 271 required = False, 272 ) 273 274 # A struct returned by resultdb.test_presentation. 275 # 276 # See resultdb.test_presentation function for all details. 277 # 278 # Fields: 279 # column_keys: list of string keys that will be rendered as 'columns'. 280 # grouping_keys: list of string keys that will be used for grouping tests. 281 _test_presentation_config_ctor = __native__.genstruct("test_presentation.config") 282 283 def _test_presentation(*, column_keys = None, grouping_keys = None): 284 """Specifies how test should be rendered. 285 286 Args: 287 column_keys: list of string keys that will be rendered as 'columns'. 288 status is always the first column and name is always the last column 289 (you don't need to specify them). A key must be one of the following: 290 1. 'v.{variant_key}': variant.def[variant_key] of the test variant 291 (e.g. v.gpu). 292 If None, defaults to []. 293 grouping_keys: list of string keys that will be used for grouping tests. 294 A key must be one of the following: 295 1. 'status': status of the test variant. 296 2. 'name': name of the test variant. 297 3. 'v.{variant_key}': variant.def[variant_key] of the test variant 298 (e.g. v.gpu). 299 If None, defaults to ['status']. 300 Caveat: test variants with only expected results are not affected by 301 this setting and are always in their own group. 302 303 Returns: 304 test_presentation.config struct with fields `column_keys` and 305 `grouping_keys`. 306 """ 307 column_keys = validate.str_list("column_keys", column_keys) 308 for key in column_keys: 309 if not key.startswith("v."): 310 fail("invalid column key: %r should be a variant key with 'v.' prefix" % key) 311 312 grouping_keys = validate.str_list("grouping_keys", grouping_keys) or ["status"] 313 for key in grouping_keys: 314 if key not in ["status", "name"] and not key.startswith("v."): 315 fail("invalid grouping key: %r should be 'status', 'name', or a variant key with 'v.' prefix" % key) 316 317 return _test_presentation_config_ctor( 318 column_keys = column_keys, 319 grouping_keys = grouping_keys, 320 ) 321 322 def _validate_test_presentation(attr, config, required = False): 323 """Validates a test presentation config. 324 325 Args: 326 attr: field name with caches, for error messages. Required. 327 config: a test_presentation.config to validate. 328 required: if False, allow 'config' to be None, return None in this case. 329 330 Returns: 331 A validated test_presentation.config. 332 """ 333 return validate.struct(attr, config, _test_presentation_config_ctor, required = required) 334 335 def _test_presentation_to_dict(config): 336 """Converts a test presentation config to a dictionary. 337 338 Args: 339 config: a test_presentation.config to be converted to a dictionary. 340 341 Returns: 342 A dictionary representing the test presentation config. 343 """ 344 345 return { 346 "column_keys": config.column_keys, 347 "grouping_keys": config.grouping_keys, 348 } 349 350 resultdb = struct( 351 settings = _settings, 352 export_test_results = _export_test_results, 353 test_result_predicate = _test_result_predicate, 354 validate_settings = _validate_settings, 355 history_options = _history_options, 356 export_text_artifacts = _export_text_artifacts, 357 artifact_predicate = _artifact_predicate, 358 test_presentation = _test_presentation, 359 validate_test_presentation = _validate_test_presentation, 360 ) 361 362 resultdbimpl = struct( 363 test_presentation_to_dict = _test_presentation_to_dict, 364 )