Repository: mesonbuild/meson

Issue:
When a test failures due to termination by a signal report that in the log
I've been helping bring the [Korn shell](https://github.com/att/ast/) project into the 21st century. It is not unusual for a unit test to fail, with no output, due to the ksh binary dying from a signal (e.g., SIGSEGV). Meson correctly reports the test failed but the details in *meson-logs/testlog.txt* is ambiguous. It would greatly help if details about the unit test exit status were reported in the log. For example, "Test terminated with exit status 3" or "Test terminated due to signal SIGSEGV". If the test passes there is no need to report "Test terminated with exit status 0".


Discussion:
This makes sense, and is also an easy enhancement, would you like to work on it? :)

The relevant code is in `mesonbuild/mtest.py`.

Resolving patch:
diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py
--- a/mesonbuild/mtest.py
+++ b/mesonbuild/mtest.py
@@ -105,6 +105,32 @@ def buildparser():
     return parser
 
 
+def returncode_to_status(retcode):
+    # Note: We can't use `os.WIFSIGNALED(result.returncode)` and the related
+    # functions here because the status returned by subprocess is munged. It
+    # returns a negative value if the process was killed by a signal rather than
+    # the raw status returned by `wait()`. Also, If a shell sits between Meson
+    # the the actual unit test that shell is likely to convert a termination due
+    # to a signal into an exit status of 128 plus the signal number.
+    if retcode < 0:
+        signum = -retcode
+        try:
+            signame = signal.Signals(signum).name
+        except ValueError:
+            signame = 'SIGinvalid'
+        return '(killed by signal %d %s)' % (signum, signame)
+
+    if retcode <= 128:
+        return '(exit status %d)' % (retcode,)
+
+    signum = retcode - 128
+    try:
+        signame = signal.Signals(signum).name
+    except ValueError:
+        signame = 'SIGinvalid'
+    return '(exit status %d or signal %d %s)' % (retcode, signum, signame)
+
+
 class TestException(mesonlib.MesonException):
     pass
 
@@ -421,11 +447,16 @@ def print_stats(self, numlen, tests, name, result, i):
         num = '%s%d/%d' % (startpad, i + 1, len(tests))
         padding1 = ' ' * (38 - len(name))
         padding2 = ' ' * (8 - len(result.res.value))
-        result_str = '%s %s  %s%s%s%5.2f s' % \
-            (num, name, padding1, result.res.value, padding2, result.duration)
+        status = ''
+
+        if result.res is TestResult.FAIL:
+            status = returncode_to_status(result.returncode)
+        result_str = '%s %s  %s%s%s%5.2f s %s' % \
+            (num, name, padding1, result.res.value, padding2, result.duration,
+             status)
         if not self.options.quiet or result.res is not TestResult.OK:
             if result.res is not TestResult.OK and mlog.colorize_console:
-                if result.res is TestResult.FAIL or result.res is TestResult.TIMEOUT:
+                if result.res in (TestResult.FAIL, TestResult.TIMEOUT):
                     decorator = mlog.red
                 elif result.res is TestResult.SKIP:
                     decorator = mlog.yellow


Test patch:
diff --git a/test cases/failing test/2 signal/main.c b/test cases/failing test/2 signal/main.c
new file mode 100644
--- /dev/null
+++ b/test cases/failing test/2 signal/main.c	
@@ -0,0 +1,6 @@
+#include <signal.h>
+#include <unistd.h>
+
+int main(void) {
+    kill(getpid(), SIGSEGV);
+}
diff --git a/test cases/failing test/2 signal/meson.build b/test cases/failing test/2 signal/meson.build
new file mode 100644
--- /dev/null
+++ b/test cases/failing test/2 signal/meson.build	
@@ -0,0 +1,7 @@
+project('signal', 'c')
+
+if build_machine.system() == 'windows'
+    error('MESON_SKIP_TEST test is not compatible with MS Windows.')
+else
+    test('My Signal Test', executable('main', 'main.c'))
+endif
diff --git a/test cases/failing test/3 ambiguous/main.c b/test cases/failing test/3 ambiguous/main.c
new file mode 100644
--- /dev/null
+++ b/test cases/failing test/3 ambiguous/main.c	
@@ -0,0 +1,6 @@
+#include <signal.h>
+#include <unistd.h>
+
+int main(void) {
+    kill(getpid(), SIGSEGV);
+}
diff --git a/test cases/failing test/3 ambiguous/meson.build b/test cases/failing test/3 ambiguous/meson.build
new file mode 100644
--- /dev/null
+++ b/test cases/failing test/3 ambiguous/meson.build	
@@ -0,0 +1,10 @@
+project('ambiguous', 'c')
+
+if build_machine.system() == 'windows'
+    error('MESON_SKIP_TEST test is not compatible with MS Windows.')
+else
+    exe = executable('main', 'main.c')
+    test_runner = find_program('test_runner.sh')
+
+    test('My Ambiguous Status Test', test_runner, args : [exe.full_path()])
+endif
diff --git a/test cases/failing test/3 ambiguous/test_runner.sh b/test cases/failing test/3 ambiguous/test_runner.sh
new file mode 100755
--- /dev/null
+++ b/test cases/failing test/3 ambiguous/test_runner.sh	
@@ -0,0 +1,7 @@
+#!/bin/sh
+#
+# This tests that using a shell as an intermediary between Meson and the
+# actual unit test which dies due to a signal is still recorded correctly.
+#
+# The quotes are because the path may contain spaces.
+"$1"