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  }