kunit: check if kernel crashed during kunit

- outputs error message with most recent log if KUnit crashes
unexpectedly or does not complete

Change-Id: Ic5677ab3a4fe215b9ba7c69e1d1e63a903a872af
Signed-off-by: Felix Guo <felixguo@google.com>
diff --git a/tools/testing/kunit/kunit_parser.py b/tools/testing/kunit/kunit_parser.py
index 6040db0..0ec2299 100644
--- a/tools/testing/kunit/kunit_parser.py
+++ b/tools/testing/kunit/kunit_parser.py
@@ -4,15 +4,21 @@
 kunit_start_re = re.compile('console .* enabled')
 kunit_end_re = re.compile('List of all partitions:')
 
+class KernelCrashException(Exception):
+	pass
+
 def isolate_kunit_output(kernel_output):
 	started = False
 	for line in kernel_output:
 		if kunit_start_re.match(line):
 			started = True
 		elif kunit_end_re.match(line):
-			break
+			return
 		elif started:
 			yield line
+	# Output ended without encountering end marker, kernel probably panicked
+	# or crashed unexpectedly.
+	raise KernelCrashException()
 
 def raw_output(kernel_output):
 	for line in kernel_output:
@@ -56,56 +62,73 @@
 		return match.group(1) + ":" + match.group(2)
 
 	current_case_log = []
+	did_kernel_crash = False
+
 	def end_one_test(match, log):
 		log.clear()
 		total_tests.add(get_test_name(match))
 
 	print_with_timestamp(DIVIDER)
-	for line in isolate_kunit_output(kernel_output):
-		# Ignore module output:
-		if (test_module_success.match(line) or
-		    test_module_fail.match(line)):
-			print_with_timestamp(DIVIDER)
-			continue
+	try:
+		for line in isolate_kunit_output(kernel_output):
+			# Ignore module output:
+			if (test_module_success.match(line) or
+			test_module_fail.match(line)):
+				print_with_timestamp(DIVIDER)
+				continue
 
-		match = re.match(test_case_success, line)
-		if match:
-			print_with_timestamp(green("[PASSED] ") +
-					     get_test_name(match))
-			end_one_test(match, current_case_log)
-			continue
+			match = re.match(test_case_success, line)
+			if match:
+				print_with_timestamp(green("[PASSED] ") +
+						get_test_name(match))
+				end_one_test(match, current_case_log)
+				continue
 
-		match = re.match(test_case_fail, line)
-		# Crashed tests will report as both failed and crashed. We only
-		# want to show and count it once.
-		if match and get_test_name(match) not in crashed_tests:
-			failed_tests.add(get_test_name(match))
-			print_with_timestamp(red("[FAILED] " +
-						 get_test_name(match)))
-			print_log(map(yellow, current_case_log))
-			print_with_timestamp("")
-			end_one_test(match, current_case_log)
-			continue
+			match = re.match(test_case_fail, line)
+			# Crashed tests will report as both failed and crashed. We only
+			# want to show and count it once.
+			if match and get_test_name(match) not in crashed_tests:
+				failed_tests.add(get_test_name(match))
+				print_with_timestamp(red("[FAILED] " +
+							get_test_name(match)))
+				print_log(map(yellow, current_case_log))
+				print_with_timestamp("")
+				end_one_test(match, current_case_log)
+				continue
 
-		match = re.match(test_case_crash, line)
-		if match:
-			crashed_tests.add(get_test_name(match))
-			print_with_timestamp(yellow("[CRASH] " +
-						    get_test_name(match)))
-			print_log(current_case_log)
-			print_with_timestamp("")
-			end_one_test(match, current_case_log)
-			continue
+			match = re.match(test_case_crash, line)
+			if match:
+				crashed_tests.add(get_test_name(match))
+				print_with_timestamp(yellow("[CRASH] " +
+							get_test_name(match)))
+				print_log(current_case_log)
+				print_with_timestamp("")
+				end_one_test(match, current_case_log)
+				continue
 
-		# Strip off the `kunit module-name:` prefix
-		match = re.match(test_case_output, line)
-		if match:
-			current_case_log.append(match.group(1))
-		else:
-			current_case_log.append(line)
+			# Strip off the `kunit module-name:` prefix
+			match = re.match(test_case_output, line)
+			if match:
+				current_case_log.append(match.group(1))
+			else:
+				current_case_log.append(line)
+	except KernelCrashException:
+		did_kernel_crash = True
+		print_with_timestamp(
+			red("The KUnit kernel crashed unexpectedly and was " +
+			    "unable to finish running tests!"))
+		print_with_timestamp(red("These are the logs from the most " +
+					 "recently running test:"))
+		print_with_timestamp(DIVIDER)
+		print_log(current_case_log)
+		print_with_timestamp(DIVIDER)
 
-	fmt = green if (len(failed_tests) + len(crashed_tests) == 0) else red
+	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.")
+
 	print_with_timestamp(
-		fmt("Testing complete. %d tests run. %d failed. %d crashed." %
+		fmt(message + " %d tests run. %d failed. %d crashed." %
 		    (len(total_tests), len(failed_tests), len(crashed_tests))))