blob: 1ed80e19f77dc1008fb9820ab1de9d2ceee4e53c [file] [log] [blame]
// Copyright 2016 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 discovery
import (
"bytes"
"fmt"
"os"
"reflect"
"testing"
"v.io/v23/context"
"v.io/v23/logging"
"v.io/v23/security"
wire "v.io/v23/services/syncbase"
)
func TestJoinSplitPatterns(t *testing.T) {
cases := []struct {
patterns []security.BlessingPattern
joined string
}{
{nil, ""},
{[]security.BlessingPattern{"a", "b"}, "a,b"},
{[]security.BlessingPattern{"a:b:c", "d:e:f"}, "a:b:c,d:e:f"},
{[]security.BlessingPattern{"alpha:one", "alpha:two", "alpha:three"}, "alpha:one,alpha:two,alpha:three"},
}
for _, c := range cases {
if got := joinPatterns(c.patterns); got != c.joined {
t.Errorf("%#v, got %q, wanted %q", c.patterns, got, c.joined)
}
if got := splitPatterns(c.joined); !reflect.DeepEqual(got, c.patterns) {
t.Errorf("%q, got %#v, wanted %#v", c.joined, got, c.patterns)
}
}
// Special case, Joining an empty non-nil list results in empty string.
if got := joinPatterns([]security.BlessingPattern{}); got != "" {
t.Errorf("Joining empty list: got %q, want %q", got, "")
}
}
type logger struct {
logging.Logger
}
func (l logger) InfoDepth(depth int, args ...interface{}) {
fmt.Fprintln(os.Stdout, args...)
}
func TestInviteQueue(t *testing.T) {
ctx, cancel := context.RootContext()
defer cancel()
ctx = context.WithLogger(ctx, logger{})
q := newInviteQueue()
if q.size() != 0 {
t.Errorf("got %d, want 0", q.size())
}
want := []string{"0", "1", "2", "3", "4"}
// Test inserts during a long scan.
ch := make(chan struct{})
go func() {
scan(ctx, q, want)
close(ch)
}()
// Add a bunch of entries.
for i, w := range want {
q.add(Invite{Syncgroup: wire.Id{Name: w}, key: w})
if q.size() != i+1 {
t.Errorf("got %d, want %d", q.size(), i+1)
}
if err := scan(ctx, q, want[:i+1]); err != nil {
t.Error(err)
}
}
// Make sure long term scan finished.
<-ch
// Start another long scan that will proceed across deletes
steps := make(chan struct{})
go func() {
ctx, cancel := context.WithCancel(ctx)
c := q.scan()
if inv, ok := q.next(ctx, c); !ok || inv.Syncgroup.Name != want[0] {
t.Error("next should have suceeded.")
}
if inv, ok := q.next(ctx, c); !ok || inv.Syncgroup.Name != want[1] {
t.Error("next should have suceeded.")
}
steps <- struct{}{}
<-steps
if inv, ok := q.next(ctx, c); !ok || inv.Syncgroup.Name != want[3] {
t.Error("next should have suceeded.")
}
if inv, ok := q.next(ctx, c); !ok || inv.Syncgroup.Name != want[4] {
t.Error("next should have suceeded.")
}
cancel()
steps <- struct{}{}
}()
// Wait for the scan to read the first two values.
<-steps
// Remove a bunch of entries.
for i, w := range want {
q.remove(Invite{Syncgroup: wire.Id{Name: w}, key: w})
if i == 2 {
// Tell the scan to read the next two values
steps <- struct{}{}
// Wait for it to finish.
<-steps
}
if q.size() != len(want)-i-1 {
t.Errorf("got %d, want %d", q.size(), len(want)-i-1)
}
if err := scan(ctx, q, want[i+1:]); err != nil {
t.Error(err)
}
}
}
func logList(ctx *context.T, q *inviteQueue) {
buf := &bytes.Buffer{}
for e := q.sentinel.next; e != &q.sentinel; e = e.next {
fmt.Fprintf(buf, "%p ", e)
}
ctx.Info("list", buf.String())
}
func scan(ctx *context.T, q *inviteQueue, want []string) error {
ctx, cancel := context.WithCancel(ctx)
c := q.scan()
for i, w := range want {
inv, ok := q.next(ctx, c)
if !ok {
return fmt.Errorf("scan ended after %d entries, wanted %d", i, len(want))
}
if got := inv.Syncgroup.Name; got != w {
return fmt.Errorf("got %s, want %s", got, w)
}
if i == len(want)-1 {
// Calling cancel should allow next to fail, exiting the loop.
cancel()
}
}
return nil
}