blob: 06176d3cdaec234e9ab15262b92076f7a32acb14 [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 vine
import (
"testing"
"time"
"v.io/v23/context"
"v.io/v23/discovery"
idiscovery "v.io/x/ref/lib/discovery"
"v.io/x/ref/lib/discovery/plugins/testutil"
_ "v.io/x/ref/runtime/factories/generic"
"v.io/x/ref/test"
"v.io/x/ref/test/timekeeper"
)
func TestBasic(t *testing.T) {
ctx, shutdown := test.V23InitWithMounttable()
defer shutdown()
adinfos := []idiscovery.AdInfo{
{
Ad: discovery.Advertisement{
Id: discovery.AdId{1, 2, 3},
InterfaceName: "v.io/x",
Addresses: []string{"/@6@wsh@v1.com@@/x"},
Attributes: discovery.Attributes{"a": "123"},
Attachments: discovery.Attachments{"a": []byte{1, 2, 3}},
},
Hash: idiscovery.AdHash{1, 2, 3},
},
{
Ad: discovery.Advertisement{
Id: discovery.AdId{4, 5, 6},
InterfaceName: "v.io/y",
Addresses: []string{"/@6@wsh@v2.com@@/y"},
},
Hash: idiscovery.AdHash{4, 5, 6},
},
{
Ad: discovery.Advertisement{
Id: discovery.AdId{7, 8, 9},
InterfaceName: "v.io/x",
Addresses: []string{"/@6@wsh@v3.com@@/y"},
},
Hash: idiscovery.AdHash{7, 8, 9},
},
}
configs := []struct {
name string
peers []string
}{
{"v1", []string{"v2", "v3"}},
{"v2", []string{"v1"}},
{"v3", []string{"v1"}},
}
plugins := make([]idiscovery.Plugin, len(configs))
for i, config := range configs {
var err error
peers := config.peers
plugins[i], err = New(ctx, config.name, func(*context.T) []string { return peers })
if err != nil {
t.Fatal(err)
}
defer plugins[i].Close()
}
var stops []func()
for i, _ := range adinfos {
stop, err := testutil.Advertise(ctx, plugins[i], &adinfos[i])
if err != nil {
t.Fatal(err)
}
stops = append(stops, stop)
}
// Make sure all advertisements are discovered by connected peers.
if err := testutil.ScanAndMatch(ctx, plugins[0], "v.io/x", adinfos[2]); err != nil {
t.Error(err)
}
if err := testutil.ScanAndMatch(ctx, plugins[0], "v.io/y", adinfos[1]); err != nil {
t.Error(err)
}
if err := testutil.ScanAndMatch(ctx, plugins[0], "", adinfos[1], adinfos[2]); err != nil {
t.Error(err)
}
if err := testutil.ScanAndMatch(ctx, plugins[0], "v.io/z"); err != nil {
t.Error(err)
}
if err := testutil.ScanAndMatch(ctx, plugins[1], "", adinfos[0]); err != nil {
t.Error(err)
}
if err := testutil.ScanAndMatch(ctx, plugins[1], "v.io/y"); err != nil {
t.Error(err)
}
if err := testutil.ScanAndMatch(ctx, plugins[2], "", adinfos[0]); err != nil {
t.Error(err)
}
if err := testutil.ScanAndMatch(ctx, plugins[1], "v.io/y"); err != nil {
t.Error(err)
}
// Stop advertisings and make sure they are not discovered by others.
stops[0]()
if err := testutil.ScanAndMatch(ctx, plugins[1], "v.io/x"); err != nil {
t.Error(err)
}
if err := testutil.ScanAndMatch(ctx, plugins[2], ""); err != nil {
t.Error(err)
}
stops[1]()
if err := testutil.ScanAndMatch(ctx, plugins[0], "v.io/y"); err != nil {
t.Error(err)
}
// Open a new scan channel and consume expected advertisements first.
scanCh, scanStop, err := testutil.Scan(ctx, plugins[0], "")
if err != nil {
t.Error(err)
}
defer scanStop()
adinfo := *<-scanCh
if !testutil.MatchFound([]idiscovery.AdInfo{adinfo}, adinfos[2]) {
t.Errorf("Unexpected scan: %v, but want %v", adinfo, adinfos[2])
}
// Make sure scan returns the lost advertisement when advertising is stopped.
stops[2]()
adinfo = *<-scanCh
if !testutil.MatchLost([]idiscovery.AdInfo{adinfo}, adinfos[2]) {
t.Errorf("Unexpected scan: %v, but want %v as lost", adinfo, adinfos[2])
}
}
func TestExpired(t *testing.T) {
ctx, shutdown := test.V23InitWithMounttable()
defer shutdown()
adinfos := []idiscovery.AdInfo{
{
Ad: discovery.Advertisement{
Id: discovery.AdId{1, 2, 3},
InterfaceName: "v.io/x",
Addresses: []string{"/@6@wsh@v1.com@@/x"},
},
Hash: idiscovery.AdHash{1, 2, 3},
},
{
Ad: discovery.Advertisement{
Id: discovery.AdId{4, 5, 6},
InterfaceName: "v.io/y",
Addresses: []string{"/@6@wsh@v2.com@@/y"},
},
Hash: idiscovery.AdHash{4, 5, 6},
},
}
configs := []struct {
name string
peers []string
ttl time.Duration
clock timekeeper.ManualTime
}{
{"v1", []string{"v3"}, 10 * time.Minute, timekeeper.NewManualTime()},
{"v2", []string{"v3"}, 20 * time.Minute, timekeeper.NewManualTime()},
{"v3", []string{}, 0, timekeeper.NewManualTime()},
}
plugins := make([]idiscovery.Plugin, len(configs))
for i, config := range configs {
var err error
peers := config.peers
plugins[i], err = newWithClock(ctx, config.name, func(*context.T) []string { return peers }, config.ttl, config.clock)
if err != nil {
t.Fatal(err)
}
defer plugins[i].Close()
}
for i, _ := range adinfos {
stop, err := testutil.Advertise(ctx, plugins[i], &adinfos[i])
if err != nil {
t.Fatal(err)
}
defer stop()
}
// All advertisements should be discovered for now.
if err := testutil.ScanAndMatch(ctx, plugins[2], "", adinfos...); err != nil {
t.Error(err)
}
// Advance the clock past the first advertisement TTL and make sure it has been expired.
<-configs[2].clock.Requests()
configs[2].clock.AdvanceTime(15 * time.Minute)
if err := testutil.ScanAndMatch(ctx, plugins[2], "", adinfos[1]); err != nil {
t.Error(err)
}
// Advance the clock past the second advertisement TTL and make sure it has been expired too.
<-configs[2].clock.Requests()
configs[2].clock.AdvanceTime(10 * time.Minute)
if err := testutil.ScanAndMatch(ctx, plugins[2], ""); err != nil {
t.Error(err)
}
}
func TestRefresh(t *testing.T) {
ctx, shutdown := test.V23InitWithMounttable()
defer shutdown()
adinfo := idiscovery.AdInfo{
Ad: discovery.Advertisement{
Id: discovery.AdId{1, 2, 3},
InterfaceName: "v.io/x",
Addresses: []string{"/@6@wsh@v1.com@@/x"},
Attributes: discovery.Attributes{"a": "123"},
Attachments: discovery.Attachments{"a": []byte{1, 2, 3}},
},
Hash: idiscovery.AdHash{1, 2, 3},
}
configs := []struct {
name string
peers []string
}{
{"v1", []string{"v2"}},
{"v2", []string{}},
}
const TTL = 10 * time.Minute
clock := timekeeper.NewManualTime()
plugins := make([]idiscovery.Plugin, len(configs))
for i, config := range configs {
var err error
peers := config.peers
plugins[i], err = newWithClock(ctx, config.name, func(*context.T) []string { return peers }, TTL, clock)
if err != nil {
t.Fatal(err)
}
defer plugins[i].Close()
}
stop, err := testutil.Advertise(ctx, plugins[0], &adinfo)
if err != nil {
t.Fatal(err)
}
defer stop()
// Make sure all advertisements are discovered.
if err := testutil.ScanAndMatch(ctx, plugins[1], "", adinfo); err != nil {
t.Error(err)
}
// Make sure that the advertisement are refreshed on every ttl time.
for i := 0; i < 5; i++ {
for {
// Wait until the advertising plugin is waiting for refreshing explicitly
// since the scanning plugin may call After() multiple times depending on
// the goroutine scheduling during the test. Note that the scanning plugin
// waits for TTL with a slack time.
if d := <-clock.Requests(); d == TTL {
break
}
}
clock.AdvanceTime(TTL * 2)
if err := testutil.ScanAndMatch(ctx, plugins[1], "", adinfo); err != nil {
t.Error(err)
}
}
}