blob: 87df8fc69cc662672975c48198ac8c1ded220a39 [file] [log] [blame]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package main
import (
"strings"
)
// scoreChangedFileProximity computes a 0-100 score of the proximity of a Gerrit
// change's affected files to the given test GN label.
func scoreChangedFileProximity(changedFiles []string, gnLabel string) int {
// If there's no GN label and we can't do this analysis, that significantly
// lowers the confidence.
if gnLabel == "" {
return 0
}
strippedLabel := strings.Trim(strings.Split(gnLabel, ":")[0], "/")
labelParts := strings.Split(strippedLabel, "/")
var proximityScores []float64
for _, file := range changedFiles {
pl := sharedPrefixLength(labelParts, strings.Split(file, "/"))
// Proximity is determined by the ratio of the shared prefix length to
// the length of the GN label directory.
//
// If a test is declared in directory `src/foo/bar`, any files within
// `src/foo/bar` or any subdirectory thereof will have a proximity ratio
// of 1.
proximityScore := 100 * float64(pl) / float64(len(labelParts))
proximityScores = append(proximityScores, proximityScore)
// Add extra weighting for higher proximity scores. If a change touches
// a couple files close to the test, along with a large number of
// unrelated files, it should still be scored higher than a change that
// touches only files that are moderately close to the test.
for i := 0; i <= int(proximityScore/25); i++ {
proximityScores = append(proximityScores, proximityScore)
}
}
return int(average(proximityScores))
}
// sharedPrefixLength computes the length of the longest shared prefix of the
// two slices, i.e. the maximum number N such that p1[:N] == p2[:N].
func sharedPrefixLength(p1, p2 []string) int {
var i int
for ; i < len(p1) && i < len(p2); i++ {
if p1[i] != p2[i] {
break
}
}
return i
}