Added advertiser test.
Change-Id: I8f474e837d27b633bed14d6a421eba65436dd449
diff --git a/Makefile b/Makefile
index fc0ecee..054b34d 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,3 @@
-
include ../shared/mojo.mk
V23_GO_FILES := $(shell find $(JIRI_ROOT) -name "*.go")
@@ -13,6 +12,14 @@
--config-alias DISCOVERY_DIR=$(PWD) \
--config-alias DISCOVERY_BUILD_DIR=$(DISCOVERY_BUILD_DIR)
+define CGO_TEST
+ GOPATH="$(GOPATH)" \
+ CGO_CFLAGS="-I$(MOJO_DIR)/src $(CGO_CFLAGS)" \
+ CGO_CXXFLAGS="-I$(MOJO_DIR)/src $(CGO_CXXFLAGS)" \
+ CGO_LDFLAGS="-L$(dir $(MOJO_SHARED_LIB)) -lsystem_thunk $(CGO_LDFLAGS)" \
+ $(GOROOT)/bin/go test -v $1
+
+endef
all: $(DISCOVERY_BUILD_DIR)/discovery.mojo
@@ -30,6 +37,10 @@
$(DISCOVERY_BUILD_DIR)/scanner.mojo: $(V23_GO_FILES) $(MOJO_SHARED_LIB) go/src/mojom/vanadium/discovery/discovery.mojom.go | mojo-env-check
$(call MOGO_BUILD,examples/scanner,$@)
+discovery_test: $(V23_GO_FILES) $(MOJO_SHARED_LIB) | mojo-env-check
+ echo $(GOPATH)
+ $(call CGO_TEST,vanadium/discovery/internal)
+
run-advertiser: $(DISCOVERY_BUILD_DIR)/advertiser.mojo $(DISCOVERY_BUILD_DIR)/discovery.mojo
sudo $(MOJO_DIR)/src/mojo/devtools/common/mojo_run --config-file $(PWD)/mojoconfig $(MOJO_SHELL_FLAGS) $(MOJO_ANDROID_FLAGS) https://mojo.v.io/advertiser.mojo \
--args-for="https://mojo.v.io/discovery.mojo host1"
diff --git a/go/src/vanadium/discovery/discovery.go b/go/src/vanadium/discovery/discovery.go
index 0c826d7..7c4a2d2 100644
--- a/go/src/vanadium/discovery/discovery.go
+++ b/go/src/vanadium/discovery/discovery.go
@@ -2,12 +2,8 @@
import (
"log"
- "sync"
"v.io/v23"
- "v.io/v23/context"
- "v.io/v23/discovery"
- "v.io/v23/verror"
idiscovery "v.io/x/ref/lib/discovery"
"v.io/x/ref/lib/discovery/plugins/ble"
"v.io/x/ref/lib/discovery/plugins/mdns"
@@ -15,6 +11,7 @@
_ "v.io/x/ref/runtime/factories/generic"
mojom "mojom/vanadium/discovery"
+ "vanadium/discovery/internal"
"mojo/public/go/application"
"mojo/public/go/bindings"
@@ -24,126 +21,9 @@
//#include "mojo/public/c/system/types.h"
import "C"
-type discoveryService struct {
- mu sync.Mutex
- ctx *context.T
- s discovery.T
- trigger *idiscovery.Trigger
- nextAdv int32
- pendingAdvs map[int32]chan struct{}
- nextScan int32
- pendingScans map[int32]chan struct{}
-}
-
-func convertToErrorStruct(err error) *mojom.Error {
- outErr := &mojom.Error{
- Id: "v.io/verror/Unknown",
- Msg: err.Error(),
- }
- if e, ok := err.(verror.E); ok {
- outErr.Id = string(e.ID)
- outErr.Action = int32(e.Action)
- }
- return outErr
-}
-
-func (d *discoveryService) Advertise(s mojom.Service, patterns []string) (int32, *mojom.Error, error) {
- vService := discovery.Service{
- InstanceUuid: s.InstanceUuid,
- InterfaceName: s.InterfaceName,
- Attrs: discovery.Attributes(s.Attrs),
- Addrs: s.Addrs,
- }
-
- ctx, c := context.WithCancel(d.ctx)
-
- err := d.s.Advertise(ctx, vService, nil)
- if err != nil {
- return 0, convertToErrorStruct(err), nil
- }
- ch := make(chan struct{})
- d.mu.Lock()
- id := d.nextAdv
- d.pendingAdvs[id] = ch
- d.nextAdv++
- d.mu.Unlock()
- d.trigger.Add(c, ch)
- return id, nil, nil
-}
-
-func (d *discoveryService) StopAdvertising(handle int32) error {
- d.mu.Lock()
- ch := d.pendingAdvs[handle]
- d.mu.Unlock()
- if ch != nil {
- close(ch)
- }
- return nil
-}
-
-func vServiceTomService(s discovery.Service) mojom.Service {
- return mojom.Service{
- InstanceUuid: s.InstanceUuid,
- InterfaceName: s.InterfaceName,
- Attrs: s.Attrs,
- Addrs: s.Addrs,
- }
-}
-
-func (d *discoveryService) Scan(query string, scanHandler mojom.ScanHandler_Pointer) (int32, *mojom.Error, error) {
- ctx, c := context.WithCancel(d.ctx)
- proxy := mojom.NewScanHandlerProxy(scanHandler, bindings.GetAsyncWaiter())
- scanCh, err := d.s.Scan(ctx, query)
- if err != nil {
- return 0, convertToErrorStruct(err), nil
- }
- ch := make(chan struct{})
- d.mu.Lock()
- id := d.nextScan
- d.pendingScans[id] = ch
- d.nextScan++
- d.mu.Unlock()
-
- d.trigger.Add(c, ch)
- go func() {
- for v := range scanCh {
- switch value := v.(type) {
- case discovery.UpdateFound:
- proxy.Found(vServiceTomService(value.Value.Service))
- case discovery.UpdateLost:
- proxy.Lost(vServiceTomService(value.Value.Service))
- default:
- }
- }
- }()
- return id, nil, nil
-}
-
-func (d *discoveryService) StopScan(handle int32) error {
- d.mu.Lock()
- ch := d.pendingScans[handle]
- d.mu.Unlock()
- if ch != nil {
- close(ch)
- }
- return nil
-}
-
-func (d *discoveryService) stop() {
- d.mu.Lock()
- for _, ch := range d.pendingScans {
- close(ch)
- }
-
- for _, ch := range d.pendingAdvs {
- close(ch)
- }
- d.mu.Unlock()
-}
-
type discoveryDelegate struct {
stubs []*bindings.Stub
- impl *discoveryService
+ impl *internal.DiscoveryService
shutdown v23.Shutdown
}
@@ -161,13 +41,7 @@
log.Println("Failed to start bplugin", err)
}
- d.impl = &discoveryService{
- ctx: ctx,
- trigger: idiscovery.NewTrigger(),
- s: idiscovery.New([]idiscovery.Plugin{mplugin, bplugin}),
- pendingAdvs: map[int32]chan struct{}{},
- pendingScans: map[int32]chan struct{}{},
- }
+ d.impl = internal.NewDiscoveryService(ctx, idiscovery.New([]idiscovery.Plugin{mplugin, bplugin}))
d.shutdown = shutdown
}
@@ -192,7 +66,7 @@
}
func (d *discoveryDelegate) Quit() {
- d.impl.stop()
+ d.impl.Stop()
d.shutdown()
for _, stub := range d.stubs {
stub.Close()
diff --git a/go/src/vanadium/discovery/internal/discovery.go b/go/src/vanadium/discovery/internal/discovery.go
new file mode 100644
index 0000000..9b4cd17
--- /dev/null
+++ b/go/src/vanadium/discovery/internal/discovery.go
@@ -0,0 +1,162 @@
+package internal
+
+import (
+ "sync"
+
+ "v.io/v23/context"
+ "v.io/v23/discovery"
+ "v.io/v23/verror"
+ idiscovery "v.io/x/ref/lib/discovery"
+
+ "mojo/public/go/bindings"
+ mojom "mojom/vanadium/discovery"
+)
+
+// DiscoveryService implements the mojom interface mojom/vanadium/discovery.DiscoveryService. It
+// is basically a thin wrapper around the Vanadium Discovery API.
+type DiscoveryService struct {
+ ctx *context.T
+ s discovery.T
+ trigger *idiscovery.Trigger
+
+ // mu protects pending* and next*
+ mu sync.Mutex
+
+ // The id to assign the next advertisement.
+ nextAdv int32
+ // A map of advertisement ids to the cancellation channel. When StopAdvertisement is
+ // called, the channel will be closed.
+ pendingAdvs map[int32]chan struct{}
+ // The id to assign to the next scan.
+ nextScan int32
+ // A map of scan id to the cancellataion channel. When StopScan is called, the channel
+ // for that id will be closed.
+ pendingScans map[int32]chan struct{}
+}
+
+func convertToErrorStruct(err error) *mojom.Error {
+ outErr := &mojom.Error{
+ Id: "v.io/verror/Unknown",
+ Msg: err.Error(),
+ }
+ if e, ok := err.(verror.E); ok {
+ outErr.Id = string(e.ID)
+ outErr.Action = int32(e.Action)
+ }
+ return outErr
+}
+
+// NewDiscoveryService returns a new DiscoveryService bound to the context and the Vanadium
+// Discovery implementation passed in.
+func NewDiscoveryService(ctx *context.T, vDiscovery discovery.T) *DiscoveryService {
+ return &DiscoveryService{
+ ctx: ctx,
+ s: vDiscovery,
+ trigger: idiscovery.NewTrigger(),
+ pendingAdvs: map[int32]chan struct{}{},
+ pendingScans: map[int32]chan struct{}{},
+ }
+}
+
+// Advertise advertises the mojom service passed only to the giveen blessing patterns. Returns the
+// handle to this Advertise call.
+func (d *DiscoveryService) Advertise(s mojom.Service, patterns []string) (int32, *mojom.Error, error) {
+ vService := discovery.Service{
+ InstanceUuid: s.InstanceUuid,
+ InterfaceName: s.InterfaceName,
+ Attrs: discovery.Attributes(s.Attrs),
+ Addrs: s.Addrs,
+ }
+
+ ctx, c := context.WithCancel(d.ctx)
+
+ err := d.s.Advertise(ctx, vService, nil)
+ if err != nil {
+ return 0, convertToErrorStruct(err), nil
+ }
+ ch := make(chan struct{})
+ d.mu.Lock()
+ id := d.nextAdv
+ d.pendingAdvs[id] = ch
+ d.nextAdv++
+ d.mu.Unlock()
+ d.trigger.Add(c, ch)
+ return id, nil, nil
+}
+
+// StopAdvertising stops advertising for the given advertising id.
+func (d *DiscoveryService) StopAdvertising(handle int32) error {
+ d.mu.Lock()
+ ch := d.pendingAdvs[handle]
+ delete(d.pendingAdvs, handle)
+ d.mu.Unlock()
+ if ch != nil {
+ close(ch)
+ }
+ return nil
+}
+
+func vServiceTomService(s discovery.Service) mojom.Service {
+ return mojom.Service{
+ InstanceUuid: s.InstanceUuid,
+ InterfaceName: s.InterfaceName,
+ Attrs: s.Attrs,
+ Addrs: s.Addrs,
+ }
+}
+
+// Scan scans for all services that match the query string passed in and calls scanHandler with updates.
+// Returns the handle to this Scan.
+func (d *DiscoveryService) Scan(query string, scanHandler mojom.ScanHandler_Pointer) (int32, *mojom.Error, error) {
+ ctx, c := context.WithCancel(d.ctx)
+ proxy := mojom.NewScanHandlerProxy(scanHandler, bindings.GetAsyncWaiter())
+ scanCh, err := d.s.Scan(ctx, query)
+ if err != nil {
+ return 0, convertToErrorStruct(err), nil
+ }
+ ch := make(chan struct{})
+ d.mu.Lock()
+ id := d.nextScan
+ d.pendingScans[id] = ch
+ d.nextScan++
+ d.mu.Unlock()
+
+ d.trigger.Add(c, ch)
+ go func() {
+ for v := range scanCh {
+ switch value := v.(type) {
+ case discovery.UpdateFound:
+ proxy.Found(vServiceTomService(value.Value.Service))
+ case discovery.UpdateLost:
+ proxy.Lost(vServiceTomService(value.Value.Service))
+ default:
+ }
+ }
+ }()
+ return id, nil, nil
+}
+
+// SopScan Stops the scan.
+func (d *DiscoveryService) StopScan(handle int32) error {
+ d.mu.Lock()
+ ch := d.pendingScans[handle]
+ delete(d.pendingScans, handle)
+ d.mu.Unlock()
+ if ch != nil {
+ close(ch)
+ }
+ return nil
+}
+
+// Stop Stops all scans and advertisements.
+func (d *DiscoveryService) Stop() {
+ d.mu.Lock()
+ for _, ch := range d.pendingScans {
+ close(ch)
+ }
+
+ for _, ch := range d.pendingAdvs {
+ close(ch)
+ }
+ d.mu.Unlock()
+}
diff --git a/go/src/vanadium/discovery/internal/discovery_test.go b/go/src/vanadium/discovery/internal/discovery_test.go
new file mode 100644
index 0000000..dea5351
--- /dev/null
+++ b/go/src/vanadium/discovery/internal/discovery_test.go
@@ -0,0 +1,125 @@
+package internal
+import (
+ "reflect"
+ "testing"
+
+ "v.io/v23/discovery"
+ "v.io/v23/security/access"
+ "v.io/v23/context"
+ idiscovery "v.io/x/ref/lib/discovery"
+ vtest "v.io/x/ref/test"
+
+ _ "v.io/x/ref/runtime/factories/generic"
+
+ mojom "mojom/vanadium/discovery"
+)
+
+
+type mockAdv struct {
+ s discovery.Service
+}
+
+type discoveryMock struct {
+ trigger *idiscovery.Trigger
+ id int64
+ services map[int64]discovery.Service
+ // An item will be put in deleteCh when something has been deleted.
+ deleteCh chan struct{}
+}
+
+
+func (d *discoveryMock) Advertise(ctx *context.T,s discovery.Service, perms access.Permissions) error {
+ currId := d.id
+ d.services[currId] = s
+ d.id++
+ c := func() {
+ delete(d.services, currId)
+ d.deleteCh <- struct{}{}
+ }
+ d.trigger.Add(c, ctx.Done())
+ return nil
+}
+
+func (*discoveryMock) Scan(ctx *context.T, query string) (<-chan discovery.Update, error) {
+ return nil, nil
+}
+
+func compare(t *testing.T, vService discovery.Service, mService mojom.Service) {
+ if !reflect.DeepEqual(vService.Addrs, mService.Addrs) {
+ t.Errorf("addrs not the same: %v, %v", vService.Addrs, mService.Addrs)
+ }
+
+ if vService.InterfaceName != mService.InterfaceName {
+ t.Errorf("interface name not the same: %v, %v", vService.InterfaceName, mService.InterfaceName)
+ }
+
+ if !reflect.DeepEqual(map[string]string(vService.Attrs), mService.Attrs) {
+ t.Errorf("attributes not the same: %v, %v", vService.Attrs, mService.Attrs)
+ }
+}
+
+func TestAdvertising(t *testing.T) {
+ ctx, shutdown := vtest.V23Init()
+ defer shutdown()
+ mock := &discoveryMock{
+ trigger: idiscovery.NewTrigger(),
+ services: map[int64]discovery.Service{},
+ deleteCh: make(chan struct{}),
+ }
+ s := NewDiscoveryService(ctx, mock)
+ testService := mojom.Service {
+ InterfaceName: "v.io/v23/discovery.T",
+ Attrs: map[string]string{
+ "key1": "value1",
+ "key2": "value2",
+ },
+ Addrs: []string{ "addr1", "addr2"},
+ }
+ id, e1, e2 := s.Advertise(testService, nil)
+
+ if e1 != nil || e2 != nil {
+ t.Fatalf("Failed to start service: %v, %v", e1, e2)
+ }
+ if len(mock.services) != 1 {
+ t.Errorf("service missing in mock")
+ }
+
+ for _, service := range mock.services {
+ compare(t, service, testService)
+ }
+
+ testService2 := mojom.Service {
+ InterfaceName: "v.io/v23/naming.T",
+ Attrs: map[string]string{
+ "key1": "value1",
+ "key2": "value2",
+ },
+ Addrs: []string{ "addr1", "addr2"},
+
+ }
+
+ _, e1, e2 = s.Advertise(testService2, nil)
+ if e1 != nil || e2 != nil {
+ t.Fatalf("Failed to start service: %v, %v", e1, e2)
+ }
+
+ s.StopAdvertising(id)
+ // Wait for the deletion to finish.
+ <-mock.deleteCh
+ if len(mock.services) != 1 {
+ t.Errorf("service should have been removed")
+ }
+
+ for _, service := range mock.services {
+ compare(t, service, testService2)
+ }
+
+ s.Stop()
+ <-mock.deleteCh
+ if len(mock.services) != 0 {
+ t.Errorf("service should have been removed")
+ }
+}
+
+
+