Merge "Relaunch execserver on after a link error."
diff --git a/cherry/adb.go b/cherry/adb.go
index 2a1e6c5..2f1bb75 100644
--- a/cherry/adb.go
+++ b/cherry/adb.go
@@ -346,6 +346,12 @@
 	)
 }
 
+func RelaunchAndroidExecServer (adbSerialNumber string) error {
+	return runCommands(
+		exec.Command("adb", "-s", adbSerialNumber, "shell", "am", "start", "-n", "com.drawelements.deqp/.execserver.ServiceStarter"),
+	)
+}
+
 func RemoveADBPortForward (adbSerialNumber string, localPort int) error {
 	forwardInfos, err := getADBPortForwardInfos()
 	if err != nil { return err }
diff --git a/cherry/tcplink.go b/cherry/tcplink.go
index 717520d..c7c1f39 100644
--- a/cherry/tcplink.go
+++ b/cherry/tcplink.go
@@ -66,6 +66,16 @@
 	STOP_REQUEST_TIMEOUT_SECONDS	= 10
 )
 
+// Enums
+
+type ExecStatusCode int
+
+const (
+	EXEC_STATUS_DONE			ExecStatusCode = iota
+	EXEC_STATUS_LINK_ERROR
+	EXEC_STATUS_TIMEOUT
+)
+
 // TestExecutorEvent
 
 type TestExecutorEvent interface {
@@ -97,6 +107,7 @@
 }
 
 type EventExecutionFinished struct {
+	status			ExecStatusCode
 }
 
 // LineSplitter
@@ -119,7 +130,6 @@
 
 func (link *CommLinkTcpIp) handleConnection (conn net.Conn, eventChannel chan TestExecutorEvent, stopRequest <-chan struct{}) {
 	defer log.Printf("[comm] handleConnection() returning..\n")
-	defer func(){ eventChannel <- EventExecutionFinished{} }()
 
 	type received struct {
 		msg		Message
@@ -170,6 +180,8 @@
 		}
 	}
 
