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