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  }