go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/bisection/model/model.go (about) 1 // Copyright 2022 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 // Package model contains the datastore model for LUCI Bisection. 16 package model 17 18 import ( 19 "context" 20 "errors" 21 "strings" 22 "time" 23 24 pb "go.chromium.org/luci/bisection/proto/v1" 25 buildbucketpb "go.chromium.org/luci/buildbucket/proto" 26 "go.chromium.org/luci/common/logging" 27 "go.chromium.org/luci/gae/service/datastore" 28 ) 29 30 type RerunBuildType string 31 32 const ( 33 RerunBuildType_CulpritVerification RerunBuildType = "Culprit Verification" 34 RerunBuildType_NthSection RerunBuildType = "NthSection" 35 ) 36 37 type SuspectVerificationStatus string 38 39 const ( 40 // The suspect is not verified and no verification is happening 41 SuspectVerificationStatus_Unverified SuspectVerificationStatus = "Unverified" 42 // The suspect is scheduled to be verified (via a task queue) 43 SuspectVerificationStatus_VerificationScheduled SuspectVerificationStatus = "Verification Scheduled" 44 // The suspect is under verification 45 SuspectVerificationStatus_UnderVerification SuspectVerificationStatus = "Under Verification" 46 // The suspect is confirmed to be culprit 47 SuspectVerificationStatus_ConfirmedCulprit SuspectVerificationStatus = "Confirmed Culprit" 48 // This is a false positive - the suspect is not the culprit 49 SuspectVerificationStatus_Vindicated SuspectVerificationStatus = "Vindicated" 50 // Some error happened during verification 51 SuspectVerificationStatus_VerificationError SuspectVerificationStatus = "Verification Error" 52 // The verification is canceled 53 SuspectVerificationStatus_Canceled SuspectVerificationStatus = "Canceled" 54 ) 55 56 type SuspectType string 57 58 const ( 59 SuspectType_Heuristic SuspectType = "Heuristic" 60 SuspectType_NthSection SuspectType = "NthSection" 61 ) 62 63 type Platform string 64 65 const ( 66 // The build didn't specified a platform 67 PlatformUnspecified Platform = "unspecified" 68 // Platform is not win, mac or linux 69 PlatformUnknown Platform = "unknown" 70 PlatformWindows Platform = "win" 71 PlatformMac Platform = "mac" 72 PlatformLinux Platform = "linux" 73 ) 74 75 // LuciBuild represents one LUCI build. 76 // Deprecated. Please use LUCIBuild model instead. 77 // It is kept here because some old code is still using it. 78 type LuciBuild struct { 79 BuildId int64 `gae:"build_id"` 80 Project string `gae:"project"` 81 Bucket string `gae:"bucket"` 82 Builder string `gae:"builder"` 83 BuildNumber int `gae:"build_number"` 84 buildbucketpb.GitilesCommit 85 CreateTime time.Time `gae:"create_time"` 86 EndTime time.Time `gae:"end_time"` 87 StartTime time.Time `gae:"start_time"` 88 Status buildbucketpb.Status `gae:"status"` 89 } 90 91 type LUCIBuild struct { 92 BuildID int64 `gae:"build_id"` 93 Project string `gae:"project"` 94 Bucket string `gae:"bucket"` 95 Builder string `gae:"builder"` 96 BuildNumber int `gae:"build_number"` 97 GitilesCommit *buildbucketpb.GitilesCommit `gae:"gitiles_commit"` 98 CreateTime time.Time `gae:"create_time"` 99 EndTime time.Time `gae:"end_time"` 100 StartTime time.Time `gae:"start_time"` 101 Status buildbucketpb.Status `gae:"status"` 102 } 103 104 type LuciFailedBuild struct { 105 // Id is the build Id 106 Id int64 `gae:"$id"` 107 LuciBuild 108 // Obsolete field - specify BuildFailureType instead 109 FailureType string `gae:"failure_type"` 110 // Failure type for the build 111 BuildFailureType pb.BuildFailureType `gae:"build_failure_type"` 112 // The platform of the failure 113 Platform Platform `gae:"platform"` 114 // The sheriff rotations that watch the builder. 115 SheriffRotations []string `gae:"sheriff_rotations"` 116 } 117 118 // CompileFailure represents a compile failure in one or more targets. 119 type CompileFailure struct { 120 // Id is the build Id of the compile failure 121 Id int64 `gae:"$id"` 122 // The key to LuciFailedBuild that the failure belongs to. 123 Build *datastore.Key `gae:"$parent"` 124 125 // The list of output targets that failed to compile. 126 // This is to speed up the compilation process, as we only want to rerun failed targets. 127 OutputTargets []string `gae:"output_targets"` 128 129 // The list of source files resulting in compile failures 130 FailedFiles []string `gae:"failed_files"` 131 132 // Compile rule, e.g. ACTION, CXX, etc. 133 // For chromium builds, it can be found in json.output[ninja_info] log of 134 // compile step. 135 // For chromeos builds, it can be found in an output property 'compile_failure' 136 // of the build. 137 Rule string `gae:"rule"` 138 139 // Only for CC and CXX rules 140 // These are the source files that this compile failure uses as input 141 Dependencies []string `gae:"dependencies"` 142 143 // Key to the CompileFailure that this failure merges into. 144 // If this exists, no analysis on current failure, instead use the results 145 // of merged_failure. 146 MergedFailureKey *datastore.Key `gae:"merged_failure_key"` 147 } 148 149 // CompileFailureAnalysis is the analysis for CompileFailure. 150 // This stores information that is needed during the analysis, and also 151 // some metadata for the analysis. 152 type CompileFailureAnalysis struct { 153 Id int64 `gae:"$id"` 154 // Key to the CompileFailure that this analysis analyses. 155 CompileFailure *datastore.Key `gae:"compile_failure"` 156 // Time when the analysis is created. 157 CreateTime time.Time `gae:"create_time"` 158 // Time when the analysis starts to run. 159 StartTime time.Time `gae:"start_time"` 160 // Time when the analysis ends, or canceled. 161 EndTime time.Time `gae:"end_time"` 162 // Status of the analysis 163 Status pb.AnalysisStatus `gae:"status"` 164 // Run status of the analysis 165 RunStatus pb.AnalysisRunStatus `gae:"run_status"` 166 // Id of the build in which the compile failures occurred the first time in 167 // a sequence of consecutive failed builds. 168 FirstFailedBuildId int64 `gae:"first_failed_build_id"` 169 // Id of the latest build in which the failures did not happen. 170 LastPassedBuildId int64 `gae:"last_passed_build_id"` 171 // Initial regression range to find the culprit 172 InitialRegressionRange *pb.RegressionRange `gae:"initial_regression_range"` 173 // Key to the heuristic suspects that was verified by Culprit verification 174 // In some rare cases, there are more than 1 culprit for the regression range. 175 VerifiedCulprits []*datastore.Key `gae:"verified_culprits"` 176 // Indicates whether the analysis should be cancelled or not, 177 // such as in the situation where the corresponding builder start passing again 178 ShouldCancel bool `gae:"should_cancel"` 179 // Is this analysis for a tree closer failure. 180 // If it is, all reruns of this analysis should have higher priority. 181 IsTreeCloser bool `gae:"is_tree_closer"` 182 } 183 184 // CompileRerunBuild is one rerun build for CompileFailureAnalysis. 185 // The rerun build may be for nth-section analysis or for culprit verification. 186 type CompileRerunBuild struct { 187 // Id is the buildbucket Id for the rerun build. 188 Id int64 `gae:"$id"` 189 // LUCI build data 190 LuciBuild 191 // For backward compatibility due to removed fields. 192 // See https://source.chromium.org/chromium/infra/infra/+/main:go/src/go.chromium.org/luci/gae/service/datastore/pls.go;l=100 193 _ datastore.PropertyMap `gae:"-,extra"` 194 } 195 196 // SingleRerun represents one rerun for a particular compile/test failures for a particular commit. 197 // Initially, we wanted to trigger multiple reruns for different commits in the same build, 198 // but it is not possible. We can only trigger one rerun per rerun build, i.e. the 199 // relationship between single rerun : rerun build is 1:1. 200 type SingleRerun struct { 201 Id int64 `gae:"$id"` 202 // Key to the parent CompileRerunBuild 203 RerunBuild *datastore.Key `gae:"rerun_build"` 204 // Type for the rerun build 205 // Either culprit verification or nth section run. 206 Type RerunBuildType `gae:"rerun_type"` 207 // Key to the CompileFailureAnalysis of this SingleRerun 208 // This is mainly used for getting all reruns for an analysis, 209 // for the purpose of nth-section analysis 210 Analysis *datastore.Key `gae:"analysis"` 211 // The commit that this SingleRerun runs on 212 buildbucketpb.GitilesCommit 213 // Time when the rerun was created 214 CreateTime time.Time `gae:"create_time"` 215 // Time when the rerun starts. 216 StartTime time.Time `gae:"start_time"` 217 // Time when the rerun ends. 218 EndTime time.Time `gae:"end_time"` 219 // Status of the rerun 220 Status pb.RerunStatus 221 // Key to the Suspect, if this is for culprit verification 222 Suspect *datastore.Key `gae:"suspect"` 223 // Key to NthSectionAnalysis, if this is for nthsection 224 NthSectionAnalysis *datastore.Key `gae:"nthsection_analysis"` 225 // Priority of this run 226 Priority int32 `gae:"priority"` 227 // The dimensions of the rerun build. 228 Dimensions *pb.Dimensions `gae:"dimensions"` 229 } 230 231 // ActionDetails encapsulate the details of actions performed by LUCI Bisection, 232 // e.g. creating a revert, commenting on a culprit, etc. 233 type ActionDetails struct { 234 // URL to the code review of the revert. 235 RevertURL string `gae:"revert_url"` 236 237 // Whether LUCI Bisection has created the revert 238 IsRevertCreated bool `gae:"is_revert_created"` 239 240 // Time when the revert was created 241 RevertCreateTime time.Time `gae:"revert_create_time"` 242 243 // Whether LUCI Bisection has committed the revert 244 IsRevertCommitted bool `gae:"is_revert_committed"` 245 246 // Time when the revert for the suspect was bot-committed 247 RevertCommitTime time.Time `gae:"revert_commit_time"` 248 249 // Whether LUCI Bisection has added a supporting comment to an existing revert 250 HasSupportRevertComment bool `gae:"has_support_revert_comment"` 251 252 // Time when LUCI Bisection added a supporting comment to an existing revert 253 SupportRevertCommentTime time.Time `gae:"support_revert_comment_time"` 254 255 // Whether LUCI Bisection has added a comment to the culprit CL 256 HasCulpritComment bool `gae:"has_culprit_comment"` 257 258 // Time when LUCI Bisection commented on the culprit 259 CulpritCommentTime time.Time `gae:"culprit_comment_time"` 260 261 // Optional explanation for when processing the culprit results in no action. 262 InactionReason pb.CulpritInactionReason `gae:"inaction_reason"` 263 264 // HasTakenActions indicates if the all actions for culprit 265 // (e.g. comment, revert...) have been taken. 266 // This field is only valid if a culprit is confirmed by culprit verification. 267 // If there is no action to be taken (e.g. if actions are disabled), 268 // then this field is set to true. 269 HasTakenActions bool `gae:"has_taken_actions"` 270 } 271 272 // Suspect is the suspect of heuristic analysis or nthsection. 273 type Suspect struct { 274 Id int64 `gae:"$id"` 275 276 // Type of the suspect, either heuristic or nthsection 277 Type SuspectType `gae:"type"` 278 279 // Key to the CompileFailureHeuristicAnalysis or CompileFailureNthSectionAnalysis 280 // or TestNthSectionAnalysis that results in this suspect 281 ParentAnalysis *datastore.Key `gae:"$parent"` 282 283 // The commit of the suspect 284 buildbucketpb.GitilesCommit 285 286 // The Url where the suspect was reviewed 287 ReviewUrl string `gae:"review_url"` 288 289 // Title of the review for the suspect 290 ReviewTitle string `gae:"review_title"` 291 292 // Score is an integer representing the how confident we believe the suspect 293 // is indeed the culprit. 294 // A higher score means a stronger signal that the suspect is responsible for 295 // a failure. 296 // Only applies to Heuristic suspect 297 Score int `gae:"score"` 298 299 // A short, human-readable string that concisely describes a fact about the 300 // suspect. e.g. 'add a/b/x.cc' 301 // Only applies to Heuristic suspect 302 Justification string `gae:"justification,noindex"` 303 304 // Whether if a suspect has been verified 305 VerificationStatus SuspectVerificationStatus `gae:"verification_status"` 306 307 // Key to the CompileRerunBuild or TestSingleRerun of the suspect, for culprit verification purpose. 308 SuspectRerunBuild *datastore.Key `gae:"suspect_rerun_build"` 309 310 // Key to the CompileRerunBuild or TestSingleRerun of the parent commit of the suspect, for culprit verification purpose. 311 ParentRerunBuild *datastore.Key `gae:"parent_rerun_build"` 312 313 // Details of actions performed by LUCI Bisection for this suspect. 314 ActionDetails 315 316 // Type of the suspect. 317 AnalysisType pb.AnalysisType `gae:"analysis_type"` 318 319 // The time that this suspect was committed. 320 // For now, it is only populated for test failure suspects. 321 CommitTime time.Time `gae:"commit_time"` 322 323 // For backward compatibility due to removed fields. 324 // See https://source.chromium.org/chromium/infra/infra/+/main:go/src/go.chromium.org/luci/gae/service/datastore/pls.go;l=100 325 _ datastore.PropertyMap `gae:"-,extra"` 326 } 327 328 // CompileHeuristicAnalysis is heuristic analysis for compile failures. 329 type CompileHeuristicAnalysis struct { 330 Id int64 `gae:"$id"` 331 // Key to the parent CompileFailureAnalysis 332 ParentAnalysis *datastore.Key `gae:"$parent"` 333 // Time when the analysis starts to run. 334 StartTime time.Time `gae:"start_time"` 335 // Time when the analysis ends, or canceled 336 EndTime time.Time `gae:"end_time"` 337 // Status of the analysis 338 Status pb.AnalysisStatus `gae:"status"` 339 // Run status of the analysis 340 RunStatus pb.AnalysisRunStatus `gae:"run_status"` 341 } 342 343 // CompileNthSectionAnalysis is nth-section analysis for compile failures. 344 type CompileNthSectionAnalysis struct { 345 Id int64 `gae:"$id"` 346 // Key to the parent CompileFailureAnalysis 347 ParentAnalysis *datastore.Key `gae:"$parent"` 348 // Time when the analysis starts to run. 349 StartTime time.Time `gae:"start_time"` 350 // Time when the analysis ends, or canceled 351 EndTime time.Time `gae:"end_time"` 352 // Status of the analysis 353 Status pb.AnalysisStatus `gae:"status"` 354 // Run status of the analysis 355 RunStatus pb.AnalysisRunStatus `gae:"run_status"` 356 357 // When storing protobuf message, datastore will compress the data if it is big 358 // https://source.corp.google.com/chops_infra_internal/infra/go/src/go.chromium.org/luci/gae/service/datastore/protos.go;l=88 359 // We can also declare zstd compression here, but there seems to be a bug where 360 // the message size is 0 361 BlameList *pb.BlameList `gae:"blame_list"` 362 363 // Suspect is the result of nthsection analysis. 364 // Note: We call it "suspect" because it has not been verified (by culprit verification component) 365 Suspect *datastore.Key `gae:"suspect"` 366 } 367 368 // TestFailure represents a failure on a test variant. 369 type TestFailure struct { 370 ID int64 `gae:"$id"` 371 // The LUCI project of this test variant. 372 Project string `gae:"project"` 373 // Test ID of the test variant. 374 TestID string `gae:"test_id"` 375 // Variant hash of the test variant. 376 VariantHash string `gae:"variant_hash"` 377 // The variant of the test. 378 Variant *pb.Variant `gae:"variant"` 379 // The name of the test (used in recipe). Note that it is different 380 // from TestID. 381 TestName string `gae:"test_name"` 382 // The name of the test suite that this test variant belongs to. 383 // For chromium, this information can be derived from Variant field. 384 TestSuiteName string `gae:"test_suite_name"` 385 // Hash of the ref to identify the branch in the source control. 386 RefHash string `gae:"ref_hash"` 387 // The LUCI bucket for the builder of this test failure. 388 Bucket string `gae:"bucket"` 389 // The name for the builder of this test failure. 390 Builder string `gae:"builder"` 391 // The branch where this failure happens. 392 Ref *pb.SourceRef `gae:"ref"` 393 // Start commit position of the regression range exclusive. 394 RegressionStartPosition int64 `gae:"regression_start_position"` 395 // End commit position of the regression range inclusive. 396 RegressionEndPosition int64 `gae:"regression_end_position"` 397 // Expected failure rate at regression_start_position, between 0 and 1 inclusive. 398 StartPositionFailureRate float64 `gae:"start_position_failure_rate"` 399 // Expected failure rate at regression_end_position, between 0 and 1 inclusive. 400 EndPositionFailureRate float64 `gae:"end_position_failure_rate"` 401 // When run multiple test variants in a bisection build, the bisection path 402 // follows the test variant of the primary test failure. 403 IsPrimary bool 404 // IsDiverged is true when the bisection path of this test failure diverges from 405 // the primary test failure. This suggests that this test failure has a different root cause. 406 // We will not attempt to re-bisect this test failure. 407 IsDiverged bool `gae:"is_diverged"` 408 // Key to the TestFailureAnalysis that analyses this TestFailure. 409 AnalysisKey *datastore.Key `gae:"analysis_key"` 410 // RedundancyScore of the test failure, between 0 and 1, larger score means more redundant. 411 // Only set for primary test failure. 412 RedundancyScore float64 `gae:"redundancy_score"` 413 // The time when the failure starts, truncated into hours. 414 StartHour time.Time `gae:"start_hour"` 415 // The time when we get last got the failure result, truncated into hours. 416 EndHour time.Time `gae:"end_hour"` 417 } 418 419 // TestFailureAnalysis is the analysis for test failure. 420 // This stores information that is needed during the analysis, and also 421 // some metadata for the analysis. 422 type TestFailureAnalysis struct { 423 ID int64 `gae:"$id"` 424 // The LUCI project of the test variants this analysis analyses. 425 Project string `gae:"project"` 426 // The LUCI bucket for the builder that this analysis analyses. 427 Bucket string `gae:"bucket"` 428 // The name for the builder that this analysis analyses. 429 Builder string `gae:"builder"` 430 // Key to the primary TestFailure entity that this analysis analyses. 431 TestFailure *datastore.Key `gae:"test_failure"` 432 // Time when the entity is first created. 433 CreateTime time.Time `gae:"create_time"` 434 // Time when the analysis starts to run. 435 StartTime time.Time `gae:"start_time"` 436 // Time when the analysis ends, or canceled. 437 EndTime time.Time `gae:"end_time"` 438 // Status of the analysis 439 Status pb.AnalysisStatus `gae:"status"` 440 // Run status of the analysis 441 RunStatus pb.AnalysisRunStatus `gae:"run_status"` 442 // Key to the suspect that was verified by Culprit verification 443 VerifiedCulpritKey *datastore.Key `gae:"verified_culprit_key"` 444 // Priority of this run. 445 Priority int32 `gae:"priority"` 446 // The start commit hash (exclusive) of the regression range that this analysis analyses. 447 // It corresponds to the RegressionStartPosition. 448 StartCommitHash string `gae:"start_commit_hash"` 449 // The end commit hash (inclusive) of the regression range that this analysis analyses. 450 // It corresponds to the RegressionEndPosition. 451 EndCommitHash string `gae:"end_commit_hash"` 452 // An example Buildbucket ID in which the test failed. 453 FailedBuildID int64 `gae:"failed_build_id"` 454 // The sheriff rotations that watch the builder. 455 SheriffRotations []string `gae:"sheriff_rotations"` 456 } 457 458 // TestNthSectionAnalysis is nth-section analysis for test failures. 459 type TestNthSectionAnalysis struct { 460 ID int64 `gae:"$id"` 461 // Key to the parent TestFailureAnalysis. 462 ParentAnalysisKey *datastore.Key `gae:"parent_analysis_key"` 463 // Time when the analysis starts to run. 464 StartTime time.Time `gae:"start_time"` 465 // Time when the analysis ends, or canceled. 466 EndTime time.Time `gae:"end_time"` 467 // Status of the analysis. 468 Status pb.AnalysisStatus `gae:"status"` 469 // Run status of the analysis. 470 RunStatus pb.AnalysisRunStatus `gae:"run_status"` 471 472 // When storing protobuf message, datastore will compress the data if it is big 473 // https://source.corp.google.com/chops_infra_internal/infra/go/src/go.chromium.org/luci/gae/service/datastore/protos.go;l=88 474 BlameList *pb.BlameList `gae:"blame_list"` 475 476 // Culprit is the result of nthsection analysis. 477 // Nthsection analysis follows the path of the primary test failure, 478 // so the culprit here is the culprit of the primary test failure. 479 CulpritKey *datastore.Key `gae:"culprit"` 480 } 481 482 // TestSingleRerun represents one rerun for test failures 483 // at a particular commit. 484 // A TestSingleRerun corresponds to one buildbucket run, and may 485 // run multiple test failures at the same time. 486 // A TestSingleRerun may be for nth-section or for culprit verification. 487 type TestSingleRerun struct { 488 // The buildbucket ID of the rerun. 489 ID int64 `gae:"$id"` 490 // LUCI build data for the rerun build. 491 LUCIBuild `gae:"luci_build"` 492 // Type for the rerun build 493 // Either culprit verification or nth section run. 494 Type RerunBuildType `gae:"rerun_type"` 495 // Key to the TestFailureAnalysis of this SingleRerun 496 AnalysisKey *datastore.Key `gae:"analysis_key"` 497 // Time when the rerun send the result to bisection from recipe. 498 ReportTime time.Time `gae:"report_time"` 499 // The dimensions of the rerun build. 500 Dimensions *pb.Dimensions `gae:"dimensions"` 501 // Key to the culprit (Suspect model), if this is for culprit verification 502 CulpritKey *datastore.Key `gae:"culprit_key"` 503 // Key to TestNthSectionAnalysis, if this is for nthsection. 504 NthSectionAnalysisKey *datastore.Key `gae:"nthsection_analysis_key"` 505 // Priority of this run. 506 Priority int32 `gae:"priority"` 507 // Results of the test runs. 508 // The TestFailureKey field of test result will be populated when this model 509 // is first created. 510 // This is useful to know which test failures are running for this rerun without 511 // waiting for the result. 512 TestResults RerunTestResults `gae:"test_results"` 513 // Status of the rerun. 514 // If the rerun ended, this result will base on the result 515 // of the primary test failure. See pb.RerunStatus for more information. 516 Status pb.RerunStatus `gae:"status"` 517 } 518 519 // RerunTestResults captures test results of TestSingleRerun. 520 type RerunTestResults struct { 521 // IsFinalized indicates whether the results have been finalized and 522 // is ready to be consumed. 523 IsFinalized bool `gae:"is_finalized"` 524 Results []RerunSingleTestResult `gae:"results"` 525 } 526 527 // RerunSingleTestResult is the result for one TestFailure. 528 type RerunSingleTestResult struct { 529 // Key to TestFailure model. 530 TestFailureKey *datastore.Key `gae:"test_failure_key"` 531 532 // TODO (nqmtuan): Consider breaking this to status level (e.g. 533 // unexpected pass count). But for now, keeping the total expected 534 // and unexpected count may be enough, as we only support bisection 535 // from expected to unexpected. 536 // We use number count instead of status to open for possibility 537 // to support flakiness bisection in the future (where a test may need 538 // to be rerun multiple times to get the flakiness level). 539 // The number of expected results. Skipped results are not counted. 540 ExpectedCount int64 `gae:"expected_count"` 541 // The number of unexpected results. Skipped results are not counted. 542 UnexpectedCount int64 `gae:"unexpected_count"` 543 } 544 545 func (cfa *CompileFailureAnalysis) HasEnded() bool { 546 return cfa.RunStatus == pb.AnalysisRunStatus_ENDED || cfa.RunStatus == pb.AnalysisRunStatus_CANCELED 547 } 548 549 func (tfa *TestFailureAnalysis) HasEnded() bool { 550 return tfa.RunStatus == pb.AnalysisRunStatus_ENDED || tfa.RunStatus == pb.AnalysisRunStatus_CANCELED 551 } 552 553 func (tfa *TestFailureAnalysis) HasStarted() bool { 554 return tfa.RunStatus == pb.AnalysisRunStatus_ENDED || tfa.RunStatus == pb.AnalysisRunStatus_CANCELED || tfa.RunStatus == pb.AnalysisRunStatus_STARTED 555 } 556 557 func (ha *CompileHeuristicAnalysis) HasEnded() bool { 558 return ha.RunStatus == pb.AnalysisRunStatus_ENDED || ha.RunStatus == pb.AnalysisRunStatus_CANCELED 559 } 560 561 func (nsa *CompileNthSectionAnalysis) HasEnded() bool { 562 return nsa.RunStatus == pb.AnalysisRunStatus_ENDED || nsa.RunStatus == pb.AnalysisRunStatus_CANCELED 563 } 564 565 func (rerun *SingleRerun) HasEnded() bool { 566 return rerun.Status == pb.RerunStatus_RERUN_STATUS_FAILED || rerun.Status == pb.RerunStatus_RERUN_STATUS_PASSED || rerun.Status == pb.RerunStatus_RERUN_STATUS_INFRA_FAILED || rerun.Status == pb.RerunStatus_RERUN_STATUS_CANCELED 567 } 568 569 func (rerun *TestSingleRerun) HasEnded() bool { 570 return rerun.Status == pb.RerunStatus_RERUN_STATUS_FAILED || rerun.Status == pb.RerunStatus_RERUN_STATUS_PASSED || rerun.Status == pb.RerunStatus_RERUN_STATUS_INFRA_FAILED || rerun.Status == pb.RerunStatus_RERUN_STATUS_CANCELED || rerun.Status == pb.RerunStatus_RERUN_STATUS_TEST_SKIPPED 571 } 572 573 func (rerun *TestSingleRerun) HasStarted() bool { 574 return rerun.LUCIBuild.Status != buildbucketpb.Status_STATUS_UNSPECIFIED && rerun.LUCIBuild.Status != buildbucketpb.Status_SCHEDULED 575 } 576 577 func (nsa *TestNthSectionAnalysis) HasEnded() bool { 578 return nsa.RunStatus == pb.AnalysisRunStatus_ENDED || nsa.RunStatus == pb.AnalysisRunStatus_CANCELED 579 } 580 581 // TestFailureBundle contains TestFailure models that will be bisected together. 582 type TestFailureBundle struct { 583 // Primary test failure. 584 primaryFailure *TestFailure 585 // Non-primary test failures. 586 otherTestFailures []*TestFailure 587 // Contains metadata that is common for this bundle that 588 // is not suitable to put in TestFailure model. 589 Metadata *BundleMetaData 590 } 591 592 type BundleMetaData struct { 593 SheriffRotations []string 594 } 595 596 func (tfb *TestFailureBundle) Add(testFailures []*TestFailure) error { 597 for _, tf := range testFailures { 598 if tf.IsPrimary { 599 if tfb.primaryFailure != nil { 600 return errors.New("added more than 1 primary test failure in bundle") 601 } 602 tfb.primaryFailure = tf 603 } else { 604 tfb.otherTestFailures = append(tfb.otherTestFailures, tf) 605 } 606 } 607 return nil 608 } 609 610 // Primary returns the primary test failure for the bundle. 611 func (tfb *TestFailureBundle) Primary() *TestFailure { 612 return tfb.primaryFailure 613 } 614 615 // Others returns other test failures for the bundle. 616 func (tfb *TestFailureBundle) Others() []*TestFailure { 617 result := make([]*TestFailure, len(tfb.otherTestFailures)) 618 // Copy here to prevent callers from adding things to the slice. 619 copy(result, tfb.otherTestFailures) 620 return result 621 } 622 623 // All return all test failures for the bundle. 624 func (tfb *TestFailureBundle) All() []*TestFailure { 625 if tfb.primaryFailure == nil { 626 return tfb.Others() 627 } 628 return append(tfb.otherTestFailures, tfb.primaryFailure) 629 } 630 631 // NonDiverged returns all non-diverged test failures for the bundle. 632 func (tfb *TestFailureBundle) NonDiverged() []*TestFailure { 633 result := []*TestFailure{} 634 if tfb.primaryFailure != nil { 635 result = append(result, tfb.primaryFailure) 636 } 637 for _, other := range tfb.otherTestFailures { 638 if !other.IsDiverged { 639 result = append(result, other) 640 } 641 } 642 return result 643 } 644 645 func SuspectStatus(rerunStatus pb.RerunStatus, parentRerunStatus pb.RerunStatus) SuspectVerificationStatus { 646 if rerunStatus == pb.RerunStatus_RERUN_STATUS_FAILED && parentRerunStatus == pb.RerunStatus_RERUN_STATUS_PASSED { 647 return SuspectVerificationStatus_ConfirmedCulprit 648 } 649 if rerunStatus == pb.RerunStatus_RERUN_STATUS_PASSED || parentRerunStatus == pb.RerunStatus_RERUN_STATUS_FAILED { 650 return SuspectVerificationStatus_Vindicated 651 } 652 if rerunStatus == pb.RerunStatus_RERUN_STATUS_INFRA_FAILED || parentRerunStatus == pb.RerunStatus_RERUN_STATUS_INFRA_FAILED { 653 return SuspectVerificationStatus_VerificationError 654 } 655 if rerunStatus == pb.RerunStatus_RERUN_STATUS_UNSPECIFIED || parentRerunStatus == pb.RerunStatus_RERUN_STATUS_UNSPECIFIED { 656 return SuspectVerificationStatus_Unverified 657 } 658 if rerunStatus == pb.RerunStatus_RERUN_STATUS_TEST_SKIPPED || parentRerunStatus == pb.RerunStatus_RERUN_STATUS_TEST_SKIPPED { 659 return SuspectVerificationStatus_VerificationError 660 } 661 return SuspectVerificationStatus_UnderVerification 662 } 663 664 func PlatformFromOS(ctx context.Context, os string) Platform { 665 val := strings.ToLower(os) 666 if strings.Contains(val, "linux") || strings.Contains(val, "ubuntu") { 667 return PlatformLinux 668 } 669 if strings.Contains(val, "win") { 670 return PlatformWindows 671 } 672 if strings.Contains(val, "mac") { 673 return PlatformMac 674 } 675 logging.Warningf(ctx, "Unknown OS platform: %s", val) 676 return PlatformUnknown 677 }