Repository: mesonbuild/meson

Issue:
testing: timeout behavior
when testing via `meson test mytestcase` on meson 0.42.0, I am seeing 2 issues:
* stdout does not appear to be included in `build/meson-logs/testlog.txt` if the test times out. If it passes or fails, it is included as expected, and stderr is included in all cases.
* I haven't found a convenient way to kill the test executable when running the meson test harness. ctrl-C kills the `meson test` parent process, but does not appear to kill the test executable.


Discussion:
> I haven't found a convenient way to kill the test executable when running the meson test harness. ctrl-C kills the meson test parent process, but does not appear to kill the test executable.

This is a bug, we should forward SIGINT to child processes in `mesontest.py`
This was fixed a long time ago in #377 - I hope a new fix can make it into the next release. I have tests that sometimes get stuck and I need this to be working. I wonder when it broke?
We should add a unit test when we fix it again.
Is this still a problem with 0.44+? I was trying to reproduce meson not killing children but I **think** it's working. 

This code looks a bit fishy though...

```python
            if not self.options.verbose:
                stdout = subprocess.PIPE
                stderr = subprocess.PIPE if self.options and self.options.split else subprocess.STDOUT

                if not is_windows():
                    setsid = os.setsid

            p = subprocess.Popen(cmd,
                                 stdout=stdout,
                                 stderr=stderr,
                                 env=child_env,
                                 cwd=test.workdir,
                                 preexec_fn=setsid)
```

This is only setting the `setsid` variable if it's not in verbose mode.
scratch that - confirmed, it's still an issue.

Ok, it seems that the timeout is working as expected. But when you `ctrl-c`, it does not work - the children still survive.

UPDATE: I know this isn't a real fix but it confirms the children are properly killed when you ctrl-C.

```diff
diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py
index 2032e05..7dc8a3e 100644
--- a/mesonbuild/mtest.py
+++ b/mesonbuild/mtest.py
@@ -257,7 +257,7 @@ class TestHarness:
                 timeout = test.timeout * self.options.timeout_multiplier
             try:
                 (stdo, stde) = p.communicate(timeout=timeout)
-            except subprocess.TimeoutExpired:
+            except (subprocess.TimeoutExpired, KeyboardInterrupt):
                 if self.options.verbose:
                     print("%s time out (After %d seconds)" % (test.name, timeout))
                 timed_out = True
```

Resolving patch:
diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py
--- a/mesonbuild/mtest.py
+++ b/mesonbuild/mtest.py
@@ -241,8 +241,8 @@ def run_single_test(self, wrap, test):
                 stdout = subprocess.PIPE
                 stderr = subprocess.PIPE if self.options and self.options.split else subprocess.STDOUT
 
-                if not is_windows():
-                    setsid = os.setsid
+            if not is_windows():
+                setsid = os.setsid
 
             p = subprocess.Popen(cmd,
                                  stdout=stdout,
@@ -251,6 +251,7 @@ def run_single_test(self, wrap, test):
                                  cwd=test.workdir,
                                  preexec_fn=setsid)
             timed_out = False
+            kill_test = False
             if test.timeout is None:
                 timeout = None
             else:
@@ -261,6 +262,11 @@ def run_single_test(self, wrap, test):
                 if self.options.verbose:
                     print("%s time out (After %d seconds)" % (test.name, timeout))
                 timed_out = True
+            except KeyboardInterrupt:
+                mlog.warning("CTRL-C detected while running %s" % (test.name))
+                kill_test = True
+
+            if kill_test or timed_out:
                 # Python does not provide multiplatform support for
                 # killing a process and all its children so we need
                 # to roll our own.


Test patch: