go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/cv/internal/prjmanager/copyonwrite/cow_test.go (about) 1 // Copyright 2021 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 copyonwrite 16 17 import ( 18 "testing" 19 20 . "github.com/smartystreets/goconvey/convey" 21 . "go.chromium.org/luci/common/testing/assertions" 22 ) 23 24 type el struct { 25 id, val int 26 } 27 28 // evenCubedOddDeleted deletes `el`s with odd IDs and sets val to id^3 for even 29 // ones. 30 func evenCubedOddDeleted(v any) any { 31 in := v.(*el) 32 if in.id&1 == 1 { 33 return Deletion 34 } 35 c := in.id * in.id * in.id 36 if in.val == c { 37 return in 38 } 39 return &el{in.id, c} 40 } 41 42 func TestUpdate(t *testing.T) { 43 t.Parallel() 44 45 mustNoop := func(in Slice, m Modifier, add Slice) { 46 out, u := Update(in, m, add) 47 So(u, ShouldBeFalse) 48 So(out, ShouldResemble, in) 49 } 50 51 Convey("Update noops", t, func() { 52 Convey("empty", func() { 53 mustNoop(nil, nil, nil) 54 mustNoop(elSlice{}, nil, nil) 55 mustNoop(nil, evenCubedOddDeleted, nil) 56 mustNoop(nil, evenCubedOddDeleted, elSlice{}) 57 }) 58 Convey("no changes", func() { 59 mustNoop(elSlice{{0, 0}, {2, 8}, {4, 64}}, nil, nil) 60 mustNoop(elSlice{{0, 0}, {2, 8}, {4, 64}}, evenCubedOddDeleted, nil) 61 mustNoop(elSlice{{0, 0}, {2, 8}, {4, 64}}, evenCubedOddDeleted, elSlice{}) 62 }) 63 }) 64 65 Convey("Update works on Slice", t, func() { 66 Convey("deletes", func() { 67 res, u := Update(elSlice{{1, 0}, {4, 64}, {3, 0}, {0, 0}}, evenCubedOddDeleted, nil) 68 So(u, ShouldBeTrue) 69 So(res, ShouldResemble, elSlice{{4, 64}, {0, 0}}) 70 }) 71 Convey("modifies", func() { 72 res, u := Update(elSlice{{2, 8}, {4, 0}}, evenCubedOddDeleted, nil) 73 So(u, ShouldBeTrue) 74 So(res, ShouldResemble, elSlice{{2, 8}, {4, 64}}) 75 }) 76 Convey("modifies and deletes", func() { 77 res, u := Update(elSlice{{1, 0}, {2, 8}, {3, 0}, {4, 0}}, evenCubedOddDeleted, nil) 78 So(u, ShouldBeTrue) 79 So(res, ShouldResemble, elSlice{{2, 8}, {4, 64}}) 80 }) 81 Convey("creates on empty", func() { 82 res, u := Update(nil, nil, elSlice{{6, 0}, {5, 0}}) 83 So(u, ShouldBeTrue) 84 So(res, ShouldResemble, elSlice{{6, 0}, {5, 0}}) 85 }) 86 Convey("creates", func() { 87 res, u := Update(elSlice{{1, 0}}, nil, elSlice{{6, 0}, {5, 0}}) 88 So(u, ShouldBeTrue) 89 So(res, ShouldResemble, elSlice{{6, 0}, {5, 0}, {1, 0}}) 90 }) 91 Convey("creates, modifies and deletes", func() { 92 res, u := Update(elSlice{{1, 0}, {2, 8}, {3, 0}, {4, 0}}, evenCubedOddDeleted, elSlice{{5, 25}, {0, 0}}) 93 So(u, ShouldBeTrue) 94 So(res, ShouldResemble, elSlice{{5, 25}, {0, 0}, {2, 8}, {4, 64}}) 95 }) 96 }) 97 98 Convey("Update works on SortedSlice", t, func() { 99 Convey("panics if toAdd is not sorted", func() { 100 So(func() { Update(elSortedSlice{}, nil, elSlice{{3, 8}}) }, 101 ShouldPanicLike, "Different types for in and toAdd slices") 102 }) 103 Convey("creates sorted", func() { 104 res, u := Update(elSortedSlice{}, nil, elSortedSlice{{3, 8}, {1, 8}, {4, 1}, {2, 0}}) 105 So(u, ShouldBeTrue) 106 So(res, ShouldResemble, elSortedSlice{{1, 8}, {2, 0}, {3, 8}, {4, 1}}) 107 }) 108 Convey("modifies and deletes", func() { 109 res, u := Update(elSortedSlice{{1, 0}, {2, 8}, {3, 0}, {4, 0}}, evenCubedOddDeleted, nil) 110 So(u, ShouldBeTrue) 111 So(res, ShouldResemble, elSortedSlice{{2, 8}, {4, 64}}) 112 }) 113 Convey("deletes everything", func() { 114 res, u := Update(elSortedSlice{{1, 0}, {3, 0}}, evenCubedOddDeleted, nil) 115 So(u, ShouldBeTrue) 116 So(res, ShouldResemble, elSortedSlice{}) 117 }) 118 Convey("creates, modifies and deletes", func() { 119 in := elSortedSlice{{1, 0}, {2, 8}, {3, 0}, {4, 0}, {6, 1}, {7, 3}} 120 res, u := Update(in, evenCubedOddDeleted, elSortedSlice{{10, 100}, {0, 0}, {5, 25}}) 121 So(u, ShouldBeTrue) 122 So(res, ShouldResemble, elSortedSlice{{0, 0}, {2, 8}, {4, 64}, {5, 25}, {6, 216}, {10, 100}}) 123 124 res, u = Update(in, evenCubedOddDeleted, elSortedSlice{{3, 3}}) 125 So(u, ShouldBeTrue) 126 So(res, ShouldResemble, elSortedSlice{{2, 8}, {3, 3}, {4, 64}, {6, 216}}) 127 }) 128 }) 129 } 130 131 type elSlice []*el 132 133 var _ Slice = elSlice(nil) 134 135 func (e elSlice) Len() int { 136 return len(e) 137 } 138 139 func (e elSlice) At(index int) any { 140 return e[index] 141 } 142 143 func (e elSlice) Append(v any) Slice { 144 return append(e, v.(*el)) 145 } 146 147 func (e elSlice) CloneShallow(length int, capacity int) Slice { 148 r := make(elSlice, length, capacity) 149 copy(r, e[:length]) 150 return r 151 } 152 153 type elSortedSlice []*el 154 155 var _ SortedSlice = elSortedSlice(nil) 156 157 func (e elSortedSlice) Len() int { 158 return len(e) 159 } 160 161 func (e elSortedSlice) At(index int) any { 162 return e[index] 163 } 164 165 func (e elSortedSlice) Append(v any) Slice { 166 return append(e, v.(*el)) 167 } 168 169 func (e elSortedSlice) CloneShallow(length int, capacity int) Slice { 170 r := make(elSortedSlice, length, capacity) 171 copy(r, e[:length]) 172 return r 173 } 174 175 func (e elSortedSlice) Less(i int, j int) bool { 176 return e[i].id < e[j].id 177 } 178 179 func (e elSortedSlice) Swap(i int, j int) { 180 e[i], e[j] = e[j], e[i] 181 } 182 183 func (_ elSortedSlice) LessElements(a any, b any) bool { 184 return a.(*el).id < b.(*el).id 185 }