• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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