blob: 6cbc87903924f3aaff3830ccb76ede070d97dc5e [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 utiltest
import (
"crypto/md5"
"encoding/hex"
"fmt"
"io"
"io/ioutil"
"os"
"reflect"
"testing"
"v.io/v23"
"v.io/v23/context"
"v.io/v23/rpc"
"v.io/v23/security"
"v.io/v23/security/access"
"v.io/v23/services/application"
"v.io/v23/services/binary"
"v.io/v23/services/repository"
"v.io/v23/verror"
)
const MockBinaryRepoName = "br"
const MockApplicationRepoName = "ar"
func StartMockRepos(t *testing.T, ctx *context.T) (*application.Envelope, func()) {
envelope, appCleanup := StartApplicationRepository(ctx)
binaryCleanup := StartBinaryRepository(ctx)
return envelope, func() {
binaryCleanup()
appCleanup()
}
}
// StartApplicationRepository sets up a server running the application
// repository. It returns a pointer to the envelope that the repository returns
// to clients (so that it can be changed). It also returns a cleanup function.
func StartApplicationRepository(ctx *context.T) (*application.Envelope, func()) {
invoker := new(arInvoker)
name := MockApplicationRepoName
ctx, cancel := context.WithCancel(ctx)
ctx, server, err := v23.WithNewServer(ctx, name, repository.ApplicationServer(invoker), security.AllowEveryone())
if err != nil {
ctx.Fatalf("NewServer(%v) failed: %v", name, err)
}
WaitForMount(ctx, ctx, name, server)
return &invoker.envelope, func() {
cancel()
<-server.Closed()
}
}
// arInvoker holds the state of an application repository invocation mock. The
// mock returns the value of the wrapped envelope, which can be subsequently be
// changed at any time. Client is responsible for synchronization if desired.
type arInvoker struct {
envelope application.Envelope
}
// APPLICATION REPOSITORY INTERFACE IMPLEMENTATION
func (i *arInvoker) Match(ctx *context.T, _ rpc.ServerCall, profiles []string) (application.Envelope, error) {
ctx.VI(1).Infof("Match()")
if want := []string{"test-profile"}; !reflect.DeepEqual(profiles, want) {
return application.Envelope{}, fmt.Errorf("Expected profiles %v, got %v", want, profiles)
}
return i.envelope, nil
}
func (i *arInvoker) GetPermissions(ctx *context.T, _ rpc.ServerCall) (perms access.Permissions, version string, err error) {
return nil, "", nil
}
func (i *arInvoker) SetPermissions(ctx *context.T, _ rpc.ServerCall, perms access.Permissions, version string) error {
return nil
}
func (i *arInvoker) TidyNow(_ *context.T, _ rpc.ServerCall) error {
return nil
}
// brInvoker holds the state of a binary repository invocation mock. It always
// serves the current running binary.
type brInvoker struct{}
// StartBinaryRepository sets up a server running the binary repository and
// returns a cleanup function.
func StartBinaryRepository(ctx *context.T) func() {
name := MockBinaryRepoName
ctx, cancel := context.WithCancel(ctx)
ctx, server, err := v23.WithNewServer(ctx, name, repository.BinaryServer(new(brInvoker)), security.AllowEveryone())
if err != nil {
ctx.Fatalf("Serve(%q) failed: %v", name, err)
}
WaitForMount(ctx, ctx, name, server)
return func() {
cancel()
<-server.Closed()
}
}
// BINARY REPOSITORY INTERFACE IMPLEMENTATION
// TODO(toddw): Move the errors from dispatcher.go into a common location.
const pkgPath = "v.io/x/ref/services/device/deviced/internal/impl/utiltest"
var ErrOperationFailed = verror.Register(pkgPath+".OperationFailed", verror.NoRetry, "")
func (*brInvoker) Create(ctx *context.T, _ rpc.ServerCall, _ int32, _ repository.MediaInfo) error {
ctx.VI(1).Infof("Create()")
return nil
}
func (i *brInvoker) Delete(ctx *context.T, _ rpc.ServerCall) error {
ctx.VI(1).Infof("Delete()")
return nil
}
func mockBinaryBytesReader() (io.Reader, func(), error) {
file, err := os.Open(os.Args[0])
if err != nil {
return nil, nil, err
}
cleanup := func() {
file.Close()
}
return file, cleanup, nil
}
func (i *brInvoker) Download(ctx *context.T, call repository.BinaryDownloadServerCall, _ int32) error {
ctx.VI(1).Infof("Download()")
file, cleanup, err := mockBinaryBytesReader()
if err != nil {
ctx.Errorf("Open() failed: %v", err)
return verror.New(ErrOperationFailed, ctx)
}
defer cleanup()
bufferLength := 4096
buffer := make([]byte, bufferLength)
sender := call.SendStream()
for {
n, err := file.Read(buffer)
switch err {
case io.EOF:
return nil
case nil:
if err := sender.Send(buffer[:n]); err != nil {
ctx.Errorf("Send() failed: %v", err)
return verror.New(ErrOperationFailed, ctx)
}
default:
ctx.Errorf("Read() failed: %v", err)
return verror.New(ErrOperationFailed, ctx)
}
}
}
func (*brInvoker) DownloadUrl(ctx *context.T, _ rpc.ServerCall) (string, int64, error) {
ctx.VI(1).Infof("DownloadUrl()")
return "", 0, nil
}
func (*brInvoker) Stat(ctx *context.T, call rpc.ServerCall) ([]binary.PartInfo, repository.MediaInfo, error) {
ctx.VI(1).Infof("Stat()")
h := md5.New()
bytes, err := ioutil.ReadFile(os.Args[0])
if err != nil {
return []binary.PartInfo{}, repository.MediaInfo{}, verror.New(ErrOperationFailed, ctx)
}
h.Write(bytes)
part := binary.PartInfo{Checksum: hex.EncodeToString(h.Sum(nil)), Size: int64(len(bytes))}
return []binary.PartInfo{part}, repository.MediaInfo{Type: "application/octet-stream"}, nil
}
func (i *brInvoker) Upload(ctx *context.T, _ repository.BinaryUploadServerCall, _ int32) error {
ctx.VI(1).Infof("Upload()")
return nil
}
func (i *brInvoker) GetPermissions(*context.T, rpc.ServerCall) (perms access.Permissions, version string, err error) {
return nil, "", nil
}
func (i *brInvoker) SetPermissions(_ *context.T, _ rpc.ServerCall, perms access.Permissions, version string) error {
return nil
}