1# Copyright 2016 gRPC authors. 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14"""Tests clean exit of server/client on Python Interpreter exit/sigint. 15 16The tests in this module spawn a subprocess for each test case, the 17test is considered successful if it doesn't hang/timeout. 18""" 19 20import atexit 21import os 22import signal 23import six 24import subprocess 25import sys 26import threading 27import time 28import unittest 29import logging 30 31from tests.unit import _exit_scenarios 32 33SCENARIO_FILE = os.path.abspath( 34 os.path.join(os.path.dirname(os.path.realpath(__file__)), 35 '_exit_scenarios.py')) 36INTERPRETER = sys.executable 37BASE_COMMAND = [INTERPRETER, SCENARIO_FILE] 38BASE_SIGTERM_COMMAND = BASE_COMMAND + ['--wait_for_interrupt'] 39 40INIT_TIME = 1.0 41 42processes = [] 43process_lock = threading.Lock() 44 45 46# Make sure we attempt to clean up any 47# processes we may have left running 48def cleanup_processes(): 49 with process_lock: 50 for process in processes: 51 try: 52 process.kill() 53 except Exception: # pylint: disable=broad-except 54 pass 55 56 57atexit.register(cleanup_processes) 58 59 60def interrupt_and_wait(process): 61 with process_lock: 62 processes.append(process) 63 time.sleep(INIT_TIME) 64 os.kill(process.pid, signal.SIGINT) 65 process.wait() 66 67 68def wait(process): 69 with process_lock: 70 processes.append(process) 71 process.wait() 72 73 74class ExitTest(unittest.TestCase): 75 76 def test_unstarted_server(self): 77 process = subprocess.Popen(BASE_COMMAND + 78 [_exit_scenarios.UNSTARTED_SERVER], 79 stdout=sys.stdout, 80 stderr=sys.stderr) 81 wait(process) 82 83 def test_unstarted_server_terminate(self): 84 process = subprocess.Popen(BASE_SIGTERM_COMMAND + 85 [_exit_scenarios.UNSTARTED_SERVER], 86 stdout=sys.stdout) 87 interrupt_and_wait(process) 88 89 def test_running_server(self): 90 process = subprocess.Popen(BASE_COMMAND + 91 [_exit_scenarios.RUNNING_SERVER], 92 stdout=sys.stdout, 93 stderr=sys.stderr) 94 wait(process) 95 96 def test_running_server_terminate(self): 97 process = subprocess.Popen(BASE_SIGTERM_COMMAND + 98 [_exit_scenarios.RUNNING_SERVER], 99 stdout=sys.stdout, 100 stderr=sys.stderr) 101 interrupt_and_wait(process) 102 103 def test_poll_connectivity_no_server(self): 104 process = subprocess.Popen( 105 BASE_COMMAND + [_exit_scenarios.POLL_CONNECTIVITY_NO_SERVER], 106 stdout=sys.stdout, 107 stderr=sys.stderr) 108 wait(process) 109 110 def test_poll_connectivity_no_server_terminate(self): 111 process = subprocess.Popen( 112 BASE_SIGTERM_COMMAND + 113 [_exit_scenarios.POLL_CONNECTIVITY_NO_SERVER], 114 stdout=sys.stdout, 115 stderr=sys.stderr) 116 interrupt_and_wait(process) 117 118 def test_poll_connectivity(self): 119 process = subprocess.Popen(BASE_COMMAND + 120 [_exit_scenarios.POLL_CONNECTIVITY], 121 stdout=sys.stdout, 122 stderr=sys.stderr) 123 wait(process) 124 125 def test_poll_connectivity_terminate(self): 126 process = subprocess.Popen(BASE_SIGTERM_COMMAND + 127 [_exit_scenarios.POLL_CONNECTIVITY], 128 stdout=sys.stdout, 129 stderr=sys.stderr) 130 interrupt_and_wait(process) 131 132 @unittest.skipIf(os.name == 'nt', 133 'os.kill does not have required permission on Windows') 134 def test_in_flight_unary_unary_call(self): 135 process = subprocess.Popen(BASE_COMMAND + 136 [_exit_scenarios.IN_FLIGHT_UNARY_UNARY_CALL], 137 stdout=sys.stdout, 138 stderr=sys.stderr) 139 interrupt_and_wait(process) 140 141 @unittest.skipIf(six.PY2, 'https://github.com/grpc/grpc/issues/6999') 142 @unittest.skipIf(os.name == 'nt', 143 'os.kill does not have required permission on Windows') 144 def test_in_flight_unary_stream_call(self): 145 process = subprocess.Popen( 146 BASE_COMMAND + [_exit_scenarios.IN_FLIGHT_UNARY_STREAM_CALL], 147 stdout=sys.stdout, 148 stderr=sys.stderr) 149 interrupt_and_wait(process) 150 151 @unittest.skipIf(os.name == 'nt', 152 'os.kill does not have required permission on Windows') 153 def test_in_flight_stream_unary_call(self): 154 process = subprocess.Popen( 155 BASE_COMMAND + [_exit_scenarios.IN_FLIGHT_STREAM_UNARY_CALL], 156 stdout=sys.stdout, 157 stderr=sys.stderr) 158 interrupt_and_wait(process) 159 160 @unittest.skipIf(six.PY2, 'https://github.com/grpc/grpc/issues/6999') 161 @unittest.skipIf(os.name == 'nt', 162 'os.kill does not have required permission on Windows') 163 def test_in_flight_stream_stream_call(self): 164 process = subprocess.Popen( 165 BASE_COMMAND + [_exit_scenarios.IN_FLIGHT_STREAM_STREAM_CALL], 166 stdout=sys.stdout, 167 stderr=sys.stderr) 168 interrupt_and_wait(process) 169 170 @unittest.skipIf(six.PY2, 'https://github.com/grpc/grpc/issues/6999') 171 @unittest.skipIf(os.name == 'nt', 172 'os.kill does not have required permission on Windows') 173 def test_in_flight_partial_unary_stream_call(self): 174 process = subprocess.Popen( 175 BASE_COMMAND + 176 [_exit_scenarios.IN_FLIGHT_PARTIAL_UNARY_STREAM_CALL], 177 stdout=sys.stdout, 178 stderr=sys.stderr) 179 interrupt_and_wait(process) 180 181 @unittest.skipIf(os.name == 'nt', 182 'os.kill does not have required permission on Windows') 183 def test_in_flight_partial_stream_unary_call(self): 184 process = subprocess.Popen( 185 BASE_COMMAND + 186 [_exit_scenarios.IN_FLIGHT_PARTIAL_STREAM_UNARY_CALL], 187 stdout=sys.stdout, 188 stderr=sys.stderr) 189 interrupt_and_wait(process) 190 191 @unittest.skipIf(six.PY2, 'https://github.com/grpc/grpc/issues/6999') 192 @unittest.skipIf(os.name == 'nt', 193 'os.kill does not have required permission on Windows') 194 def test_in_flight_partial_stream_stream_call(self): 195 process = subprocess.Popen( 196 BASE_COMMAND + 197 [_exit_scenarios.IN_FLIGHT_PARTIAL_STREAM_STREAM_CALL], 198 stdout=sys.stdout, 199 stderr=sys.stderr) 200 interrupt_and_wait(process) 201 202 203if __name__ == '__main__': 204 logging.basicConfig() 205 unittest.main(verbosity=2) 206