go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/analysis/internal/clustering/clusterresults.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 clustering 16 17 import ( 18 "time" 19 ) 20 21 // ClusterResults represents the results of clustering a list of 22 // test failures. 23 type ClusterResults struct { 24 // AlgorithmsVersion is the version of clustering algorithms used to 25 // cluster test results in this chunk. (This is a version over the 26 // set of algorithms, distinct from the version of a single algorithm, 27 // e.g.: v1 -> {reason-v1}, v2 -> {reason-v1, testname-v1}, 28 // v3 -> {reason-v2, testname-v1}.) 29 AlgorithmsVersion int64 30 // ConfigVersion is the version of LUCI Analysis project configuration 31 // used to cluster the test results. Clustering algorithms can rely 32 // on the configuration to alter their behaviour, so changes to 33 // the configuration should trigger re-clustering of test results. 34 ConfigVersion time.Time 35 // RulesVersion is the version of failure association rules used 36 // to cluster test results. This is most recent PredicateLastUpdated 37 // time in the snapshot of failure association rules used to cluster 38 // the test results. 39 RulesVersion time.Time 40 // Algorithms is the set of algorithms that were used to cluster 41 // the test results. Each entry is an algorithm name. 42 // When stored alongside the clustered test results, this allows only 43 // the new algorithms to be run when re-clustering (for efficiency). 44 Algorithms map[string]struct{} 45 // Clusters records the clusters each test result is in; 46 // one slice of ClusterIDs for each test result. For each test result, 47 // clusters must be in sorted order, with no duplicates. 48 Clusters [][]ClusterID 49 } 50 51 // AlgorithmsAndClustersEqual returns whether the algorithms and clusters of 52 // two cluster results are equivalent. 53 func AlgorithmsAndClustersEqual(a *ClusterResults, b *ClusterResults) bool { 54 if !setsEqual(a.Algorithms, b.Algorithms) { 55 return false 56 } 57 if len(a.Clusters) != len(b.Clusters) { 58 return false 59 } 60 for i, aClusters := range a.Clusters { 61 bClusters := b.Clusters[i] 62 if !ClustersEqual(aClusters, bClusters) { 63 return false 64 } 65 } 66 return true 67 } 68 69 // ClustersEqual returns whether the clusters in `as` are element-wise 70 // equal to those in `bs`. 71 // To test set-wise cluster equality, this method is called with 72 // clusters in sorted order, and no duplicates. 73 func ClustersEqual(as []ClusterID, bs []ClusterID) bool { 74 if len(as) != len(bs) { 75 return false 76 } 77 for i, a := range as { 78 b := bs[i] 79 if a.Algorithm != b.Algorithm { 80 return false 81 } 82 if a.ID != b.ID { 83 return false 84 } 85 } 86 return true 87 } 88 89 // setsEqual returns whether two sets are equal. 90 func setsEqual(a map[string]struct{}, b map[string]struct{}) bool { 91 if len(a) != len(b) { 92 return false 93 } 94 for key := range a { 95 if _, ok := b[key]; !ok { 96 return false 97 } 98 } 99 return true 100 }