go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/cv/internal/tryjob/requirement/diff_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 "math/rand" 19 "testing" 20 21 "google.golang.org/protobuf/proto" 22 23 buildbucketpb "go.chromium.org/luci/buildbucket/proto" 24 "go.chromium.org/luci/common/clock/testclock" 25 26 cfgpb "go.chromium.org/luci/cv/api/config/v2" 27 "go.chromium.org/luci/cv/internal/tryjob" 28 29 . "github.com/smartystreets/goconvey/convey" 30 . "go.chromium.org/luci/common/testing/assertions" 31 ) 32 33 func TestDiff(t *testing.T) { 34 t.Parallel() 35 36 Convey("Diff works", t, func() { 37 makeBBTryjobDefinition := func(host, project, bucket, builder string) *tryjob.Definition { 38 return &tryjob.Definition{ 39 Backend: &tryjob.Definition_Buildbucket_{ 40 Buildbucket: &tryjob.Definition_Buildbucket{ 41 Host: host, 42 Builder: &buildbucketpb.BuilderID{ 43 Project: project, 44 Bucket: bucket, 45 Builder: builder, 46 }, 47 }, 48 }, 49 } 50 } 51 52 Convey("Compare definitions", func() { 53 Convey("Empty base and empty target", func() { 54 res := Diff(&tryjob.Requirement{}, &tryjob.Requirement{}) 55 So(res.AddedDefs, ShouldBeEmpty) 56 So(res.ChangedDefs, ShouldBeEmpty) 57 So(res.RemovedDefs, ShouldBeEmpty) 58 }) 59 Convey("Empty base", func() { 60 def := makeBBTryjobDefinition("a.example.com", "infra", "try", "someBuilder") 61 res := Diff( 62 &tryjob.Requirement{}, 63 &tryjob.Requirement{ 64 Definitions: []*tryjob.Definition{def}, 65 }) 66 So(res.AddedDefs, ShouldHaveLength, 1) 67 So(res.AddedDefs, ShouldContainKey, def) 68 So(res.ChangedDefs, ShouldBeEmpty) 69 So(res.RemovedDefs, ShouldBeEmpty) 70 }) 71 Convey("Empty target", func() { 72 def := makeBBTryjobDefinition("a.example.com", "infra", "try", "someBuilder") 73 res := Diff( 74 &tryjob.Requirement{ 75 Definitions: []*tryjob.Definition{def}, 76 }, 77 &tryjob.Requirement{}) 78 So(res.AddedDefs, ShouldBeEmpty) 79 So(res.ChangedDefs, ShouldBeEmpty) 80 So(res.RemovedDefs, ShouldHaveLength, 1) 81 So(res.RemovedDefs, ShouldContainKey, def) 82 }) 83 Convey("Target has one extra", func() { 84 shared := makeBBTryjobDefinition("a.example.com", "infra", "try", "someBuilder") 85 extra := makeBBTryjobDefinition("a.example.com", "infra", "ci", "someBuilder") 86 res := Diff( 87 &tryjob.Requirement{ 88 Definitions: []*tryjob.Definition{shared}, 89 }, 90 &tryjob.Requirement{ 91 Definitions: []*tryjob.Definition{proto.Clone(shared).(*tryjob.Definition), extra}, 92 }) 93 So(res.AddedDefs, ShouldHaveLength, 1) 94 So(res.AddedDefs, ShouldContainKey, extra) 95 So(res.ChangedDefs, ShouldBeEmpty) 96 So(res.UnchangedDefs, ShouldHaveLength, 1) 97 So(res.UnchangedDefs[shared], ShouldResembleProto, shared) 98 So(res.RemovedDefs, ShouldBeEmpty) 99 }) 100 Convey("Target has one removed", func() { 101 shared := makeBBTryjobDefinition("a.example.com", "infra", "try", "someBuilder") 102 removed := makeBBTryjobDefinition("a.example.com", "infra", "ci", "someBuilder") 103 res := Diff( 104 &tryjob.Requirement{ 105 Definitions: []*tryjob.Definition{shared, removed}, 106 }, 107 &tryjob.Requirement{ 108 Definitions: []*tryjob.Definition{proto.Clone(shared).(*tryjob.Definition)}, 109 }) 110 So(res.AddedDefs, ShouldBeEmpty) 111 So(res.ChangedDefs, ShouldBeEmpty) 112 So(res.UnchangedDefs, ShouldHaveLength, 1) 113 So(res.UnchangedDefs[shared], ShouldResembleProto, shared) 114 So(res.RemovedDefs, ShouldHaveLength, 1) 115 So(res.RemovedDefs, ShouldContainKey, removed) 116 }) 117 Convey("Target has one changed", func() { 118 baseDef := makeBBTryjobDefinition("a.example.com", "infra", "try", "someBuilder") 119 targetDef := proto.Clone(baseDef).(*tryjob.Definition) 120 Convey("Change in equivalent", func() { 121 targetDef.EquivalentTo = makeBBTryjobDefinition("a.example.com", "infra", "try", "equiBuilder") 122 }) 123 Convey("Change in disable_reuse", func() { 124 targetDef.DisableReuse = !baseDef.GetDisableReuse() 125 }) 126 res := Diff( 127 &tryjob.Requirement{ 128 Definitions: []*tryjob.Definition{baseDef}, 129 }, 130 &tryjob.Requirement{ 131 Definitions: []*tryjob.Definition{targetDef}, 132 }) 133 So(res.AddedDefs, ShouldBeEmpty) 134 So(res.ChangedDefs, ShouldHaveLength, 1) 135 So(res.ChangedDefs[baseDef], ShouldResembleProto, targetDef) 136 So(res.ChangedDefsReverse, ShouldHaveLength, 1) 137 So(res.ChangedDefsReverse[targetDef], ShouldResembleProto, baseDef) 138 So(res.RemovedDefs, ShouldBeEmpty) 139 }) 140 Convey("Multiple Definitions", func() { 141 builder1 := makeBBTryjobDefinition("a.example.com", "infra", "try", "builder1") 142 builder2 := makeBBTryjobDefinition("a.example.com", "infra", "try", "builder2") 143 builder3 := makeBBTryjobDefinition("a.example.com", "infra", "try", "builder3") 144 builder3Changed := proto.Clone(builder3).(*tryjob.Definition) 145 builder3Changed.DisableReuse = !builder3.GetDisableReuse() 146 builder4 := makeBBTryjobDefinition("a.example.com", "infra", "try", "builder4") 147 builderDiffProj := makeBBTryjobDefinition("a.example.com", "chrome", "try", "builder1") 148 builderDiffProjChanged := proto.Clone(builderDiffProj).(*tryjob.Definition) 149 builderDiffProjChanged.EquivalentTo = makeBBTryjobDefinition("a.example.com", "chrome", "try", "equi-builder1") 150 151 base := &tryjob.Requirement{ 152 Definitions: []*tryjob.Definition{builder1, builder2, builder3, builderDiffProj}, 153 } 154 target := &tryjob.Requirement{ 155 Definitions: []*tryjob.Definition{proto.Clone(builder1).(*tryjob.Definition), builder3Changed, builder4, builderDiffProjChanged}, 156 } 157 rand.Seed(testclock.TestRecentTimeUTC.Unix()) 158 rand.Shuffle(len(base.Definitions), func(i, j int) { 159 base.Definitions[i], base.Definitions[j] = base.Definitions[j], base.Definitions[i] 160 }) 161 rand.Shuffle(len(target.Definitions), func(i, j int) { 162 target.Definitions[i], target.Definitions[j] = target.Definitions[j], target.Definitions[i] 163 }) 164 res := Diff(base, target) 165 So(res.AddedDefs, ShouldHaveLength, 1) 166 So(res.AddedDefs, ShouldContainKey, builder4) 167 So(res.RemovedDefs, ShouldHaveLength, 1) 168 So(res.RemovedDefs, ShouldContainKey, builder2) 169 So(res.ChangedDefs, ShouldHaveLength, 2) 170 So(res.ChangedDefs[builder3], ShouldResembleProto, builder3Changed) 171 So(res.ChangedDefs[builderDiffProj], ShouldResembleProto, builderDiffProjChanged) 172 So(res.ChangedDefsReverse, ShouldHaveLength, 2) 173 So(res.ChangedDefsReverse[builder3Changed], ShouldResembleProto, builder3) 174 So(res.ChangedDefsReverse[builderDiffProjChanged], ShouldResembleProto, builderDiffProj) 175 So(res.UnchangedDefs, ShouldHaveLength, 1) 176 So(res.UnchangedDefs[builder1], ShouldResembleProto, builder1) 177 }) 178 }) 179 180 Convey("Compare retry configuration", func() { 181 Convey("Both empty", func() { 182 res := Diff(&tryjob.Requirement{}, &tryjob.Requirement{}) 183 So(res.RetryConfigChanged, ShouldBeFalse) 184 }) 185 Convey("Base nil, target non nil", func() { 186 res := Diff( 187 &tryjob.Requirement{}, 188 &tryjob.Requirement{ 189 RetryConfig: &cfgpb.Verifiers_Tryjob_RetryConfig{ 190 SingleQuota: 1, 191 }, 192 }) 193 So(res.RetryConfigChanged, ShouldBeTrue) 194 }) 195 Convey("Base non nil, target nil", func() { 196 res := Diff( 197 &tryjob.Requirement{ 198 RetryConfig: &cfgpb.Verifiers_Tryjob_RetryConfig{ 199 SingleQuota: 1, 200 }, 201 }, 202 &tryjob.Requirement{}) 203 So(res.RetryConfigChanged, ShouldBeTrue) 204 }) 205 Convey("Retry config changed", func() { 206 res := Diff( 207 &tryjob.Requirement{ 208 RetryConfig: &cfgpb.Verifiers_Tryjob_RetryConfig{ 209 SingleQuota: 1, 210 }, 211 }, 212 &tryjob.Requirement{ 213 RetryConfig: &cfgpb.Verifiers_Tryjob_RetryConfig{ 214 SingleQuota: 2, 215 }, 216 }) 217 So(res.RetryConfigChanged, ShouldBeTrue) 218 }) 219 }) 220 }) 221 }