[testing] Fix signal handling on Windows.
See https://bugs.chromium.org/p/chromium/issues/detail?id=733612#c6
for details.
Also add tests both for windows and non-windows.
Bug: 733612
Change-Id: Id656635a5fc79ea8f805658873599b526e764406
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1796572
Reviewed-by: Dirk Pranke <[email protected]>
Reviewed-by: John Chen <[email protected]>
Commit-Queue: Caleb Rouleau <[email protected]>
Cr-Commit-Position: refs/heads/master@{#695429}
diff --git a/testing/test_env_unittest.py b/testing/test_env_unittest.py
new file mode 100755
index 0000000..76d5766
--- /dev/null
+++ b/testing/test_env_unittest.py
@@ -0,0 +1,85 @@
+#!/usr/bin/env python
+# Copyright (c) 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Unit tests for test_env.py functionality.
+
+Each unit test is launches python process that uses test_env.py
+to launch another python process. Then signal handling and
+propagation is tested. This similates how Swarming uses test_env.py.
+"""
+
+import os
+import signal
+import subprocess
+import sys
+import time
+import unittest
+
+
+TEST_SCRIPT = 'test_env_user_script.py'
+
+
+def launch_process_windows(args):
+ return subprocess.Popen(
+ [sys.executable, TEST_SCRIPT] + args, stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT, env=os.environ.copy(),
+ creationflags=subprocess.CREATE_NEW_PROCESS_GROUP)
+
+def launch_process_nonwindows(args):
+ return subprocess.Popen(
+ [sys.executable, TEST_SCRIPT] + args, stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT, env=os.environ.copy())
+
+
+def read_subprocess_message(proc, starts_with):
+ """Finds the value after first line prefix condition."""
+ for line in proc.stdout:
+ if line.startswith(starts_with):
+ return line.rstrip().replace(starts_with, '')
+
+
+def send_and_wait(proc, sig, sleep_time=0.3):
+ """Sends a signal to subprocess."""
+ time.sleep(sleep_time) # gives process time to launch.
+ os.kill(proc.pid, sig)
+ proc.wait()
+
+
+class SignalingWindowsTest(unittest.TestCase):
+
+ def setUp(self):
+ super(SignalingWindowsTest, self).setUp()
+ if sys.platform != 'win32':
+ self.skipTest('test only runs on Windows')
+
+ def test_send_ctrl_break_event(self):
+ proc = launch_process_windows([])
+ send_and_wait(proc, signal.CTRL_BREAK_EVENT)
+ sig = read_subprocess_message(proc, 'Signal :')
+ self.assertEqual(sig, str(signal.SIGBREAK))
+
+
+class SignalingNonWindowsTest(unittest.TestCase):
+
+ def setUp(self):
+ super(SignalingNonWindowsTest, self).setUp()
+ if sys.platform == 'win32':
+ self.skipTest('test does not run on Windows')
+
+ def test_send_sigterm(self):
+ proc = launch_process_nonwindows([])
+ send_and_wait(proc, signal.SIGTERM)
+ sig = read_subprocess_message(proc, 'Signal :')
+ self.assertEqual(sig, str(signal.SIGTERM))
+
+ def test_send_sigint(self):
+ proc = launch_process_nonwindows([])
+ send_and_wait(proc, signal.SIGINT)
+ sig = read_subprocess_message(proc, 'Signal :')
+ self.assertEqual(sig, str(signal.SIGINT))
+
+
+if __name__ == '__main__':
+ unittest.main()