Merge "profiles/internal: Add ServesLeaf/IsLeaf to the Endpoint"
diff --git a/lib/exec/child.go b/lib/exec/child.go
index cf2a141..b80033c 100644
--- a/lib/exec/child.go
+++ b/lib/exec/child.go
@@ -6,19 +6,22 @@
import (
"encoding/binary"
- "errors"
"io"
"os"
"strconv"
"sync"
"unicode/utf8"
+ "v.io/v23/verror"
"v.io/x/ref/lib/exec/consts"
)
var (
- ErrNoVersion = errors.New(consts.ExecVersionVariable + " environment variable missing")
- ErrUnsupportedVersion = errors.New("Unsupported version of v.io/x/ref/lib/exec request by " + consts.ExecVersionVariable + " environment variable")
+ ErrNoVersion = verror.Register(pkgPath+".ErrNoVersion", verror.NoRetry, "{1:}{2:} "+consts.ExecVersionVariable+" environment variable missing{:_}")
+ ErrUnsupportedVersion = verror.Register(pkgPath+".ErrUnsupportedVersion", verror.NoRetry, "{1:}{2:} Unsupported version of v.io/x/ref/lib/exec request by "+consts.ExecVersionVariable+" environment variable{:_}")
+
+ errDifferentStatusSent = verror.Register(pkgPath+".errDifferentStatusSent", verror.NoRetry, "{1:}{2:} A different status: {3} has already been sent{:_}")
+ errPartialRead = verror.Register(pkgPath+".PartialRead", verror.NoRetry, "{1:}{2:} partial read{:_}")
)
type ChildHandle struct {
@@ -92,7 +95,7 @@
_, c.statusErr = c.statusPipe.Write(toWrite)
c.statusPipe.Close()
} else if c.sentStatus != status {
- return errors.New("A different status: " + c.sentStatus + " has already been sent.")
+ return verror.New(errDifferentStatusSent, nil, c.sentStatus)
}
return c.statusErr
}
@@ -123,11 +126,11 @@
// version #s.
switch os.Getenv(consts.ExecVersionVariable) {
case "":
- return nil, ErrNoVersion
+ return nil, verror.New(ErrNoVersion, nil)
case version1:
os.Setenv(consts.ExecVersionVariable, "")
default:
- return nil, ErrUnsupportedVersion
+ return nil, verror.New(ErrUnsupportedVersion, nil)
}
dataPipe := os.NewFile(3, "data_rd")
serializedConfig, err := decodeString(dataPipe)
@@ -160,7 +163,7 @@
if err != nil {
return "", err
} else {
- return "", errors.New("partial read")
+ return "", verror.New(errPartialRead, nil)
}
}
return string(data), nil
diff --git a/lib/exec/exec_test.go b/lib/exec/exec_test.go
index b930bd7..0eaf7af 100644
--- a/lib/exec/exec_test.go
+++ b/lib/exec/exec_test.go
@@ -17,6 +17,7 @@
"time"
"unicode/utf8"
+ "v.io/v23/verror"
vexec "v.io/x/ref/lib/exec"
"v.io/x/ref/lib/exec/consts"
// Use mock timekeeper to avoid actually sleeping during the test.
@@ -160,7 +161,7 @@
func TestNoVersion(t *testing.T) {
// Make sure that Init correctly tests for the presence of VEXEC_VERSION
_, err := vexec.GetChildHandle()
- if err != vexec.ErrNoVersion {
+ if verror.ErrorID(err) != vexec.ErrNoVersion.ID {
t.Errorf("Should be missing Version")
}
}
@@ -266,7 +267,7 @@
cmd := helperCommand(name, "failed", "to", "start")
ph := vexec.NewParentHandle(cmd)
err := waitForReady(t, cmd, name, 4, ph)
- if err == nil || err.Error() != "failed to start" {
+ if err == nil || !strings.Contains(err.Error(), "failed to start") {
t.Errorf("unexpected error: %v", err)
}
}
@@ -276,7 +277,7 @@
cmd := helperCommand(name, "invalid", "utf8", string([]byte{0xFF}), "in", string([]byte{0xFC}), "error", "message")
ph := vexec.NewParentHandle(cmd)
err := waitForReady(t, cmd, name, 4, ph)
- if err == nil || err.Error() != "invalid utf8 "+string(utf8.RuneError)+" in "+string(utf8.RuneError)+" error message" {
+ if err == nil || !strings.Contains(err.Error(), "invalid utf8 "+string(utf8.RuneError)+" in "+string(utf8.RuneError)+" error message") {
t.Errorf("unexpected error: %v", err)
}
}
@@ -288,7 +289,7 @@
stderr, _ := cmd.StderrPipe()
ph := vexec.NewParentHandle(cmd)
err := waitForReady(t, cmd, name, 1, ph)
- if err != vexec.ErrTimeout {
+ if verror.ErrorID(err) != vexec.ErrTimeout.ID {
t.Errorf("Failed to get timeout: got %v\n", err)
} else {
// block waiting for error from child
@@ -322,7 +323,7 @@
tk.AdvanceTime(toWait)
}()
err = waitForReady(t, cmd, name, 1, ph)
- if err != vexec.ErrTimeout {
+ if verror.ErrorID(err) != vexec.ErrTimeout.ID {
t.Errorf("Failed to get timeout: got %v\n", err)
} else {
// After the parent timed out, wake up the child and let it
@@ -449,7 +450,7 @@
<-tk.Requests()
tk.AdvanceTime(2 * time.Second)
}()
- if got, want := ph.Wait(time.Second), vexec.ErrTimeout; got == nil || got.Error() != want.Error() {
+ if got, want := ph.Wait(time.Second), vexec.ErrTimeout.ID; got == nil || verror.ErrorID(got) != want {
t.Errorf("Wait returned %v, wanted %v instead", got, want)
}
if got, want := ph.Clean(), "signal: killed"; got == nil || got.Error() != want {
diff --git a/lib/exec/noprotocol_test.go b/lib/exec/noprotocol_test.go
index 8e9aa21..78564c9 100644
--- a/lib/exec/noprotocol_test.go
+++ b/lib/exec/noprotocol_test.go
@@ -12,6 +12,7 @@
"testing"
"time"
+ "v.io/v23/verror"
vexec "v.io/x/ref/lib/exec"
"v.io/x/ref/lib/exec/consts"
)
@@ -23,7 +24,7 @@
if err := ph.Start(); err != nil {
t.Fatal(err)
}
- if got, want := ph.WaitForReady(time.Minute), vexec.ErrNotUsingProtocol; got != want {
+ if got, want := ph.WaitForReady(time.Minute), vexec.ErrNotUsingProtocol.ID; verror.ErrorID(got) != want {
t.Fatalf("got %v, want %v", got, want)
}
re := regexp.MustCompile(fmt.Sprintf(".*%s=.*", consts.ExecVersionVariable))
diff --git a/lib/exec/parent.go b/lib/exec/parent.go
index fc11a2b..d011cfb 100644
--- a/lib/exec/parent.go
+++ b/lib/exec/parent.go
@@ -7,7 +7,6 @@
import (
"bytes"
"encoding/binary"
- "errors"
"fmt"
"io"
"os"
@@ -18,17 +17,27 @@
"syscall"
"time"
+ "v.io/v23/verror"
+
"v.io/x/lib/vlog"
"v.io/x/ref/lib/exec/consts"
"v.io/x/ref/lib/timekeeper"
)
+const pkgPath = "v.io/x/ref/lib/exec"
+
var (
- ErrAuthTimeout = errors.New("timeout in auth handshake")
- ErrTimeout = errors.New("timeout waiting for child")
- ErrSecretTooLarge = errors.New("secret is too large")
- ErrNotUsingProtocol = errors.New("not using parent/child exec protocol")
+ ErrAuthTimeout = verror.Register(pkgPath+".ErrAuthTimeout", verror.NoRetry, "{1:}{2:} timeout in auth handshake{:_}")
+ ErrTimeout = verror.Register(pkgPath+".ErrTimeout", verror.NoRetry, "{1:}{2:} timeout waiting for child{:_}")
+ ErrSecretTooLarge = verror.Register(pkgPath+".ErrSecretTooLarge", verror.NoRetry, "{1:}{2:} secret is too large{:_}")
+ ErrNotUsingProtocol = verror.Register(pkgPath+".ErrNotUsingProtocol", verror.NoRetry, "{1:}{2:} not using parent/child exec protocol{:_}")
+
+ errFailedStatus = verror.Register(pkgPath+".errFailedStatus", verror.NoRetry, "{1:}{2:} {_}")
+ errUnrecognizedStatus = verror.Register(pkgPath+".errUnrecognizedStatus", verror.NoRetry, "{1:}{2:} unrecognised status from subprocess{:_}")
+ errUnexpectedType = verror.Register(pkgPath+".errUnexpectedType", verror.NoRetry, "{1:}{2:} unexpected type {3}{:_}")
+ errNoSuchProcess = verror.Register(pkgPath+".errNoSuchProcess", verror.NoRetry, "{1:}{2:} no such process{:_}")
+ errPartialWrite = verror.Register(pkgPath+".errPartialWrite", verror.NoRetry, "{1:}{2:} partial write{:_}")
)
// A ParentHandle is the Parent process' means of managing a single child.
@@ -234,7 +243,7 @@
// WaitForReady will wait for the child process to become ready.
func (p *ParentHandle) WaitForReady(timeout time.Duration) error {
if !p.protocol {
- return ErrNotUsingProtocol
+ return verror.New(ErrNotUsingProtocol, nil)
}
// An invariant of WaitForReady is that both statusWrite and statusRead
// get closed before WaitForStatus returns (statusRead gets closed by
@@ -260,11 +269,11 @@
return nil
}
if strings.HasPrefix(m, failedStatus) {
- return fmt.Errorf("%s", strings.TrimPrefix(m, failedStatus))
+ return verror.New(errFailedStatus, nil, strings.TrimPrefix(m, failedStatus))
}
- return fmt.Errorf("unrecognised status from subprocess: %q", m)
+ return verror.New(errUnrecognizedStatus, nil, m)
default:
- return fmt.Errorf("unexpected type %T", m)
+ return verror.New(errUnexpectedType, nil, fmt.Sprintf("%T", m))
}
case <-p.tk.After(timeout):
vlog.Errorf("Timed out waiting for child status")
@@ -281,7 +290,7 @@
// c. Waiting on c ensures that r.Close() in waitForStatus
// already executed.
<-c
- return ErrTimeout
+ return verror.New(ErrTimeout, nil)
}
panic("unreachable")
}
@@ -315,7 +324,7 @@
if timeout > 0 {
select {
case <-p.tk.After(timeout):
- return ErrTimeout
+ return verror.New(ErrTimeout, nil)
case err := <-c:
return err
}
@@ -350,7 +359,7 @@
// Kill kills the child process.
func (p *ParentHandle) Kill() error {
if p.c.Process == nil {
- return errors.New("no such process")
+ return verror.New(errNoSuchProcess, nil)
}
return p.c.Process.Kill()
}
@@ -358,7 +367,7 @@
// Signal sends the given signal to the child process.
func (p *ParentHandle) Signal(sig syscall.Signal) error {
if p.c.Process == nil {
- return errors.New("no such process")
+ return verror.New(errNoSuchProcess, nil)
}
return syscall.Kill(p.c.Process.Pid, sig)
}
@@ -380,7 +389,7 @@
if err != nil {
return err
} else {
- return errors.New("partial write")
+ return verror.New(errPartialWrite, nil)
}
}
return nil
diff --git a/lib/exec/util.go b/lib/exec/util.go
index 813730c..323097b 100644
--- a/lib/exec/util.go
+++ b/lib/exec/util.go
@@ -5,8 +5,13 @@
package exec
import (
- "errors"
"strings"
+
+ "v.io/v23/verror"
+)
+
+var (
+ errNotFound = verror.Register(pkgPath+".errNotFound", verror.NoRetry, "{1:}{2:} not found{:_}")
)
// Getenv retrieves the value of the given variable from the given
@@ -17,7 +22,7 @@
return strings.TrimPrefix(v, name+"="), nil
}
}
- return "", errors.New("not found")
+ return "", verror.New(errNotFound, nil)
}
// Setenv updates / adds the value assignment for the given variable
diff --git a/profiles/internal/rt/mgmt.go b/profiles/internal/rt/mgmt.go
index d27d689..6033e93 100644
--- a/profiles/internal/rt/mgmt.go
+++ b/profiles/internal/rt/mgmt.go
@@ -26,12 +26,14 @@
func (rt *Runtime) initMgmt(ctx *context.T) error {
handle, err := exec.GetChildHandle()
- if err == exec.ErrNoVersion {
+ if err == nil {
+ // No error; fall through.
+ } else if verror.ErrorID(err) == exec.ErrNoVersion.ID {
// Do not initialize the mgmt runtime if the process has not
// been started through the vanadium exec library by a device
// manager.
return nil
- } else if err != nil {
+ } else {
return err
}
parentName, err := handle.Config.Get(mgmt.ParentNameConfigKey)
diff --git a/profiles/internal/rt/security.go b/profiles/internal/rt/security.go
index ccdc157..9bdbb59 100644
--- a/profiles/internal/rt/security.go
+++ b/profiles/internal/rt/security.go
@@ -15,6 +15,7 @@
"v.io/v23/mgmt"
"v.io/v23/rpc"
"v.io/v23/security"
+ "v.io/v23/verror"
"v.io/x/ref/lib/exec"
vsecurity "v.io/x/ref/security"
@@ -71,7 +72,7 @@
// agent.
func agentFD() (int, error) {
handle, err := exec.GetChildHandle()
- if err != nil && err != exec.ErrNoVersion {
+ if err != nil && verror.ErrorID(err) != exec.ErrNoVersion.ID {
return -1, err
}
var fd string
diff --git a/profiles/internal/testing/mocks/mocknet/mocknet.go b/profiles/internal/testing/mocks/mocknet/mocknet.go
new file mode 100644
index 0000000..0252003
--- /dev/null
+++ b/profiles/internal/testing/mocks/mocknet/mocknet.go
@@ -0,0 +1,262 @@
+// 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 mocknet implements a mock net.Conn that can simulate a variety of
+// network errors and/or be used for tracing.
+package mocknet
+
+import (
+ "net"
+ "sync"
+ "time"
+)
+
+// TODO(cnicolaou): consider extending Dialer/Listener API to include a cipher
+// to allow access to encrypted data.
+
+type Mode int
+
+const (
+ Trace Mode = iota // Log the sizes of each read/write call
+ Close // Close the connection after a specified #bytes are read/written
+ Drop // Drop byes as per a policy specified in opts
+)
+
+type Opts struct {
+ // The underlying network protocol to use, e.g. "tcp", defaults to tcp.
+ UnderlyingProtocol string
+
+ // The mode to operate under.
+ Mode Mode
+
+ // Buffers to store the transmit and receive message sizes when
+ // in Trace mode.
+ Tx, Rx chan int
+
+ // The number of rx and tx bytes respectively to be seen before the
+ // connection is closed when in Close mode.
+ RxCloseAt, TxCloseAt int
+
+ // TXDropAfter is called to obtain the number of tx bytes to be sent
+ // before dropping the rest of the data passed to that write call. The
+ // number of bytes returned by TxDroptAfter will always be written,
+ // but the number of bytes dropped is unspecified since it depends
+ // on the size of the buffer passed to that write call. TxDropAfter
+ // will be called again after each drop and the current count of
+ // byte sent reset to zero.
+ TxDropAfter func() (pos int)
+}
+
+// DialerWithOpts is intended for use with rpc.RegisterProtocol via
+// a closure:
+//
+// dialer := func(network, address string, timeout time.Duration) (net.Conn, error) {
+// return mocknet.DialerWithOpts(mocknet.Opts{UnderlyingProtocol:"tcp"}, network, address, timeout)
+// }
+// rpc.RegisterProtocol("brkDial", dialer, net.Listen)
+//
+func DialerWithOpts(opts Opts, network, address string, timeout time.Duration) (net.Conn, error) {
+ protocol := opts.UnderlyingProtocol
+ if len(protocol) == 0 {
+ protocol = "tcp"
+ }
+ c, err := net.DialTimeout(protocol, address, timeout)
+ if err != nil {
+ return nil, err
+ }
+ return newMockConn(opts, c), nil
+}
+
+// ListenerWithOpts is intended for use with rpc.RegisterProtocol via
+// a closure as per DialerWithOpts.
+func ListenerWithOpts(opts Opts, network, laddr string) (net.Listener, error) {
+ protocol := opts.UnderlyingProtocol
+ if len(protocol) == 0 {
+ protocol = "tcp"
+ }
+ ln, err := net.Listen(protocol, laddr)
+ if err != nil {
+ return nil, err
+ }
+ return &listener{opts, ln}, nil
+}
+
+func newMockConn(opts Opts, c net.Conn) net.Conn {
+ switch opts.Mode {
+ case Trace:
+ return &traceConn{
+ conn: c,
+ rx: opts.Rx,
+ tx: opts.Tx}
+ case Close:
+ return &closeConn{
+ conn: c,
+ rxCloseAt: opts.RxCloseAt,
+ txCloseAt: opts.TxCloseAt,
+ }
+ case Drop:
+ return &dropConn{
+ opts: opts,
+ conn: c,
+ txDropAfter: opts.TxDropAfter(),
+ }
+ }
+ return nil
+}
+
+type dropConn struct {
+ sync.Mutex
+ opts Opts
+ conn net.Conn
+ tx int
+ txDropAfter int
+}
+
+func (c *dropConn) Read(b []byte) (n int, err error) {
+ return c.conn.Read(b)
+}
+
+func (c *dropConn) Write(b []byte) (n int, err error) {
+ c.Lock()
+ defer c.Unlock()
+ dropped := false
+ if c.tx+len(b) >= c.txDropAfter {
+ b = b[0 : c.txDropAfter-c.tx]
+ c.txDropAfter = c.opts.TxDropAfter()
+ dropped = true
+ }
+ n, err = c.conn.Write(b)
+ if dropped {
+ c.tx = 0
+ } else {
+ c.tx += n
+ }
+ return
+}
+
+func (c *dropConn) Close() error { return c.conn.Close() }
+func (c *dropConn) LocalAddr() net.Addr { return c.conn.LocalAddr() }
+func (c *dropConn) RemoteAddr() net.Addr {
+ return c.conn.RemoteAddr()
+}
+func (c *dropConn) SetDeadline(t time.Time) error {
+ return c.conn.SetDeadline(t)
+}
+func (c *dropConn) SetReadDeadline(t time.Time) error {
+ return c.conn.SetReadDeadline(t)
+}
+func (c *dropConn) SetWriteDeadline(t time.Time) error {
+ return c.conn.SetWriteDeadline(t)
+}
+
+type closeConn struct {
+ sync.Mutex
+ conn net.Conn
+ rx, tx int
+ rxCloseAt, txCloseAt int
+ closed bool
+}
+
+func (c *closeConn) Read(b []byte) (n int, err error) {
+ c.Lock()
+ defer c.Unlock()
+ n = len(b)
+ if c.rx+n >= c.rxCloseAt {
+ n = c.rxCloseAt - c.rx
+ }
+ b = b[:n]
+ n, err = c.conn.Read(b[:n])
+ c.rx += n
+ if c.rx == c.rxCloseAt {
+ c.conn.Close()
+ }
+ return
+}
+
+func (c *closeConn) Write(b []byte) (n int, err error) {
+ c.Lock()
+ defer c.Unlock()
+ n = len(b)
+ if c.tx+n >= c.txCloseAt {
+ n = c.txCloseAt - c.tx
+ }
+ n, err = c.conn.Write(b[:n])
+ c.tx += n
+ if c.tx == c.txCloseAt {
+ c.conn.Close()
+ }
+ return
+}
+
+func (c *closeConn) Close() error { return c.conn.Close() }
+func (c *closeConn) LocalAddr() net.Addr { return c.conn.LocalAddr() }
+func (c *closeConn) RemoteAddr() net.Addr {
+ return c.conn.RemoteAddr()
+}
+func (c *closeConn) SetDeadline(t time.Time) error {
+ return c.conn.SetDeadline(t)
+}
+func (c *closeConn) SetReadDeadline(t time.Time) error {
+ return c.conn.SetReadDeadline(t)
+}
+func (c *closeConn) SetWriteDeadline(t time.Time) error {
+ return c.conn.SetWriteDeadline(t)
+}
+
+type traceConn struct {
+ conn net.Conn
+ tx, rx chan int
+}
+
+func (c *traceConn) Read(b []byte) (n int, err error) {
+ n, err = c.conn.Read(b)
+ c.rx <- n
+ return n, err
+}
+
+func (c *traceConn) Write(b []byte) (n int, err error) {
+ n, err = c.conn.Write(b)
+ c.tx <- n
+ return
+}
+
+func (c *traceConn) Close() error {
+ c.rx <- -1
+ c.tx <- -1
+ return c.conn.Close()
+}
+
+func (c *traceConn) LocalAddr() net.Addr { return c.conn.LocalAddr() }
+func (c *traceConn) RemoteAddr() net.Addr { return c.conn.RemoteAddr() }
+func (c *traceConn) SetDeadline(t time.Time) error {
+ return c.conn.SetDeadline(t)
+}
+func (c *traceConn) SetReadDeadline(t time.Time) error {
+ return c.conn.SetReadDeadline(t)
+}
+func (c *traceConn) SetWriteDeadline(t time.Time) error {
+ return c.conn.SetWriteDeadline(t)
+}
+
+// listener is a wrapper around net.Listener.
+type listener struct {
+ opts Opts
+ netLn net.Listener
+}
+
+func (ln *listener) Accept() (net.Conn, error) {
+ c, err := ln.netLn.Accept()
+ if err != nil {
+ return nil, err
+ }
+ return newMockConn(ln.opts, c), nil
+}
+
+func (ln *listener) Close() error {
+ return ln.netLn.Close()
+}
+
+func (ln *listener) Addr() net.Addr {
+ return ln.netLn.Addr()
+}
diff --git a/profiles/internal/testing/mocks/mocknet/mocknet_test.go b/profiles/internal/testing/mocks/mocknet/mocknet_test.go
new file mode 100644
index 0000000..1e62ac7
--- /dev/null
+++ b/profiles/internal/testing/mocks/mocknet/mocknet_test.go
@@ -0,0 +1,214 @@
+// 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 mocknet_test
+
+import (
+ "errors"
+ "io"
+ "net"
+ "reflect"
+ "sync"
+ "testing"
+ "time"
+
+ "v.io/x/ref/profiles/internal/testing/mocks/mocknet"
+)
+
+//go:generate v23 test generate
+
+func newListener(t *testing.T, opts mocknet.Opts) net.Listener {
+ ln, err := mocknet.ListenerWithOpts(opts, "test", "127.0.0.1:0")
+ if err != nil {
+ t.Fatal(err)
+ }
+ return ln
+}
+
+func TestTrace(t *testing.T) {
+ opts := mocknet.Opts{
+ Mode: mocknet.Trace,
+ Tx: make(chan int, 100),
+ Rx: make(chan int, 100),
+ }
+ ln := newListener(t, opts)
+ defer ln.Close()
+
+ var rxconn net.Conn
+ var rxerr error
+ var wg sync.WaitGroup
+ wg.Add(1)
+ go func() {
+ rxconn, rxerr = ln.Accept()
+ wg.Done()
+ }()
+
+ txconn, err := mocknet.DialerWithOpts(opts, "test", ln.Addr().String(), time.Minute)
+ if err != nil {
+ t.Fatal(err)
+ }
+ wg.Wait()
+
+ rw := func(s string) {
+ b := make([]byte, len(s))
+ txconn.Write([]byte(s))
+ rxconn.Read(b[:])
+ if got, want := string(b), s; got != want {
+ t.Fatalf("got %v, want %v", got, want)
+ }
+ }
+
+ sizes := []int{}
+ for _, s := range []string{"hello", " ", "world"} {
+ rw(s)
+ sizes = append(sizes, len(s))
+ }
+ rxconn.Close()
+ close(opts.Tx)
+ close(opts.Rx)
+ sizes = append(sizes, -1)
+
+ drain := func(ch chan int) []int {
+ r := []int{}
+ for v := range ch {
+ r = append(r, v)
+ }
+ return r
+ }
+
+ if got, want := drain(opts.Rx), sizes; !reflect.DeepEqual(got, want) {
+ t.Fatalf("got %v, want %v", got, want)
+ }
+ if got, want := drain(opts.Tx), sizes; !reflect.DeepEqual(got, want) {
+ t.Fatalf("got %v, want %v", got, want)
+ }
+}
+
+func TestClose(t *testing.T) {
+ cases := []struct {
+ txClose, rxClose int
+ tx []string
+ rx []string
+ err error
+ }{
+ {6, 10, []string{"hello", "world"}, []string{"hello", "w"}, io.EOF},
+ {5, 10, []string{"hello", "world"}, []string{"hello", ""}, io.EOF},
+ {8, 6, []string{"hello", "world"}, []string{"hello", "w"}, io.EOF},
+ {8, 5, []string{"hello", "world"}, []string{"hello", ""}, errors.New("use of closed network connection")},
+ }
+
+ for ci, c := range cases {
+ opts := mocknet.Opts{
+ Mode: mocknet.Close,
+ TxCloseAt: c.txClose,
+ RxCloseAt: c.rxClose,
+ }
+
+ ln := newListener(t, opts)
+ defer ln.Close()
+
+ var rxconn net.Conn
+ var rxerr error
+ var wg sync.WaitGroup
+ wg.Add(1)
+ go func() {
+ rxconn, rxerr = ln.Accept()
+ wg.Done()
+ }()
+
+ txconn, err := mocknet.DialerWithOpts(opts, "test", ln.Addr().String(), time.Minute)
+ if err != nil {
+ t.Fatal(err)
+ }
+ wg.Wait()
+
+ rw := func(s string) (int, int, string, error) {
+ b := make([]byte, len(s))
+ tx, _ := txconn.Write([]byte(s))
+ rx, err := rxconn.Read(b[:])
+ return tx, rx, string(b[0:rx]), err
+ }
+
+ txBytes := 0
+ rxBytes := 0
+ for i, m := range c.tx {
+ tx, rx, rxed, err := rw(m)
+ if got, want := rxed, c.rx[i]; got != want {
+ t.Fatalf("%d: got %q, want %q", ci, got, want)
+ }
+ txBytes += tx
+ rxBytes += rx
+ if err != nil {
+ if got, want := err.Error(), c.err.Error(); got != want {
+ t.Fatalf("%d: got %v, want %v", ci, got, want)
+ }
+ }
+ }
+ if got, want := txBytes, c.txClose; got != want {
+ t.Fatalf("%d: got %v, want %v", ci, got, want)
+ }
+ rxWant := c.rxClose
+ if rxWant > c.txClose {
+ rxWant = c.txClose
+ }
+ if got, want := rxBytes, rxWant; got != want {
+ t.Fatalf("%d: got %v, want %v", ci, got, want)
+
+ }
+ }
+}
+
+func TestDrop(t *testing.T) {
+ cases := []struct {
+ txDropAfter int
+ tx []string
+ rx []string
+ }{
+ {6, []string{"hello", "world"}, []string{"hello", "w"}},
+ {2, []string{"hello", "world"}, []string{"he", "wo"}},
+ {0, []string{"hello", "world"}, []string{"", ""}},
+ }
+
+ for ci, c := range cases {
+ opts := mocknet.Opts{
+ Mode: mocknet.Drop,
+ TxDropAfter: func() int { return c.txDropAfter },
+ }
+
+ ln := newListener(t, opts)
+ defer ln.Close()
+
+ var rxconn net.Conn
+ var rxerr error
+ var wg sync.WaitGroup
+ wg.Add(1)
+ go func() {
+ rxconn, rxerr = ln.Accept()
+ wg.Done()
+ }()
+
+ txconn, err := mocknet.DialerWithOpts(opts, "test", ln.Addr().String(), time.Minute)
+ if err != nil {
+ t.Fatal(err)
+ }
+ wg.Wait()
+
+ rw := func(s string, l int) (int, int, string, error) {
+ b := make([]byte, l)
+ tx, _ := txconn.Write([]byte(s))
+ rx, err := rxconn.Read(b[:])
+ return tx, rx, string(b[0:rx]), err
+ }
+
+ for i, m := range c.tx {
+ tx, rx, rxed, _ := rw(m, len(c.rx[i]))
+ if got, want := rxed, c.rx[i]; got != want {
+ t.Fatalf("%d: got %q, want %q", ci, got, want)
+ }
+ if tx != rx {
+ t.Fatalf("%d: tx %d, rx %d", ci, tx, rx)
+ }
+ }
+ }
+}
diff --git a/profiles/internal/util.go b/profiles/internal/util.go
index a6c45c7..600ff0a 100644
--- a/profiles/internal/util.go
+++ b/profiles/internal/util.go
@@ -10,6 +10,7 @@
"strings"
"v.io/v23/rpc"
+ "v.io/v23/verror"
"v.io/x/lib/vlog"
"v.io/x/lib/netstate"
@@ -23,14 +24,13 @@
// profile can use or modify the flags as it pleases.
func ParseFlags(f *flags.Flags) error {
handle, err := exec.GetChildHandle()
- switch err {
- case exec.ErrNoVersion:
- // The process has not been started through the vanadium exec
- // library. No further action is needed.
- case nil:
+ if err == nil {
// The process has been started through the vanadium exec
// library.
- default:
+ } else if verror.ErrorID(err) == exec.ErrNoVersion.ID {
+ // The process has not been started through the vanadium exec
+ // library. No further action is needed.
+ } else {
return err
}
diff --git a/services/mgmt/device/impl/callback.go b/services/mgmt/device/impl/callback.go
index 6ffef42..4241e60 100644
--- a/services/mgmt/device/impl/callback.go
+++ b/services/mgmt/device/impl/callback.go
@@ -7,6 +7,7 @@
import (
"v.io/v23/context"
"v.io/v23/mgmt"
+ "v.io/v23/verror"
"v.io/x/lib/vlog"
"v.io/x/ref/lib/exec"
@@ -17,8 +18,7 @@
// is expected to be this device manager's object name).
func InvokeCallback(ctx *context.T, name string) {
handle, err := exec.GetChildHandle()
- switch err {
- case nil:
+ if err == nil {
// Device manager was started by self-update, notify the parent.
callbackName, err := handle.Config.Get(mgmt.ParentNameConfigKey)
if err != nil {
@@ -31,8 +31,7 @@
if err := client.Set(ctx, mgmt.ChildNameConfigKey, name); err != nil {
vlog.Fatalf("Set(%v, %v) failed: %v", mgmt.ChildNameConfigKey, name, err)
}
- case exec.ErrNoVersion:
- default:
+ } else if verror.ErrorID(err) != exec.ErrNoVersion.ID {
vlog.Fatalf("GetChildHandle() failed: %v", err)
}
}
diff --git a/test/modules/exec.go b/test/modules/exec.go
index d9837c7..35c34e6 100644
--- a/test/modules/exec.go
+++ b/test/modules/exec.go
@@ -15,6 +15,7 @@
"time"
"v.io/v23/mgmt"
+ "v.io/v23/verror"
"v.io/x/lib/vlog"
vexec "v.io/x/ref/lib/exec"
"v.io/x/ref/test/expect"
@@ -248,7 +249,7 @@
// The child has exited already.
case <-time.After(eh.opts.ShutdownTimeout):
// Time out waiting for child to exit.
- procErr = vexec.ErrTimeout
+ procErr = verror.New(vexec.ErrTimeout, nil)
// Force close stdout to unblock any readers of stdout
// (including the drain loop started above).
eh.stdout.Close()
diff --git a/test/modules/modules_test.go b/test/modules/modules_test.go
index ced8fc8..b0745f0 100644
--- a/test/modules/modules_test.go
+++ b/test/modules/modules_test.go
@@ -21,6 +21,7 @@
"time"
"v.io/v23"
+ "v.io/v23/verror"
"v.io/x/ref/lib/exec"
execconsts "v.io/x/ref/lib/exec/consts"
@@ -469,8 +470,8 @@
t.Fatalf("unexpected error: %s", err)
}
var stdoutBuf, stderrBuf bytes.Buffer
- if err := sh.Cleanup(&stdoutBuf, &stderrBuf); err == nil || err.Error() != exec.ErrTimeout.Error() {
- t.Errorf("unexpected error in Cleanup: got %v, want %v", err, exec.ErrTimeout)
+ if err := sh.Cleanup(&stdoutBuf, &stderrBuf); err == nil || verror.ErrorID(err) != exec.ErrTimeout.ID {
+ t.Errorf("unexpected error in Cleanup: got %v, want %v", err, exec.ErrTimeout.ID)
}
if err := syscall.Kill(h.Pid(), syscall.SIGINT); err != nil {
t.Errorf("Kill failed: %v", err)