playground: Fix Javascript tests to use VDL generation.

Added support for Javascript VDL to builder.
Added VDL to pingpong test and fortune example.
Reduced test output verbosity.

Change-Id: If27176d891f478324011bcfba349e316cf2a756c
diff --git a/client/bundles/fortune/ex0_js/server.js b/client/bundles/fortune/ex0_js/server.js
deleted file mode 100644
index 32d9ee9..0000000
--- a/client/bundles/fortune/ex0_js/server.js
+++ /dev/null
@@ -1,90 +0,0 @@
-// index=1
-var veyron = require('veyron');
-var vom = veyron.vom;
-
-/**
- * 1) Implement a simple fortune service
- */
-
-var fortuneService = {
-  // List of fortunes
-  fortunes: [],
-
-  numFortunesServed: 0,
-
-  // Gets a random fortune
-  getRandomFortune: function(ctx) {
-    var numExistingfortunes = this.fortunes.length;
-    if(numExistingfortunes === 0) {
-      throw new Error('Sorry! No fortune available :(');
-    }
-    var randomIndex = Math.floor(Math.random() * numExistingfortunes);
-    var fortune = this.fortunes[randomIndex];
-    this.numFortunesServed++;
-    console.info('Serving:', fortune);
-    return fortune;
-  },
-
-  // Adds a new fortune
-  addNewFortune: function(ctx, fortune) {
-    if(!fortune || fortune.trim() === '') {
-      throw new Error('Sorry! Can\'t add empty or null fortune!');
-    }
-    console.info('Adding:', fortune);
-    this.fortunes.push(fortune);
-  },
-
-  // Describes the fortune service.
-  // TODO(alexfandrianto): The correct way to do this is to generate the JS code
-  // from the VDL file and inherit from the generated service stub.
-  _serviceDescription: {
-    methods: [
-      {
-        name: 'GetRandomFortune',
-        outArgs: [
-          {
-            type: vom.Types.STRING
-          }
-        ]
-      },
-      {
-        name: 'AddNewFortune',
-        inArgs: [
-          {
-            name: 'fortune',
-            type: vom.Types.STRING
-          }
-        ],
-        outArgs: []
-      }
-    ]
-  }
-};
-
-/**
- * 2) Publish the fortune service
- */
-
-// Create a Vanadium runtime using the configuration
-veyron.init().then(function(rt) {
-  var server = rt.newServer();
-  // Serve the fortune server under a name. Serve returns a Promise object
-  server.serve('bakery/cookie/fortune', fortuneService).then(function() {
-    console.log('Fortune server serving under: bakery/cookie/fortune');
-  }).catch(function(err) {
-    console.error('Failed to serve the fortune server because:\n', err);
-    process.exit(1);
-  });
-}).catch(function(err) {
-  console.error('Failed to start the fortune server because:\n', err);
-  process.exit(1);
-});
-
-// Let's add a few fortunes to start with
-[
-  'The fortune you seek is in another cookie.',
-  'Everything will now come your way.',
-  'Conquer your fears or they will conquer you.'
-].forEach(function(fortune) {
-  fortuneService.addNewFortune(null, fortune);
-});
diff --git a/client/bundles/fortune/ex0_js/client.js b/client/bundles/fortune/ex0_js/src/client/client.js
similarity index 100%
rename from client/bundles/fortune/ex0_js/client.js
rename to client/bundles/fortune/ex0_js/src/client/client.js
diff --git a/client/bundles/fortune/ex0_js/src/fortune/fortune.vdl b/client/bundles/fortune/ex0_js/src/fortune/fortune.vdl
new file mode 100644
index 0000000..e3c3ea8
--- /dev/null
+++ b/client/bundles/fortune/ex0_js/src/fortune/fortune.vdl
@@ -0,0 +1,10 @@
+// index=3
+package fortune
+
+type Fortune interface {
+	// Returns a random fortune.
+	GetRandomFortune() (Fortune string | error)
+
+	// Adds a fortune to the set used by GetRandomFortune().
+	AddNewFortune(Fortune string) error
+}
diff --git a/client/bundles/fortune/ex0_js/src/server/server.js b/client/bundles/fortune/ex0_js/src/server/server.js
new file mode 100644
index 0000000..79822b2
--- /dev/null
+++ b/client/bundles/fortune/ex0_js/src/server/server.js
@@ -0,0 +1,67 @@
+// index=1
+var inherits = require('util').inherits;
+var veyron = require('veyron');
+
+var fortune = require('../fortune/fortune');
+
+/**
+ * 1) Implement a simple fortune service
+ */
+
+function FortuneService() {
+  // List of fortunes
+  this.fortunes = [];
+}
+
+inherits(FortuneService, fortune.Fortune);
+
+// Gets a random fortune
+FortuneService.prototype.GetRandomFortune = function(ctx) {
+  var numExistingfortunes = this.fortunes.length;
+  if(numExistingfortunes === 0) {
+    throw new Error('Sorry! No fortune available :(');
+  }
+  var randomIndex = Math.floor(Math.random() * numExistingfortunes);
+  var fortune = this.fortunes[randomIndex];
+  console.info('Serving:', fortune);
+  return fortune;
+};
+
+// Adds a new fortune
+FortuneService.prototype.AddNewFortune = function(ctx, fortune) {
+  if(!fortune || fortune.trim() === '') {
+    throw new Error('Sorry! Can\'t add empty or null fortune!');
+  }
+  console.info('Adding:', fortune);
+  this.fortunes.push(fortune);
+};
+
+/**
+ * 2) Publish the fortune service
+ */
+
+var fortuneService = new FortuneService();
+
+// Create a Vanadium runtime using the configuration
+veyron.init().then(function(rt) {
+  var server = rt.newServer();
+  // Serve the fortune server under a name. Serve returns a Promise object
+  server.serve('bakery/cookie/fortune', fortuneService).then(function() {
+    console.log('Fortune server serving under: bakery/cookie/fortune');
+  }).catch(function(err) {
+    console.error('Failed to serve the fortune server because:\n', err);
+    process.exit(1);
+  });
+}).catch(function(err) {
+  console.error('Failed to start the fortune server because:\n', err);
+  process.exit(1);
+});
+
+// Let's add a few fortunes to start with
+[
+  'The fortune you seek is in another cookie.',
+  'Everything will now come your way.',
+  'Conquer your fears or they will conquer you.'
+].forEach(function(fortune) {
+  fortuneService.AddNewFortune(null, fortune);
+});
diff --git a/client/test.sh b/client/test.sh
index 3423138..21968c7 100755
--- a/client/test.sh
+++ b/client/test.sh
@@ -27,7 +27,7 @@
   for e in $EXAMPLES; do
     local d="${PG_BUNDLES_DIR}/${e}"
     echo -e "\n\n>>>>> Test ${d}\n\n"
