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