1#!/usr/bin/env python3 2# Copyright 2019 The Chromium Authors 3# Use of this source code is governed by a BSD-style license that can be 4# found in the LICENSE file. 5 6"""Unit tests for test_env.py functionality. 7 8Each unit test is launches python process that uses test_env.py 9to launch another python process. Then signal handling and 10propagation is tested. This similates how Swarming uses test_env.py. 11""" 12 13import os 14import signal 15import subprocess 16import sys 17import time 18import unittest 19 20HERE = os.path.dirname(os.path.abspath(__file__)) 21TEST_SCRIPT = os.path.join(HERE, 'test_env_user_script.py') 22 23 24def launch_process_windows(args): 25 # The `universal_newlines` option is equivalent to `text` in Python 3. 26 return subprocess.Popen( 27 [sys.executable, TEST_SCRIPT] + args, 28 stdout=subprocess.PIPE, 29 stderr=subprocess.STDOUT, 30 env=os.environ.copy(), 31 creationflags=subprocess.CREATE_NEW_PROCESS_GROUP, 32 universal_newlines=True) 33 34 35def launch_process_nonwindows(args): 36 # The `universal_newlines` option is equivalent to `text` in Python 3. 37 return subprocess.Popen( 38 [sys.executable, TEST_SCRIPT] + args, 39 stdout=subprocess.PIPE, 40 stderr=subprocess.STDOUT, 41 env=os.environ.copy(), 42 universal_newlines=True) 43 44 45# pylint: disable=inconsistent-return-statements 46def read_subprocess_message(proc, starts_with): 47 """Finds the value after first line prefix condition.""" 48 for line in proc.stdout: 49 if line.startswith(starts_with): 50 return line.rstrip().replace(starts_with, '') 51# pylint: enable=inconsistent-return-statements 52 53 54def send_and_wait(proc, sig, sleep_time=0.6): 55 """Sends a signal to subprocess.""" 56 time.sleep(sleep_time) # gives process time to launch. 57 os.kill(proc.pid, sig) 58 proc.wait() 59 60 61class SignalingWindowsTest(unittest.TestCase): 62 63 def setUp(self): 64 super().setUp() 65 if sys.platform != 'win32': 66 self.skipTest('test only runs on Windows') 67 68 def test_send_ctrl_break_event(self): 69 proc = launch_process_windows([]) 70 send_and_wait(proc, signal.CTRL_BREAK_EVENT) # pylint: disable=no-member 71 sig = read_subprocess_message(proc, 'Signal :') 72 # This test is flaky because it relies on the child process starting quickly 73 # "enough", which it fails to do sometimes. This is tracked by 74 # https://crbug.com/1335123 and it is hoped that increasing the timeout will 75 # reduce the flakiness. 76 self.assertEqual(sig, str(int(signal.SIGBREAK))) # pylint: disable=no-member 77 78 79class SignalingNonWindowsTest(unittest.TestCase): 80 81 def setUp(self): 82 super().setUp() 83 if sys.platform == 'win32': 84 self.skipTest('test does not run on Windows') 85 86 def test_send_sigterm(self): 87 proc = launch_process_nonwindows([]) 88 send_and_wait(proc, signal.SIGTERM) 89 sig = read_subprocess_message(proc, 'Signal :') 90 self.assertEqual(sig, str(int(signal.SIGTERM))) 91 92 def test_send_sigint(self): 93 proc = launch_process_nonwindows([]) 94 send_and_wait(proc, signal.SIGINT) 95 sig = read_subprocess_message(proc, 'Signal :') 96 self.assertEqual(sig, str(int(signal.SIGINT))) 97 98 99if __name__ == '__main__': 100 unittest.main() 101