go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/gae/impl/memory/memstore_iter_test.go (about) 1 // Copyright 2015 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 memory 16 17 import ( 18 "bytes" 19 "testing" 20 21 "go.chromium.org/luci/common/data/cmpbin" 22 23 "go.chromium.org/luci/gae/service/datastore" 24 25 . "github.com/smartystreets/goconvey/convey" 26 ) 27 28 func mkNum(n int64) []byte { 29 buf := &bytes.Buffer{} 30 _, err := cmpbin.WriteInt(buf, n) 31 memoryCorruption(err) 32 33 return buf.Bytes() 34 } 35 36 func readNum(data []byte) int64 { 37 ret, _, err := cmpbin.ReadInt(bytes.NewBuffer(data)) 38 memoryCorruption(err) 39 40 return ret 41 } 42 43 func countItems(mc memCollection) int { 44 count := 0 45 mc.ForEachItem(func(_, _ []byte) bool { 46 count++ 47 return true 48 }) 49 return count 50 } 51 52 func TestIterator(t *testing.T) { 53 t.Parallel() 54 55 s := newMemStore() 56 c := s.GetOrCreateCollection("zup") 57 prev := []byte{} 58 for i := 5; i < 100; i++ { 59 data := mkNum(int64(i)) 60 c.Set(data, prev) 61 prev = data 62 } 63 c = s.Snapshot().GetCollection("zup") 64 65 iterCB := func(it *iterator, cb func(k, v []byte)) { 66 for ent := it.next(); ent != nil; ent = it.next() { 67 cb(ent.key, ent.value) 68 } 69 } 70 71 get := func(c C, t *iterator) any { 72 if ent := t.next(); ent != nil { 73 return readNum(ent.key) 74 } 75 return nil 76 } 77 78 skipGet := func(c C, t *iterator, skipTo int64) any { 79 t.skip(mkNum(skipTo)) 80 return get(c, t) 81 } 82 83 didIterate := func(t *iterator) (did bool) { 84 iterCB(t, func(k, v []byte) { 85 did = true 86 }) 87 return 88 } 89 90 Convey("Test iterator", t, func() { 91 Convey("start at nil", func(ctx C) { 92 t := (&iterDefinition{c: c}).mkIter() 93 So(get(ctx, t), ShouldEqual, 5) 94 So(get(ctx, t), ShouldEqual, 6) 95 So(get(ctx, t), ShouldEqual, 7) 96 97 Convey("And can skip", func(ctx C) { 98 So(skipGet(ctx, t, 10), ShouldEqual, 10) 99 So(get(ctx, t), ShouldEqual, 11) 100 101 Convey("But not forever", func(ctx C) { 102 t.skip(mkNum(200)) 103 So(didIterate(t), ShouldBeFalse) 104 So(didIterate(t), ShouldBeFalse) 105 }) 106 }) 107 108 Convey("Can iterate explicitly", func(ctx C) { 109 So(skipGet(ctx, t, 7), ShouldEqual, 8) 110 So(skipGet(ctx, t, 8), ShouldEqual, 9) 111 112 // Giving the immediately next key doesn't cause an internal reset. 113 So(skipGet(ctx, t, 10), ShouldEqual, 10) 114 }) 115 116 Convey("Going backwards is ignored", func(ctx C) { 117 So(skipGet(ctx, t, 3), ShouldEqual, 8) 118 So(get(ctx, t), ShouldEqual, 9) 119 So(skipGet(ctx, t, 20), ShouldEqual, 20) 120 So(get(ctx, t), ShouldEqual, 21) 121 }) 122 123 Convey("will stop at the end of the list", func(ctx C) { 124 So(skipGet(ctx, t, 95), ShouldEqual, 95) 125 So(get(ctx, t), ShouldEqual, 96) 126 So(get(ctx, t), ShouldEqual, 97) 127 So(get(ctx, t), ShouldEqual, 98) 128 So(get(ctx, t), ShouldEqual, 99) 129 So(get(ctx, t), ShouldBeNil) 130 So(get(ctx, t), ShouldBeNil) 131 }) 132 }) 133 134 Convey("can have caps on both sides", func(ctx C) { 135 t := (&iterDefinition{c: c, start: mkNum(20), end: mkNum(25)}).mkIter() 136 So(get(ctx, t), ShouldEqual, 20) 137 So(get(ctx, t), ShouldEqual, 21) 138 So(get(ctx, t), ShouldEqual, 22) 139 So(get(ctx, t), ShouldEqual, 23) 140 So(get(ctx, t), ShouldEqual, 24) 141 142 So(didIterate(t), ShouldBeFalse) 143 }) 144 145 Convey("can skip over starting cap", func(ctx C) { 146 t := (&iterDefinition{c: c, start: mkNum(20), end: mkNum(25)}).mkIter() 147 So(skipGet(ctx, t, 22), ShouldEqual, 22) 148 So(get(ctx, t), ShouldEqual, 23) 149 So(get(ctx, t), ShouldEqual, 24) 150 151 So(didIterate(t), ShouldBeFalse) 152 }) 153 }) 154 } 155 156 func TestMultiIteratorSimple(t *testing.T) { 157 t.Parallel() 158 159 // Simulate an index with 2 columns (int and int). 160 vals := [][]int64{ 161 {1, 0}, 162 {1, 2}, 163 {1, 4}, 164 {1, 7}, 165 {1, 9}, 166 {3, 10}, 167 {3, 11}, 168 } 169 170 valBytes := make([][]byte, len(vals)) 171 for i, nms := range vals { 172 numbs := make([][]byte, len(nms)) 173 for j, n := range nms { 174 numbs[j] = mkNum(n) 175 } 176 valBytes[i] = cmpbin.ConcatBytes(numbs...) 177 } 178 179 otherVals := [][]int64{ 180 {3, 0}, 181 {4, 10}, 182 {19, 7}, 183 {20, 2}, 184 {20, 3}, 185 {20, 4}, 186 {20, 8}, 187 {20, 11}, 188 } 189 190 otherValBytes := make([][]byte, len(otherVals)) 191 for i, nms := range otherVals { 192 numbs := make([][]byte, len(nms)) 193 for i, n := range nms { 194 numbs[i] = mkNum(n) 195 } 196 otherValBytes[i] = cmpbin.ConcatBytes(numbs...) 197 } 198 199 Convey("Test MultiIterator", t, func() { 200 s := newMemStore() 201 c := s.GetOrCreateCollection("zup1") 202 for _, row := range valBytes { 203 c.Set(row, []byte{}) 204 } 205 c2 := s.GetOrCreateCollection("zup2") 206 for _, row := range otherValBytes { 207 c2.Set(row, []byte{}) 208 } 209 c = s.Snapshot().GetCollection("zup1") 210 c2 = s.Snapshot().GetCollection("zup2") 211 212 Convey("can join the same collection twice", func() { 213 // get just the (1, *) 214 // starting at (1, 2) (i.e. >= 2) 215 // ending at (1, 4) (i.e. < 7) 216 defs := []*iterDefinition{ 217 {c: c, prefix: mkNum(1), prefixLen: len(mkNum(1)), start: mkNum(2), end: mkNum(7)}, 218 {c: c, prefix: mkNum(1), prefixLen: len(mkNum(1)), start: mkNum(2), end: mkNum(7)}, 219 } 220 221 i := 1 222 So(multiIterate(defs, func(suffix []byte) error { 223 So(readNum(suffix), ShouldEqual, vals[i][1]) 224 i++ 225 return nil 226 }), shouldBeSuccessful) 227 228 So(i, ShouldEqual, 3) 229 }) 230 231 Convey("can make empty iteration", func() { 232 // get just the (20, *) (doesn't exist) 233 defs := []*iterDefinition{ 234 {c: c, prefix: mkNum(20)}, 235 {c: c, prefix: mkNum(20)}, 236 } 237 238 i := 0 239 So(multiIterate(defs, func(suffix []byte) error { 240 panic("never") 241 }), shouldBeSuccessful) 242 243 So(i, ShouldEqual, 0) 244 }) 245 246 Convey("can join (other, val, val)", func() { 247 // 'other' must start with 20, 'vals' must start with 1 248 // no range constraints 249 defs := []*iterDefinition{ 250 {c: c2, prefix: mkNum(20)}, 251 {c: c, prefix: mkNum(1)}, 252 {c: c, prefix: mkNum(1)}, 253 } 254 255 expect := []int64{2, 4} 256 i := 0 257 So(multiIterate(defs, func(suffix []byte) error { 258 So(readNum(suffix), ShouldEqual, expect[i]) 259 i++ 260 return nil 261 }), shouldBeSuccessful) 262 }) 263 264 Convey("Can stop early", func() { 265 defs := []*iterDefinition{ 266 {c: c, prefix: mkNum(1), prefixLen: len(mkNum(1))}, 267 {c: c, prefix: mkNum(1), prefixLen: len(mkNum(1))}, 268 } 269 270 i := 0 271 So(multiIterate(defs, func(suffix []byte) error { 272 So(readNum(suffix), ShouldEqual, vals[i][1]) 273 i++ 274 return nil 275 }), shouldBeSuccessful) 276 So(i, ShouldEqual, 5) 277 278 i = 0 279 So(multiIterate(defs, func(suffix []byte) error { 280 So(readNum(suffix), ShouldEqual, vals[i][1]) 281 i++ 282 return datastore.Stop 283 }), shouldBeSuccessful) 284 So(i, ShouldEqual, 1) 285 }) 286 287 }) 288 289 }