go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/bisection/compilefailureanalysis/heuristic/changelog_analyzer_test.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 heuristic 16 17 import ( 18 "context" 19 "testing" 20 21 . "github.com/smartystreets/goconvey/convey" 22 "go.chromium.org/luci/bisection/model" 23 ) 24 25 func TestChangeLogAnalyzer(t *testing.T) { 26 t.Parallel() 27 28 Convey("AreRelelatedExtensions", t, func() { 29 So(AreRelelatedExtensions("c", "cpp"), ShouldBeTrue) 30 So(AreRelelatedExtensions("py", "pyc"), ShouldBeTrue) 31 So(AreRelelatedExtensions("gyp", "gypi"), ShouldBeTrue) 32 So(AreRelelatedExtensions("c", "py"), ShouldBeFalse) 33 So(AreRelelatedExtensions("abc", "xyz"), ShouldBeFalse) 34 }) 35 36 Convey("NormalizeObjectFilePath", t, func() { 37 data := map[string]string{ 38 "obj/a/T.x.o": "a/x.o", 39 "obj/a/T.x.y.o": "a/x.y.o", 40 "x.o": "x.o", 41 "obj/a/x.obj": "a/x.obj", 42 "a.cc.obj": "a.cc.obj", 43 "T.a.c.o": "a.c.o", 44 "T.a.o": "a.o", 45 "T.a.b.c": "T.a.b.c", 46 } 47 for k, v := range data { 48 So(NormalizeObjectFilePath(k), ShouldEqual, v) 49 } 50 }) 51 52 Convey("AnalyzeOneChangeLog", t, func() { 53 c := context.Background() 54 signal := &model.CompileFailureSignal{ 55 Files: map[string][]int{ 56 "src/a/b/x.cc": {27}, 57 "obj/content/util.o": {}, 58 }, 59 Edges: []*model.CompileFailureEdge{ 60 { 61 Dependencies: []string{ 62 "x/y/aa_impl_mac.cc", 63 "y/z/bb_impl.cc", 64 }, 65 }, 66 }, 67 } 68 signal.CalculateDependencyMap(c) 69 Convey("Changelog from a non-blamable email", func() { 70 cl := &model.ChangeLog{ 71 Author: model.ChangeLogActor{ 72 Email: "chrome-release-bot@chromium.org", 73 }, 74 } 75 76 justification, err := AnalyzeOneChangeLog(c, signal, cl) 77 So(err, ShouldBeNil) 78 So(justification, ShouldResemble, &model.SuspectJustification{IsNonBlamable: true}) 79 }) 80 81 Convey("Changelog did not touch any file", func() { 82 cl := &model.ChangeLog{ 83 ChangeLogDiffs: []model.ChangeLogDiff{ 84 { 85 Type: model.ChangeType_ADD, 86 NewPath: "some_file.cc", 87 }, 88 }, 89 } 90 justification, err := AnalyzeOneChangeLog(c, signal, cl) 91 So(err, ShouldBeNil) 92 So(justification, ShouldResemble, &model.SuspectJustification{}) 93 }) 94 95 Convey("Changelog touched relevant files", func() { 96 cl := &model.ChangeLog{ 97 ChangeLogDiffs: []model.ChangeLogDiff{ 98 { 99 Type: model.ChangeType_MODIFY, 100 OldPath: "content/util.c", 101 NewPath: "content/util.c", 102 }, 103 { 104 Type: model.ChangeType_ADD, 105 NewPath: "dir/a/b/x.cc", 106 }, 107 { 108 Type: model.ChangeType_RENAME, 109 OldPath: "unrelated_file_1.cc", 110 NewPath: "unrelated_file_2.cc", 111 }, 112 { 113 Type: model.ChangeType_DELETE, 114 OldPath: "x/y/aa.h", 115 }, 116 { 117 Type: model.ChangeType_MODIFY, 118 OldPath: "y/z/bb.cc", 119 NewPath: "y/z/bb.cc", 120 }, 121 }, 122 } 123 justification, err := AnalyzeOneChangeLog(c, signal, cl) 124 So(err, ShouldBeNil) 125 So(justification, ShouldResemble, &model.SuspectJustification{ 126 Items: []*model.SuspectJustificationItem{ 127 { 128 Score: 10, 129 FilePath: "dir/a/b/x.cc", 130 Reason: `The file "dir/a/b/x.cc" was added and it was in the failure log.`, 131 Type: model.JustificationType_FAILURELOG, 132 }, 133 { 134 Score: 2, 135 FilePath: "content/util.c", 136 Reason: "The file \"content/util.c\" was modified. It was related to the file obj/content/util.o which was in the failure log.", 137 Type: model.JustificationType_FAILURELOG, 138 }, 139 { 140 Score: 1, 141 FilePath: "x/y/aa.h", 142 Reason: "The file \"x/y/aa.h\" was deleted. It was related to the dependency x/y/aa_impl_mac.cc.", 143 Type: model.JustificationType_DEPENDENCY, 144 }, 145 { 146 Score: 1, 147 FilePath: "y/z/bb.cc", 148 Reason: "The file \"y/z/bb.cc\" was modified. It was related to the dependency y/z/bb_impl.cc.", 149 Type: model.JustificationType_DEPENDENCY, 150 }, 151 }, 152 }) 153 }) 154 }) 155 156 Convey("AnalyzeChangeLogs", t, func() { 157 c := context.Background() 158 signal := &model.CompileFailureSignal{ 159 Files: map[string][]int{ 160 "src/a/b/x.cc": {27}, 161 "obj/content/util.o": {}, 162 }, 163 } 164 165 Convey("Results should be sorted", func() { 166 cls := []*model.ChangeLog{ 167 { 168 Commit: "abcd", 169 Message: "First blah blah\nReviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/123\n bla", 170 ChangeLogDiffs: []model.ChangeLogDiff{ 171 { 172 Type: model.ChangeType_MODIFY, 173 NewPath: "content/util.c", 174 }, 175 }, 176 }, 177 { 178 Commit: "efgh", 179 Message: "Second blah blah\nReviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/456\n bla", 180 ChangeLogDiffs: []model.ChangeLogDiff{ 181 { 182 Type: model.ChangeType_RENAME, 183 OldPath: "unrelated_file_1.cc", 184 NewPath: "unrelated_file_2.cc", 185 }, 186 }, 187 }, 188 { 189 Commit: "wxyz", 190 Message: "Third blah blah\nReviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/789\n bla", 191 ChangeLogDiffs: []model.ChangeLogDiff{ 192 { 193 Type: model.ChangeType_ADD, 194 NewPath: "dir/a/b/x.cc", 195 }, 196 }, 197 }, 198 } 199 200 analysisResult, err := AnalyzeChangeLogs(c, signal, cls) 201 So(err, ShouldBeNil) 202 So(analysisResult, ShouldResemble, &model.HeuristicAnalysisResult{ 203 Items: []*model.HeuristicAnalysisResultItem{ 204 { 205 Commit: "wxyz", 206 ReviewUrl: "https://chromium-review.googlesource.com/c/chromium/src/+/789", 207 ReviewTitle: "Third blah blah", 208 Justification: &model.SuspectJustification{ 209 Items: []*model.SuspectJustificationItem{ 210 { 211 Score: 10, 212 FilePath: "dir/a/b/x.cc", 213 Reason: `The file "dir/a/b/x.cc" was added and it was in the failure log.`, 214 Type: model.JustificationType_FAILURELOG, 215 }, 216 }, 217 }, 218 }, 219 { 220 Commit: "abcd", 221 ReviewUrl: "https://chromium-review.googlesource.com/c/chromium/src/+/123", 222 ReviewTitle: "First blah blah", 223 Justification: &model.SuspectJustification{ 224 Items: []*model.SuspectJustificationItem{ 225 { 226 Score: 2, 227 FilePath: "content/util.c", 228 Reason: "The file \"content/util.c\" was modified. It was related to the file obj/content/util.o which was in the failure log.", 229 Type: model.JustificationType_FAILURELOG, 230 }, 231 }, 232 }, 233 }, 234 }, 235 }) 236 }) 237 }) 238 239 }