go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/cv/internal/tryjob/requirement/location_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 requirement 16 17 import ( 18 "testing" 19 20 gerritpb "go.chromium.org/luci/common/proto/gerrit" 21 22 cfgpb "go.chromium.org/luci/cv/api/config/v2" 23 "go.chromium.org/luci/cv/internal/changelist" 24 "go.chromium.org/luci/cv/internal/common" 25 "go.chromium.org/luci/cv/internal/cvtesting" 26 gf "go.chromium.org/luci/cv/internal/gerrit/gerritfake" 27 "go.chromium.org/luci/cv/internal/run" 28 29 . "github.com/smartystreets/goconvey/convey" 30 ) 31 32 func TestLocationFilterMatch(t *testing.T) { 33 Convey("locationFilterMatch works", t, func() { 34 ct := cvtesting.Test{} 35 ctx, cancel := ct.SetUp(t) 36 defer cancel() 37 38 // For the purpose of locationFilterMatch, only the Gerrit host, 39 // project and paths matter. Parent commits are also taken into 40 // consideration when checking if a CL is a merge commit. 41 makeCL := func(host, project string, paths []string) *run.RunCL { 42 return &run.RunCL{ 43 ID: common.CLID(1234), 44 Detail: &changelist.Snapshot{ 45 Kind: &changelist.Snapshot_Gerrit{ 46 Gerrit: &changelist.Gerrit{ 47 Info: &gerritpb.ChangeInfo{Project: project}, 48 Host: host, 49 Files: paths, 50 }, 51 }, 52 }, 53 } 54 } 55 56 Convey("with includes and excludes", func() { 57 lfs := []*cfgpb.Verifiers_Tryjob_Builder_LocationFilter{ 58 { 59 GerritHostRegexp: "x-review.googlesource.com", 60 GerritProjectRegexp: "gp", 61 PathRegexp: "included/.*", 62 Exclude: false, 63 }, 64 { 65 GerritHostRegexp: "x-review.googlesource.com", 66 GerritProjectRegexp: "gp", 67 PathRegexp: "included/excluded/.*", 68 Exclude: true, 69 }, 70 } 71 72 Convey("in included dir", func() { 73 included, err := locationFilterMatch(ctx, lfs, 74 []*run.RunCL{makeCL("x-review.googlesource.com", "gp", []string{"included/readme.md"})}) 75 So(err, ShouldBeNil) 76 So(included, ShouldBeTrue) 77 }) 78 79 Convey("in excluded dir", func() { 80 included, err := locationFilterMatch(ctx, lfs, 81 []*run.RunCL{makeCL("x-review.googlesource.com", "gp", []string{"included/excluded/foo"})}) 82 So(err, ShouldBeNil) 83 So(included, ShouldBeFalse) 84 }) 85 86 Convey("host doesn't match", func() { 87 included, err := locationFilterMatch(ctx, lfs, 88 []*run.RunCL{makeCL("other-review.googlesource.com", "gp", []string{"included/foo"})}) 89 So(err, ShouldBeNil) 90 So(included, ShouldBeFalse) 91 }) 92 93 Convey("project doesn't match", func() { 94 included, err := locationFilterMatch(ctx, lfs, 95 []*run.RunCL{makeCL("x-review.googlesource.com", "xyz", []string{"included/foo"})}) 96 So(err, ShouldBeNil) 97 So(included, ShouldBeFalse) 98 }) 99 100 Convey("merge commit: no files changed and two parents", func() { 101 mergeCL := makeCL("x-review.googlesource.com", "gp", []string{}) 102 mergeCL.Detail.GetGerrit().Info = gf.CI(1234, gf.Project("gp"), gf.ParentCommits([]string{"one", "two"})) 103 included, err := locationFilterMatch(ctx, lfs, []*run.RunCL{mergeCL}) 104 So(err, ShouldBeNil) 105 So(included, ShouldBeTrue) 106 }) 107 108 Convey("no files changed and one parents", func() { 109 // Any empty diff with no files is possible for non-merge-commits. 110 // Not treated as merge commit, so builder should not be triggered. 111 mergeCL := makeCL("x-review.googlesource.com", "gp", []string{}) 112 mergeCL.Detail.GetGerrit().Info = gf.CI(1234, gf.Project("gp"), gf.ParentCommits([]string{"one"})) 113 included, err := locationFilterMatch(ctx, lfs, []*run.RunCL{mergeCL}) 114 So(err, ShouldBeNil) 115 So(included, ShouldBeFalse) 116 }) 117 118 Convey("with multiple CLs and multiple files", func() { 119 included, err := locationFilterMatch(ctx, lfs, 120 []*run.RunCL{ 121 makeCL("x-review.googlesource.com", "gp", []string{"included/readme.md", "included/excluded/foo.txt"}), 122 makeCL("x-review.googlesource.com", "foo", []string{"readme.md"}), 123 }) 124 So(err, ShouldBeNil) 125 So(included, ShouldBeTrue) 126 }) 127 }) 128 129 Convey("with initial exclude", func() { 130 lfs := []*cfgpb.Verifiers_Tryjob_Builder_LocationFilter{ 131 { 132 GerritHostRegexp: "x-review.googlesource.com", 133 GerritProjectRegexp: "gp", 134 PathRegexp: "excluded/.*", 135 Exclude: true, 136 }, 137 } 138 139 included, err := locationFilterMatch(ctx, lfs, 140 []*run.RunCL{makeCL("x-review.googlesource.com", "gp", []string{"excluded/readme.md"})}) 141 So(err, ShouldBeNil) 142 So(included, ShouldBeFalse) 143 144 included, err = locationFilterMatch(ctx, lfs, 145 []*run.RunCL{makeCL("x-review.googlesource.com", "gp", []string{"somewhere/else"})}) 146 So(err, ShouldBeNil) 147 So(included, ShouldBeTrue) 148 }) 149 150 Convey("with some patterns empty", func() { 151 lfs := []*cfgpb.Verifiers_Tryjob_Builder_LocationFilter{ 152 { 153 PathRegexp: "included/.*", 154 Exclude: false, 155 }, 156 } 157 158 included, err := locationFilterMatch(ctx, lfs, 159 []*run.RunCL{makeCL("x-review.googlesource.com", "gp", []string{"included/readme.md"})}) 160 So(err, ShouldBeNil) 161 So(included, ShouldBeTrue) 162 163 included, err = locationFilterMatch(ctx, lfs, 164 []*run.RunCL{makeCL("example.com", "blah", []string{"included/readme.md"})}) 165 So(err, ShouldBeNil) 166 So(included, ShouldBeTrue) 167 168 included, err = locationFilterMatch(ctx, lfs, 169 []*run.RunCL{makeCL("example.com", "blah", []string{"somewhere/else"})}) 170 So(err, ShouldBeNil) 171 So(included, ShouldBeFalse) 172 }) 173 174 Convey("returns error given invalid regex", func() { 175 lfs := []*cfgpb.Verifiers_Tryjob_Builder_LocationFilter{ 176 { 177 PathRegexp: "([i*", 178 }, 179 } 180 included, err := locationFilterMatch(ctx, lfs, 181 []*run.RunCL{makeCL("x-review.googlesource.com", "gp", []string{"included/readme.md"})}) 182 So(err, ShouldNotBeNil) 183 So(included, ShouldBeFalse) 184 }) 185 186 Convey("with nested include exclude include", func() { 187 lfs := []*cfgpb.Verifiers_Tryjob_Builder_LocationFilter{ 188 { 189 PathRegexp: "included/.*", 190 Exclude: false, 191 }, 192 { 193 PathRegexp: "included/excluded/.*", 194 Exclude: true, 195 }, 196 { 197 PathRegexp: "included/excluded/included.txt", 198 Exclude: false, 199 }, 200 } 201 202 included, err := locationFilterMatch(ctx, lfs, 203 []*run.RunCL{makeCL("x-review.googlesource.com", "gp", []string{"included/readme.md"})}) 204 So(err, ShouldBeNil) 205 So(included, ShouldBeTrue) 206 207 // In excluded dir. 208 included, err = locationFilterMatch(ctx, lfs, 209 []*run.RunCL{makeCL("x-review.googlesource.com", "gp", []string{"included/excluded/foo"})}) 210 So(err, ShouldBeNil) 211 So(included, ShouldBeFalse) 212 213 // In excluded dir, but an exception. 214 included, err = locationFilterMatch(ctx, lfs, 215 []*run.RunCL{makeCL("x-review.googlesource.com", "gp", []string{"included/excluded/included.txt"})}) 216 So(err, ShouldBeNil) 217 So(included, ShouldBeTrue) 218 }) 219 }) 220 } 221 222 func TestHelperFunctions(t *testing.T) { 223 Convey("isMergeCommit", t, func() { 224 225 ct := cvtesting.Test{} 226 ctx, cancel := ct.SetUp(t) 227 defer cancel() 228 Convey("returns false on missing data", func() { 229 So(isMergeCommit(ctx, nil), ShouldBeFalse) 230 So(isMergeCommit(ctx, &changelist.Gerrit{}), ShouldBeFalse) 231 So(isMergeCommit(ctx, &changelist.Gerrit{Info: &gerritpb.ChangeInfo{}}), ShouldBeFalse) 232 So(isMergeCommit(ctx, &changelist.Gerrit{Info: gf.CI(10)}), ShouldBeFalse) 233 }) 234 Convey("returns true with no files and two parents", func() { 235 So(isMergeCommit( 236 ctx, 237 &changelist.Gerrit{ 238 Files: []string{}, 239 Info: gf.CI(10, gf.ParentCommits([]string{"one", "two"})), 240 }), ShouldBeTrue) 241 }) 242 Convey("returns false with no files and one parent", func() { 243 So(isMergeCommit( 244 ctx, 245 &changelist.Gerrit{ 246 Files: []string{}, 247 Info: gf.CI(10, gf.ParentCommits([]string{"one"})), 248 }), ShouldBeFalse) 249 }) 250 }) 251 252 Convey("hostAndProjectMatch", t, func() { 253 ct := cvtesting.Test{} 254 ctx, cancel := ct.SetUp(t) 255 defer cancel() 256 257 Convey("when only some files in a repo are included", func() { 258 lfs := []*cfgpb.Verifiers_Tryjob_Builder_LocationFilter{ 259 { 260 GerritHostRegexp: "x-review.googlesource.com", 261 GerritProjectRegexp: "gp", 262 PathRegexp: "included/.*", 263 Exclude: false, 264 }, 265 { 266 GerritHostRegexp: "x-review.googlesource.com", 267 GerritProjectRegexp: "gp", 268 PathRegexp: "included/excluded/.*", 269 Exclude: true, 270 }, 271 } 272 compiled, err := compileLocationFilters(ctx, lfs) 273 So(err, ShouldBeNil) 274 So(hostAndProjectMatch(compiled, "x-review.googlesource.com", "gp"), ShouldBeTrue) 275 So(hostAndProjectMatch(compiled, "x-review.googlesource.com", "other"), ShouldBeFalse) 276 }) 277 278 Convey("simple match all case", func() { 279 lfs := []*cfgpb.Verifiers_Tryjob_Builder_LocationFilter{ 280 { 281 GerritHostRegexp: ".*", 282 GerritProjectRegexp: ".*", 283 PathRegexp: "foo/.*", 284 Exclude: false, 285 }, 286 } 287 compiled, err := compileLocationFilters(ctx, lfs) 288 So(err, ShouldBeNil) 289 So(hostAndProjectMatch(compiled, "x-review.googlesource.com", "gp"), ShouldBeTrue) 290 }) 291 292 Convey("default include case", func() { 293 lfs := []*cfgpb.Verifiers_Tryjob_Builder_LocationFilter{ 294 { 295 GerritHostRegexp: ".*", 296 GerritProjectRegexp: ".*", 297 PathRegexp: "foo/.*", 298 Exclude: true, 299 }, 300 } 301 compiled, err := compileLocationFilters(ctx, lfs) 302 So(err, ShouldBeNil) 303 So(hostAndProjectMatch(compiled, "x-review.googlesource.com", "gp"), ShouldBeTrue) 304 }) 305 }) 306 }