go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/analysis/internal/clustering/algorithms/testname/testname_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 testname 16 17 import ( 18 "testing" 19 20 "go.chromium.org/luci/analysis/internal/clustering" 21 "go.chromium.org/luci/analysis/internal/clustering/rules/lang" 22 "go.chromium.org/luci/analysis/internal/config/compiledcfg" 23 configpb "go.chromium.org/luci/analysis/proto/config" 24 pb "go.chromium.org/luci/analysis/proto/v1" 25 26 . "github.com/smartystreets/goconvey/convey" 27 ) 28 29 func TestAlgorithm(t *testing.T) { 30 rules := []*configpb.TestNameClusteringRule{ 31 { 32 Name: "Blink Web Tests", 33 Pattern: `^ninja://:blink_web_tests/(virtual/[^/]+/)?(?P<testname>([^/]+/)+[^/]+\.[a-zA-Z]+).*$`, 34 LikeTemplate: "ninja://:blink\\_web\\_tests/%${testname}%", 35 }, 36 } 37 cfgpb := &configpb.ProjectConfig{ 38 Clustering: &configpb.Clustering{ 39 TestNameRules: rules, 40 }, 41 } 42 43 Convey(`Name`, t, func() { 44 // Algorithm name should be valid. 45 a := &Algorithm{} 46 So(clustering.AlgorithmRe.MatchString(a.Name()), ShouldBeTrue) 47 }) 48 Convey(`Cluster`, t, func() { 49 a := &Algorithm{} 50 cfg, err := compiledcfg.NewConfig(cfgpb) 51 So(err, ShouldBeNil) 52 53 Convey(`ID of appropriate length`, func() { 54 id := a.Cluster(cfg, &clustering.Failure{ 55 TestID: "ninja://test_name", 56 }) 57 // IDs may be 16 bytes at most. 58 So(len(id), ShouldBeGreaterThan, 0) 59 So(len(id), ShouldBeLessThanOrEqualTo, clustering.MaxClusterIDBytes) 60 }) 61 Convey(`Same ID for same test name`, func() { 62 Convey(`No matching rules`, func() { 63 id1 := a.Cluster(cfg, &clustering.Failure{ 64 TestID: "ninja://test_name_one/", 65 Reason: &pb.FailureReason{PrimaryErrorMessage: "A"}, 66 }) 67 id2 := a.Cluster(cfg, &clustering.Failure{ 68 TestID: "ninja://test_name_one/", 69 Reason: &pb.FailureReason{PrimaryErrorMessage: "B"}, 70 }) 71 So(id2, ShouldResemble, id1) 72 }) 73 Convey(`Matching rules`, func() { 74 id1 := a.Cluster(cfg, &clustering.Failure{ 75 TestID: "ninja://:blink_web_tests/virtual/abc/folder/test-name.html", 76 Reason: &pb.FailureReason{PrimaryErrorMessage: "A"}, 77 }) 78 id2 := a.Cluster(cfg, &clustering.Failure{ 79 TestID: "ninja://:blink_web_tests/folder/test-name.html?param=2", 80 Reason: &pb.FailureReason{PrimaryErrorMessage: "B"}, 81 }) 82 So(id2, ShouldResemble, id1) 83 }) 84 }) 85 Convey(`Different ID for different clusters`, func() { 86 Convey(`No matching rules`, func() { 87 id1 := a.Cluster(cfg, &clustering.Failure{ 88 TestID: "ninja://test_name_one/", 89 }) 90 id2 := a.Cluster(cfg, &clustering.Failure{ 91 TestID: "ninja://test_name_two/", 92 }) 93 So(id2, ShouldNotResemble, id1) 94 }) 95 Convey(`Matching rules`, func() { 96 id1 := a.Cluster(cfg, &clustering.Failure{ 97 TestID: "ninja://:blink_web_tests/virtual/abc/folder/test-name-a.html", 98 Reason: &pb.FailureReason{PrimaryErrorMessage: "A"}, 99 }) 100 id2 := a.Cluster(cfg, &clustering.Failure{ 101 TestID: "ninja://:blink_web_tests/folder/test-name-b.html?param=2", 102 Reason: &pb.FailureReason{PrimaryErrorMessage: "B"}, 103 }) 104 So(id2, ShouldNotResemble, id1) 105 }) 106 }) 107 }) 108 Convey(`Failure Association Rule`, t, func() { 109 a := &Algorithm{} 110 cfg, err := compiledcfg.NewConfig(cfgpb) 111 So(err, ShouldBeNil) 112 113 test := func(failure *clustering.Failure, expectedRule string) { 114 rule := a.FailureAssociationRule(cfg, failure) 115 So(rule, ShouldEqual, expectedRule) 116 117 // Test the rule is valid syntax and matches at least the example failure. 118 expr, err := lang.Parse(rule) 119 So(err, ShouldBeNil) 120 So(expr.Evaluate(failure), ShouldBeTrue) 121 } 122 Convey(`No matching rules`, func() { 123 failure := &clustering.Failure{ 124 TestID: "ninja://test_name_one/", 125 } 126 test(failure, `test = "ninja://test_name_one/"`) 127 }) 128 Convey(`Matching rule`, func() { 129 failure := &clustering.Failure{ 130 TestID: "ninja://:blink_web_tests/virtual/dark-color-scheme/fast/forms/color-scheme/select/select-multiple-hover-unselected.html", 131 } 132 test(failure, `test LIKE "ninja://:blink\\_web\\_tests/%fast/forms/color-scheme/select/select-multiple-hover-unselected.html%"`) 133 }) 134 Convey(`Escapes LIKE syntax`, func() { 135 failure := &clustering.Failure{ 136 TestID: `ninja://:blink_web_tests/a/b_\%c.html`, 137 } 138 test(failure, `test LIKE "ninja://:blink\\_web\\_tests/%a/b\\_\\\\\\%c.html%"`) 139 }) 140 Convey(`Escapes non-graphic Unicode characters`, func() { 141 failure := &clustering.Failure{ 142 TestID: "\u0000\r\n\v\u202E\u2066", 143 } 144 test(failure, `test = "\x00\r\n\v\u202e\u2066"`) 145 }) 146 }) 147 Convey(`Cluster Title`, t, func() { 148 a := &Algorithm{} 149 cfg, err := compiledcfg.NewConfig(cfgpb) 150 So(err, ShouldBeNil) 151 152 Convey(`No matching rules`, func() { 153 failure := &clustering.Failure{ 154 TestID: "ninja://test_name_one", 155 } 156 title := a.ClusterTitle(cfg, failure) 157 So(title, ShouldEqual, "ninja://test_name_one") 158 }) 159 Convey(`Matching rule`, func() { 160 failure := &clustering.Failure{ 161 TestID: "ninja://:blink_web_tests/virtual/dark-color-scheme/fast/forms/color-scheme/select/select-multiple-hover-unselected.html", 162 } 163 title := a.ClusterTitle(cfg, failure) 164 So(title, ShouldEqual, `ninja://:blink\\_web\\_tests/%fast/forms/color-scheme/select/select-multiple-hover-unselected.html%`) 165 }) 166 }) 167 Convey(`Cluster Description`, t, func() { 168 a := &Algorithm{} 169 cfg, err := compiledcfg.NewConfig(cfgpb) 170 So(err, ShouldBeNil) 171 172 Convey(`No matching rules`, func() { 173 summary := &clustering.ClusterSummary{ 174 Example: clustering.Failure{ 175 TestID: "ninja://test_name_one", 176 }, 177 } 178 description, err := a.ClusterDescription(cfg, summary) 179 So(err, ShouldBeNil) 180 So(description.Title, ShouldEqual, "ninja://test_name_one") 181 So(description.Description, ShouldContainSubstring, "ninja://test_name_one") 182 }) 183 Convey(`Matching rule`, func() { 184 summary := &clustering.ClusterSummary{ 185 Example: clustering.Failure{ 186 TestID: "ninja://:blink_web_tests/virtual/dark-color-scheme/fast/forms/color-scheme/select/select-multiple-hover-unselected.html", 187 }, 188 } 189 description, err := a.ClusterDescription(cfg, summary) 190 So(err, ShouldBeNil) 191 So(description.Title, ShouldEqual, `ninja://:blink\\_web\\_tests/%fast/forms/color-scheme/select/select-multiple-hover-unselected.html%`) 192 So(description.Description, ShouldContainSubstring, `ninja://:blink\\_web\\_tests/%fast/forms/color-scheme/select/select-multiple-hover-unselected.html%`) 193 }) 194 }) 195 }