Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(128)

Unified Diff: common/data/treapstore/store_test.go

Issue 2607663002: Add treapstore, a treap-based in-memory data store (Closed)
Patch Set: Use treapstore for LogDog BigTable testing. Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: common/data/treapstore/store_test.go
diff --git a/common/data/treapstore/store_test.go b/common/data/treapstore/store_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..b5219d92e0f4d87a00a5269ee9fbc89333c04777
--- /dev/null
+++ b/common/data/treapstore/store_test.go
@@ -0,0 +1,180 @@
+// Copyright 2016 The LUCI Authors. All rights reserved.
+// Use of this source code is governed under the Apache License, Version 2.0
+// that can be found in the LICENSE file.
+
+package treapstore
+
+import (
+ "fmt"
+ "strconv"
+ "strings"
+ "testing"
+
+ "github.com/luci/gtreap"
+
+ . "github.com/smartystreets/goconvey/convey"
+)
+
+func newStringStore() *Store {
+ return New(func(a, b interface{}) int {
+ return strings.Compare(a.(string), b.(string))
+ })
+}
+
+func iterAll(it *gtreap.Iterator) []string {
+ all := []string{}
+ for {
+ v, ok := it.Next()
+ if !ok {
+ return all
+ }
+ all = append(all, v.(string))
+ }
+}
+
+func TestStore(t *testing.T) {
+ t.Parallel()
+
+ Convey(`Testing a string Store`, t, func() {
+ st := newStringStore()
+ st.GetOrCreateCollection("")
+
+ Convey(`When empty`, func() {
+ checkEmpty := func(c *Collection) {
+ So(c.Get("foo"), ShouldBeNil)
+ So(iterAll(c.Iterator("")), ShouldResemble, []string{})
+ }
+
+ // Check the basic Store.
+ checkEmpty(st.GetCollection(""))
+
+ // Take a snapshot, then mutate the base Store. Assert that the snapshot
+ // is still empty.
+ snap := st.Snapshot()
+ st.GetCollection("").Put("foo")
+ checkEmpty(snap.GetCollection(""))
+ })
+
+ Convey(`With keys`, func() {
+ coll := st.GetCollection("")
+ coll.Put("a")
+ coll.Put("b")
+ coll.Put("z")
+
+ checkKeys := func(coll *Collection, keys ...string) {
+ for _, k := range keys {
+ So(coll.Get(k), ShouldEqual, k)
+ }
+
+ So(iterAll(coll.Iterator("")), ShouldResemble, keys)
+ for i, k := range keys {
+ So(iterAll(coll.Iterator(k)), ShouldResemble, keys[i:])
+ So(iterAll(coll.Iterator(k+"1")), ShouldResemble, keys[i+1:])
+ }
+ }
+ checkKeys(coll, "a", "b", "z")
+
+ // Take a snapshot, then mutate the base Store. Assert that the snapshot
+ // is still empty.
+ snap := st.Snapshot()
+ snapColl := snap.GetCollection("")
+ coll.Put("foo")
+ checkKeys(snapColl, "a", "b", "z")
+ checkKeys(coll, "a", "b", "foo", "z")
+ })
+ })
+}
+
+// TestStoreParallel performs several rounds of parallel accesses. Each round
+// takes a snapshot of the "head" Store, then simultaneusly dispatches a round
+// of parallel writes agains the "head" store, reads against the snapshot, and
+// reads against the "head" store.
+//
+// This is meant to be run with "-race" to trigger on race conditions.
+func TestStoreParallel(t *testing.T) {
+ t.Parallel()
+
+ Convey(`Testing a string Store for parallel access.`, t, func() {
+ const (
+ readers = 128
+ writers = 16
+ rounds = 8
+ )
+
+ head := newStringStore()
+ var snaps []*Store
+
+ // Dispatch readers.
+ doReads := func() int {
+ readDoneC := make(chan int, readers)
+ for i := 0; i < readers; i++ {
+ go func() {
+ var (
+ doneC = make(chan int, 1+len(snaps))
+ )
+
+ // "head"
+ go func() {
+ doneC <- len(iterAll(head.GetCollection("").Iterator("")))
+ }()
+
+ // "snap"
+ for _, snap := range snaps {
+ go func(snap *Store) {
+ doneC <- len(iterAll(snap.GetCollection("").Iterator("")))
+ }(snap)
+ }
+
+ total := 0
+ for i := 0; i < 1+len(snaps); i++ {
+ total += <-doneC
+ }
+ readDoneC <- total
+ }()
+ }
+
+ total := 0
+ for i := 0; i < readers; i++ {
+ total += <-readDoneC
+ }
+ return total
+ }
+
+ // Dispatch writers.
+ doWrite := func(base string) {
+ writeDoneC := make(chan struct{}, writers)
+ for i := 0; i < writers; i++ {
+ go func(idx int) {
+ head.GetOrCreateCollection("").Put(fmt.Sprintf("%s.%d", base, idx))
+ writeDoneC <- struct{}{}
+ }(i)
+ }
+
+ for i := 0; i < writers; i++ {
+ <-writeDoneC
+ }
+ }
+
+ // Main loop.
+ for i := 0; i < rounds; i++ {
+ writeDoneC := make(chan struct{})
+ readDoneC := make(chan int)
+ go func() {
+ doWrite(strconv.Itoa(i))
+ close(writeDoneC)
+ }()
+ if i > 0 {
+ // The first round has to actually create the Collection.
+ go func() {
+ readDoneC <- doReads()
+ }()
+ }
+
+ <-writeDoneC
+ if i > 0 {
+ So(<-readDoneC, ShouldBeGreaterThan, 0)
+ }
+ snaps = append(snaps, head.Snapshot())
+ }
+ })
+}

Powered by Google App Engine
This is Rietveld 408576698