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  }