veyron/tools/naming/simulator: tidy up error handling, make sleep interruptible.

Change-Id: I2fae361a21c10d5419075970c32c7d02c9831a18
diff --git a/lib/expect/expect.go b/lib/expect/expect.go
index d5f738d..c36bec4 100644
--- a/lib/expect/expect.go
+++ b/lib/expect/expect.go
@@ -58,11 +58,11 @@
 
 // Session represents the state of an expect session.
 type Session struct {
-	input   *bufio.Reader
-	timeout time.Duration
-	t       Testing
-	verbose bool
-	err     error
+	input     *bufio.Reader
+	timeout   time.Duration
+	t         Testing
+	verbose   bool
+	oerr, err error
 }
 
 type Testing interface {
@@ -81,10 +81,18 @@
 }
 
 // Error returns the error code (possibly nil) currently stored in the Session.
+// This will include the file and line of the calling function that experienced
+// the error. Use OriginalError to obtain the original error code.
 func (s *Session) Error() error {
 	return s.err
 }
 
+// OriginalError returns any error code (possibly nil) returned by the
+// underlying library routines called.
+func (s *Session) OriginalError() error {
+	return s.oerr
+}
+
 // SetVerbosity enables/disable verbose debugging information, in particular,
 // every line of input read will be logged via Testing.Logf or, if it is nil,
 // to stderr.
@@ -120,6 +128,7 @@
 // be incorrect.
 func (s *Session) error(err error) error {
 	_, file, line, _ := runtime.Caller(2)
+	s.oerr = err
 	s.err = fmt.Errorf("%s:%d: %s", filepath.Base(file), line, err)
 	s.ReportError()
 	return s.err
diff --git a/lib/modules/core/misc.go b/lib/modules/core/misc.go
index e0c1496..033fd09 100644
--- a/lib/modules/core/misc.go
+++ b/lib/modules/core/misc.go
@@ -7,6 +7,8 @@
 
 	"veyron.io/veyron/veyron2/naming"
 	"veyron.io/veyron/veyron2/rt"
+
+	"veyron.io/veyron/veyron/lib/modules"
 )
 
 func sleep(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
@@ -17,10 +19,20 @@
 			return err
 		}
 	}
-	fmt.Fprintf(stdout, "Sleeping for %s", d)
-	// TODO(cnicolaou): we should probably also listen for stdin closing
-	// and return before the sleep completes.
-	time.Sleep(d)
+	fmt.Fprintf(stdout, "Sleeping for %s\n", d)
+	eof := make(chan struct{})
+	go func() {
+		modules.WaitForEOF(stdin)
+		close(eof)
+	}()
+
+	then := time.Now()
+	select {
+	case <-time.After(d):
+		fmt.Fprintf(stdout, "Slept for %s\n", time.Now().Sub(then))
+	case <-eof:
+		fmt.Fprintf(stdout, "Aborted after %s\n", time.Now().Sub(then))
+	}
 	return nil
 }
 
diff --git a/lib/modules/func.go b/lib/modules/func.go
index 319a673..f44127b 100644
--- a/lib/modules/func.go
+++ b/lib/modules/func.go
@@ -70,9 +70,6 @@
 
 		err := main(stdin, stdout, stderr, sh.mergeOSEnv(), args...)
 		if err != nil {
-			// Print the error to stdout to ensure that anyone reading
-			// only stdout sees the error.
-			fmt.Fprintf(stdout, "%s\n", err)
 			fmt.Fprintf(stderr, "%s\n", err)
 		}
 
diff --git a/tools/naming/simulator/ambiguity.scr b/tools/naming/simulator/ambiguity.scr
index 2974986..1df3410 100644
--- a/tools/naming/simulator/ambiguity.scr
+++ b/tools/naming/simulator/ambiguity.scr
@@ -37,9 +37,12 @@
 mount $s2/b $s3/c 1h
 
 echoServer "Echo" $s3/c
+set es_h=$_
 
 ls $s1/...
 wait $_
 
 ls $s1/a
 wait $_
+
+stop $es_h
diff --git a/tools/naming/simulator/commands.go b/tools/naming/simulator/commands.go
index b05538a..3ec00dc 100644
--- a/tools/naming/simulator/commands.go
+++ b/tools/naming/simulator/commands.go
@@ -1,7 +1,9 @@
 package main
 
 import (
+	"bytes"
 	"fmt"
+	"io"
 	"os"
 	"regexp"
 	"strings"
@@ -26,6 +28,7 @@
 	"read":   {-1, "read <handle> [var]", true, read},
 	"eval":   {1, "eval <handle>", true, eval},
 	"wait":   {1, "wait <handle>", true, wait},
+	"stop":   {1, "stop <handle>", true, stop},
 	"list":   {0, "list", false, list},
 	"quit":   {0, "quit", false, quit},
 }
