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  }