1#!/usr/bin/env python 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"""Unit tests for xvfb.py functionality. 6 7Each unit test is launching xvfb_test_script.py 8through xvfb.py as a subprocess, then tests its expected output. 9""" 10 11import os 12import signal 13import subprocess 14import sys 15import time 16import unittest 17 18# pylint: disable=super-with-arguments 19 20TEST_FILE = __file__.replace('.pyc', '.py') 21XVFB = TEST_FILE.replace('_unittest', '') 22XVFB_TEST_SCRIPT = TEST_FILE.replace('_unittest', '_test_script') 23 24 25def launch_process(args): 26 """Launches a sub process to run through xvfb.py.""" 27 return subprocess.Popen([XVFB, XVFB_TEST_SCRIPT] + args, 28 stdout=subprocess.PIPE, 29 stderr=subprocess.STDOUT, 30 env=os.environ.copy()) 31 32 33# pylint: disable=inconsistent-return-statements 34def read_subprocess_message(proc, starts_with): 35 """Finds the value after first line prefix condition.""" 36 for line in proc.stdout.read().decode('utf-8').splitlines(True): 37 if str(line).startswith(starts_with): 38 return line.rstrip().replace(starts_with, '') 39 40 41# pylint: enable=inconsistent-return-statements 42 43 44def send_signal(proc, sig, sleep_time=0.3): 45 """Sends a signal to subprocess.""" 46 time.sleep(sleep_time) # gives process time to launch. 47 os.kill(proc.pid, sig) 48 proc.wait() 49 50 51class XvfbLinuxTest(unittest.TestCase): 52 53 def setUp(self): 54 super(XvfbLinuxTest, self).setUp() 55 if not sys.platform.startswith('linux'): 56 self.skipTest('linux only test') 57 self._procs = [] 58 59 def test_no_xvfb_display(self): 60 self._procs.append(launch_process(['--no-xvfb'])) 61 self._procs[0].wait() 62 display = read_subprocess_message(self._procs[0], 'Display :') 63 self.assertEqual(display, os.environ.get('DISPLAY', 'None')) 64 65 def test_xvfb_display(self): 66 self._procs.append(launch_process([])) 67 self._procs[0].wait() 68 display = read_subprocess_message(self._procs[0], 'Display :') 69 self.assertIsNotNone(display) # Openbox likely failed to open DISPLAY 70 self.assertNotEqual(display, os.environ.get('DISPLAY', 'None')) 71 72 def test_no_xvfb_flag(self): 73 self._procs.append(launch_process(['--no-xvfb'])) 74 self._procs[0].wait() 75 76 def test_xvfb_flag(self): 77 self._procs.append(launch_process([])) 78 self._procs[0].wait() 79 80 @unittest.skip('flaky; crbug.com/1320399') 81 def test_xvfb_race_condition(self): 82 self._procs = [launch_process([]) for _ in range(15)] 83 for proc in self._procs: 84 proc.wait() 85 display_list = [ 86 read_subprocess_message(p, 'Display :') for p in self._procs 87 ] 88 for display in display_list: 89 self.assertIsNotNone(display) # Openbox likely failed to open DISPLAY 90 self.assertNotEqual(display, os.environ.get('DISPLAY', 'None')) 91 92 def tearDown(self): 93 super(XvfbLinuxTest, self).tearDown() 94 for proc in self._procs: 95 if proc.stdout: 96 proc.stdout.close() 97 98 99class XvfbTest(unittest.TestCase): 100 101 def setUp(self): 102 super(XvfbTest, self).setUp() 103 if sys.platform == 'win32': 104 self.skipTest('non-win32 test') 105 self._proc = None 106 107 def test_send_sigint(self): 108 self._proc = launch_process(['--sleep']) 109 # Give time for subprocess to install signal handlers 110 time.sleep(.3) 111 send_signal(self._proc, signal.SIGINT, 1) 112 sig = read_subprocess_message(self._proc, 'Signal :') 113 self.assertIsNotNone(sig) # OpenBox likely failed to start 114 self.assertEqual(int(sig), int(signal.SIGINT)) 115 116 def test_send_sigterm(self): 117 self._proc = launch_process(['--sleep']) 118 # Give time for subprocess to install signal handlers 119 time.sleep(.3) 120 send_signal(self._proc, signal.SIGTERM, 1) 121 sig = read_subprocess_message(self._proc, 'Signal :') 122 self.assertIsNotNone(sig) # OpenBox likely failed to start 123 self.assertEqual(int(sig), int(signal.SIGTERM)) 124 125 def tearDown(self): 126 super(XvfbTest, self).tearDown() 127 if self._proc.stdout: 128 self._proc.stdout.close() 129 130 131if __name__ == '__main__': 132 unittest.main() 133