@@ -100,6 +103,14 @@
 	return "", nil
 }
 
+func readStderr(state *cmdState) (string, error) {
+	var b bytes.Buffer
+	if err := state.Handle.Shutdown(&b); err != nil && err != io.EOF {
+		return b.String(), err
+	}
+	return b.String(), nil
+}
+
 func handleWrapper(sh *modules.Shell, fn builtinCmd, args ...string) (string, error) {
 	if len(args) < 1 {
 		return "", fmt.Errorf("missing handle argument")
@@ -108,7 +119,16 @@
 	if state == nil {
 		return "", fmt.Errorf("invalid handle")
 	}
-	return fn(sh, state, args...)
+	errstr := ""
+	r, err := fn(sh, state, args...)
+	if err != nil {
+		errstr, _ = readStderr(state)
+		errstr = strings.TrimSuffix(errstr, "\n")
+		if len(errstr) > 0 {
+			err = fmt.Errorf("%s: %v", errstr, err)
+		}
+	}
+	return r, err
 }
 
 func read(sh *modules.Shell, state *cmdState, args ...string) (string, error) {
@@ -116,15 +136,14 @@
 	for _, a := range args[1:] {
 		sh.SetVar(a, l)
 	}
-	err := state.Session.Error()
-	if err != nil && strings.HasSuffix(err.Error(), "EOF") {
-		return l, fmt.Errorf("EOF")
-	}
-	return l, state.Session.Error()
+	return l, state.Session.OriginalError()
 }
 
 func eval(sh *modules.Shell, state *cmdState, args ...string) (string, error) {
 	l := state.Session.ReadLine()
+	if err := state.Session.OriginalError(); err != nil {
+		return l, err
+	}
 	k, v, err := parseVar(l)
 	if err != nil {
 		return "", err
@@ -133,11 +152,23 @@
 	return l, nil
 }
 
-func wait(sh *modules.Shell, state *cmdState, args ...string) (string, error) {
+func stop(sh *modules.Shell, state *cmdState, args ...string) (string, error) {
 	state.Handle.CloseStdin()
+	return wait(sh, state, args...)
+}
+
+func wait(sh *modules.Shell, state *cmdState, args ...string) (string, error) {
+	// Read and return stdout
 	r, err := state.Session.Finish(nil)
 	delete(handles, args[0])
-	return r, err
+	if err != nil {
+		return r, err
+	}
+	// Now read and return the contents of stderr as e
+	if str, err := readStderr(state); err != nil && err != io.EOF {
+		return str, err
+	}
+	return r, nil
 }
 
 func list(sh *modules.Shell, _ *cmdState, args ...string) (string, error) {
diff --git a/tools/naming/simulator/echo.scr b/tools/naming/simulator/echo.scr
index eb5cefc..788d1b0 100644
--- a/tools/naming/simulator/echo.scr
+++ b/tools/naming/simulator/echo.scr
@@ -15,7 +15,7 @@
 read $ec line
 assert $line "text: test"
 
-wait $es
+stop $es
 wait $ec
 
 # now use a nameserver.
@@ -53,3 +53,4 @@
 eval $r
 assert $R0 $root//a/b
 
+stop $es
diff --git a/tools/naming/simulator/mt_complex.scr b/tools/naming/simulator/mt_complex.scr
index a16a1f1..3a23946 100644
--- a/tools/naming/simulator/mt_complex.scr
+++ b/tools/naming/simulator/mt_complex.scr
@@ -5,12 +5,14 @@
 cache off
 
 root
-eval $_
+set root_h=$_
+eval $root_h
 set root=$MT_NAME
 
 set NAMESPACE_ROOT=$root
 mt tl/a
 set m=$_
+set mt_a_h=$m
 eval $m
 eval $m
 set mt_a_name=$MT_NAME
@@ -18,6 +20,7 @@
 
 mt tl/b
 set m=$_
+set mt_b_h=$m
 eval $m
 eval $m
 set mt_b_name=$MT_NAME
@@ -98,7 +101,7 @@
 assert $R0 /$es_E1_addr//
 
 # let's have the echo server shut down
-wait $es_E1
+stop $es_E1
 
 # and now, we can see the mount tables again.
 ls ...
@@ -244,6 +247,8 @@
 eval $r
 # returns nothing since symlink is an 'interior' node.
 assert $RN 0
+# don't close or wait for this command since it'll error out.
+
 
 # resolveMT will return the original mount point
 resolveMT tl/b/symlink
@@ -253,3 +258,9 @@
 eval $r
 assert $R0 /$mt_b_addr//$symlink_target
 
+stop $es_E3
+stop $es_E2
+
+quit
+
+