kunit.py: handle timeout

Make kunit_kernel.py terminate process and append timeout status to log
on timeout.
Make kunit_parser.py handle timeout case when parsing and displaying
output.

Signed-off-by: Avi Kondareddy <avikr@google.com>
Google-Bug-Id: 116318265
Change-Id: Ieb4190586e69c70b3c754d21e40f7e80593323d5
diff --git a/tools/testing/kunit/kunit_kernel.py b/tools/testing/kunit/kunit_kernel.py
index c1259b1..5ef8fcd 100644
--- a/tools/testing/kunit/kunit_kernel.py
+++ b/tools/testing/kunit/kunit_kernel.py
@@ -50,8 +50,13 @@
 			stdin=subprocess.PIPE,
 			stdout=subprocess.PIPE,
 			stderr=subprocess.PIPE)
-		process.wait(timeout=timeout)
-		return process
+		timed_out = False
+		try:
+			process.wait(timeout=timeout)
+		except subprocess.TimeoutExpired:
+			process.terminate()
+			timed_out = True
+		return process, timed_out
 
 
 class LinuxSourceTree(object):
@@ -115,8 +120,11 @@
 
 	def run_kernel(self, args=[], timeout=None):
 		args.extend(['mem=256M'])
-		process = self._ops.linux_bin(args, timeout)
+		process, timed_out = self._ops.linux_bin(args, timeout)
 		with open('test.log', 'w') as f:
 			for line in process.stdout:
 				f.write(line.rstrip().decode('ascii') + '\n')
 				yield line.rstrip().decode('ascii')
+			if timed_out:
+				f.write('Timeout Reached - Process Terminated\n')
+				yield 'Timeout Reached - Process Terminated\n'
diff --git a/tools/testing/kunit/kunit_parser.py b/tools/testing/kunit/kunit_parser.py
index 67ffb7b..d94b144 100644
--- a/tools/testing/kunit/kunit_parser.py
+++ b/tools/testing/kunit/kunit_parser.py
@@ -63,6 +63,7 @@
 
 	current_case_log = []
 	did_kernel_crash = False
+	did_timeout = False
 
 	def end_one_test(match, log):
 		log.clear()
@@ -106,6 +107,11 @@
 				end_one_test(match, current_case_log)
 				continue
 
+			if line == 'Timeout Reached - Process Terminated\n':
+				yield timestamp(red("[TIMED-OUT] Process Terminated"))
+				did_timeout = True
+				break
+
 			# Strip off the `kunit module-name:` prefix
 			match = re.match(test_case_output, line)
 			if match:
@@ -124,9 +130,12 @@
 		yield timestamp(DIVIDER)
 
 	fmt = green if (len(failed_tests) + len(crashed_tests) == 0
-			and not did_kernel_crash) else red
-	message = ("Before the crash:" if did_kernel_crash else
-		   "Testing complete.")
+			and not did_kernel_crash and not did_timeout) else red
+	message = "Testing complete."
+	if did_kernel_crash:
+		message = "Before the crash:"
+	elif did_timeout:
+		message = "Before timing out:"
 
 	yield timestamp(
 		fmt(message + " %d tests run. %d failed. %d crashed." %
diff --git a/tools/testing/kunit/kunit_test.py b/tools/testing/kunit/kunit_test.py
index 288d705..65729a3 100755
--- a/tools/testing/kunit/kunit_test.py
+++ b/tools/testing/kunit/kunit_test.py
@@ -150,5 +150,15 @@
 			result)
 		file.close()
 
+	def test_timed_out_test(self):
+		timed_out_log = get_absolute_path(
+			'test_data/test_is_test_passed-timed_out.log')
+		file = open(timed_out_log)
+		result = kunit_parser.parse_run_tests(file.readlines())
+		self.assertContains(
+			'Before timing out: 3 tests run. 0 failed. 0 crashed.',
+			result)
+		file.close()
+
 if __name__ == '__main__':
-	unittest.main()
\ No newline at end of file
+	unittest.main()
diff --git a/tools/testing/kunit/test_data/test_is_test_passed-timed_out.log b/tools/testing/kunit/test_data/test_is_test_passed-timed_out.log
new file mode 100644
index 0000000..8957128
--- /dev/null
+++ b/tools/testing/kunit/test_data/test_is_test_passed-timed_out.log
@@ -0,0 +1,13 @@
+Console initialized on /dev/tty0
+console [tty0] enabled
+kunit example: initializing
+kunit example: example_simple_test passed
+kunit example: exiting
+kunit example: initializing
+kunit example: example_mock_test passed
+kunit example: exiting
+kunit example: all tests passed
+dummy 0-0053: hwmon0: sensor 'lm75'
+kunit lm75: lm75_test_read passed
+kunit lm75: all tests passed
+Timeout Reached - Process Terminated