// TestEngineMerge tests that the passing through of engine merge operations // to the goMerge function works as expected. The semantics are tested more // exhaustively in the merge tests themselves. func TestEngineMerge(t *testing.T) { runWithAllEngines(func(engine Engine, t *testing.T) { testKey := Key("haste not in life") merges := [][]byte{ []byte(encoding.MustGobEncode(Appender("x"))), []byte(encoding.MustGobEncode(Appender("y"))), []byte(encoding.MustGobEncode(Appender("z"))), } for i, update := range merges { if err := engine.Merge(testKey, update); err != nil { t.Fatalf("%d: %v", i, err) } } result, _ := engine.Get(testKey) if !bytes.Equal(encoding.MustGobDecode(result).(Appender), Appender("xyz")) { t.Errorf("unexpected append-merge result") } }, t) }
func TestEngineBatch(t *testing.T) { runWithAllEngines(func(engine Engine, t *testing.T) { numShuffles := 100 key := Key("a") // Those are randomized below. batch := []interface{}{ BatchPut{Key: key, Value: []byte("~ockroachDB")}, BatchPut{Key: key, Value: []byte("C~ckroachDB")}, BatchPut{Key: key, Value: []byte("Co~kroachDB")}, BatchPut{Key: key, Value: []byte("Coc~roachDB")}, BatchPut{Key: key, Value: []byte("C**k~oachDB")}, BatchPut{Key: key, Value: []byte("Cockr~achDB")}, BatchPut{Key: key, Value: []byte("Cockro~chDB")}, BatchPut{Key: key, Value: []byte("Cockroa~hDB")}, BatchPut{Key: key, Value: []byte("Cockroac~DB")}, BatchPut{Key: key, Value: []byte("Cockroach~B")}, BatchPut{Key: key, Value: []byte("CockroachD~")}, BatchDelete(key), BatchMerge{Key: key, Value: encoding.MustGobEncode(Appender("C"))}, BatchMerge{Key: key, Value: encoding.MustGobEncode(Appender(" o"))}, BatchMerge{Key: key, Value: encoding.MustGobEncode(Appender(" c"))}, BatchMerge{Key: key, Value: encoding.MustGobEncode(Appender(" k"))}, BatchMerge{Key: key, Value: encoding.MustGobEncode(Appender("r"))}, BatchMerge{Key: key, Value: encoding.MustGobEncode(Appender(" o"))}, BatchMerge{Key: key, Value: encoding.MustGobEncode(Appender(" a"))}, BatchMerge{Key: key, Value: encoding.MustGobEncode(Appender(" c"))}, BatchMerge{Key: key, Value: encoding.MustGobEncode(Appender("h"))}, BatchMerge{Key: key, Value: encoding.MustGobEncode(Appender(" D"))}, BatchMerge{Key: key, Value: encoding.MustGobEncode(Appender(" B"))}, } for i := 0; i < numShuffles; i++ { // In each run, create an array of shuffled operations. shuffledIndices := rand.Perm(len(batch)) currentBatch := make([]interface{}, len(batch)) for k := range currentBatch { currentBatch[k] = batch[shuffledIndices[k]] } // Reset the key engine.Clear(key) // Run it once with individual operations and remember the result. for _, op := range currentBatch { if err := engine.WriteBatch([]interface{}{op}); err != nil { t.Errorf("batch test: %d: op %v: %v", i, op, err) continue } } correctValue, _ := engine.Get(key) // Run the whole thing as a batch and compare. if err := engine.WriteBatch(currentBatch); err != nil { t.Errorf("batch test: %d: %v", i, err) continue } actualValue, _ := engine.Get(key) if !bytes.Equal(actualValue, correctValue) { t.Errorf("batch test: %d: result inconsistent", i) } } }, t) }
// TestGoMerge tests the function goMerge but not the integration with // the storage engines. For that, see the engine tests. func TestGoMerge(t *testing.T) { // Let's start with stuff that should go wrong. badCombinations := []struct { old, update interface{} }{ {Counter(0), Appender("")}, {[]byte("apple"), nil}, {"", ""}, {0, 0}, {Appender(""), Counter(0)}, {0, "asd"}, {float64(1.3), Counter(0)}, {Counter(0), nil}, } for i, c := range badCombinations { _, err := goMerge(encoding.MustGobEncode(c.old), encoding.MustGobEncode(c.update)) if err == nil { t.Errorf("goMerge: %d: expected error", i) } } testCasesCounter := []struct { old, update, expected Counter wantError bool }{ {0, 10, 10, false}, {10, 20, 30, false}, {595, -600, -5, false}, // Close to overflow, but not quite there. {math.MinInt64 + 3, -3, math.MinInt64, false}, {math.MaxInt64, 0, math.MaxInt64, false}, // Overflows. {math.MaxInt64, 1, 0, true}, {-1, math.MinInt64, 0, true}, } gibber1, gibber2 := gibberishBytes(100), gibberishBytes(200) testCasesAppender := []struct { old, update, expected Appender }{ {Appender(""), Appender(""), Appender("")}, {nil, Appender(""), Appender("")}, {nil, nil, nil}, {Appender("\n "), Appender(" \t "), Appender("\n \t ")}, {Appender("ქართული"), Appender("\nKhartuli"), Appender("ქართული\nKhartuli")}, {gibber1, gibber2, append(append([]byte(nil), gibber1...), gibber2...)}, } for i, c := range testCasesCounter { oEncoded := encoding.MustGobEncode(c.old) uEncoded := encoding.MustGobEncode(c.update) result, err := goMerge(oEncoded, uEncoded) if c.wantError { if err == nil { t.Errorf("goMerge: %d: wanted error but got success", i) } continue } if err != nil { t.Errorf("goMerge error: %d: %v", i, err) continue } resultDecoded := encoding.MustGobDecode(result) if resultDecoded != c.expected { t.Errorf("goMerge error: %d: want %v, get %v", i, c.expected, result) } } for i, c := range testCasesAppender { oEncoded := encoding.MustGobEncode(c.old) uEncoded := encoding.MustGobEncode(c.update) result, err := goMerge(oEncoded, uEncoded) if err != nil { t.Errorf("goMerge error: %d: %v", i, err) continue } resultDecoded := encoding.MustGobDecode(result) if !bytes.Equal(resultDecoded.(Appender), c.expected) { t.Errorf("goMerge error: %d: want %v, get %v", i, c.expected, result) } } }