tests/commands.py
Go to the documentation of this file.
1 # Copyright 2015 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 """Provides distutils command classes for the gRPC Python setup process."""
15 
16 from distutils import errors as _errors
17 import glob
18 import os
19 import os.path
20 import platform
21 import re
22 import shutil
23 import sys
24 
25 import setuptools
26 from setuptools.command import build_ext
27 from setuptools.command import build_py
28 from setuptools.command import easy_install
29 from setuptools.command import install
30 from setuptools.command import test
31 
32 PYTHON_STEM = os.path.dirname(os.path.abspath(__file__))
33 GRPC_STEM = os.path.abspath(PYTHON_STEM + '../../../../')
34 GRPC_PROTO_STEM = os.path.join(GRPC_STEM, 'src', 'proto')
35 PROTO_STEM = os.path.join(PYTHON_STEM, 'src', 'proto')
36 PYTHON_PROTO_TOP_LEVEL = os.path.join(PYTHON_STEM, 'src')
37 
38 
39 class CommandError(object):
40  pass
41 
42 
43 class GatherProto(setuptools.Command):
44 
45  description = 'gather proto dependencies'
46  user_options = []
47 
48  def initialize_options(self):
49  pass
50 
51  def finalize_options(self):
52  pass
53 
54  def run(self):
55  # TODO(atash) ensure that we're running from the repository directory when
56  # this command is used
57  try:
58  shutil.rmtree(PROTO_STEM)
59  except Exception as error:
60  # We don't care if this command fails
61  pass
62  shutil.copytree(GRPC_PROTO_STEM, PROTO_STEM)
63  for root, _, _ in os.walk(PYTHON_PROTO_TOP_LEVEL):
64  path = os.path.join(root, '__init__.py')
65  open(path, 'a').close()
66 
67 
68 class BuildPy(build_py.build_py):
69  """Custom project build command."""
70 
71  def run(self):
72  try:
73  self.run_command('build_package_protos')
74  except CommandError as error:
75  sys.stderr.write('warning: %s\n' % error.message)
76  build_py.build_py.run(self)
77 
78 
79 class TestLite(setuptools.Command):
80  """Command to run tests without fetching or building anything."""
81 
82  description = 'run tests without fetching or building anything.'
83  user_options = []
84 
85  def initialize_options(self):
86  pass
87 
88  def finalize_options(self):
89  # distutils requires this override.
90  pass
91 
92  def run(self):
93  self._add_eggs_to_path()
94 
95  import tests
96  loader = tests.Loader()
97  loader.loadTestsFromNames(['tests'])
98  runner = tests.Runner(dedicated_threads=True)
99  result = runner.run(loader.suite)
100  if not result.wasSuccessful():
101  sys.exit('Test failure')
102 
103  def _add_eggs_to_path(self):
104  """Fetch install and test requirements"""
105  self.distribution.fetch_build_eggs(self.distribution.install_requires)
106  self.distribution.fetch_build_eggs(self.distribution.tests_require)
107 
108 
109 class TestPy3Only(setuptools.Command):
110  """Command to run tests for Python 3+ features.
111 
112  This does not include asyncio tests, which are housed in a separate
113  directory.
114  """
115 
116  description = 'run tests for py3+ features'
117  user_options = []
118 
120  pass
121 
122  def finalize_options(self):
123  pass
124 
125  def run(self):
126  self._add_eggs_to_path()
127  import tests
128  loader = tests.Loader()
129  loader.loadTestsFromNames(['tests_py3_only'])
130  runner = tests.Runner()
131  result = runner.run(loader.suite)
132  if not result.wasSuccessful():
133  sys.exit('Test failure')
134 
135  def _add_eggs_to_path(self):
136  self.distribution.fetch_build_eggs(self.distribution.install_requires)
137  self.distribution.fetch_build_eggs(self.distribution.tests_require)
138 
139 
140 class TestAio(setuptools.Command):
141  """Command to run aio tests without fetching or building anything."""
142 
143  description = 'run aio tests without fetching or building anything.'
144  user_options = []
145 
147  pass
148 
149  def finalize_options(self):
150  pass
151 
152  def run(self):
153  self._add_eggs_to_path()
154 
155  import tests
156  loader = tests.Loader()
157  loader.loadTestsFromNames(['tests_aio'])
158  # Even without dedicated threads, the framework will somehow spawn a
159  # new thread for tests to run upon. New thread doesn't have event loop
160  # attached by default, so initialization is needed.
161  runner = tests.Runner(dedicated_threads=False)
162  result = runner.run(loader.suite)
163  if not result.wasSuccessful():
164  sys.exit('Test failure')
165 
166  def _add_eggs_to_path(self):
167  """Fetch install and test requirements"""
168  self.distribution.fetch_build_eggs(self.distribution.install_requires)
169  self.distribution.fetch_build_eggs(self.distribution.tests_require)
170 
171 
172 class TestGevent(setuptools.Command):
173  """Command to run tests w/gevent."""
174 
175  BANNED_TESTS = (
176  # Fork support is not compatible with gevent
177  'fork._fork_interop_test.ForkInteropTest',
178  # These tests send a lot of RPCs and are really slow on gevent. They will
179  # eventually succeed, but need to dig into performance issues.
180  'unit._cython._no_messages_server_completion_queue_per_call_test.Test.test_rpcs',
181  'unit._cython._no_messages_single_server_completion_queue_test.Test.test_rpcs',
182  'unit._compression_test',
183  # TODO(https://github.com/grpc/grpc/issues/16890) enable this test
184  'unit._cython._channel_test.ChannelTest.test_multiple_channels_lonely_connectivity',
185  # I have no idea why this doesn't work in gevent, but it shouldn't even be
186  # using the c-core
187  'testing._client_test.ClientTest.test_infinite_request_stream_real_time',
188  # TODO(https://github.com/grpc/grpc/issues/15743) enable this test
189  'unit._session_cache_test.SSLSessionCacheTest.testSSLSessionCacheLRU',
190  # TODO(https://github.com/grpc/grpc/issues/14789) enable this test
191  'unit._server_ssl_cert_config_test',
192  # TODO(https://github.com/grpc/grpc/issues/14901) enable this test
193  'protoc_plugin._python_plugin_test.PythonPluginTest',
194  'protoc_plugin._python_plugin_test.SimpleStubsPluginTest',
195  # Beta API is unsupported for gevent
196  'protoc_plugin.beta_python_plugin_test',
197  'unit.beta._beta_features_test',
198  # TODO(https://github.com/grpc/grpc/issues/15411) unpin gevent version
199  # This test will stuck while running higher version of gevent
200  'unit._auth_context_test.AuthContextTest.testSessionResumption',
201  # TODO(https://github.com/grpc/grpc/issues/15411) enable these tests
202  'unit._channel_ready_future_test.ChannelReadyFutureTest.test_immediately_connectable_channel_connectivity',
203  "unit._cython._channel_test.ChannelTest.test_single_channel_lonely_connectivity",
204  'unit._exit_test.ExitTest.test_in_flight_unary_unary_call',
205  'unit._exit_test.ExitTest.test_in_flight_unary_stream_call',
206  'unit._exit_test.ExitTest.test_in_flight_stream_unary_call',
207  'unit._exit_test.ExitTest.test_in_flight_stream_stream_call',
208  'unit._exit_test.ExitTest.test_in_flight_partial_unary_stream_call',
209  'unit._exit_test.ExitTest.test_in_flight_partial_stream_unary_call',
210  'unit._exit_test.ExitTest.test_in_flight_partial_stream_stream_call',
211  # TODO(https://github.com/grpc/grpc/issues/18980): Reenable.
212  'unit._signal_handling_test.SignalHandlingTest',
213  'unit._metadata_flags_test',
214  'health_check._health_servicer_test.HealthServicerTest.test_cancelled_watch_removed_from_watch_list',
215  # TODO(https://github.com/grpc/grpc/issues/17330) enable these three tests
216  'channelz._channelz_servicer_test.ChannelzServicerTest.test_many_subchannels',
217  'channelz._channelz_servicer_test.ChannelzServicerTest.test_many_subchannels_and_sockets',
218  'channelz._channelz_servicer_test.ChannelzServicerTest.test_streaming_rpc',
219  # TODO(https://github.com/grpc/grpc/issues/15411) enable this test
220  'unit._cython._channel_test.ChannelTest.test_negative_deadline_connectivity',
221  # TODO(https://github.com/grpc/grpc/issues/15411) enable this test
222  'unit._local_credentials_test.LocalCredentialsTest',
223  # TODO(https://github.com/grpc/grpc/issues/22020) LocalCredentials
224  # aren't supported with custom io managers.
225  'unit._contextvars_propagation_test',
226  'testing._time_test.StrictRealTimeTest',
227  )
228  BANNED_WINDOWS_TESTS = (
229  # TODO(https://github.com/grpc/grpc/pull/15411) enable this test
230  'unit._dns_resolver_test.DNSResolverTest.test_connect_loopback',
231  # TODO(https://github.com/grpc/grpc/pull/15411) enable this test
232  'unit._server_test.ServerTest.test_failed_port_binding_exception',
233  )
234  BANNED_MACOS_TESTS = (
235  # TODO(https://github.com/grpc/grpc/issues/15411) enable this test
236  'unit._dynamic_stubs_test.DynamicStubTest',)
237  description = 'run tests with gevent. Assumes grpc/gevent are installed'
238  user_options = []
239 
240  def initialize_options(self):
241  pass
242 
243  def finalize_options(self):
244  # distutils requires this override.
245  pass
246 
247  def run(self):
248  import gevent
249  from gevent import monkey
250  monkey.patch_all()
251 
252  threadpool = gevent.hub.get_hub().threadpool
253 
254  # Currently, each channel corresponds to a single native thread in the
255  # gevent threadpool. Thus, when the unit test suite spins up hundreds of
256  # channels concurrently, some will be starved out, causing the test to
257  # increase in duration. We increase the max size here so this does not
258  # happen.
259  threadpool.maxsize = 1024
260  threadpool.size = 32
261 
263 
264  import tests
266 
267  import gevent
268 
269  import tests
270  loader = tests.Loader()
271  loader.loadTestsFromNames(['tests', 'tests_gevent'])
272  runner = tests.Runner()
273  if sys.platform == 'win32':
274  runner.skip_tests(self.BANNED_TESTS + self.BANNED_WINDOWS_TESTS)
275  elif sys.platform == 'darwin':
276  runner.skip_tests(self.BANNED_TESTS + self.BANNED_MACOS_TESTS)
277  else:
278  runner.skip_tests(self.BANNED_TESTS)
279  result = gevent.spawn(runner.run, loader.suite)
280  result.join()
281  if not result.value.wasSuccessful():
282  sys.exit('Test failure')
283 
284 
285 class RunInterop(test.test):
286 
287  description = 'run interop test client/server'
288  user_options = [
289  ('args=', None, 'pass-thru arguments for the client/server'),
290  ('client', None, 'flag indicating to run the client'),
291  ('server', None, 'flag indicating to run the server'),
292  ('use-asyncio', None, 'flag indicating to run the asyncio stack')
293  ]
294 
296  self.args = ''
297  self.client = False
298  self.server = False
299  self.use_asyncio = False
300 
301  def finalize_options(self):
302  if self.client and self.server:
303  raise _errors.DistutilsOptionError(
304  'you may only specify one of client or server')
305 
306  def run(self):
307  if self.distribution.install_requires:
308  self.distribution.fetch_build_eggs(
309  self.distribution.install_requires)
310  if self.distribution.tests_require:
311  self.distribution.fetch_build_eggs(self.distribution.tests_require)
312  if self.client:
313  self.run_client()
314  elif self.server:
315  self.run_server()
316 
317  def run_server(self):
318  # We import here to ensure that our setuptools parent has had a chance to
319  # edit the Python system path.
320  if self.use_asyncio:
321  import asyncio
322 
323  from tests_aio.interop import server
324  sys.argv[1:] = self.args.split()
325  asyncio.get_event_loop().run_until_complete(server.serve())
326  else:
327  from tests.interop import server
328  sys.argv[1:] = self.args.split()
329  server.serve()
330 
331  def run_client(self):
332  # We import here to ensure that our setuptools parent has had a chance to
333  # edit the Python system path.
334  from tests.interop import client
335  sys.argv[1:] = self.args.split()
336  client.test_interoperability()
337 
338 
339 class RunFork(test.test):
340 
341  description = 'run fork test client'
342  user_options = [('args=', 'a', 'pass-thru arguments for the client')]
343 
345  self.args = ''
346 
347  def finalize_options(self):
348  # distutils requires this override.
349  pass
350 
351  def run(self):
352  if self.distribution.install_requires:
353  self.distribution.fetch_build_eggs(
354  self.distribution.install_requires)
355  if self.distribution.tests_require:
356  self.distribution.fetch_build_eggs(self.distribution.tests_require)
357  # We import here to ensure that our setuptools parent has had a chance to
358  # edit the Python system path.
359  from tests.fork import client
360  sys.argv[1:] = self.args.split()
361  client.test_fork()
commands.GatherProto.finalize_options
def finalize_options(self)
Definition: tests/commands.py:51
commands.RunInterop.finalize_options
def finalize_options(self)
Definition: tests/commands.py:301
tests.fork
Definition: src/python/grpcio_tests/tests/fork/__init__.py:1
commands.RunFork.finalize_options
def finalize_options(self)
Definition: tests/commands.py:347
tests.Loader
Loader
Definition: src/python/grpcio_tests/tests/__init__.py:20
grpc::experimental.gevent.init_gevent
def init_gevent()
Definition: gevent.py:19
commands.BuildPy
Definition: commands.py:123
commands.TestAio._add_eggs_to_path
def _add_eggs_to_path(self)
Definition: tests/commands.py:166
commands.RunInterop.run_client
def run_client(self)
Definition: tests/commands.py:331
commands.RunInterop.run
def run(self)
Definition: tests/commands.py:306
commands.GatherProto.run
def run(self)
Definition: tests/commands.py:54
commands.CommandError
Definition: commands.py:40
commands.TestPy3Only.finalize_options
def finalize_options(self)
Definition: tests/commands.py:122
commands.TestLite
Definition: tests/commands.py:79
commands.RunFork.run
def run(self)
Definition: tests/commands.py:351
commands.RunFork
Definition: tests/commands.py:339
commands.RunInterop.server
server
Definition: tests/commands.py:298
commands.RunInterop.args
args
Definition: tests/commands.py:296
commands.TestPy3Only
Definition: tests/commands.py:109
tests.Runner
Runner
Definition: src/python/grpcio_tests/tests/__init__.py:21
commands.RunInterop.initialize_options
def initialize_options(self)
Definition: tests/commands.py:295
commands.TestGevent
Definition: tests/commands.py:172
commands.TestAio.run
def run(self)
Definition: tests/commands.py:152
commands.TestLite._add_eggs_to_path
def _add_eggs_to_path(self)
Definition: tests/commands.py:103
close
#define close
Definition: test-fs.c:48
commands.RunInterop.client
client
Definition: tests/commands.py:297
commands.GatherProto.initialize_options
def initialize_options(self)
Definition: tests/commands.py:48
commands.TestPy3Only.run
def run(self)
Definition: tests/commands.py:125
client.run
def run()
Definition: examples/python/async_streaming/client.py:109
tests_aio.interop
Definition: src/python/grpcio_tests/tests_aio/interop/__init__.py:1
commands.TestLite.initialize_options
def initialize_options(self)
Definition: tests/commands.py:85
tests.interop
Definition: src/python/grpcio_tests/tests/interop/__init__.py:1
commands.RunInterop.run_server
def run_server(self)
Definition: tests/commands.py:317
commands.RunInterop
Definition: tests/commands.py:285
commands.RunFork.initialize_options
def initialize_options(self)
Definition: tests/commands.py:344
commands.GatherProto
Definition: tests/commands.py:43
server.serve
None serve(int port, str hostname, int maintenance_port, bool secure_mode)
Definition: examples/python/xds/server.py:95
commands.TestLite.run
def run(self)
Definition: tests/commands.py:92
commands.TestAio.initialize_options
def initialize_options(self)
Definition: tests/commands.py:146
commands.TestAio
Definition: tests/commands.py:140
commands.TestAio.finalize_options
def finalize_options(self)
Definition: tests/commands.py:149
open
#define open
Definition: test-fs.c:46
commands.TestPy3Only.initialize_options
def initialize_options(self)
Definition: tests/commands.py:119
commands.BuildPy.run
def run(self)
Definition: commands.py:126
split
static void split(const char *s, char ***ss, size_t *ns)
Definition: debug/trace.cc:111
commands.RunFork.args
args
Definition: tests/commands.py:345
commands.RunInterop.use_asyncio
use_asyncio
Definition: tests/commands.py:299
commands.TestLite.finalize_options
def finalize_options(self)
Definition: tests/commands.py:88
commands.TestPy3Only._add_eggs_to_path
def _add_eggs_to_path(self)
Definition: tests/commands.py:135
grpc::experimental.gevent
Definition: gevent.py:1


grpc
Author(s):
autogenerated on Thu Mar 13 2025 02:58:51