Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2015 The LUCI Authors. All rights reserved. | 1 // Copyright 2015 The LUCI Authors. All rights reserved. |
| 2 // Use of this source code is governed under the Apache License, Version 2.0 | 2 // Use of this source code is governed under the Apache License, Version 2.0 |
| 3 // that can be found in the LICENSE file. | 3 // that can be found in the LICENSE file. |
| 4 | 4 |
| 5 package memory | 5 package memory |
| 6 | 6 |
| 7 import ( | 7 import ( |
| 8 "fmt" | 8 "fmt" |
| 9 "strings" | 9 "strings" |
| 10 "testing" | 10 "testing" |
| 11 "time" | 11 "time" |
| 12 | 12 |
| 13 "github.com/luci/gae/service/blobstore" | 13 "github.com/luci/gae/service/blobstore" |
| 14 ds "github.com/luci/gae/service/datastore" | 14 ds "github.com/luci/gae/service/datastore" |
| 15 "github.com/luci/gae/service/info" | 15 "github.com/luci/gae/service/info" |
| 16 "golang.org/x/net/context" | 16 "golang.org/x/net/context" |
| 17 | 17 |
| 18 . "github.com/luci/luci-go/common/testing/assertions" | 18 . "github.com/luci/luci-go/common/testing/assertions" |
| 19 . "github.com/smartystreets/goconvey/convey" | 19 . "github.com/smartystreets/goconvey/convey" |
| 20 ) | 20 ) |
| 21 | 21 |
| 22 func mkKey(appID, namespace string, elems ...interface{}) *ds.Key { | 22 func mkKey(appID, namespace string, elems ...interface{}) *ds.Key { |
| 23 return ds.MkKeyContext(appID, namespace).MakeKey(elems...) | 23 return ds.MkKeyContext(appID, namespace).MakeKey(elems...) |
| 24 } | 24 } |
| 25 | 25 |
| 26 var enableOnly = false | |
| 27 | |
| 28 func enable() bool { | |
|
dnj
2016/11/11 08:37:53
This is something useful for debugging specific te
| |
| 29 enableOnly = true | |
| 30 return true | |
| 31 } | |
| 32 | |
| 26 type qExpect struct { | 33 type qExpect struct { |
| 27 q *ds.Query | 34 q *ds.Query |
| 28 inTxn bool | 35 inTxn bool |
| 29 | 36 |
| 30 get []ds.PropertyMap | 37 get []ds.PropertyMap |
| 31 keys []*ds.Key | 38 keys []*ds.Key |
| 32 count int | 39 count int |
| 40 | |
| 41 enable bool | |
| 33 } | 42 } |
| 34 | 43 |
| 35 type qExStage struct { | 44 type qExStage struct { |
| 36 addIdxs []*ds.IndexDefinition | 45 addIdxs []*ds.IndexDefinition |
| 37 putEnts []ds.PropertyMap | 46 putEnts []ds.PropertyMap |
| 38 delEnts []*ds.Key | 47 delEnts []*ds.Key |
| 39 | 48 |
| 40 expect []qExpect | 49 expect []qExpect |
| 41 | 50 |
| 42 extraFns []func(context.Context) | 51 extraFns []func(context.Context) |
| (...skipping 202 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 245 | 254 |
| 246 {q: nq("Kind").Gt("Val", 2).Lte("Val", 5), get: []ds.PropertyMap{ | 255 {q: nq("Kind").Gt("Val", 2).Lte("Val", 5), get: []ds.PropertyMap{ |
| 247 stage1Data[0], stage1Data[3], | 256 stage1Data[0], stage1Data[3], |
| 248 }}, | 257 }}, |
| 249 | 258 |
| 250 {q: nq("Kind").Gt("Val", 2).Lte("Val", 5).Order( "-Val"), get: []ds.PropertyMap{ | 259 {q: nq("Kind").Gt("Val", 2).Lte("Val", 5).Order( "-Val"), get: []ds.PropertyMap{ |
| 251 stage1Data[3], stage1Data[0], | 260 stage1Data[3], stage1Data[0], |
| 252 }}, | 261 }}, |
| 253 | 262 |
| 254 {q: nq("").Gt("__key__", key("Kind", 2)), | 263 {q: nq("").Gt("__key__", key("Kind", 2)), |
| 255 » » » » » // count counts from the index with Keys Only and so counts the deleted | 264 » » » » » count: 7, |
|
dnj
2016/11/11 08:37:53
(This is no longer true, since we don't auto-KeysO
| |
| 256 » » » » » // entity Unique/1. | |
| 257 » » » » » count: 8, | |
| 258 get: []ds.PropertyMap{ | 265 get: []ds.PropertyMap{ |
| 259 // TODO(riannucci): determine if the real datastore shows metadata | 266 // TODO(riannucci): determine if the real datastore shows metadata |
| 260 // during kindless queries. The documentation seems to imply so, but | 267 // during kindless queries. The documentation seems to imply so, but |
| 261 // I'd like to be sure. | 268 // I'd like to be sure. |
| 262 pmap("$key", key("Kind", 2, "__e ntity_group__", 1), Next, | 269 pmap("$key", key("Kind", 2, "__e ntity_group__", 1), Next, |
| 263 "__version__", 1), | 270 "__version__", 1), |
| 264 stage1Data[2], | 271 stage1Data[2], |
| 265 stage1Data[4], | 272 stage1Data[4], |
| 266 // this is 5 because the value i s retrieved from HEAD and not from | 273 // this is 5 because the value i s retrieved from HEAD and not from |
| 267 // the index snapshot! | 274 // the index snapshot! |
| (...skipping 148 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 416 " - name: Val", | 423 " - name: Val", |
| 417 }, "\n")) | 424 }, "\n")) |
| 418 }, | 425 }, |
| 419 }, | 426 }, |
| 420 }, | 427 }, |
| 421 | 428 |
| 422 { | 429 { |
| 423 expect: []qExpect{ | 430 expect: []qExpect{ |
| 424 // eventual consistency; Unique/1 is deleted at HEAD. Keysonly finds it, | 431 // eventual consistency; Unique/1 is deleted at HEAD. Keysonly finds it, |
| 425 // but 'normal' doesn't. | 432 // but 'normal' doesn't. |
| 433 {q: nq("Unique").Gt("__key__", key("AKind", 5)). Lte("__key__", key("Zeta", "prime")).KeysOnly(true), | |
| 434 keys: []*ds.Key{key("Unique", 1)}}, | |
| 426 {q: nq("Unique").Gt("__key__", key("AKind", 5)). Lte("__key__", key("Zeta", "prime")), | 435 {q: nq("Unique").Gt("__key__", key("AKind", 5)). Lte("__key__", key("Zeta", "prime")), |
| 427 » » » » » keys: []*ds.Key{key("Unique", 1)}, | 436 » » » » » get: []ds.PropertyMap{}}, |
| 428 » » » » » get: []ds.PropertyMap{}}, | |
| 429 | 437 |
| 430 {q: nq("Kind").Eq("Val", 1, 3), get: []ds.Proper tyMap{ | 438 {q: nq("Kind").Eq("Val", 1, 3), get: []ds.Proper tyMap{ |
| 431 stage1Data[0], stage2Data[2], | 439 stage1Data[0], stage2Data[2], |
| 432 }}, | 440 }}, |
| 433 }, | 441 }, |
| 434 }, | 442 }, |
| 435 }}, | 443 }}, |
| 436 {"collapsed types", []qExStage{ | 444 {"collapsed types", []qExStage{ |
| 437 { | 445 { |
| 438 putEnts: collapsedData, | 446 putEnts: collapsedData, |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 476 }, | 484 }, |
| 477 { | 485 { |
| 478 q: nq("Kind").Eq("Val", []byte("uwutm8") ), | 486 q: nq("Kind").Eq("Val", []byte("uwutm8") ), |
| 479 get: []ds.PropertyMap{ | 487 get: []ds.PropertyMap{ |
| 480 collapsedData[5], | 488 collapsedData[5], |
| 481 }, | 489 }, |
| 482 }, | 490 }, |
| 483 }, | 491 }, |
| 484 }, | 492 }, |
| 485 }}, | 493 }}, |
| 494 | |
| 495 {"regression: tombstones and limit/offset queries", []qExStage{ | |
| 496 { | |
| 497 putEnts: []ds.PropertyMap{ | |
| 498 pmap("$key", key("Kind", 1)), | |
| 499 pmap("$key", key("Kind", 2)), | |
| 500 pmap("$key", key("Kind", 3)), | |
| 501 }, | |
| 502 delEnts: []*ds.Key{key("Kind", 2)}, | |
| 503 }, | |
| 504 { | |
| 505 expect: []qExpect{ | |
| 506 { | |
| 507 q: nq("Kind").Limit(2), | |
| 508 get: []ds.PropertyMap{ | |
| 509 pmap("$key", key("Kind", 1)), | |
| 510 pmap("$key", key("Kind", 3)), | |
| 511 }, | |
| 512 }, | |
| 513 | |
| 514 { | |
| 515 q: nq("Kind").Offset(2), | |
| 516 get: []ds.PropertyMap{}, | |
| 517 }, | |
| 518 }, | |
| 519 }, | |
| 520 }}, | |
| 486 } | 521 } |
| 487 | 522 |
| 488 func TestQueryExecution(t *testing.T) { | 523 func TestQueryExecution(t *testing.T) { |
| 489 t.Parallel() | 524 t.Parallel() |
| 490 | 525 |
| 491 Convey("Test query execution", t, func() { | 526 Convey("Test query execution", t, func() { |
| 492 c, err := info.Namespace(Use(context.Background()), "ns") | 527 c, err := info.Namespace(Use(context.Background()), "ns") |
| 493 if err != nil { | 528 if err != nil { |
| 494 panic(err) | 529 panic(err) |
| 495 } | 530 } |
| (...skipping 23 matching lines...) Expand all Loading... | |
| 519 panic(err) | 554 panic(err) |
| 520 } | 555 } |
| 521 } | 556 } |
| 522 | 557 |
| 523 if err := ds.Delete(c, stage.delEnts); e rr != nil { | 558 if err := ds.Delete(c, stage.delEnts); e rr != nil { |
| 524 panic(err) | 559 panic(err) |
| 525 } | 560 } |
| 526 | 561 |
| 527 Convey(fmt.Sprintf("stage %d", i), func( ) { | 562 Convey(fmt.Sprintf("stage %d", i), func( ) { |
| 528 for j, expect := range stage.exp ect { | 563 for j, expect := range stage.exp ect { |
| 564 if enableOnly && !expect .enable { | |
| 565 continue | |
| 566 } | |
| 529 runner := func(c context .Context, f func(ic context.Context) error, _ *ds.TransactionOptions) error { | 567 runner := func(c context .Context, f func(ic context.Context) error, _ *ds.TransactionOptions) error { |
| 530 return f(c) | 568 return f(c) |
| 531 } | 569 } |
| 532 if expect.inTxn { | 570 if expect.inTxn { |
| 533 runner = ds.RunI nTransaction | 571 runner = ds.RunI nTransaction |
| 534 } | 572 } |
| 535 | 573 |
| 536 if expect.count == 0 { | 574 if expect.count == 0 { |
| 537 if len(expect.ke ys) > 0 { | 575 if len(expect.ke ys) > 0 { |
| 538 expect.c ount = len(expect.keys) | 576 expect.c ount = len(expect.keys) |
| (...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 633 | 671 |
| 634 v, ok := actual.(error) | 672 v, ok := actual.(error) |
| 635 if !ok { | 673 if !ok { |
| 636 return fmt.Sprintf("type of 'actual' must be error, not %T", act ual) | 674 return fmt.Sprintf("type of 'actual' must be error, not %T", act ual) |
| 637 } | 675 } |
| 638 if v == nil || v == ds.Stop { | 676 if v == nil || v == ds.Stop { |
| 639 return "" | 677 return "" |
| 640 } | 678 } |
| 641 return fmt.Sprintf("expected success value, not %v", v) | 679 return fmt.Sprintf("expected success value, not %v", v) |
| 642 } | 680 } |
| OLD | NEW |