Merge "Relaunch execserver on after a link error."
diff --git a/README b/README
index 5154f07..a0b75cf 100644
--- a/README
+++ b/README
@@ -4,9 +4,17 @@
 Setting Up Development Environment
 ----------------------------------
 
+LINUX
+
+- Install golang (sudo apt-get install golang or https://golang.org/doc/install)
+
+- Set up go path (For example, export GOPATH=$HOME/gopath)
+
+WINDOWS
+
 In order to be able to build Cherry on Windows, the following are required.
 
-- Go distribution: https://code.google.com/p/go/downloads/list
+- Go distribution: https://golang.org/doc/install
   * (use 32-bit installation on Windows, 64-bit doesn't compile sqlite out-of-the-box)
   * set GOPATH=C:\go or GOPATH=<path to cherry>
     + All dependiencies (go get) will be installed here
@@ -18,8 +26,7 @@
   * note: using Cygwin GCC doesn't work
 
 
-Using 64-bit Go on Windows
---------------------------
+64-BIT WINDOWS
 
 64-bit build of sqlite driver requires MinGW toolchain that supports x64. Such toolchain
 is maintained by mingw-w64 project (http://mingw-w64.sourceforge.net/).
@@ -51,9 +58,11 @@
 Running Cherry
 --------------
 
-1. Run "go run server.go" in your cherry/ directory.
+1. Make sure adb executable is in the path
 
-2. Point your favorite browser to http://127.0.0.1:8080.
+2. Run "go run server.go" in your cherry/ directory.
+
+3. Point your favorite browser to http://127.0.0.1:8080.
 
 
 Running Selenium Tests
diff --git a/cherry/qpa.go b/cherry/qpa.go
index 2b33288..a1c2152 100644
--- a/cherry/qpa.go
+++ b/cherry/qpa.go
@@ -217,17 +217,18 @@
 
 			case "#beginTestCaseResult":
 				if !parser.isInsideSession {
-					log.Println("[qpa] warning: first test case before #beginSession; ignoring")
+					log.Println("[qpa] warning: first test case before #beginSession; assuming session started")
+					parser.beginSession()
+					parser.isInsideSession = true
+				}
+				if rest == "" {
+					log.Println("[qpa] warning: empty test case name in #beginTestCaseResult; ignoring test case")
 				} else {
-					if rest == "" {
-						log.Println("[qpa] warning: empty test case name in #beginTestCaseResult; ignoring test case")
-					} else {
-						if parser.currentCasePath != "" {
-							log.Println("[qpa] warning: #beginTestCaseResult when already parsing a test case; terminating current case")
-							parser.endTestCase(TEST_STATUS_CODE_INTERNAL_ERROR)
-						}
-						parser.beginTestCase(rest)
+					if parser.currentCasePath != "" {
+						log.Println("[qpa] warning: #beginTestCaseResult when already parsing a test case; terminating current case")
+						parser.endTestCase(TEST_STATUS_CODE_INTERNAL_ERROR)
 					}
+					parser.beginTestCase(rest)
 				}
 
 			case "#endTestCaseResult":
diff --git a/client/css/style.css b/client/css/style.css
index 56536a2..7cb6f74 100644
--- a/client/css/style.css
+++ b/client/css/style.css
@@ -90,6 +90,7 @@
 
 .de-text {
 	white-space: pre-wrap;
+	min-height: 1em;
 }
 
 .de-section, .de-shader-program, .de-shader, .de-egl-config-set, .de-build-info, .de-image-set {
diff --git a/client/index.html b/client/index.html
index 858d18e..db91c62 100644
--- a/client/index.html
+++ b/client/index.html
@@ -71,7 +71,7 @@
 
 	<!-- Main -->
 	<div class="container-fluid">
-		<div ui-view autoscroll="false">
+		<div ui-view>
 		</div>
 	</div>
 
diff --git a/client/js/directives.js b/client/js/directives.js
index 4a73c2f..e2ce122 100644
--- a/client/js/directives.js
+++ b/client/js/directives.js
@@ -99,8 +99,11 @@
 			var leftElemWidthRatio		= barLeftRatio;
 			var rightElemWidthRatio		= 1.0 - barWidthRatio - barLeftRatio;
 
-			leftElem.width(percentStr(leftElemWidthRatio - pads(leftElem)/containerElem.width()));
-			rightElem.width(percentStr(rightElemWidthRatio - pads(rightElem)/containerElem.width()));
+			// on some configurations (precision issues?) we need to give some leeway for the implementation
+			var errorTolerance			= 0.01;
+
+			leftElem.width(percentStr(leftElemWidthRatio - pads(leftElem)/containerElem.width() - errorTolerance));
+			rightElem.width(percentStr(rightElemWidthRatio - pads(rightElem)/containerElem.width() - errorTolerance));
 
 			currentBarLeftRatio = barLeftRatio;
 		}
diff --git a/client/js/services.js b/client/js/services.js
index fbfb995..08cd25a 100644
--- a/client/js/services.js
+++ b/client/js/services.js
@@ -154,17 +154,46 @@
 
 .factory('rpc', ['$q', 'socket', 'usSpinnerService', function($q, socket, usSpinnerService)
 {
-	var nextRequestId	= 1;
-	var pendingRequests	= {};
-	var modules			= {};
+	var nextRequestId		= 1;
+	var pendingRequests		= {};
+	var spinnerSpinning		= false;
+	var spinnerStartIssued	= false;
+	var modules				= {};
+
+	var updateSpinner = function()
+	{
+		var shouldBeSpinning = !_.isEmpty(pendingRequests);
+
+		// \todo [petri] use data binding instead of explicit calls?
+
+		if (spinnerSpinning && !shouldBeSpinning)
+		{
+			spinnerSpinning = false;
+			usSpinnerService.stop('rpc-spinner');
+		}
+		else if (!spinnerSpinning && shouldBeSpinning)
+		{
+			// Delay spinner start to avoid costly relayout for short rpc queries
+			if (!spinnerStartIssued)
+			{
+				spinnerStartIssued = true;
+				setTimeout(function()
+				{
+					spinnerStartIssued = false;
+
+					var shouldBeSpinning = !_.isEmpty(pendingRequests);
+					if (shouldBeSpinning)
+					{
+						spinnerSpinning = true;
+						usSpinnerService.spin('rpc-spinner');
+					}
+				}, 300);
+			}
+		}
+	};
 
 	var call = function(method)
 	{
-		// Start spinner.
-		// \todo [petri] use data binding instead of explicit calls?
-		if (_.isEmpty(pendingRequests))
-			usSpinnerService.spin('rpc-spinner');
-
 		var requestId = nextRequestId++;
 		var params = Array.prototype.slice.call(arguments, 1)
 		console.log('[rpc] call ' + method + ': ' + debugStr(JSON.stringify(params)));
@@ -173,20 +202,21 @@
 		// \todo [petri] possible race condition?
 		var deferred = $q.defer();
 		pendingRequests[requestId] = deferred;
+
+		updateSpinner();
+
 		return deferred.promise;
 	};
 
 	var cast = function(method)
 	{
-		// Start spinner.
-		if (_.isEmpty(pendingRequests))
-			usSpinnerService.spin('rpc-spinner');
-
 		var requestId = nextRequestId++;
 		var params = Array.prototype.slice.call(arguments, 1)
-		socket.emit({ jsonrpc:"2.0", method:method, params:params, id:requestId });
+		socket.emit({ jsonrpc:"2.0", id:requestId, method:method, params:params });
 		pendingRequests[requestId] = $q.defer(); // not used!
 		// \todo [petri] don't create deferred object?
+
+		updateSpinner();
 	};
 
 	socket.onmessage(function(socket, args)
@@ -230,9 +260,7 @@
 				else
 					deferred.reject(args.error);
 
-				// Stop spinner, if all pending requests done.
-				if (_.isEmpty(pendingRequests))
-					usSpinnerService.stop('rpc-spinner');
+				updateSpinner();
 			}
 		}
 	});
diff --git a/client/partials/batchResult.html b/client/partials/batchResult.html
index 9e0246a..ab0fdf7 100644
--- a/client/partials/batchResult.html
+++ b/client/partials/batchResult.html
@@ -100,7 +100,7 @@
 
 		<!-- Test case result or test group view -->
 		<div id="testCaseContainer" style="float:left;">
-			<ui-view autoscroll="false" ng-if="isInitialized" />
+			<ui-view ng-if="isInitialized" />
 		</div>
 	</div>
 </div>
diff --git a/client/partials/batchResultComparison.html b/client/partials/batchResultComparison.html
index 71cee4e..48e8ae8 100644
--- a/client/partials/batchResultComparison.html
+++ b/client/partials/batchResultComparison.html
@@ -86,7 +86,7 @@
 			<div sized-by-window class="size-drag-bar" style="float:left;" size-drag-bar='{"left":"testCaseListContainer", "right":"testCaseContainer", "container":"mainPanelBody", "leftMinWidthRatio":0.2, "rightMinWidthRatio":0.2, "initialLeftWidthRatio":0.3}'></div>
 
 			<div id="testCaseContainer" style="float:left;">
-				<ui-view autoscroll="false" ng-if="isInitialized" />
+				<ui-view ng-if="isInitialized" />
 			</div>
 		</div>
 	</div>