blob: 2bbcea766793fef1d661f4cf02525c698dd05861 [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 ble
import (
"testing"
"time"
"v.io/v23/discovery"
idiscovery "v.io/x/ref/lib/discovery"
"v.io/x/ref/lib/discovery/plugins/testutil"
"v.io/x/ref/test"
)
func TestBasic(t *testing.T) {
ctx, shutdown := test.TestContext()
defer shutdown()
adinfos := []idiscovery.AdInfo{
{
Ad: discovery.Advertisement{
Id: discovery.AdId{1, 2, 3},
InterfaceName: "v.io/x",
Addresses: []string{"/@6@wsh@foo.com:1234@@/x"},
Attributes: discovery.Attributes{"a": "a1234"},
},
Hash: idiscovery.AdHash{1, 2, 3},
TimestampNs: 1001,
},
{
Ad: discovery.Advertisement{
Id: discovery.AdId{4, 5, 6},
InterfaceName: "v.io/y",
Addresses: []string{"/@6@wsh@bar.com:1234@@/y"},
},
Hash: idiscovery.AdHash{4, 5, 6},
TimestampNs: 1002,
},
}
neighborhood := newNeighborhood()
defer neighborhood.shutdown()
p1, err := newWithTTL(ctx, "h1", defaultTTL)
if err != nil {
t.Fatalf("New() failed: %v", err)
}
defer p1.Close()
var stops []func()
for i, _ := range adinfos {
stop, err := testutil.Advertise(ctx, p1, &adinfos[i])
if err != nil {
t.Fatal(err)
}
stops = append(stops, stop)
}
p2, err := newWithTTL(ctx, "h2", 10*time.Millisecond)
if err != nil {
t.Fatalf("New() failed: %v", err)
}
defer p2.Close()
// Make sure all advertisements are discovered.
if err := testutil.ScanAndMatch(ctx, p2, "v.io/x", adinfos[0]); err != nil {
t.Error(err)
}
if err := testutil.ScanAndMatch(ctx, p2, "v.io/y", adinfos[1]); err != nil {
t.Error(err)
}
if err := testutil.ScanAndMatch(ctx, p2, "", adinfos...); err != nil {
t.Error(err)
}
if err := testutil.ScanAndMatch(ctx, p2, "v.io/z"); err != nil {
t.Error(err)
}
// Make sure it is not discovered when advertising is stopped.
stops[0]()
if err := testutil.ScanAndMatch(ctx, p2, "v.io/x"); err != nil {
t.Error(err)
}
if err := testutil.ScanAndMatch(ctx, p2, "", adinfos[1]); err != nil {
t.Error(err)
}
// Open a new scan channel and consume an expected advertisement first.
scanCh, scanStop, err := testutil.Scan(ctx, p2, "v.io/y")
if err != nil {
t.Error(err)
}
defer scanStop()
if got := *<-scanCh; !testutil.MatchFound([]idiscovery.AdInfo{got}, adinfos[1]) {
t.Errorf("Unexpected scan: %v, but want %v", got, adinfos[1])
}
// Make sure scan returns the lost advertisement when advertising is stopped.
stops[1]()
if got := *<-scanCh; !testutil.MatchLost([]idiscovery.AdInfo{got}, adinfos[1]) {
t.Errorf("Unexpected scan: %v, but want %v as lost", got, adinfos[1])
}
// Shouldn't discover anything with a new scan.
if err := testutil.ScanAndMatch(ctx, p2, ""); err != nil {
t.Error(err)
}
// Make sure it is discovered again when advertising is restarted.
stops[1], err = testutil.Advertise(ctx, p1, &adinfos[1])
if err != nil {
t.Fatal(err)
}
defer stops[1]()
if got := *<-scanCh; !testutil.MatchFound([]idiscovery.AdInfo{got}, adinfos[1]) {
t.Errorf("Unexpected scan: %v, but want %v", got, adinfos[1])
}
// Should discover it with a new scan.
if err := testutil.ScanAndMatch(ctx, p2, "", adinfos[1]); err != nil {
t.Error(err)
}
}
func TestMultipleScans(t *testing.T) {
ctx, shutdown := test.TestContext()
defer shutdown()
adinfos := []idiscovery.AdInfo{
{
Ad: discovery.Advertisement{
Id: discovery.AdId{1, 2, 3},
InterfaceName: "v.io/x",
Addresses: []string{"/@6@wsh@foo.com:1234@@/x"},
Attributes: discovery.Attributes{"a": "a1234"},
},
Hash: idiscovery.AdHash{1, 2, 3},
TimestampNs: 1001,
},
{
Ad: discovery.Advertisement{
Id: discovery.AdId{4, 5, 6},
InterfaceName: "v.io/y",
Addresses: []string{"/@6@wsh@bar.com:1234@@/y"},
},
Hash: idiscovery.AdHash{4, 5, 6},
TimestampNs: 1002,
},
}
neighborhood := newNeighborhood()
defer neighborhood.shutdown()
p1, err := newWithTTL(ctx, "h1", defaultTTL)
if err != nil {
t.Fatalf("New() failed: %v", err)
}
defer p1.Close()
for i, _ := range adinfos {
stop, err := testutil.Advertise(ctx, p1, &adinfos[i])
if err != nil {
t.Fatal(err)
}
defer stop()
}
p2, err := newWithTTL(ctx, "h2", defaultTTL)
if err != nil {
t.Fatalf("New() failed: %v", err)
}
defer p2.Close()
scanCh1, scanStop1, err := testutil.Scan(ctx, p2, "v.io/x")
if err != nil {
t.Error(err)
}
defer scanStop1()
adinfo := *<-scanCh1
if !testutil.MatchFound([]idiscovery.AdInfo{adinfo}, adinfos[0]) {
t.Errorf("Unexpected scan: %v, but want %v", adinfo, adinfos[0])
}
p3, err := newWithTTL(ctx, "h3", defaultTTL)
if err != nil {
t.Fatalf("New() failed: %v", err)
}
defer p3.Close()
scanCh2, scanStop2, err := testutil.Scan(ctx, p3, "v.io/y")
if err != nil {
t.Error(err)
}
defer scanStop2()
adinfo = *<-scanCh2
if !testutil.MatchFound([]idiscovery.AdInfo{adinfo}, adinfos[1]) {
t.Errorf("Unexpected scan: %v, but want %v", adinfo, adinfos[1])
}
// They should not affect other scans.
select {
case got := <-scanCh1:
t.Errorf("Unexpected scan: %v", *got)
case got := <-scanCh2:
t.Errorf("Unexpected scan: %v", *got)
case <-time.After(5 * time.Millisecond):
}
}
func TestAttachments(t *testing.T) {
ctx, shutdown := test.TestContext()
defer shutdown()
adinfos := []idiscovery.AdInfo{
{
Ad: discovery.Advertisement{
Id: discovery.AdId{1, 2, 3},
InterfaceName: "v.io/x",
Addresses: []string{"/@6@wsh@foo.com:1234@@/x"},
},
DirAddrs: []string{"/@6@wsh@foo.com:1234@@/d"},
},
{
Ad: discovery.Advertisement{
Id: discovery.AdId{4, 5, 6},
InterfaceName: "v.io/y",
Addresses: []string{"/@6@wsh@foo.com:1234@@/x"},
Attachments: discovery.Attachments{"a": []byte{11, 12, 13}},
},
DirAddrs: []string{"/@6@wsh@foo.com:1234@@/d"},
},
}
neighborhood := newNeighborhood()
defer neighborhood.shutdown()
p1, err := newWithTTL(ctx, "h1", defaultTTL)
if err != nil {
t.Fatalf("New() failed: %v", err)
}
defer p1.Close()
for i, _ := range adinfos {
stop, err := testutil.Advertise(ctx, p1, &adinfos[i])
if err != nil {
t.Fatal(err)
}
defer stop()
}
p2, err := newWithTTL(ctx, "h2", defaultTTL)
if err != nil {
t.Fatalf("New() failed: %v", err)
}
defer p2.Close()
withAdStatus := func(adinfo idiscovery.AdInfo, status idiscovery.AdStatus) idiscovery.AdInfo {
switch status {
case idiscovery.AdReady:
adinfo.DirAddrs = nil
adinfo.Status = idiscovery.AdReady
case idiscovery.AdPartiallyReady:
adinfo.Ad.Attachments = nil
adinfo.Status = idiscovery.AdPartiallyReady
}
return adinfo
}
wanted := []idiscovery.AdInfo{
withAdStatus(adinfos[0], idiscovery.AdReady),
withAdStatus(adinfos[1], idiscovery.AdPartiallyReady),
}
if err := testutil.ScanAndMatch(ctx, p2, "", wanted...); err != nil {
t.Error(err)
}
}
func TestMultipleInstances(t *testing.T) {
ctx, shutdown := test.TestContext()
defer shutdown()
adinfos := []idiscovery.AdInfo{
{
Ad: discovery.Advertisement{
Id: discovery.AdId{1, 2, 3},
InterfaceName: "v.io/x",
Addresses: []string{"/@6@wsh@foo.com:1234@@/x"},
},
},
{
Ad: discovery.Advertisement{
Id: discovery.AdId{4, 5, 6},
InterfaceName: "v.io/x",
Addresses: []string{"/@6@wsh@foo.com:5678@@/x"},
},
},
{
Ad: discovery.Advertisement{
Id: discovery.AdId{7, 8, 9},
InterfaceName: "v.io/x",
Addresses: []string{"/@6@wsh@bar.com:1234@@/x"},
},
},
}
neighborhood := newNeighborhood()
defer neighborhood.shutdown()
p1, err := newWithTTL(ctx, "h1", defaultTTL)
if err != nil {
t.Fatalf("New() failed: %v", err)
}
defer p1.Close()
stop0, err := testutil.Advertise(ctx, p1, &adinfos[0])
if err != nil {
t.Fatal(err)
}
p2, err := newWithTTL(ctx, "h2", 10*time.Millisecond)
if err != nil {
t.Fatalf("New() failed: %v", err)
}
defer p2.Close()
// Open a new scan channel and consume an expected advertisement first.
scanCh, scanStop, err := testutil.Scan(ctx, p2, "v.io/x")
if err != nil {
t.Error(err)
}
defer scanStop()
if got := *<-scanCh; !testutil.MatchFound([]idiscovery.AdInfo{got}, adinfos[0]) {
t.Errorf("Unexpected scan: %v, but want %v", got, adinfos[0])
}
// Try to advertise another instance of the same interface.
stop1, err := testutil.Advertise(ctx, p1, &adinfos[1])
if err != nil {
t.Fatal(err)
}
defer stop1()
// We might lose the existing advertisements before rescanning under very
// heavy load. Wait until the expected advertisement is scanned.
if err := testutil.WaitUntilMatchFound(scanCh, adinfos[1]); err != nil {
t.Error(err)
}
// Make sure scan returns the lost advertisement when advertising is stopped.
stop0()
if err := testutil.WaitUntilMatchLost(scanCh, adinfos[0]); err != nil {
t.Error(err)
}
// Other device should be able to advertise an instance of the same interface too.
p3, err := newWithTTL(ctx, "h3", defaultTTL)
if err != nil {
t.Fatalf("New() failed: %v", err)
}
defer p3.Close()
stop2, err := testutil.Advertise(ctx, p3, &adinfos[2])
if err != nil {
t.Fatal(err)
}
defer stop2()
if err := testutil.WaitUntilMatchFound(scanCh, adinfos[2]); err != nil {
t.Error(err)
}
// Make sure all active advertisements are discovered by a new scan.
if err := testutil.ScanAndMatch(ctx, p2, "", adinfos[1], adinfos[2]); err != nil {
t.Error(err)
}
}
func TestMaximumMultipleInstances(t *testing.T) {
ctx, shutdown := test.TestContext()
defer shutdown()
neighborhood := newNeighborhood()
defer neighborhood.shutdown()
p1, err := newWithTTL(ctx, "h1", defaultTTL)
if err != nil {
t.Fatalf("New() failed: %v", err)
}
defer p1.Close()
var adinfos []idiscovery.AdInfo
for i := 0; i < maxNumPackedServices; i++ {
adinfo := idiscovery.AdInfo{
Ad: discovery.Advertisement{
Id: discovery.AdId{byte(i + 1)},
InterfaceName: "v.io/y",
Addresses: []string{"/@6@wsh@foo.com:1234@@/y"},
},
}
stop, err := testutil.Advertise(ctx, p1, &adinfo)
if err != nil {
t.Error(err)
continue
}
defer stop()
adinfos = append(adinfos, adinfo)
}
// Make sure no more instance is allowed.
oneMore := adinfos[0]
oneMore.Ad.Id[0] = byte(maxNumPackedServices + 1)
if _, err := testutil.Advertise(ctx, p1, &oneMore); err == nil {
t.Error("Expected an error; but got none")
}
// Make sure all advertisements are discovered.
p2, err := newWithTTL(ctx, "h2", defaultTTL)
if err != nil {
t.Fatalf("New() failed: %v", err)
}
defer p2.Close()
if err := testutil.ScanAndMatch(ctx, p2, "", adinfos...); err != nil {
t.Error(err)
}
}
func TestTimestamp(t *testing.T) {
ctx, shutdown := test.TestContext()
defer shutdown()
adinfo := idiscovery.AdInfo{
Ad: discovery.Advertisement{
Id: discovery.AdId{1, 2, 3},
InterfaceName: "v.io/x",
Addresses: []string{"/@6@wsh@foo.com:1234@@/x"},
},
Hash: idiscovery.AdHash{1, 2, 3},
TimestampNs: 1001,
}
neighborhood := newNeighborhood()
defer neighborhood.shutdown()
p1, err := newWithTTL(ctx, "h1", defaultTTL)
if err != nil {
t.Fatalf("New() failed: %v", err)
}
defer p1.Close()
stop, err := testutil.Advertise(ctx, p1, &adinfo)
if err != nil {
t.Fatal(err)
}
p2, err := newWithTTL(ctx, "h2", defaultTTL)
if err != nil {
t.Fatalf("New() failed: %v", err)
}
defer p2.Close()
// Open a new scan channel and consume expected advertisements first.
scanCh, scanStop, err := testutil.Scan(ctx, p2, "")
if err != nil {
t.Error(err)
}
defer scanStop()
if got := <-scanCh; !testutil.MatchFound([]idiscovery.AdInfo{*got}, adinfo) {
t.Errorf("Unexpected scan: %v, but want %v", *got, adinfo)
}
// Stop and re-advertise it. We should not receive any new advertisement.
stop()
stop, err = testutil.Advertise(ctx, p1, &adinfo)
if err != nil {
t.Fatal(err)
}
select {
case got := <-scanCh:
t.Errorf("Unexpected scan: %v", *got)
case <-time.After(5 * time.Millisecond):
}
// Stop and re-advertise it with an older timestamp. We should ignore it.
stop()
adinfo.Ad.Addresses = []string{"/@6@wsh@foo.com:1234@@/y"}
adinfo.Hash = idiscovery.AdHash{4, 5, 6}
adinfo.TimestampNs = 1000
stop, err = testutil.Advertise(ctx, p1, &adinfo)
if err != nil {
t.Fatal(err)
}
select {
case got := <-scanCh:
t.Errorf("Unexpected scan: %v", *got)
case <-time.After(5 * time.Millisecond):
}
// Stop and re-advertise it with a newer timestamp. We should discover it.
stop()
adinfo.Ad.Addresses = []string{"/@6@wsh@foo.com:1234@@/z"}
adinfo.Hash = idiscovery.AdHash{7, 8, 9}
adinfo.TimestampNs = 1002
stop, err = testutil.Advertise(ctx, p1, &adinfo)
if err != nil {
t.Fatal(err)
}
if got := <-scanCh; !testutil.MatchFound([]idiscovery.AdInfo{*got}, adinfo) {
t.Errorf("Unexpected scan: %v, but want %v", *got, adinfo)
}
}