| OLD | NEW |
| (Empty) |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 package memory | |
| 6 | |
| 7 import ( | |
| 8 "testing" | |
| 9 "time" | |
| 10 | |
| 11 "github.com/luci/gae" | |
| 12 "github.com/luci/gkvlite" | |
| 13 . "github.com/smartystreets/goconvey/convey" | |
| 14 ) | |
| 15 | |
| 16 func init() { | |
| 17 indexCreationDeterministic = true | |
| 18 } | |
| 19 | |
| 20 var fakeKey = key("knd", 10, key("parentKind", "sid")) | |
| 21 | |
| 22 func TestCollated(t *testing.T) { | |
| 23 t.Parallel() | |
| 24 | |
| 25 Convey("TestCollated", t, func() { | |
| 26 Convey("nil list", func() { | |
| 27 pm := (gae.DSPropertyMap)(nil) | |
| 28 sip := partiallySerialize(pm) | |
| 29 So(sip, ShouldBeNil) | |
| 30 | |
| 31 Convey("nil collated", func() { | |
| 32 Convey("defaultIndicies", func() { | |
| 33 idxs := defaultIndicies("knd", pm) | |
| 34 So(len(idxs), ShouldEqual, 1) | |
| 35 So(idxs[0].String(), ShouldEqual, "B:knd
") | |
| 36 }) | |
| 37 Convey("indexEntries", func() { | |
| 38 s := sip.indexEntries(fakeKey, defaultIn
dicies("knd", pm)) | |
| 39 numItems, _ := s.GetCollection("idx").Ge
tTotals() | |
| 40 So(numItems, ShouldEqual, 1) | |
| 41 itm := s.GetCollection("idx").MinItem(fa
lse) | |
| 42 So(itm.Key, ShouldResemble, cat(indx("kn
d"))) | |
| 43 numItems, _ = s.GetCollection("idx:ns:"
+ string(itm.Key)).GetTotals() | |
| 44 So(numItems, ShouldEqual, 1) | |
| 45 }) | |
| 46 }) | |
| 47 }) | |
| 48 | |
| 49 Convey("list", func() { | |
| 50 pm := gae.DSPropertyMap{ | |
| 51 "wat": {propNI("thing"), prop("hat"), prop(100)
}, | |
| 52 "nerd": {prop(103.7)}, | |
| 53 "spaz": {propNI(false)}, | |
| 54 } | |
| 55 sip := partiallySerialize(pm) | |
| 56 So(len(sip), ShouldEqual, 2) | |
| 57 | |
| 58 Convey("single collated", func() { | |
| 59 Convey("indexableMap", func() { | |
| 60 So(sip, ShouldResemble, serializedIndexa
blePmap{ | |
| 61 "wat": { | |
| 62 cat(gae.DSPTInt, 100), | |
| 63 cat(gae.DSPTString, "hat
"), | |
| 64 // 'thing' is skipped, b
ecause it's not NoIndex | |
| 65 }, | |
| 66 "nerd": { | |
| 67 cat(gae.DSPTFloat, 103.7
), | |
| 68 }, | |
| 69 }) | |
| 70 }) | |
| 71 Convey("defaultIndicies", func() { | |
| 72 idxs := defaultIndicies("knd", pm) | |
| 73 So(len(idxs), ShouldEqual, 5) | |
| 74 So(idxs[0].String(), ShouldEqual, "B:knd
") | |
| 75 So(idxs[1].String(), ShouldEqual, "B:knd
/-nerd") | |
| 76 So(idxs[2].String(), ShouldEqual, "B:knd
/-wat") | |
| 77 So(idxs[3].String(), ShouldEqual, "B:knd
/nerd") | |
| 78 So(idxs[4].String(), ShouldEqual, "B:knd
/wat") | |
| 79 }) | |
| 80 }) | |
| 81 }) | |
| 82 }) | |
| 83 } | |
| 84 | |
| 85 var rgenComplexTime = time.Date( | |
| 86 1986, time.October, 26, 1, 20, 00, 00, time.UTC) | |
| 87 var rgenComplexKey = key("kind", "id") | |
| 88 | |
| 89 var rowGenTestCases = []struct { | |
| 90 name string | |
| 91 pmap gae.DSPropertyMap | |
| 92 withBuiltin bool | |
| 93 idxs []*qIndex | |
| 94 | |
| 95 // These are checked in TestIndexRowGen. nil to skip test case. | |
| 96 expected []serializedPvals | |
| 97 | |
| 98 // just the collections you want to assert. These are checked in | |
| 99 // TestIndexEntries. nil to skip test case. | |
| 100 collections map[string][]kv | |
| 101 }{ | |
| 102 { | |
| 103 name: "simple including builtins", | |
| 104 pmap: gae.DSPropertyMap{ | |
| 105 "wat": {propNI("thing"), prop("hat"), prop(100)}, | |
| 106 "nerd": {prop(103.7)}, | |
| 107 "spaz": {propNI(false)}, | |
| 108 }, | |
| 109 withBuiltin: true, | |
| 110 idxs: []*qIndex{ | |
| 111 indx("knd", "-wat", "nerd"), | |
| 112 }, | |
| 113 expected: []serializedPvals{ | |
| 114 {{}}, // B:knd | |
| 115 {icat(gae.DSPTFloat, 103.7)}, /
/ B:knd/-nerd | |
| 116 {icat(gae.DSPTString, "hat"), icat(gae.DSPTInt, 100)}, /
/ B:knd/-wat | |
| 117 {cat(gae.DSPTFloat, 103.7)}, /
/ B:knd/nerd | |
| 118 {cat(gae.DSPTInt, 100), cat(gae.DSPTString, "hat")}, /
/ B:knd/wat | |
| 119 { // B:knd/-wat/nerd | |
| 120 cat(icat(gae.DSPTString, "hat"), cat(gae.DSPTFlo
at, 103.7)), | |
| 121 cat(icat(gae.DSPTInt, 100), cat(gae.DSPTFloat, 1
03.7)), | |
| 122 }, | |
| 123 }, | |
| 124 collections: map[string][]kv{ | |
| 125 "idx": { | |
| 126 // 0 == builtin, 1 == complex | |
| 127 {cat(byte(0), "knd", byte(1), 0), []byte{}}, | |
| 128 {cat(byte(0), "knd", byte(1), 1, byte(0), "nerd"
), []byte{}}, | |
| 129 {cat(byte(0), "knd", byte(1), 1, byte(0), "wat")
, []byte{}}, | |
| 130 {cat(byte(0), "knd", byte(1), 1, byte(1), "nerd"
), []byte{}}, | |
| 131 {cat(byte(0), "knd", byte(1), 1, byte(1), "wat")
, []byte{}}, | |
| 132 {cat(byte(1), "knd", byte(1), 2, byte(1), "wat",
byte(0), "nerd"), []byte{}}, | |
| 133 }, | |
| 134 "idx:ns:" + sat(indx("knd")): { | |
| 135 {cat(fakeKey), []byte{}}, | |
| 136 }, | |
| 137 "idx:ns:" + sat(indx("knd", "wat")): { | |
| 138 {cat(gae.DSPTInt, 100, fakeKey), []byte{}}, | |
| 139 {cat(gae.DSPTString, "hat", fakeKey), cat(gae.DS
PTInt, 100)}, | |
| 140 }, | |
| 141 "idx:ns:" + sat(indx("knd", "-wat")): { | |
| 142 {cat(icat(gae.DSPTString, "hat"), fakeKey), []by
te{}}, | |
| 143 {cat(icat(gae.DSPTInt, 100), fakeKey), icat(gae.
DSPTString, "hat")}, | |
| 144 }, | |
| 145 }, | |
| 146 }, | |
| 147 { | |
| 148 name: "complex", | |
| 149 pmap: gae.DSPropertyMap{ | |
| 150 "yerp": {prop("hat"), prop(73.9)}, | |
| 151 "wat": { | |
| 152 prop(rgenComplexTime), | |
| 153 prop(gae.DSByteString("value")), | |
| 154 prop(rgenComplexKey)}, | |
| 155 "spaz": {prop(nil), prop(false), prop(true)}, | |
| 156 }, | |
| 157 idxs: []*qIndex{ | |
| 158 indx("knd", "-wat", "nerd", "spaz"), // doesn't match, s
o empty | |
| 159 indx("knd", "yerp", "-wat", "spaz"), | |
| 160 }, | |
| 161 expected: []serializedPvals{ | |
| 162 {}, // C:knd/-wat/nerd/spaz, no match | |
| 163 { // C:knd/yerp/-wat/spaz | |
| 164 // thank goodness the binary serialization only
happens 1/val in the | |
| 165 // real code :). | |
| 166 cat(cat(gae.DSPTString, "hat"), icat(gae.DSPTKey
, rgenComplexKey), cat(gae.DSPTNull)), | |
| 167 cat(cat(gae.DSPTString, "hat"), icat(gae.DSPTKey
, rgenComplexKey), cat(gae.DSPTBoolFalse)), | |
| 168 cat(cat(gae.DSPTString, "hat"), icat(gae.DSPTKey
, rgenComplexKey), cat(gae.DSPTBoolTrue)), | |
| 169 cat(cat(gae.DSPTString, "hat"), icat(gae.DSPTByt
es, "value"), cat(gae.DSPTNull)), | |
| 170 cat(cat(gae.DSPTString, "hat"), icat(gae.DSPTByt
es, "value"), cat(gae.DSPTBoolFalse)), | |
| 171 cat(cat(gae.DSPTString, "hat"), icat(gae.DSPTByt
es, "value"), cat(gae.DSPTBoolTrue)), | |
| 172 cat(cat(gae.DSPTString, "hat"), icat(gae.DSPTTim
e, rgenComplexTime), cat(gae.DSPTNull)), | |
| 173 cat(cat(gae.DSPTString, "hat"), icat(gae.DSPTTim
e, rgenComplexTime), cat(gae.DSPTBoolFalse)), | |
| 174 cat(cat(gae.DSPTString, "hat"), icat(gae.DSPTTim
e, rgenComplexTime), cat(gae.DSPTBoolTrue)), | |
| 175 | |
| 176 cat(cat(gae.DSPTFloat, 73.9), icat(gae.DSPTKey,
rgenComplexKey), cat(gae.DSPTNull)), | |
| 177 cat(cat(gae.DSPTFloat, 73.9), icat(gae.DSPTKey,
rgenComplexKey), cat(gae.DSPTBoolFalse)), | |
| 178 cat(cat(gae.DSPTFloat, 73.9), icat(gae.DSPTKey,
rgenComplexKey), cat(gae.DSPTBoolTrue)), | |
| 179 cat(cat(gae.DSPTFloat, 73.9), icat(gae.DSPTBytes
, "value"), cat(gae.DSPTNull)), | |
| 180 cat(cat(gae.DSPTFloat, 73.9), icat(gae.DSPTBytes
, "value"), cat(gae.DSPTBoolFalse)), | |
| 181 cat(cat(gae.DSPTFloat, 73.9), icat(gae.DSPTBytes
, "value"), cat(gae.DSPTBoolTrue)), | |
| 182 cat(cat(gae.DSPTFloat, 73.9), icat(gae.DSPTTime,
rgenComplexTime), cat(gae.DSPTNull)), | |
| 183 cat(cat(gae.DSPTFloat, 73.9), icat(gae.DSPTTime,
rgenComplexTime), cat(gae.DSPTBoolFalse)), | |
| 184 cat(cat(gae.DSPTFloat, 73.9), icat(gae.DSPTTime,
rgenComplexTime), cat(gae.DSPTBoolTrue)), | |
| 185 }, | |
| 186 }, | |
| 187 }, | |
| 188 { | |
| 189 name: "ancestor", | |
| 190 pmap: gae.DSPropertyMap{ | |
| 191 "wat": {prop("sup")}, | |
| 192 }, | |
| 193 idxs: []*qIndex{ | |
| 194 indx("knd!", "wat"), | |
| 195 }, | |
| 196 collections: map[string][]kv{ | |
| 197 "idx:ns:" + sat(indx("knd!", "wat")): { | |
| 198 {cat(fakeKey.Parent(), gae.DSPTString, "sup", fa
keKey), []byte{}}, | |
| 199 {cat(fakeKey, gae.DSPTString, "sup", fakeKey), [
]byte{}}, | |
| 200 }, | |
| 201 }, | |
| 202 }, | |
| 203 } | |
| 204 | |
| 205 func TestIndexRowGen(t *testing.T) { | |
| 206 t.Parallel() | |
| 207 | |
| 208 Convey("Test Index Row Generation", t, func() { | |
| 209 for _, tc := range rowGenTestCases { | |
| 210 if tc.expected == nil { | |
| 211 Convey(tc.name, nil) // shows up as 'skipped' | |
| 212 continue | |
| 213 } | |
| 214 | |
| 215 Convey(tc.name, func() { | |
| 216 mvals := partiallySerialize(tc.pmap) | |
| 217 idxs := []*qIndex(nil) | |
| 218 if tc.withBuiltin { | |
| 219 idxs = append(defaultIndicies("coolKind"
, tc.pmap), tc.idxs...) | |
| 220 } else { | |
| 221 idxs = tc.idxs | |
| 222 } | |
| 223 | |
| 224 m := matcher{} | |
| 225 for i, idx := range idxs { | |
| 226 Convey(idx.String(), func() { | |
| 227 iGen, ok := m.match(idx, mvals) | |
| 228 if len(tc.expected[i]) > 0 { | |
| 229 So(ok, ShouldBeTrue) | |
| 230 j := 0 | |
| 231 iGen.permute(func(row []
byte) { | |
| 232 So([]byte(row),
ShouldResemble, tc.expected[i][j]) | |
| 233 j++ | |
| 234 }) | |
| 235 So(j, ShouldEqual, len(t
c.expected[i])) | |
| 236 } else { | |
| 237 So(ok, ShouldBeFalse) | |
| 238 } | |
| 239 }) | |
| 240 } | |
| 241 }) | |
| 242 } | |
| 243 }) | |
| 244 } | |
| 245 | |
| 246 func TestIndexEntries(t *testing.T) { | |
| 247 t.Parallel() | |
| 248 | |
| 249 Convey("Test indexEntriesWithBuiltins", t, func() { | |
| 250 for _, tc := range rowGenTestCases { | |
| 251 if tc.collections == nil { | |
| 252 Convey(tc.name, nil) // shows up as 'skipped' | |
| 253 continue | |
| 254 } | |
| 255 | |
| 256 Convey(tc.name, func() { | |
| 257 store := (*memStore)(nil) | |
| 258 if tc.withBuiltin { | |
| 259 store = indexEntriesWithBuiltins(fakeKey
, tc.pmap, tc.idxs) | |
| 260 } else { | |
| 261 store = partiallySerialize(tc.pmap).inde
xEntries(fakeKey, tc.idxs) | |
| 262 } | |
| 263 for colName, vals := range tc.collections { | |
| 264 i := 0 | |
| 265 store.GetCollection(colName).VisitItemsA
scend(nil, true, func(itm *gkvlite.Item) bool { | |
| 266 So(itm.Key, ShouldResemble, vals
[i].k) | |
| 267 So(itm.Val, ShouldResemble, vals
[i].v) | |
| 268 i++ | |
| 269 return true | |
| 270 }) | |
| 271 So(i, ShouldEqual, len(vals)) | |
| 272 } | |
| 273 }) | |
| 274 } | |
| 275 }) | |
| 276 } | |
| 277 | |
| 278 type dumbItem struct { | |
| 279 key gae.DSKey | |
| 280 props gae.DSPropertyMap | |
| 281 } | |
| 282 | |
| 283 var updateIndiciesTests = []struct { | |
| 284 name string | |
| 285 idxs []*qIndex | |
| 286 data []dumbItem | |
| 287 expected map[string][][]byte | |
| 288 }{ | |
| 289 { | |
| 290 name: "basic", | |
| 291 data: []dumbItem{ | |
| 292 {key("knd", 1), gae.DSPropertyMap{ | |
| 293 "wat": {prop(10)}, | |
| 294 "yerp": {prop(10)}}, | |
| 295 }, | |
| 296 {key("knd", 10), gae.DSPropertyMap{ | |
| 297 "wat": {prop(1)}, | |
| 298 "yerp": {prop(200)}}, | |
| 299 }, | |
| 300 {key("knd", 1), gae.DSPropertyMap{ | |
| 301 "wat": {prop(10)}, | |
| 302 "yerp": {prop(202)}}, | |
| 303 }, | |
| 304 }, | |
| 305 expected: map[string][][]byte{ | |
| 306 "idx:ns:" + sat(indx("knd", "wat")): { | |
| 307 cat(gae.DSPTInt, 1, key("knd", 10)), | |
| 308 cat(gae.DSPTInt, 10, key("knd", 1)), | |
| 309 }, | |
| 310 "idx:ns:" + sat(indx("knd", "-wat")): { | |
| 311 cat(icat(gae.DSPTInt, 10), key("knd", 1)), | |
| 312 cat(icat(gae.DSPTInt, 1), key("knd", 10)), | |
| 313 }, | |
| 314 "idx:ns:" + sat(indx("knd", "yerp")): { | |
| 315 cat(gae.DSPTInt, 200, key("knd", 10)), | |
| 316 cat(gae.DSPTInt, 202, key("knd", 1)), | |
| 317 }, | |
| 318 }, | |
| 319 }, | |
| 320 { | |
| 321 name: "compound", | |
| 322 idxs: []*qIndex{indx("knd", "yerp", "-wat")}, | |
| 323 data: []dumbItem{ | |
| 324 {key("knd", 1), gae.DSPropertyMap{ | |
| 325 "wat": {prop(10)}, | |
| 326 "yerp": {prop(100)}}, | |
| 327 }, | |
| 328 {key("knd", 10), gae.DSPropertyMap{ | |
| 329 "wat": {prop(1)}, | |
| 330 "yerp": {prop(200)}}, | |
| 331 }, | |
| 332 {key("knd", 11), gae.DSPropertyMap{ | |
| 333 "wat": {prop(20)}, | |
| 334 "yerp": {prop(200)}}, | |
| 335 }, | |
| 336 {key("knd", 14), gae.DSPropertyMap{ | |
| 337 "wat": {prop(20)}, | |
| 338 "yerp": {prop(200)}}, | |
| 339 }, | |
| 340 {key("knd", 1), gae.DSPropertyMap{ | |
| 341 "wat": {prop(10)}, | |
| 342 "yerp": {prop(202)}}, | |
| 343 }, | |
| 344 }, | |
| 345 expected: map[string][][]byte{ | |
| 346 "idx:ns:" + sat(indx("knd", "yerp", "-wat")): { | |
| 347 cat(gae.DSPTInt, 200, icat(gae.DSPTInt, 20), key
("knd", 11)), | |
| 348 cat(gae.DSPTInt, 200, icat(gae.DSPTInt, 20), key
("knd", 14)), | |
| 349 cat(gae.DSPTInt, 200, icat(gae.DSPTInt, 1), key(
"knd", 10)), | |
| 350 cat(gae.DSPTInt, 202, icat(gae.DSPTInt, 10), key
("knd", 1)), | |
| 351 }, | |
| 352 }, | |
| 353 }, | |
| 354 } | |
| 355 | |
| 356 func TestUpdateIndicies(t *testing.T) { | |
| 357 t.Parallel() | |
| 358 | |
| 359 Convey("Test updateIndicies", t, func() { | |
| 360 for _, tc := range updateIndiciesTests { | |
| 361 Convey(tc.name, func() { | |
| 362 store := newMemStore() | |
| 363 idxColl := store.SetCollection("idx", nil) | |
| 364 for _, i := range tc.idxs { | |
| 365 idxColl.Set(cat(i), []byte{}) | |
| 366 } | |
| 367 | |
| 368 tmpLoader := map[string]gae.DSPropertyMap{} | |
| 369 for _, itm := range tc.data { | |
| 370 ks := itm.key.String() | |
| 371 prev := tmpLoader[ks] | |
| 372 updateIndicies(store, itm.key, prev, itm
.props) | |
| 373 tmpLoader[ks] = itm.props | |
| 374 } | |
| 375 tmpLoader = nil | |
| 376 | |
| 377 for colName, data := range tc.expected { | |
| 378 coll := store.GetCollection(colName) | |
| 379 So(coll, ShouldNotBeNil) | |
| 380 i := 0 | |
| 381 coll.VisitItemsAscend(nil, false, func(i
tm *gkvlite.Item) bool { | |
| 382 So(data[i], ShouldResemble, itm.
Key) | |
| 383 i++ | |
| 384 return true | |
| 385 }) | |
| 386 So(i, ShouldEqual, len(data)) | |
| 387 } | |
| 388 }) | |
| 389 } | |
| 390 }) | |
| 391 } | |
| OLD | NEW |