-    test_pg_example "${d}" "-v=true --includeServiceOutput=true --runTimeout=5000" || shell_test::fail "${d}: failed to run"
+    test_pg_example "${d}" "-v=true --runTimeout=5000" || shell_test::fail "${d}: failed to run"
     # TODO(sadovsky): Make this "clean exit" check more robust.
     grep -q "\"Exited cleanly.\"" builder.out || shell_test::fail "${d}: did not exit cleanly"
     rm -f builder.out
diff --git a/go/src/playground/builder/main.go b/go/src/playground/builder/main.go
index 12f02bd..8d74d78 100644
--- a/go/src/playground/builder/main.go
+++ b/go/src/playground/builder/main.go
@@ -115,6 +115,8 @@
 	return nil
 }
 
+// All .go and .vdl files must have paths at least two directories deep,
+// beginning with "src/".
 func parseRequest(in io.Reader) (r request, err error) {
 	debug("Parsing input")
 	data, err := ioutil.ReadAll(in)
@@ -189,36 +191,58 @@
 }
 
 // If compilation failed due to user error (bad input), returns badInput=true
-// and err=nil. Only internal errors return non-nil err.
-func compileFiles(files []*codeFile) (badInput bool, err error) {
-	needToCompile := false
+// and cerr=nil. Only internal errors return non-nil cerr.
+func compileFiles(files []*codeFile) (badInput bool, cerr error) {
+	found := make(map[string]bool)
 	for _, f := range files {
-		if f.lang == "vdl" || f.lang == "go" {
-			needToCompile = true
-			break
-		}
+		found[f.lang] = true
 	}
-	if !needToCompile {
-		return
+	if !found["go"] && !found["vdl"] {
+		// No need to compile.
+		return false, nil
 	}
 
 	debug("Compiling files")
 	pwd, err := os.Getwd()
 	if err != nil {
-		return
+		return false, fmt.Errorf("Error getting current directory: %v", err)
+	}
+	srcd := filepath.Join(pwd, "src")
+	if err = os.Chdir(srcd); err != nil {
+		panicOnError(out.Write(event.New("", "stderr", ".go or .vdl files outside src/ directory.")))
+		return true, nil
 	}
 	os.Setenv("GOPATH", pwd+":"+os.Getenv("GOPATH"))
 	os.Setenv("VDLPATH", pwd+":"+os.Getenv("VDLPATH"))
 	// We set isService=false for compilation because "go install" only produces
 	// output on error, and we always want clients to see such errors.
-	err = makeCmd("<compile>", false, "v23", "go", "install", "./...").Run()
 	// TODO(ivanpi): We assume *exec.ExitError results from uncompilable input
 	// files; other cases can result from bugs in playground backend or compiler
 	// itself.
-	if _, ok := err.(*exec.ExitError); ok {
-		badInput, err = true, nil
+	if found["js"] && found["vdl"] {
+		debug("Generating VDL for Javascript")
+		err = makeCmd("<compile>", false,
+			"vdl", "generate", "-lang=Javascript", "-js_out_dir="+srcd, "./...").Run()
+		if _, ok := err.(*exec.ExitError); ok {
+			return true, nil
+		} else if err != nil {
+			return false, err
+		}
 	}
-	return
+	if found["go"] {
+		debug("Generating VDL for Go and compiling Go")
+		err = makeCmd("<compile>", false,
+			"v23", "go", "install", "./...").Run()
+		if _, ok := err.(*exec.ExitError); ok {
+			return true, nil
+		} else if err != nil {
+			return false, err
+		}
+	}
+	if err = os.Chdir(pwd); err != nil {
+		return false, fmt.Errorf("Error returning to parent directory: %v", err)
+	}
+	return false, nil
 }
 
 func runFiles(files []*codeFile) {
diff --git a/go/src/playground/test.sh b/go/src/playground/test.sh
index 33dc5fe..1e9f197 100755
--- a/go/src/playground/test.sh
+++ b/go/src/playground/test.sh
@@ -18,7 +18,7 @@
     cp "${TESTDATA_DIR}/${f}" "${fdir}/"
   done
 
-  test_pg_example "${PGBUNDLE_DIR}" "-v=true --includeServiceOutput=true --includeV23Env=true --runTimeout=5000"
+  test_pg_example "${PGBUNDLE_DIR}" "-v=true --includeV23Env=true --runTimeout=5000"
 }
 
 main() {
@@ -32,11 +32,11 @@
 
   echo -e "\n\n>>>>> Test as the same principal\n\n"
 
-  test_with_files "src/pingpong/wire.vdl" "src/pong/pong.go" "src/ping/ping.go" || shell_test::fail "line ${LINENO}: basic ping (go -> go)"
+  test_with_files "src/pong/pong.go" "src/ping/ping.go" "src/pingpong/wire.vdl" || shell_test::fail "line ${LINENO}: basic ping (go -> go)"
   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 "src/pong/pong.js" "src/ping/ping.js" || shell_test::fail "line ${LINENO}: basic ping (js -> js)"
+  test_with_files "src/pong/pong.js" "src/ping/ping.js" "src/pingpong/wire.vdl" || shell_test::fail "line ${LINENO}: basic ping (js -> js)"
   grep -q PING builder.out || shell_test::fail "line ${LINENO}: no PING"
   grep -q PONG builder.out || shell_test::fail "line ${LINENO}: no PONG"
 
@@ -54,7 +54,7 @@
   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 "src/pong/pong.js" "src/ping/ping.js" "src/ids/authorized.id" || shell_test::fail "line ${LINENO}: authorized id (js -> js)"
+  test_with_files "src/pong/pong.js" "src/ping/ping.js" "src/pingpong/wire.vdl" "src/ids/authorized.id" || shell_test::fail "line ${LINENO}: authorized id (js -> js)"
   grep -q PING builder.out || shell_test::fail "line ${LINENO}: no PING"
   grep -q PONG builder.out || shell_test::fail "line ${LINENO}: no PONG"
 
@@ -63,7 +63,7 @@
   test_with_files "src/pong/pong.go" "src/ping/ping.go" "src/pingpong/wire.vdl" "src/ids/expired.id" || shell_test::fail  "line ${LINENO}: expired id (go -> go)"
   grep -q "not authorized" builder.out || shell_test::fail "line ${LINENO}: rpc with expired id succeeded (go -> go)"
 
-  test_with_files "src/pong/pong.js" "src/ping/ping.js" "src/ids/expired.id" || shell_test::fail  "line ${LINENO}: expired id (js -> js)"
+  test_with_files "src/pong/pong.js" "src/ping/ping.js" "src/pingpong/wire.vdl" "src/ids/expired.id" || shell_test::fail  "line ${LINENO}: expired id (js -> js)"
   grep -q "not authorized" builder.out || shell_test::fail "line ${LINENO}: rpc with expired id succeeded (js -> js)"
 
   echo -e "\n\n>>>>> Test with unauthorized blessings\n\n"
@@ -71,7 +71,7 @@
   test_with_files "src/pong/pong.go" "src/ping/ping.go" "src/pingpong/wire.vdl" "src/ids/unauthorized.id" || shell_test::fail  "line ${LINENO}: unauthorized id (go -> go)"
   grep -q "not authorized" builder.out || shell_test::fail "line ${LINENO}: rpc with unauthorized id succeeded (go -> go)"
 
-  test_with_files "src/pong/pong.js" "src/ping/ping.js" "src/ids/unauthorized.id" || shell_test::fail  "line ${LINENO}: unauthorized id (js -> js)"
+  test_with_files "src/pong/pong.js" "src/ping/ping.js" "src/pingpong/wire.vdl" "src/ids/unauthorized.id" || shell_test::fail  "line ${LINENO}: unauthorized id (js -> js)"
   grep -q "not authorized" builder.out || shell_test::fail "line ${LINENO}: rpc with unauthorized id succeeded (js -> js)"
 
   shell_test::pass
diff --git a/go/src/playground/testdata/src/pong/pong.js b/go/src/playground/testdata/src/pong/pong.js
index 466423f..5edda64 100644
--- a/go/src/playground/testdata/src/pong/pong.js
+++ b/go/src/playground/testdata/src/pong/pong.js
@@ -1,33 +1,19 @@
+var inherits = require('util').inherits;
 var veyron = require('veyron');
-var vom = veyron.vom;
 
-var pingPongService = {
-  ping: function(ctx, msg) {
-    console.log('[' + ctx.remoteBlessingStrings + '] ' + msg);
-    return 'PONG';
-  },
-  // TODO(alexfandrianto): The correct way to do this is to generate the JS code
-  // from the VDL file and inherit from the generated service stub.
-  _serviceDescription: {
-    methods: [
-      {
-        name: 'Ping',
-        inArgs: [
-          {
-            name: 'msg',
-            type: vom.Types.STRING
-          }
-        ],
-        outArgs: [
-          {
-            type: vom.Types.STRING
-          }
-        ]
-      }
-    ]
-  }
+var pingpong = require('../pingpong/pingpong');
+
+function PingPongService() {}
+
+inherits(PingPongService, pingpong.PingPong);
+
+PingPongService.prototype.Ping = function(ctx, message) {
+  console.log('[' + ctx.remoteBlessingStrings + '] ' + message);
+  return 'PONG';
 };
 
+var pingPongService = new PingPongService();
+
 veyron.init(function(err, rt) {
   if (err) throw err;