blob: 0f0de0edcc7b5e7d61dc08a35dbe1988cc8fdb20 [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 lib
import (
"reflect"
"sync"
"testing"
"v.io/v23"
"v.io/v23/context"
"v.io/v23/vdl"
"v.io/v23/vdlroot/signature"
"v.io/x/ref/profiles/fake"
"v.io/x/ref/test"
)
const (
name = "/vanadium/name"
)
func initContext(t *testing.T) (*context.T, clientWithTimesCalled, v23.Shutdown) {
ctx, shutdown := test.InitForTest()
initialSig := []signature.Interface{
{
Methods: []signature.Method{
{
Name: "Method1",
InArgs: []signature.Arg{{Type: vdl.StringType}},
},
},
},
}
client := newSimpleClient(
map[string][]interface{}{
"__Signature": []interface{}{initialSig},
},
)
ctx = fake.SetClient(ctx, client)
return ctx, client, shutdown
}
func TestFetching(t *testing.T) {
ctx, _, shutdown := initContext(t)
defer shutdown()
sm := NewSignatureManager()
got, err := sm.Signature(ctx, name)
if err != nil {
t.Errorf(`Did not expect an error but got %v`, err)
return
}
want := []signature.Interface{
{
Methods: []signature.Method{
{
Name: "Method1",
InArgs: []signature.Arg{{Type: vdl.StringType}},
},
},
},
}
if !reflect.DeepEqual(got, want) {
t.Errorf(`Signature got %v, want %v`, got, want)
}
}
func TestThatCachedAfterFetching(t *testing.T) {
ctx, _, shutdown := initContext(t)
defer shutdown()
sm := NewSignatureManager().(*signatureManager)
sig, _ := sm.Signature(ctx, name)
cache, ok := sm.cache[name]
if !ok {
t.Errorf(`Signature manager did not cache the results`)
return
}
if got, want := cache.sig, sig; !reflect.DeepEqual(got, want) {
t.Errorf(`Cached signature got %v, want %v`, got, want)
}
}
func TestThatCacheIsUsed(t *testing.T) {
ctx, client, shutdown := initContext(t)
defer shutdown()
// call twice
sm := NewSignatureManager()
sm.Signature(ctx, name)
sm.Signature(ctx, name)
// expect number of calls to Signature method of client to still be 1 since cache
// should have been used despite the second call
if client.TimesCalled("__Signature") != 1 {
t.Errorf("Signature cache was not used for the second call")
}
}
func TestThatLastAccessedGetUpdated(t *testing.T) {
ctx, _, shutdown := initContext(t)
defer shutdown()
sm := NewSignatureManager().(*signatureManager)
sm.Signature(ctx, name)
// make last accessed be in the past to account for the fact that
// two consecutive calls to time.Now() can return identical values.
sm.cache[name].lastAccessed = sm.cache[name].lastAccessed.Add(-ttl / 2)
prevAccess := sm.cache[name].lastAccessed
// access again
sm.Signature(ctx, name)
newAccess := sm.cache[name].lastAccessed
if !newAccess.After(prevAccess) {
t.Errorf("LastAccessed was not updated for cache entry")
}
}
func TestThatTTLExpires(t *testing.T) {
ctx, client, shutdown := initContext(t)
defer shutdown()
sm := NewSignatureManager().(*signatureManager)
sm.Signature(ctx, name)
// make last accessed go over the ttl
sm.cache[name].lastAccessed = sm.cache[name].lastAccessed.Add(-2 * ttl)
// make a second call
sm.Signature(ctx, name)
// expect number of calls to Signature method of client to be 2 since cache should have expired
if client.TimesCalled("__Signature") != 2 {
t.Errorf("Cache was still used but TTL had passed. It should have been fetched again")
}
}
func TestConcurrency(t *testing.T) {
ctx, client, shutdown := initContext(t)
defer shutdown()
sm := NewSignatureManager().(*signatureManager)
var wg sync.WaitGroup
wg.Add(2)
// Even though the signature calls return immediately in the fake client,
// running this with the race detector should find races if the locking is done
// poorly.
go func() {
sm.Signature(ctx, name)
wg.Done()
}()
go func() {
sm.Signature(ctx, name)
wg.Done()
}()
wg.Wait()
// expect number of calls to Signature method of client to be 1 since the second call should
// wait until the first finished.
if client.TimesCalled("__Signature") != 1 {
t.Errorf("__Signature should only be called once.")
}
}