Run-test: Execute the diff commands just once.

Run the diff once, and let the test fail as per default behaviour.

Add nice error message for that case.

Test: make a diff intentionally fail
Change-Id: I836a0ec1c01a41599c401f57f0b7da42df975673
diff --git a/test/run-test b/test/run-test
index dbba020..9c06a6a 100755
--- a/test/run-test
+++ b/test/run-test
@@ -19,8 +19,9 @@
 import etc.default_run
 
 from importlib.machinery import SourceFileLoader
+from inspect import currentframe, getframeinfo, FrameInfo
 from pathlib import Path
-from inspect import currentframe, getframeinfo
+from typing import Optional
 
 COLOR_RED = '\033[91m'
 COLOR_NORMAL = '\033[0m'
@@ -70,6 +71,13 @@
     tmp_dir = f"{TMPDIR}/{test_dir}"
   checker = f"{progdir}/../tools/checker/checker.py"
 
+  def fail(message: str, caller:Optional[FrameInfo]=None):
+    caller = caller or getframeinfo(currentframe().f_back)  # type: ignore
+    source = "{}:{}".format(Path(caller.filename).relative_to(oldwd), caller.lineno)
+    print(f"\n{COLOR_RED}{TEST_NAME} FAILED: [{source}] {message}{COLOR_NORMAL}",
+          file=sys.stderr)
+    sys.exit(1)
+
   def run(cmdline: str,
           check=True,
           fail_message=None,
@@ -85,10 +93,7 @@
     if (check and proc.returncode != 0):
       if fail_message:
         # If we have custom fail message, exit without printing the full backtrace.
-        caller = getframeinfo(currentframe().f_back)   # type: ignore
-        source = f"{caller.filename}:{caller.lineno}"  # Last line of backtrace.
-        print(f"Command failed: {fail_message} ({source})", file=sys.stderr)
-        sys.exit(1)
+        fail(fail_message, getframeinfo(currentframe().f_back))  # type: ignore
       raise Exception(f"Command failed (exit code {proc.returncode})")
     return proc
 
@@ -951,10 +956,9 @@
   Path(test_stdout).touch()
   Path(test_stderr).touch()
 
-  good = "no"
   export("TEST_RUNTIME", runtime)
 
-  verbose(f"{test_dir}: running...")
+  print(f"{test_dir}: running...")
   run_test_script()
   # NB: There is no exit code or return value.
   # Failing tests just raise python exception.
@@ -962,39 +966,28 @@
   if update_mode == "yes":
     run(f'''sed -e 's/[[:cntrl:]]$//g' <"{test_stdout}" >"{td_expected_stdout}"''')
     run(f'''sed -e 's/[[:cntrl:]]$//g' <"{test_stderr}" >"{td_expected_stderr}"''')
-    good = "yes"
 
-  proc = run(
-    f'diff --strip-trailing-cr -u "{expected_stdout}" "{test_stdout}" &&'
-    f'diff --strip-trailing-cr -u "{expected_stderr}" "{test_stderr}"',
-    check=False  # Don't crash on non-zero exit code.
-  )
-  if proc.returncode == 0:
-    good = "yes"
-    verbose(f"${test_dir}: succeeded!")
-
-  if good != "yes" and update_mode != "yes":
-    error(f"{test_dir}: FAILED!")
-    error(" ")
-    error("#################### info")
-    run(f'cat "{td_info}" | sed "s/^/# /g"')
-    error("#################### stdout diffs")
-    run(f'diff --strip-trailing-cr -u "{expected_stdout}" "{test_stdout}" | tail -n 10000', check=False)
-    error("####################")
-    error("#################### stderr diffs")
-    run(f'diff --strip-trailing-cr -u "{expected_stderr}" "{test_stderr}" | tail -n 10000', check=False)
-    error("####################")
-    if strace == "yes":
-      error("#################### strace output")
-      run(f'tail -n 3000 "{tmp_dir}/{strace_output}"')
-      error("####################")
-    SANITIZE_HOST = os.environ.get("SANITIZE_HOST")
-    if target_mode == "no" and SANITIZE_HOST == "address":
-      # Run the stack script to symbolize any ASAN aborts on the host for SANITIZE_HOST. The
-      # tools used by the given ABI work for both x86 and x86-64.
-      run(f'''echo "ABI: 'x86_64'" | cat - "{test_stdout}" "{test_stderr}"'''
-          f"""| {ANDROID_BUILD_TOP}/development/scripts/stack | tail -n 3000""")
-    error(" ")
+  print("#################### info")
+  run(f'cat "{td_info}" | sed "s/^/# /g"')
+  print("#################### stdout diff")
+  proc_out = run(f'diff --strip-trailing-cr -u "{expected_stdout}" "{test_stdout}"', check=False)
+  print("#################### stderr diff")
+  proc_err = run(f'diff --strip-trailing-cr -u "{expected_stderr}" "{test_stderr}"', check=False)
+  if strace == "yes":
+    print("#################### strace output (trimmed to 3000 lines)")
+    run(f'tail -n 3000 "{tmp_dir}/{strace_output}"')
+  SANITIZE_HOST = os.environ.get("SANITIZE_HOST")
+  if target_mode == "no" and SANITIZE_HOST == "address":
+    # Run the stack script to symbolize any ASAN aborts on the host for SANITIZE_HOST. The
+    # tools used by the given ABI work for both x86 and x86-64.
+    print("#################### symbolizer (trimmed to 3000 lines)")
+    run(f'''echo "ABI: 'x86_64'" | cat - "{test_stdout}" "{test_stderr}"'''
+        f"""| {ANDROID_BUILD_TOP}/development/scripts/stack | tail -n 3000""")
+  print("####################", flush=True)
+  if proc_out.returncode != 0 or proc_err.returncode != 0:
+    kind = ((["stdout"] if proc_out.returncode != 0 else []) +
+            (["stderr"] if proc_err.returncode != 0 else []))
+    fail("{} did not match the expected file".format(" and ".join(kind)))
 
   if run_checker == "yes":
     if target_mode == "yes":
@@ -1011,13 +1004,11 @@
       run(f"cp {cfg_output_dir}/{cfg_output} {dump_cfg_path}")
 
 # Clean up test files.
-  if (always_clean == "yes" or good == "yes") and never_clean == "no":
+  if always_clean == "yes" and never_clean == "no":
     os.chdir(oldwd)
     shutil.rmtree(tmp_dir)
     if target_mode == "yes":
       run(f"adb shell rm -rf {chroot_dex_location}")
-    if good == "yes":
-      sys.exit(0)
     verbose(f"{TEST_NAME} files deleted from host ")
     if target_mode == "yes":
       verbose("and from target")
@@ -1025,8 +1016,3 @@
     verbose(f"{TEST_NAME} files left in ${tmp_dir} on host")
     if target_mode == "yes":
       verbose("and in ${chroot_dex_location} on target")
-
-  if never_clean == "yes" and good == "yes":
-    sys.exit(0)
-  else:
-    sys.exit(1)