go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/bisection/model/suspect_justification.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 16 17 import ( 18 "sort" 19 "strings" 20 ) 21 22 type JustificationType int64 23 24 const ( 25 JustificationType_UNSPECIFIED JustificationType = 0 26 // If a commit touches a file in the failure log 27 JustificationType_FAILURELOG JustificationType = 1 28 // If a commit touches a file in the dependency 29 JustificationType_DEPENDENCY JustificationType = 2 30 ) 31 32 // SuspectJustification represents the heuristic analysis of a CL. 33 // It how likely the suspect is the real culprit and also the reason for suspecting. 34 type SuspectJustification struct { 35 IsNonBlamable bool 36 Items []*SuspectJustificationItem 37 } 38 39 // SuspectJustificationItem represents one item of SuspectJustification 40 type SuspectJustificationItem struct { 41 Score int 42 FilePath string 43 Reason string 44 Type JustificationType 45 } 46 47 func (justification *SuspectJustification) GetScore() int { 48 score := 0 49 dependencyScore := 0 50 for _, item := range justification.Items { 51 switch item.Type { 52 case JustificationType_FAILURELOG: 53 score += item.Score 54 case JustificationType_DEPENDENCY: 55 dependencyScore += item.Score 56 default: 57 // Do nothing 58 } 59 } 60 // Maximum score a suspect can gain from dependency 61 dependencyScoreThreshold := 9 62 if dependencyScore > dependencyScoreThreshold { 63 dependencyScore = dependencyScoreThreshold 64 } 65 return score + dependencyScore 66 } 67 68 func (justification *SuspectJustification) GetReasons() string { 69 if justification.IsNonBlamable { 70 return "The author is non-blamable" 71 } 72 reasons := make([]string, len(justification.Items)) 73 for i, item := range justification.Items { 74 reasons[i] = item.Reason 75 } 76 return strings.Join(reasons, "\n") 77 } 78 79 func (justification *SuspectJustification) AddItem(score int, filePath string, reason string, justificationType JustificationType) { 80 item := &SuspectJustificationItem{ 81 Score: score, 82 FilePath: filePath, 83 Reason: reason, 84 Type: justificationType, 85 } 86 if justification.Items == nil { 87 justification.Items = []*SuspectJustificationItem{} 88 } 89 justification.Items = append(justification.Items, item) 90 } 91 92 // Sort sorts the items descendingly based on score 93 func (justification *SuspectJustification) Sort() { 94 sort.Slice(justification.Items, func(i, j int) bool { 95 return justification.Items[i].Score > justification.Items[j].Score 96 }) 97 }