veyron/tools/playground: Add test.sh for the builder tool.

Change-Id: Idf7b99b3ce40f9e9bb7c6b28eb84b4c03ae49179
diff --git a/tools/playground/builder/vbuild.go b/tools/playground/builder/vbuild.go
index 3579760..b9decde 100644
--- a/tools/playground/builder/vbuild.go
+++ b/tools/playground/builder/vbuild.go
@@ -116,8 +116,15 @@
 		log.Fatal(err)
 	}
 
-	CreateIdentities(r)
-	if err = StartMount(); err != nil {
+	err = CreateIdentities(r)
+	if err != nil {
+		log.Fatal(err)
+	}
+	mt, err := StartMount()
+	if mt != nil {
+		defer mt.Kill()
+	}
+	if err != nil {
 		log.Fatal(err)
 	}
 	CompileAndRun(r)
@@ -217,10 +224,9 @@
 func (f *CodeFile) Compile() error {
 	var cmd *exec.Cmd
 	if path.Ext(f.Name) == ".vdl" {
-		cmd = MakeCmd("vdl", "generate", f.pkg)
+		cmd = MakeCmd("vdl", "generate", "--lang=go", f.pkg)
 	} else {
-		cmd = MakeCmd("go", "install", f.pkg)
-		cmd.Dir = "/usr/local/veyron/veyron"
+		cmd = MakeCmd(path.Join(os.Getenv("VEYRON_ROOT"), "veyron/scripts/build/go"), "install", f.pkg)
 	}
 	cmd.Stdout = cmd.Stderr
 	err := cmd.Run()
@@ -241,16 +247,46 @@
 }
 
 func (id Identity) Create() error {
-	args := make([]string, 0, 10)
-	args = append(args, "--name", id.Name)
-	if id.Blesser != "" {
-		args = append(args, "--blesser", path.Join("ids", id.Blesser))
+	if err := id.generate(); err != nil {
+		return err
 	}
+	if id.Blesser != "" || id.Duration != "" {
+		return id.bless()
+	}
+	return nil
+}
+
+func (id Identity) generate() error {
+	args := []string{"generate"}
+	if id.Blesser == "" && id.Duration == "" {
+		args = append(args, id.Name)
+	}
+	return runIdentity(args, path.Join("ids", id.Name))
+}
+
+func (id Identity) bless() error {
+	filename := path.Join("ids", id.Name)
+	var blesser string
+	if id.Blesser == "" {
+		blesser = filename
+	} else {
+		blesser = path.Join("ids", id.Blesser)
+	}
+	args := []string{"bless", "--with", blesser}
 	if id.Duration != "" {
-		args = append(args, "--duration", id.Duration)
+		args = append(args, "--for", id.Duration)
 	}
+	args = append(args, filename, id.Name)
+	tempfile := filename + ".tmp"
+	if err := runIdentity(args, tempfile); err != nil {
+		return err
+	}
+	return os.Rename(tempfile, filename)
+}
+
+func runIdentity(args []string, filename string) error {
 	cmd := MakeCmd("identity", args...)
-	out, err := os.Create(path.Join("ids", id.Name))
+	out, err := os.Create(filename)
 	if err != nil {
 		return err
 	}
@@ -259,14 +295,14 @@
 	return cmd.Run()
 }
 
-func StartMount() (err error) {
+func StartMount() (proc *os.Process, err error) {
 	reader, writer := io.Pipe()
 	cmd := MakeCmd("mounttabled")
 	cmd.Stdout = writer
 	cmd.Stderr = cmd.Stdout
 	err = cmd.Start()
 	if err != nil {
-		return err
+		return nil, err
 	}
 	buf := bufio.NewReader(reader)
 	pat := regexp.MustCompile("Mount table .+ endpoint: (.+)\n")
@@ -291,7 +327,7 @@
 			log.Fatal("mounttable died")
 		}
 		Log("mount at ", endpoint)
-		return os.Setenv("NAMESPACE_ROOT", endpoint)
+		return cmd.Process, os.Setenv("NAMESPACE_ROOT", endpoint)
 	}
-	return err
+	return cmd.Process, err
 }
diff --git a/tools/playground/test.sh b/tools/playground/test.sh
new file mode 100755
index 0000000..fa69c8e
--- /dev/null
+++ b/tools/playground/test.sh
@@ -0,0 +1,58 @@
+#!/bin/bash
+
+# Test the playground builder tool.
+
+source "${VEYRON_ROOT}/environment/scripts/lib/shell_test.sh"
+
+build() {
+  local -r GO="${REPO_ROOT}/scripts/build/go"
+  "${GO}" build veyron/tools/identity || shell_test::fail "line ${LINENO}: failed to build 'identity'"
+  "${GO}" build veyron/services/mounttable/mounttabled || shell_test::fail "line ${LINENO}: failed to build 'mounttabled'"
+  "${GO}" build veyron2/vdl/vdl || shell_test::fail "line ${LINENO}: failed to build 'vdl'"
+  "${GO}" build veyron/tools/playground/builder || shell_test::fail "line ${LINENO}: failed to build 'builder'"
+  "${GO}" build veyron/tools/playground/testdata/escaper || shell_test::fail "line ${LINENO}: failed to build 'escaper'"
+}
+
+test_with_files() {
+    echo '{"Files":[' > request.json
+    while [[ $# > 0 ]]; do
+	echo '{"Name":"'"$(basename $1)"'","Body":' >>request.json
+	grep -v OMIT $1 | ./escaper >>request.json
+	shift
+	if [[ $# > 0 ]]; then
+	    echo '},' >>request.json
+	else
+	    echo '}' >>request.json
+	fi
+    done
+    echo ']}' >>request.json
+    rm -f builder.out
+    ./builder <request.json 2>&1 | tee builder.out
+}
+
+main() {
+  cd $(shell::tmp_dir)
+  build
+
+  local -r DIR="${REPO_ROOT}/go/src/veyron/tools/playground/testdata"
+
+  export GOPATH="$(pwd)"
+  export PATH="$(pwd):$PATH"
+  test_with_files $DIR/pingpong/wire.vdl $DIR/pong/pong.go $DIR/ping/ping.go || shell_test::fail "line ${LINENO}: basic ping"
+  grep -q ping builder.out || shell_test::fail "line ${LINENO}: no ping"
+  grep -q pong builder.out || shell_test::fail "line ${LINENO}: no pong"
+
+  test_with_files $DIR/pingpong/wire.vdl $DIR/pong/pong.go $DIR/ping/ping.go $DIR/ids/authorized.id || shell_test::fail "line ${LINENO}: authorized id"
+  grep -q ping builder.out || shell_test::fail "line ${LINENO}: no ping"
+  grep -q pong builder.out || shell_test::fail "line ${LINENO}: no pong"
+
+  test_with_files $DIR/pingpong/wire.vdl $DIR/pong/pong.go $DIR/ping/ping.go $DIR/ids/expired.id || shell_test::fail  "line ${LINENO}: failed to build with expired id"
+  grep -q "ipc: not authorized" builder.out || shell_test::fail "line ${LINENO}: rpc with expired id succeeded"
+
+  test_with_files $DIR/pingpong/wire.vdl $DIR/pong/pong.go $DIR/ping/ping.go $DIR/ids/unauthorized.id || shell_test::fail  "line ${LINENO}: failed to build with unauthorized id"
+  grep -q "ipc: not authorized" builder.out || shell_test::fail "line ${LINENO}: rpc with unauthorized id succeeded"
+
+  shell_test::pass
+}
+
+main "$@"
diff --git a/tools/playground/testdata/escaper/main.go b/tools/playground/testdata/escaper/main.go
new file mode 100644
index 0000000..9f867e1
--- /dev/null
+++ b/tools/playground/testdata/escaper/main.go
@@ -0,0 +1,19 @@
+package main
+
+import (
+	"encoding/json"
+	"io/ioutil"
+	"os"
+)
+
+func main() {
+	data, err := ioutil.ReadAll(os.Stdin)
+	if err != nil {
+		panic(err)
+	}
+	out, err := json.Marshal(string(data))
+	if err != nil {
+		panic(err)
+	}
+	os.Stdout.Write(out)
+}
diff --git a/tools/playground/testdata/ids/authorized.id b/tools/playground/testdata/ids/authorized.id
new file mode 100644
index 0000000..8a1f950
--- /dev/null
+++ b/tools/playground/testdata/ids/authorized.id
@@ -0,0 +1,15 @@
+[
+{
+  "Name": "myorg"
+},
+{
+  "Name": "myserver",
+  "Blesser": "myorg",
+  "Files": ["pong.go"]
+},
+{
+  "Name": "myclient",
+  "Blesser": "myserver",
+  "Files": ["ping.go"]
+}
+]
\ No newline at end of file
diff --git a/tools/playground/testdata/ids/expired.id b/tools/playground/testdata/ids/expired.id
new file mode 100644
index 0000000..6ff368d
--- /dev/null
+++ b/tools/playground/testdata/ids/expired.id
@@ -0,0 +1,7 @@
+[
+{
+  "Name": "expired",
+  "Duration": "0s",
+  "Files": ["ping.go", "pong.go"]
+}
+]
\ No newline at end of file
diff --git a/tools/playground/testdata/ids/unauthorized.id b/tools/playground/testdata/ids/unauthorized.id
new file mode 100644
index 0000000..544343b
--- /dev/null
+++ b/tools/playground/testdata/ids/unauthorized.id
@@ -0,0 +1,15 @@
+[
+{
+  "Name": "myorg"
+},
+{
+  "Name": "myserver",
+  "Blesser": "myorg",
+  "Files": ["pong.go"]
+},
+{
+  "Name": "myclient",
+  "Blesser": "myorg",
+  "Files": ["ping.go"]
+}
+]
\ No newline at end of file
diff --git a/tools/playground/testdata/ping/ping.go b/tools/playground/testdata/ping/ping.go
new file mode 100644
index 0000000..725c102
--- /dev/null
+++ b/tools/playground/testdata/ping/ping.go
@@ -0,0 +1,24 @@
+// +build OMIT
+package main
+
+import (
+	"fmt"
+	"pingpong"
+	"veyron2/rt"
+)
+
+func main() {
+	runtime := rt.Init()
+	log := runtime.Logger()
+
+	s, err := pingpong.BindPingPong("pingpong")
+	if err != nil {
+		log.Fatal("error binding to server: ", err)
+	}
+
+	pong, err := s.Ping(runtime.NewContext(), "ping")
+	if err != nil {
+		log.Fatal("error pinging: ", err)
+	}
+	fmt.Println(pong)
+}
diff --git a/tools/playground/testdata/pingpong/wire.vdl b/tools/playground/testdata/pingpong/wire.vdl
new file mode 100644
index 0000000..b4e8e0a
--- /dev/null
+++ b/tools/playground/testdata/pingpong/wire.vdl
@@ -0,0 +1,5 @@
+package pingpong
+
+type PingPong interface {
+	Ping(message string) (string, error)
+}
\ No newline at end of file
diff --git a/tools/playground/testdata/pong/pong.go b/tools/playground/testdata/pong/pong.go
new file mode 100644
index 0000000..fa961f8
--- /dev/null
+++ b/tools/playground/testdata/pong/pong.go
@@ -0,0 +1,43 @@
+// +build OMIT
+package main
+
+import (
+	"fmt"
+	"pingpong"
+	"veyron/lib/signals"
+	sflag "veyron/security/flag"
+	"veyron2/ipc"
+	"veyron2/rt"
+)
+
+type pongd struct{}
+
+func (f *pongd) Ping(_ ipc.ServerContext, message string) (result string, err error) {
+	fmt.Println(message)
+	return "pong", nil
+}
+
+func main() {
+	r := rt.Init()
+	log := r.Logger()
+	s, err := r.NewServer()
+	if err != nil {
+		log.Fatal("failure creating server: ", err)
+	}
+	log.Info("Waiting for ping")
+
+	serverPong := pingpong.NewServerPingPong(&pongd{})
+
+	if endpoint, err := s.Listen("tcp", "127.0.0.1:0"); err == nil {
+		fmt.Printf("Listening at: %v\n", endpoint)
+	} else {
+		log.Fatal("error listening to service: ", err)
+	}
+
+	if err := s.Serve("pingpong", ipc.LeafDispatcher(serverPong, sflag.NewAuthorizerOrDie())); err != nil {
+		log.Fatal("error serving service: ", err)
+	}
+
+	// Wait forever.
+	<-signals.ShutdownOnSignals()
+}