#!/usr/bin/env python3 # Copyright 2019 The Chromium Authors # 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 HERE = os.path.dirname(os.path.abspath(__file__)) TEST_SCRIPT = os.path.join(HERE, 'test_env_user_script.py') def launch_process_windows(args): # The `universal_newlines` option is equivalent to `text` in Python 3. return subprocess.Popen( [sys.executable, TEST_SCRIPT] + args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=os.environ.copy(), creationflags=subprocess.CREATE_NEW_PROCESS_GROUP, universal_newlines=True) def launch_process_nonwindows(args): # The `universal_newlines` option is equivalent to `text` in Python 3. return subprocess.Popen( [sys.executable, TEST_SCRIPT] + args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=os.environ.copy(), universal_newlines=True) # pylint: disable=inconsistent-return-statements 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, '') # pylint: enable=inconsistent-return-statements def send_and_wait(proc, sig, sleep_time=0.6): """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().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) # pylint: disable=no-member sig = read_subprocess_message(proc, 'Signal :') # This test is flaky because it relies on the child process starting quickly # "enough", which it fails to do sometimes. This is tracked by # https://crbug.com/1335123 and it is hoped that increasing the timeout will # reduce the flakiness. self.assertEqual(sig, str(int(signal.SIGBREAK))) # pylint: disable=no-member class SignalingNonWindowsTest(unittest.TestCase): def setUp(self): super().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(int(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(int(signal.SIGINT))) if __name__ == '__main__': unittest.main()