playground: make outcome of "Run" clearer

This CL changes the last line displayed after the program was run
to display more detail on what happened.

For more details see CL 141477.

Dockerfile in playground repo needs to be updated
to include x/tools with CL 141477.

Updates golang/go#10590
Updates golang/go#25454

Change-Id: I6bdef66e70d693540e4e7d30478ae9f0bf85d1ba
Reviewed-on: https://go-review.googlesource.com/c/141478
Reviewed-by: Andrew Bonventre <andybons@golang.org>
Run-TryBot: Andrew Bonventre <andybons@golang.org>
diff --git a/sandbox.go b/sandbox.go
index f63c360..9b80f78 100644
--- a/sandbox.go
+++ b/sandbox.go
@@ -28,6 +28,7 @@
 	"reflect"
 	"runtime"
 	"strings"
+	"syscall"
 	"text/template"
 	"time"
 
@@ -49,8 +50,11 @@
 }
 
 type response struct {
-	Errors string
-	Events []Event
+	Errors      string
+	Events      []Event
+	Status      int
+	IsTest      bool
+	TestsFailed int
 }
 
 // commandHandler returns an http.HandlerFunc.
@@ -276,6 +280,8 @@
 }
 `))
 
+var failedTestPattern = "--- FAIL"
+
 // compileAndRun tries to build and run a user program.
 // The output of successfully ran program is returned in *response.Events.
 // If a program cannot be built or has timed out,
@@ -334,19 +340,36 @@
 	rec := new(Recorder)
 	cmd.Stdout = rec.Stdout()
 	cmd.Stderr = rec.Stderr()
+	var status int
 	if err := cmd.Run(); err != nil {
 		if ctx.Err() == context.DeadlineExceeded {
-			return &response{Errors: "process took too long"}, nil
+			// Send what was captured before the timeout.
+			events, err := rec.Events()
+			if err != nil {
+				return nil, fmt.Errorf("error decoding events: %v", err)
+			}
+			return &response{Errors: "process took too long", Events: events}, nil
 		}
-		if _, ok := err.(*exec.ExitError); !ok {
+		exitErr, ok := err.(*exec.ExitError)
+		if !ok {
 			return nil, fmt.Errorf("error running sandbox: %v", err)
 		}
+		if ws, ok := exitErr.Sys().(syscall.WaitStatus); ok {
+			status = ws.ExitStatus()
+		}
 	}
 	events, err := rec.Events()
 	if err != nil {
 		return nil, fmt.Errorf("error decoding events: %v", err)
 	}
-	return &response{Events: events}, nil
+	var fails int
+	if testParam != "" {
+		// In case of testing the TestsFailed field contains how many tests have failed.
+		for _, e := range events {
+			fails += strings.Count(e.Message, failedTestPattern)
+		}
+	}
+	return &response{Events: events, Status: status, IsTest: testParam != "", TestsFailed: fails}, nil
 }
 
 func (s *server) healthCheck() error {