go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/analysis/internal/config/compiledcfg/config_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 compiledcfg 16 17 import ( 18 "context" 19 "fmt" 20 "testing" 21 "time" 22 23 "google.golang.org/protobuf/types/known/timestamppb" 24 25 "go.chromium.org/luci/common/clock/testclock" 26 "go.chromium.org/luci/gae/impl/memory" 27 "go.chromium.org/luci/server/caching" 28 29 "go.chromium.org/luci/analysis/internal/config" 30 configpb "go.chromium.org/luci/analysis/proto/config" 31 32 . "github.com/smartystreets/goconvey/convey" 33 . "go.chromium.org/luci/common/testing/assertions" 34 ) 35 36 func TestCompiledConfig(t *testing.T) { 37 Convey(`With In-Process Cache`, t, func() { 38 ctx := context.Background() 39 ctx = memory.Use(ctx) 40 ctx = caching.WithEmptyProcessCache(ctx) 41 ctx, tc := testclock.UseTime(ctx, testclock.TestTimeUTC) 42 43 create := func(uniqifier int) { 44 cfg := generateProjectConfig(uniqifier) 45 projectsCfg := map[string]*configpb.ProjectConfig{ 46 "myproject": cfg, 47 } 48 err := config.SetTestProjectConfig(ctx, projectsCfg) 49 So(err, ShouldBeNil) 50 } 51 clear := func() { 52 projectsCfg := map[string]*configpb.ProjectConfig{} 53 err := config.SetTestProjectConfig(ctx, projectsCfg) 54 So(err, ShouldBeNil) 55 } 56 verify := func(minimumVersion time.Time, uniqifier int) { 57 cfg, err := Project(ctx, "myproject", minimumVersion) 58 So(err, ShouldBeNil) 59 60 expectedCfg := generateProjectConfig(uniqifier) 61 So(cfg.Config, ShouldResembleProto, expectedCfg) 62 So(cfg.LastUpdated, ShouldEqual, expectedCfg.LastUpdated.AsTime()) 63 So(len(cfg.TestNameRules), ShouldEqual, 1) 64 65 testName := fmt.Sprintf(`ninja://test_name/%v`, uniqifier) 66 rule := cfg.TestNameRules[0] 67 like, ok := rule(testName) 68 So(ok, ShouldBeTrue) 69 So(like, ShouldEqual, testName+"%") 70 71 So(cfg.ReasonMaskPatterns[0].String(), ShouldEqual, `(?:^\[Fixture failure\] )[a-zA-Z0-9_]+(?:[:])`) 72 } 73 verifyNotExists := func(minimumVersion time.Time) { 74 cfg, err := Project(ctx, "myproject", minimumVersion) 75 So(err, ShouldBeNil) 76 So(cfg.Config, ShouldResembleProto, &configpb.ProjectConfig{LastUpdated: timestamppb.New(config.StartingEpoch)}) 77 } 78 Convey(`Does not exist`, func() { 79 verifyNotExists(config.StartingEpoch) 80 81 Convey(`Then exists`, func() { 82 create(1) 83 84 // Verify the old cache item is retained. 85 verifyNotExists(config.StartingEpoch) 86 87 Convey(`Evict by cache expiry`, func() { 88 // Let the cache expire (note this expires the cache 89 // in the config package, not this package). 90 tc.Add(2 * config.ProjectCacheExpiry) 91 92 verify(config.StartingEpoch, 1) 93 verify(configVersion(1), 1) 94 }) 95 Convey(`Manually evict`, func() { 96 // Force the cache to be cleared by requesting 97 // a more recent version of config. 98 verify(configVersion(1), 1) 99 100 verify(config.StartingEpoch, 1) 101 }) 102 }) 103 Convey(`Then not exists`, func() { 104 clear() 105 verifyNotExists(config.StartingEpoch) 106 }) 107 }) 108 Convey(`Exists`, func() { 109 create(1) 110 verify(config.StartingEpoch, 1) 111 verify(configVersion(1), 1) 112 113 Convey(`Then modify`, func() { 114 create(2) 115 116 // Verify the old entry is retained. 117 verify(config.StartingEpoch, 1) 118 verify(configVersion(1), 1) 119 120 Convey(`Evict by cache expiry`, func() { 121 // Let the cache expire (note this expires the cache 122 // in the config package, not this package). 123 tc.Add(2 * config.ProjectCacheExpiry) 124 125 verify(config.StartingEpoch, 2) 126 verify(configVersion(1), 2) 127 verify(configVersion(2), 2) 128 }) 129 Convey(`Manually evict`, func() { 130 // Force the cache to be cleared by requesting 131 // a more recent version of config. 132 verify(configVersion(2), 2) 133 134 verify(configVersion(1), 2) 135 verify(config.StartingEpoch, 2) 136 }) 137 }) 138 Convey(`Then retain`, func() { 139 // Let the cache expire (note this expires the cache 140 // in the config package, not this package). 141 tc.Add(2 * config.ProjectCacheExpiry) 142 143 verify(config.StartingEpoch, 1) 144 verify(configVersion(1), 1) 145 }) 146 Convey(`Then delete`, func() { 147 clear() 148 149 // Let the cache expire (note this expires the cache 150 // in the config package, not this package). 151 tc.Add(2 * config.ProjectCacheExpiry) 152 153 verifyNotExists(config.StartingEpoch) 154 }) 155 }) 156 }) 157 } 158 159 func generateProjectConfig(uniqifier int) *configpb.ProjectConfig { 160 cfg := &configpb.Clustering{ 161 TestNameRules: []*configpb.TestNameClusteringRule{ 162 { 163 Name: "Google Test (Value-parameterized)", 164 Pattern: fmt.Sprintf(`^ninja://test_name/%v$`, uniqifier), 165 LikeTemplate: fmt.Sprintf(`ninja://test_name/%v%%`, uniqifier), 166 }, 167 }, 168 ReasonMaskPatterns: []string{`(?:^\[Fixture failure\] )[a-zA-Z0-9_]+(?:[:])`}, 169 } 170 version := configVersion(uniqifier) 171 projectCfg := &configpb.ProjectConfig{ 172 Clustering: cfg, 173 LastUpdated: timestamppb.New(version), 174 } 175 return projectCfg 176 } 177 178 func configVersion(uniqifier int) time.Time { 179 return time.Date(2020, 1, 2, 3, 4, 5, uniqifier, time.UTC) 180 }