+	var finishStatus ExecStatusCode
+
 	// Handle connection.
 	linkLoop:
 	for {
@@ -187,10 +199,12 @@
 				err := received.err
 				if err == io.EOF {
 					link.appendCommLogLine("Remote host closed connection")
+					finishStatus = EXEC_STATUS_DONE
 					break linkLoop
 				} else if err != nil {
 					log.Printf("[comm] GOT ERROR FROM READER: %s\n", err)
 					link.appendCommLogLine("Comm link terminating")
+					finishStatus = EXEC_STATUS_LINK_ERROR
 					break linkLoop
 				} else {
 					// Handle incoming messages.
@@ -239,11 +253,13 @@
 		if timeoutDiff >= KEEP_ALIVE_TIMEOUT_MINUTES * time.Minute {
 			link.appendCommLogLine("Client timeout")
 			// \todo[petri] handle timeout properly
+			finishStatus = EXEC_STATUS_TIMEOUT
 			break linkLoop
 		}
 
 		if isStopRequested && time.Now().Sub(firstStopRequestArrival) >= STOP_REQUEST_TIMEOUT_SECONDS * time.Second {
 			link.appendCommLogLine("Stop request timeout")
+			finishStatus = EXEC_STATUS_TIMEOUT
 			break linkLoop
 		}
 
@@ -254,6 +270,7 @@
 			msgKeepAlive := MsgKeepAlive{}
 			err := WriteMessage(conn, &msgKeepAlive)
 			if err != nil {
+				finishStatus = EXEC_STATUS_LINK_ERROR
 				break linkLoop
 			}
 			lastKeepaliveSent = time.Now()
@@ -265,6 +282,7 @@
 			err := WriteMessage(conn, &MsgStopExecution{})
 			if err != nil {
 				link.appendCommLogLine(fmt.Sprintf("Got error when sending stop command: %s", err))
+				finishStatus = EXEC_STATUS_LINK_ERROR
 				break linkLoop
 			}
 			isStopExecMsgPending = false
@@ -277,6 +295,8 @@
 		qpaParser.Terminate()
 		handleParserQueue()
 	}
+
+	eventChannel <- EventExecutionFinished{ finishStatus }
 }
 
 func (link *CommLinkTcpIp) Start () error {
diff --git a/cherry/testrunner.go b/cherry/testrunner.go
index 668afc7..351db92 100644
--- a/cherry/testrunner.go
+++ b/cherry/testrunner.go
@@ -258,6 +258,15 @@
 
 // Execute a batch that has been initialized - i.e., the batch result and its test result objects have already been created and set to pending or other suitable status.
 func (runner *TestRunner) executeBatch (batchResultId string, batchParams BatchExecParams, testCasePaths []string, stopRequest <-chan struct{}, executionLogAppend chan<- string) {
+
+	type BatchExecutionStatus int
+	const (
+		BATCH_EXEC_STATUS_RUNNING BatchExecutionStatus = iota
+		BATCH_EXEC_STATUS_DONE
+		BATCH_EXEC_STATUS_LINK_ERROR
+		BATCH_EXEC_STATUS_GENERIC_ERROR
+	)
+
 	// Start running batch result.
 
 	{
@@ -320,6 +329,7 @@
 			testPackage := runner.testPackages[packageName]
 
 			didProgress := false
+			var executionStatus BatchExecutionStatus = BATCH_EXEC_STATUS_RUNNING
 
 			// Try a few times (in case of connection errors).
 			for tryNdx := 0; tryNdx < 3; tryNdx++ {
@@ -328,6 +338,16 @@
 					time.Sleep((time.Duration)(tryNdx) * 500 * time.Millisecond)
 				}
 
+				// If previous error was link error, relaunch execserver just to be sure
+				if executionStatus == BATCH_EXEC_STATUS_LINK_ERROR && deviceConfig.IsADBDevice {
+					appendRunnerLogLine("Relaunching execserver")
+					err := RelaunchAndroidExecServer(deviceConfig.ADBSerialNumber)
+					if err != nil {
+						appendRunnerLogLine(fmt.Sprintf("Failed to relaunch ExecServer on Android via ADB: %v", err))
+						continue // Just try again, if tries left
+					}
+				}
+
 				// Create link to target.
 				linkParams := CommLinkParams {
 					SpawnProcessPath:	batchParams.SpawnLocalProcess,
@@ -360,7 +380,8 @@
 				currentlyRunningCases := make(map[string]bool) // Paths of the test cases currently running.
 
 				// Handle all events from comm link, as well as stop requests.
-				for executing := true; executing; {
+				executionStatus = BATCH_EXEC_STATUS_RUNNING
+				for executionStatus == BATCH_EXEC_STATUS_RUNNING {
 					select {
 						case <-stopRequest:
 							runCanceled = true
@@ -415,11 +436,21 @@
 								case EventProcessLaunchFailed:
 									launchFailed := event.(EventProcessLaunchFailed)
 									appendRunnerLogLine(fmt.Sprintf("Process launch failed: %s", launchFailed.reason))
-									executing = false
+									executionStatus = BATCH_EXEC_STATUS_GENERIC_ERROR
 
 								case EventExecutionFinished:
-									appendRunnerLogLine("Test execution finished")
-									executing = false
+									appendRunnerLogLine(fmt.Sprintf("Test execution finished with status %#v", event.(EventExecutionFinished).status))
+									switch (event.(EventExecutionFinished).status) {
+										case EXEC_STATUS_DONE:
+											executionStatus = BATCH_EXEC_STATUS_DONE
+										case EXEC_STATUS_LINK_ERROR:
+											executionStatus = BATCH_EXEC_STATUS_LINK_ERROR
+										case EXEC_STATUS_TIMEOUT:
+											executionStatus = BATCH_EXEC_STATUS_GENERIC_ERROR
+										default:
+											appendRunnerLogLine(fmt.Sprintf("WARNING: unknown end status received: %#v", event.(EventExecutionFinished).status))
+											executionStatus = BATCH_EXEC_STATUS_GENERIC_ERROR
+									}
 
 								default:
 									appendRunnerLogLine(fmt.Sprintf("WARNING: unknown execute event received: %#v", event))