blob: 362cf4d55aa581b94d7f870726e325f71394083d [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package vsync
// Tests for Blob support in Syncbase.
import (
"reflect"
"testing"
wire "v.io/v23/services/syncbase"
"v.io/v23/vdl"
"v.io/v23/vom"
_ "v.io/x/ref/runtime/factories/generic"
td "v.io/x/ref/services/syncbase/vsync/testdata"
)
// checkBlobRefs takes a data entry and a list of expected blob refs expected to
// be found in it. First it VOM-encodes the data entry, then VOM-decodes it and
// extracts all blob refs in it. Finally it compares the blob refs found to the
// ones expected (given in string format).
func checkBlobRefs(t *testing.T, msg string, data interface{}, exp ...string) {
buf, err := vom.Encode(data)
if err != nil {
t.Errorf("vom encode failed: %s: %v", msg, err)
return
}
var val *vdl.Value
if err = vom.Decode(buf, &val); err != nil {
t.Errorf("vom decode failed: %s: %v", msg, err)
return
}
brs := extractBlobRefs(val)
expBrs := make(map[wire.BlobRef]struct{})
for _, e := range exp {
expBrs[wire.BlobRef(e)] = struct{}{}
}
if !reflect.DeepEqual(brs, expBrs) {
t.Errorf("wrong blob refs extracted: %s: got %v, want %v", msg, brs, expBrs)
}
}
// TestExtractBlobRefs checks the extraction of blob refs from various datatypes
// with different kinds of nesting.
func TestExtractBlobRefs(t *testing.T) {
checkBlobRefs(t, "nil data", nil)
br := wire.BlobRef("123")
checkBlobRefs(t, "simple BlobRef", br, "123")
type test1 struct {
A int64
B string
C wire.BlobRef
}
v1 := test1{10, "foo", wire.BlobRef("456")}
checkBlobRefs(t, "struct with BlobRef", v1, "456")
v2 := struct {
A int64
B string
C test1
}{11, "bar", v1}
checkBlobRefs(t, "nested struct with BlobRef", v2, "456")
v3 := struct {
A string
B []wire.BlobRef
}{"hello", []wire.BlobRef{"111", "222", "333"}}
checkBlobRefs(t, "struct with BlobRef array", v3, "111", "222", "333")
v4 := struct {
A string
B map[wire.BlobRef]bool
}{"hello", make(map[wire.BlobRef]bool)}
for _, b := range []string{"999", "888", "777"} {
v4.B[wire.BlobRef(b)] = true
}
checkBlobRefs(t, "struct with map of BlobRef keys", v4, "777", "888", "999")
v5 := struct {
A map[string]wire.BlobRef
B int
}{make(map[string]wire.BlobRef), 99}
for _, b := range []string{"11", "22", "33"} {
v5.A[b] = wire.BlobRef(b)
}
checkBlobRefs(t, "struct with map of BlobRef values", v5, "11", "22", "33")
v6 := td.BlobUnionNum{45}
checkBlobRefs(t, "union w/o blob ref", v6)
v7 := td.BlobUnionBi{td.BlobInfo{"foobar", wire.BlobRef("xyz")}}
checkBlobRefs(t, "union with blob ref", v7, "xyz")
// Array of union entries, the 1st with no blob ref, the 2nd with a
// blob ref to verify that the early-break optimization doesn't apply
// when a union is used.
v7b := struct {
A string
B []td.BlobUnion
}{"hello", nil}
v7b.B = append(v7b.B, v6)
v7b.B = append(v7b.B, v7)
checkBlobRefs(t, "struct with array of union", v7b, "xyz")
v8 := td.BlobSet{"foobar", make(map[wire.BlobRef]struct{})}
for _, b := range []string{"haha", "hoho", "hihi"} {
v8.Bs[wire.BlobRef(b)] = struct{}{}
}
checkBlobRefs(t, "struct with blob set", v8, "haha", "hoho", "hihi")
// Blob refs in an array of "any" entries with the first blob ref
// appearing later in the array to verify that the early-break
// optimization doesn't apply when "any" is used.
v9 := td.BlobAny{"foobar", nil}
v9.Baa = append(v9.Baa, vom.RawBytesOf("xxxxx"))
v9.Baa = append(v9.Baa, vom.RawBytesOf(999999))
v9.Baa = append(v9.Baa, vom.RawBytesOf(br))
v9.Baa = append(v9.Baa, vom.RawBytesOf(v1))
v9.Baa = append(v9.Baa, vom.RawBytesOf(v2))
v9.Baa = append(v9.Baa, vom.RawBytesOf(v3))
v9.Baa = append(v9.Baa, vom.RawBytesOf(v4))
v9.Baa = append(v9.Baa, vom.RawBytesOf(v5))
v9.Baa = append(v9.Baa, vom.RawBytesOf(v6))
v9.Baa = append(v9.Baa, vom.RawBytesOf(v7))
v9.Baa = append(v9.Baa, vom.RawBytesOf(v8))
checkBlobRefs(t, "struct with array of any", v9, "456", "123", "111", "222", "333",
"777", "888", "999", "11", "22", "33", "xyz", "haha", "hoho", "hihi")
v10 := struct {
A string
B []int
}{"hello", []int{111, 222, 333}}
checkBlobRefs(t, "struct with array of non-blobs (early break)", v10)
v11 := struct {
A string
B map[string]int
}{"hello", map[string]int{"abc": 111, "def": 222, "ghi": 333}}
checkBlobRefs(t, "struct with map of non-blobs (early break)", v11)
v12 := td.NonBlobSet{"foobar", make(map[string]struct{})}
for _, b := range []string{"haha", "hoho", "hihi"} {
v12.S[b] = struct{}{}
}
checkBlobRefs(t, "struct with non-blob set (early break)", v12)
v13 := struct {
A map[wire.BlobRef]wire.BlobRef
B int
}{make(map[wire.BlobRef]wire.BlobRef), 99}
for _, b := range []string{"11", "22"} {
v13.A[wire.BlobRef("k-"+b)] = wire.BlobRef("v-" + b)
}
checkBlobRefs(t, "struct with map of BlobRef keys and values", v13, "k-11", "v-11", "k-22", "v-22")
v14 := struct {
A string
B [3]int
}{"hello", [3]int{111, 222, 333}}
checkBlobRefs(t, "struct with fixed-array of non-blobs (early break)", v14)
v15 := struct {
A string
B [3]wire.BlobRef
}{"hello", [3]wire.BlobRef{"111", "222", "333"}}
checkBlobRefs(t, "struct with fixed-array of blobs", v15, "111", "222", "333")
v16 := td.BlobOpt{"foobar", nil}
checkBlobRefs(t, "struct with nil optional blob", v16)
v17 := td.BlobOpt{"foobar", &td.BlobInfo{"haha", wire.BlobRef("xyz")}}
checkBlobRefs(t, "struct with non-nil optional blob", v17, "xyz")
}