blob: 715c6e036cf8a61f5aa54aae022eda78c0cdd393 [file] [log] [blame]
package lib
import (
"errors"
"reflect"
"testing"
"time"
"veyron2/context"
"veyron2/ipc"
"veyron2/naming"
"veyron2/rt"
"veyron2/vdl/vdlutil"
"veyron2/verror"
hps "veyron/examples/wspr_sample"
)
// getCacheClient initializes the runtime and creates a client binding.
func getCacheClient(address string) (hps.Cache, error) {
rt.Init()
// Bind to a rooted, terminal name to bypass the MountTable which isn't
// actually used, nor needed, in these tests.
s, err := hps.BindCache(naming.JoinAddressName(address, "//cache"))
if err != nil {
return nil, err
}
return s, nil
}
// TestValueSetGet tests setting values and then calling various Get* functions.
func TestValueSetGet(t *testing.T) {
type testCase struct {
mapFieldName string
nameOfGetMethod string
v interface{}
shouldGetError bool
}
tests := []testCase{
testCase{"val", "Get", "Test", false},
testCase{"val", "Get", 4, false},
testCase{"val", "Get", struct {
X int
Y string
}{4, "Test"}, false},
testCase{"i32", "GetAsInt32", int32(1), false},
testCase{"i64", "GetAsInt64", int64(1), false},
testCase{"byt", "GetAsByte", byte(1), false},
testCase{"ui32", "GetAsUint32", uint32(1), false},
testCase{"ui64", "GetAsUint64", uint64(1), false},
testCase{"fl32", "GetAsFloat32", float32(1), false},
testCase{"fl64", "GetAsFloat64", float64(1), false},
testCase{"b", "GetAsBool", true, false},
testCase{"s", "GetAsString", "Test", false},
testCase{"err", "GetAsError", verror.ToStandard(errors.New("Test Error")), false},
testCase{"err_i8", "GetAsByte", int64(4), true},
testCase{"err_i64", "GetAsInt64", int8(1), true},
testCase{"err_string", "GetAsString", true, true},
}
r := rt.Init()
ctx := r.NewContext()
s, endpoint, err := StartServer(r)
if err != nil {
t.Fatal("failed to start server: ", err)
}
defer s.Stop()
c, err := getCacheClient(endpoint.String())
if err != nil {
t.Fatal("failed to connect client: ", err)
}
for _, test := range tests {
// Call Set().
if err := c.Set(ctx, test.mapFieldName, test.v); err != nil {
t.Errorf("error setting: %v (test case: %v)", err, test)
continue
}
meth := reflect.ValueOf(c).MethodByName(test.nameOfGetMethod)
out := meth.Call([]reflect.Value{reflect.ValueOf(ctx), reflect.ValueOf(test.mapFieldName)})
if !test.shouldGetError {
if out[1].Interface() != nil {
t.Errorf("error getting: %v (test case: %v)", err, test)
continue
}
if out[0].Interface() != test.v {
t.Errorf("returned result does not match")
}
} else if out[1].Interface() == nil {
t.Errorf("expected error in case %v", test)
}
}
}
// settable mirrors the cache's Set method to provide a consistent way to populate test cases.
type settable interface {
Set(ctx context.T, key string, val vdlutil.Any, opts ...ipc.CallOpt) error
}
// populateObject populates a settable with 12 values.
func populateObject(ctx context.T, s settable) error {
if err := s.Set(ctx, "A", int8(3)); err != nil {
return err
}
// Set "A" again to ensure it takes the second value.
if err := s.Set(ctx, "A", "A"); err != nil {
return err
}
if err := s.Set(ctx, "B", uint16(5)); err != nil {
return err
}
if err := s.Set(ctx, "C", uint32(7)); err != nil {
return err
}
if err := s.Set(ctx, "D", verror.ToStandard(errors.New("Err"))); err != nil {
return err
}
if err := s.Set(ctx, "E", true); err != nil {
return err
}
if err := s.Set(ctx, "F", float32(5.4)); err != nil {
return err
}
if err := s.Set(ctx, "G", struct {
X int
Y string
}{4, "G"}); err != nil {
return err
}
if err := s.Set(ctx, "H", uint64(8)); err != nil {
return err
}
if err := s.Set(ctx, "I", "I"); err != nil {
return err
}
if err := s.Set(ctx, "J", float64(8.3)); err != nil {
return err
}
if err := s.Set(ctx, "K", int64(2)); err != nil {
return err
}
if err := s.Set(ctx, "L", int8(9)); err != nil {
return err
}
return nil
}
// setupManyResults starts a server and client and populates the server with the values in populateObject.
func setupManyResults(t *testing.T) (hps.Cache, ipc.Server) {
r := rt.Init()
s, endpoint, err := StartServer(r)
if err != nil {
t.Fatal("failed to start server: ", err)
}
c, err := getCacheClient(endpoint.String())
if err != nil {
t.Fatal("failed to connect client: ", err)
}
if err := populateObject(r.NewContext(), c.(settable)); err != nil {
t.Fatal("error populating cache: ", err)
}
return c, s
}
// settableMap is a map that implements the settable interface.
type settableMap map[string]vdlutil.Any
func (sm settableMap) Set(ctx context.T, key string, val vdlutil.Any, opts ...ipc.CallOpt) error {
sm[key] = val
return nil
}
// TestAsMap tests that AsMap returns the correct results.
func TestAsMap(t *testing.T) {
c, s := setupManyResults(t)
defer s.Stop()
ctx := rt.R().NewContext()
res, err := c.AsMap(ctx)
if err != nil {
t.Fatal("error calling AsMap: ", err)
}
m := settableMap(make(map[string]vdlutil.Any))
if err := populateObject(ctx, m); err != nil {
t.Fatal("error populating map: ", err)
}
for key, val := range m {
otherVal := res[key]
if val != otherVal {
t.Errorf("didn't match: %v and %v", val, otherVal)
}
}
}
// TestKeyValuePairs tests that KeyValuePairs returns the correct results.
func TestKeyValuePairs(t *testing.T) {
c, s := setupManyResults(t)
defer s.Stop()
ctx := rt.R().NewContext()
res, err := c.KeyValuePairs(ctx)
if err != nil {
t.Fatal("error calling KeyValuePairs: ", err)
}
m := settableMap(make(map[string]vdlutil.Any))
if err := populateObject(ctx, m); err != nil {
t.Fatal("error populating map: ", err)
}
for _, kvp := range res {
otherVal := m[kvp.Key]
if kvp.Value != otherVal {
t.Errorf("didn't match: %v and %v", kvp.Value, otherVal)
}
}
}
// TestKeyPageAndSize tests the KeyPage and size methods.
func TestKeyPageAndSize(t *testing.T) {
c, s := setupManyResults(t)
defer s.Stop()
ctx := rt.R().NewContext()
sz, err := c.Size(ctx)
if err != nil {
t.Fatal("error calling Size: ", err)
}
if sz != 12 {
t.Fatal("wrong number of results: ", sz)
}
res, err := c.KeyPage(ctx, 1)
if err != nil {
t.Fatal("error calling AsMap: ", err)
}
if res[0] != "K" || res[1] != "L" || res[2] != "" {
t.Fatalf("incorrect page results: %v", res)
}
}
// TestMostRecentSet tests the MostRecentSet method.
func TestMostRecentSet(t *testing.T) {
c, s := setupManyResults(t)
defer s.Stop()
ctx := rt.R().NewContext()
timeBefore := time.Now().Unix()
if err := c.Set(ctx, "B", int32(8)); err != nil {
t.Fatal("error calling Set: ", err)
}
timeAfter := time.Now().Unix()
kvp, setTime, err := c.MostRecentSet(ctx)
if err != nil {
t.Fatal("error calling MostRecentSet: ", err)
}
if kvp.Key != "B" || kvp.Value != int32(8) {
t.Errorf("unexpected key value pair: %v", kvp)
}
if setTime < timeBefore || setTime > timeAfter {
t.Errorf("time %v out of range [%v, %v]", setTime, timeBefore, timeAfter)
}
}
// TestMultiGet tests the MultiGet method.
func TestMultiGet(t *testing.T) {
c, s := setupManyResults(t)
defer s.Stop()
stream, err := c.MultiGet(rt.R().NewContext())
if err != nil {
t.Fatal("error calling MultiGet: ", err)
}
stream.Send("A")
stream.Send("C")
stream.Send("E")
if stream.Advance() {
if stream.Value() != "A" {
t.Errorf("value for 'A' didn't match")
}
} else {
t.Fatal("error on advance: %v", stream.Err())
}
if stream.Advance() {
if stream.Value() != uint32(7) {
t.Errorf("value for 'C' didn't match")
}
} else {
t.Fatal("error on advance: %v", stream.Err())
}
if stream.Advance() {
if stream.Value() != true {
t.Errorf("value for 'E' didn't match")
}
} else {
t.Fatal("error on advance: %v", stream.Err())
}
stream.CloseSend()
}