go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/analysis/internal/clustering/algorithms/testname/rules/rule_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 rules 16 17 import ( 18 "testing" 19 20 configpb "go.chromium.org/luci/analysis/proto/config" 21 22 . "github.com/smartystreets/goconvey/convey" 23 . "go.chromium.org/luci/common/testing/assertions" 24 ) 25 26 func TestRule(t *testing.T) { 27 Convey(`Evaluate`, t, func() { 28 Convey(`Valid Examples`, func() { 29 Convey(`Blink Web Tests`, func() { 30 rule := &configpb.TestNameClusteringRule{ 31 Name: "Blink Web Tests", 32 Pattern: `^ninja://:blink_web_tests/(virtual/[^/]+/)?(?P<testname>([^/]+/)+[^/]+\.[a-zA-Z]+).*$`, 33 LikeTemplate: `ninja://:blink\_web\_tests/%${testname}%`, 34 } 35 eval, err := Compile(rule) 36 So(err, ShouldBeNil) 37 38 inputs := []string{ 39 "ninja://:blink_web_tests/virtual/oopr-canvas2d/fast/canvas/canvas-getImageData.html", 40 "ninja://:blink_web_tests/virtual/oopr-canvas2d/fast/canvas/canvas-getImageData.html?param=a", 41 "ninja://:blink_web_tests/virtual/oopr-canvas3d/fast/canvas/canvas-getImageData.html?param=b", 42 "ninja://:blink_web_tests/fast/canvas/canvas-getImageData.html", 43 } 44 for _, testname := range inputs { 45 like, ok := eval(testname) 46 So(ok, ShouldBeTrue) 47 So(like, ShouldEqual, `ninja://:blink\_web\_tests/%fast/canvas/canvas-getImageData.html%`) 48 } 49 50 _, ok := eval("ninja://:not_blink_web_tests/fast/canvas/canvas-getImageData.html") 51 So(ok, ShouldBeFalse) 52 }) 53 Convey(`Google Tests`, func() { 54 rule := &configpb.TestNameClusteringRule{ 55 Name: "Google Test (Value-parameterized)", 56 // E.g. ninja:{target}/Prefix/ColorSpaceTest.testNullTransform/11 57 // Note that "Prefix/" portion may be blank/omitted. 58 Pattern: `^ninja:(?P<target>[\w/]+:\w+)/(\w+/)?(?P<suite>\w+)\.(?P<case>\w+)/\w+$`, 59 LikeTemplate: `ninja:${target}/%${suite}.${case}%`, 60 } 61 eval, err := Compile(rule) 62 So(err, ShouldBeNil) 63 64 inputs := []string{ 65 "ninja://chrome/test:interactive_ui_tests/Name/ColorSpaceTest.testNullTransform/0", 66 "ninja://chrome/test:interactive_ui_tests/Name/ColorSpaceTest.testNullTransform/0", 67 "ninja://chrome/test:interactive_ui_tests/Name/ColorSpaceTest.testNullTransform/11", 68 } 69 for _, testname := range inputs { 70 like, ok := eval(testname) 71 So(ok, ShouldBeTrue) 72 So(like, ShouldEqual, "ninja://chrome/test:interactive\\_ui\\_tests/%ColorSpaceTest.testNullTransform%") 73 } 74 75 _, ok := eval("ninja://:blink_web_tests/virtual/oopr-canvas2d/fast/canvas/canvas-getImageData.html") 76 So(ok, ShouldBeFalse) 77 }) 78 }) 79 Convey(`Test name escaping in LIKE output`, func() { 80 Convey(`Test name is escaped when substituted`, func() { 81 rule := &configpb.TestNameClusteringRule{ 82 Name: "Escape test", 83 Pattern: `^(?P<testname>.*)$`, 84 LikeTemplate: `${testname}_%`, 85 } 86 eval, err := Compile(rule) 87 So(err, ShouldBeNil) 88 89 // Verify that the test name is not injected varbatim in the generated 90 // like expression, but is escaped to avoid it being interpreted. 91 like, ok := eval(`_\%`) 92 So(ok, ShouldBeTrue) 93 So(like, ShouldEqual, `\_\\\%_%`) 94 }) 95 Convey(`Unsafe LIKE templates are rejected`, func() { 96 rule := &configpb.TestNameClusteringRule{ 97 Name: "Escape test", 98 Pattern: `^path\\(?P<testname>.*)$`, 99 // The user as incorrectly used an unfinished LIKE escape sequence 100 // (with trailing '\') before the testname substitution. 101 // If substitution were allowed, this may allow the test name to be 102 // interpreted as a LIKE expression instead as literal text. 103 // E.g. a test name of `path\%` may yield `path\\%` after template 104 // evaluation which invokes the LIKE '%' operator. 105 LikeTemplate: `path\${testname}`, 106 } 107 _, err := Compile(rule) 108 So(err, ShouldErrLike, `"path\\" is not a valid standalone LIKE expression: unfinished escape sequence "\" at end of LIKE pattern`) 109 }) 110 }) 111 Convey(`Substitution operator`, func() { 112 Convey(`Dollar sign can be inserted into output`, func() { 113 rule := &configpb.TestNameClusteringRule{ 114 Name: "Insert $", 115 Pattern: `^(?P<testname>.*)$`, 116 LikeTemplate: `${testname}$$blah$$$$`, 117 } 118 eval, err := Compile(rule) 119 So(err, ShouldBeNil) 120 121 like, ok := eval(`test`) 122 So(ok, ShouldBeTrue) 123 So(like, ShouldEqual, `test$blah$$`) 124 }) 125 Convey(`Invalid uses of substitution operator are rejected`, func() { 126 rule := &configpb.TestNameClusteringRule{ 127 Name: "Invalid use of $ (neither $$ or ${name})", 128 Pattern: `^(?P<testname>.*)$`, 129 LikeTemplate: `${testname}blah$$$`, 130 } 131 _, err := Compile(rule) 132 So(err, ShouldErrLike, `invalid use of the $ operator at position 17 in "${testname}blah$$$"`) 133 134 rule = &configpb.TestNameClusteringRule{ 135 Name: "Invalid use of $ (invalid capture group name)", 136 Pattern: `^(?P<testname>.*)$`, 137 LikeTemplate: `${template@}blah`, 138 } 139 _, err = Compile(rule) 140 So(err, ShouldErrLike, `invalid use of the $ operator at position 0 in "${template@}blah"`) 141 142 rule = &configpb.TestNameClusteringRule{ 143 Name: "Capture group name not defined", 144 Pattern: `^(?P<testname>.*)$`, 145 LikeTemplate: `${myname}blah`, 146 } 147 _, err = Compile(rule) 148 So(err, ShouldErrLike, `like_template: contains reference to non-existant capturing group with name "myname"`) 149 }) 150 }) 151 Convey(`Invalid Pattern`, func() { 152 rule := &configpb.TestNameClusteringRule{ 153 Name: "Invalid Pattern", 154 Pattern: `[`, 155 LikeTemplate: ``, 156 } 157 _, err := Compile(rule) 158 So(err, ShouldErrLike, `pattern: error parsing regexp`) 159 }) 160 }) 161 }