mirror of https://github.com/grpc/grpc.git
parent
69b7231776
commit
2010985ab2
19 changed files with 1990 additions and 0 deletions
@ -0,0 +1,23 @@ |
||||
# Copyright 2017 gRPC authors. |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
|
||||
from grpc_testing._channel import _channel |
||||
from grpc_testing._channel import _channel_state |
||||
|
||||
|
||||
# descriptors is reserved for later use. |
||||
# pylint: disable=unused-argument |
||||
def testing_channel(descriptors, time): |
||||
return _channel.TestingChannel(time, _channel_state.State()) |
||||
# pylint: enable=unused-argument |
@ -0,0 +1,62 @@ |
||||
# Copyright 2017 gRPC authors. |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
|
||||
import grpc_testing |
||||
from grpc_testing._channel import _channel_rpc |
||||
from grpc_testing._channel import _multi_callable |
||||
|
||||
|
||||
# All serializer and deserializer parameters are not (yet) used by this |
||||
# test infrastructure. |
||||
# pylint: disable=unused-argument |
||||
class TestingChannel(grpc_testing.Channel): |
||||
|
||||
def __init__(self, time, state): |
||||
self._time = time |
||||
self._state = state |
||||
|
||||
def subscribe(self, callback, try_to_connect=False): |
||||
raise NotImplementedError() |
||||
|
||||
def unsubscribe(self, callback): |
||||
raise NotImplementedError() |
||||
|
||||
def unary_unary( |
||||
self, method, request_serializer=None, response_deserializer=None): |
||||
return _multi_callable.UnaryUnary(method, self._state) |
||||
|
||||
def unary_stream( |
||||
self, method, request_serializer=None, response_deserializer=None): |
||||
return _multi_callable.UnaryStream(method, self._state) |
||||
|
||||
def stream_unary( |
||||
self, method, request_serializer=None, response_deserializer=None): |
||||
return _multi_callable.StreamUnary(method, self._state) |
||||
|
||||
def stream_stream( |
||||
self, method, request_serializer=None, response_deserializer=None): |
||||
return _multi_callable.StreamStream(method, self._state) |
||||
|
||||
def take_unary_unary(self, method_descriptor): |
||||
return _channel_rpc.unary_unary(self._state, method_descriptor) |
||||
|
||||
def take_unary_stream(self, method_descriptor): |
||||
return _channel_rpc.unary_stream(self._state, method_descriptor) |
||||
|
||||
def take_stream_unary(self, method_descriptor): |
||||
return _channel_rpc.stream_unary(self._state, method_descriptor) |
||||
|
||||
def take_stream_stream(self, method_descriptor): |
||||
return _channel_rpc.stream_stream(self._state, method_descriptor) |
||||
# pylint: enable=unused-argument |
@ -0,0 +1,119 @@ |
||||
# Copyright 2017 gRPC authors. |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
|
||||
import grpc_testing |
||||
|
||||
|
||||
class _UnaryUnary(grpc_testing.UnaryUnaryChannelRpc): |
||||
|
||||
def __init__(self, rpc_state): |
||||
self._rpc_state = rpc_state |
||||
|
||||
def send_initial_metadata(self, initial_metadata): |
||||
self._rpc_state.send_initial_metadata(initial_metadata) |
||||
|
||||
def cancelled(self): |
||||
self._rpc_state.cancelled() |
||||
|
||||
def terminate(self, response, trailing_metadata, code, details): |
||||
self._rpc_state.terminate_with_response( |
||||
response, trailing_metadata, code, details) |
||||
|
||||
|
||||
class _UnaryStream(grpc_testing.UnaryStreamChannelRpc): |
||||
|
||||
def __init__(self, rpc_state): |
||||
self._rpc_state = rpc_state |
||||
|
||||
def send_initial_metadata(self, initial_metadata): |
||||
self._rpc_state.send_initial_metadata(initial_metadata) |
||||
|
||||
def send_response(self, response): |
||||
self._rpc_state.send_response(response) |
||||
|
||||
def cancelled(self): |
||||
self._rpc_state.cancelled() |
||||
|
||||
def terminate(self, trailing_metadata, code, details): |
||||
self._rpc_state.terminate(trailing_metadata, code, details) |
||||
|
||||
|
||||
class _StreamUnary(grpc_testing.StreamUnaryChannelRpc): |
||||
|
||||
def __init__(self, rpc_state): |
||||
self._rpc_state = rpc_state |
||||
|
||||
def send_initial_metadata(self, initial_metadata): |
||||
self._rpc_state.send_initial_metadata(initial_metadata) |
||||
|
||||
def take_request(self): |
||||
return self._rpc_state.take_request() |
||||
|
||||
def requests_closed(self): |
||||
return self._rpc_state.requests_closed() |
||||
|
||||
def cancelled(self): |
||||
self._rpc_state.cancelled() |
||||
|
||||
def terminate(self, response, trailing_metadata, code, details): |
||||
self._rpc_state.terminate_with_response( |
||||
response, trailing_metadata, code, details) |
||||
|
||||
|
||||
class _StreamStream(grpc_testing.StreamStreamChannelRpc): |
||||
|
||||
def __init__(self, rpc_state): |
||||
self._rpc_state = rpc_state |
||||
|
||||
def send_initial_metadata(self, initial_metadata): |
||||
self._rpc_state.send_initial_metadata(initial_metadata) |
||||
|
||||
def take_request(self): |
||||
return self._rpc_state.take_request() |
||||
|
||||
def send_response(self, response): |
||||
self._rpc_state.send_response(response) |
||||
|
||||
def requests_closed(self): |
||||
return self._rpc_state.requests_closed() |
||||
|
||||
def cancelled(self): |
||||
self._rpc_state.cancelled() |
||||
|
||||
def terminate(self, trailing_metadata, code, details): |
||||
self._rpc_state.terminate(trailing_metadata, code, details) |
||||
|
||||
|
||||
def unary_unary(channel_state, method_descriptor): |
||||
rpc_state = channel_state.take_rpc_state(method_descriptor) |
||||
invocation_metadata, request = ( |
||||
rpc_state.take_invocation_metadata_and_request()) |
||||
return invocation_metadata, request, _UnaryUnary(rpc_state) |
||||
|
||||
|
||||
def unary_stream(channel_state, method_descriptor): |
||||
rpc_state = channel_state.take_rpc_state(method_descriptor) |
||||
invocation_metadata, request = ( |
||||
rpc_state.take_invocation_metadata_and_request()) |
||||
return invocation_metadata, request, _UnaryStream(rpc_state) |
||||
|
||||
|
||||
def stream_unary(channel_state, method_descriptor): |
||||
rpc_state = channel_state.take_rpc_state(method_descriptor) |
||||
return rpc_state.take_invocation_metadata(), _StreamUnary(rpc_state) |
||||
|
||||
|
||||
def stream_stream(channel_state, method_descriptor): |
||||
rpc_state = channel_state.take_rpc_state(method_descriptor) |
||||
return rpc_state.take_invocation_metadata(), _StreamStream(rpc_state) |
@ -0,0 +1,48 @@ |
||||
# Copyright 2017 gRPC authors. |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
|
||||
import collections |
||||
import threading |
||||
|
||||
from grpc_testing import _common |
||||
from grpc_testing._channel import _rpc_state |
||||
|
||||
|
||||
class State(_common.ChannelHandler): |
||||
|
||||
def __init__(self): |
||||
self._condition = threading.Condition() |
||||
self._rpc_states = collections.defaultdict(list) |
||||
|
||||
def invoke_rpc( |
||||
self, method_full_rpc_name, invocation_metadata, requests, |
||||
requests_closed, timeout): |
||||
rpc_state = _rpc_state.State( |
||||
invocation_metadata, requests, requests_closed) |
||||
with self._condition: |
||||
self._rpc_states[method_full_rpc_name].append(rpc_state) |
||||
self._condition.notify_all() |
||||
return rpc_state |
||||
|
||||
def take_rpc_state(self, method_descriptor): |
||||
method_full_rpc_name = '/{}/{}'.format( |
||||
method_descriptor.containing_service.full_name, |
||||
method_descriptor.name) |
||||
with self._condition: |
||||
while True: |
||||
method_rpc_states = self._rpc_states[method_full_rpc_name] |
||||
if method_rpc_states: |
||||
return method_rpc_states.pop(0) |
||||
else: |
||||
self._condition.wait() |
@ -0,0 +1,322 @@ |
||||
# Copyright 2017 gRPC authors. |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
|
||||
import logging |
||||
import threading |
||||
|
||||
import grpc |
||||
|
||||
_NOT_YET_OBSERVED = object() |
||||
|
||||
|
||||
def _cancel(handler): |
||||
return handler.cancel(grpc.StatusCode.CANCELLED, 'Locally cancelled!') |
||||
|
||||
|
||||
def _is_active(handler): |
||||
return handler.is_active() |
||||
|
||||
|
||||
def _time_remaining(unused_handler): |
||||
raise NotImplementedError() |
||||
|
||||
|
||||
def _add_callback(handler, callback): |
||||
return handler.add_callback(callback) |
||||
|
||||
|
||||
def _initial_metadata(handler): |
||||
return handler.initial_metadata() |
||||
|
||||
|
||||
def _trailing_metadata(handler): |
||||
trailing_metadata, unused_code, unused_details = handler.termination() |
||||
return trailing_metadata |
||||
|
||||
|
||||
def _code(handler): |
||||
unused_trailing_metadata, code, unused_details = handler.termination() |
||||
return code |
||||
|
||||
|
||||
def _details(handler): |
||||
unused_trailing_metadata, unused_code, details = handler.termination() |
||||
return details |
||||
|
||||
|
||||
class _Call(grpc.Call): |
||||
|
||||
def __init__(self, handler): |
||||
self._handler = handler |
||||
|
||||
def cancel(self): |
||||
_cancel(self._handler) |
||||
|
||||
def is_active(self): |
||||
return _is_active(self._handler) |
||||
|
||||
def time_remaining(self): |
||||
return _time_remaining(self._handler) |
||||
|
||||
def add_callback(self, callback): |
||||
return _add_callback(self._handler, callback) |
||||
|
||||
def initial_metadata(self): |
||||
return _initial_metadata(self._handler) |
||||
|
||||
def trailing_metadata(self): |
||||
return _trailing_metadata(self._handler) |
||||
|
||||
def code(self): |
||||
return _code(self._handler) |
||||
|
||||
def details(self): |
||||
return _details(self._handler) |
||||
|
||||
|
||||
class _RpcErrorCall(grpc.RpcError, grpc.Call): |
||||
|
||||
def __init__(self, handler): |
||||
self._handler = handler |
||||
|
||||
def cancel(self): |
||||
_cancel(self._handler) |
||||
|
||||
def is_active(self): |
||||
return _is_active(self._handler) |
||||
|
||||
def time_remaining(self): |
||||
return _time_remaining(self._handler) |
||||
|
||||
def add_callback(self, callback): |
||||
return _add_callback(self._handler, callback) |
||||
|
||||
def initial_metadata(self): |
||||
return _initial_metadata(self._handler) |
||||
|
||||
def trailing_metadata(self): |
||||
return _trailing_metadata(self._handler) |
||||
|
||||
def code(self): |
||||
return _code(self._handler) |
||||
|
||||
def details(self): |
||||
return _details(self._handler) |
||||
|
||||
|
||||
def _next(handler): |
||||
read = handler.take_response() |
||||
if read.code is None: |
||||
return read.response |
||||
elif read.code is grpc.StatusCode.OK: |
||||
raise StopIteration() |
||||
else: |
||||
raise _RpcErrorCall(handler) |
||||
|
||||
|
||||
class _HandlerExtras(object): |
||||
|
||||
def __init__(self): |
||||
self.condition = threading.Condition() |
||||
self.unary_response = _NOT_YET_OBSERVED |
||||
self.cancelled = False |
||||
|
||||
|
||||
def _with_extras_cancel(handler, extras): |
||||
with extras.condition: |
||||
if handler.cancel(grpc.StatusCode.CANCELLED, 'Locally cancelled!'): |
||||
extras.cancelled = True |
||||
return True |
||||
else: |
||||
return False |
||||
|
||||
|
||||
def _extras_without_cancelled(extras): |
||||
with extras.condition: |
||||
return extras.cancelled |
||||
|
||||
|
||||
def _running(handler): |
||||
return handler.is_active() |
||||
|
||||
|
||||
def _done(handler): |
||||
return not handler.is_active() |
||||
|
||||
|
||||
def _with_extras_unary_response(handler, extras): |
||||
with extras.condition: |
||||
if extras.unary_response is _NOT_YET_OBSERVED: |
||||
read = handler.take_response() |
||||
if read.code is None: |
||||
extras.unary_response = read.response |
||||
return read.response |
||||
else: |
||||
raise _RpcErrorCall(handler) |
||||
else: |
||||
return extras.unary_response |
||||
|
||||
|
||||
def _exception(unused_handler): |
||||
raise NotImplementedError('TODO!') |
||||
|
||||
|
||||
def _traceback(unused_handler): |
||||
raise NotImplementedError('TODO!') |
||||
|
||||
|
||||
def _add_done_callback(handler, callback, future): |
||||
adapted_callback = lambda: callback(future) |
||||
if not handler.add_callback(adapted_callback): |
||||
callback(future) |
||||
|
||||
|
||||
class _FutureCall(grpc.Future, grpc.Call): |
||||
|
||||
def __init__(self, handler, extras): |
||||
self._handler = handler |
||||
self._extras = extras |
||||
|
||||
def cancel(self): |
||||
return _with_extras_cancel(self._handler, self._extras) |
||||
|
||||
def cancelled(self): |
||||
return _extras_without_cancelled(self._extras) |
||||
|
||||
def running(self): |
||||
return _running(self._handler) |
||||
|
||||
def done(self): |
||||
return _done(self._handler) |
||||
|
||||
def result(self): |
||||
return _with_extras_unary_response(self._handler, self._extras) |
||||
|
||||
def exception(self): |
||||
return _exception(self._handler) |
||||
|
||||
def traceback(self): |
||||
return _traceback(self._handler) |
||||
|
||||
def add_done_callback(self, fn): |
||||
_add_done_callback(self._handler, fn, self) |
||||
|
||||
def is_active(self): |
||||
return _is_active(self._handler) |
||||
|
||||
def time_remaining(self): |
||||
return _time_remaining(self._handler) |
||||
|
||||
def add_callback(self, callback): |
||||
return _add_callback(self._handler, callback) |
||||
|
||||
def initial_metadata(self): |
||||
return _initial_metadata(self._handler) |
||||
|
||||
def trailing_metadata(self): |
||||
return _trailing_metadata(self._handler) |
||||
|
||||
def code(self): |
||||
return _code(self._handler) |
||||
|
||||
def details(self): |
||||
return _details(self._handler) |
||||
|
||||
|
||||
def consume_requests(request_iterator, handler): |
||||
|
||||
def _consume(): |
||||
while True: |
||||
try: |
||||
request = next(request_iterator) |
||||
added = handler.add_request(request) |
||||
if not added: |
||||
break |
||||
except StopIteration: |
||||
handler.close_requests() |
||||
break |
||||
except Exception: # pylint: disable=broad-except |
||||
details = 'Exception iterating requests!' |
||||
logging.exception(details) |
||||
handler.cancel(grpc.StatusCode.UNKNOWN, details) |
||||
|
||||
consumption = threading.Thread(target=_consume) |
||||
consumption.start() |
||||
|
||||
|
||||
def blocking_unary_response(handler): |
||||
read = handler.take_response() |
||||
if read.code is None: |
||||
unused_trailing_metadata, code, unused_details = handler.termination() |
||||
if code is grpc.StatusCode.OK: |
||||
return read.response |
||||
else: |
||||
raise _RpcErrorCall(handler) |
||||
else: |
||||
raise _RpcErrorCall(handler) |
||||
|
||||
|
||||
def blocking_unary_response_with_call(handler): |
||||
read = handler.take_response() |
||||
if read.code is None: |
||||
unused_trailing_metadata, code, unused_details = handler.termination() |
||||
if code is grpc.StatusCode.OK: |
||||
return read.response, _Call(handler) |
||||
else: |
||||
raise _RpcErrorCall(handler) |
||||
else: |
||||
raise _RpcErrorCall(handler) |
||||
|
||||
|
||||
def future_call(handler): |
||||
return _FutureCall(handler, _HandlerExtras()) |
||||
|
||||
|
||||
class ResponseIteratorCall(grpc.Call): |
||||
|
||||
def __init__(self, handler): |
||||
self._handler = handler |
||||
|
||||
def __iter__(self): |
||||
return self |
||||
|
||||
def __next__(self): |
||||
return _next(self._handler) |
||||
|
||||
def next(self): |
||||
return _next(self._handler) |
||||
|
||||
def cancel(self): |
||||
_cancel(self._handler) |
||||
|
||||
def is_active(self): |
||||
return _is_active(self._handler) |
||||
|
||||
def time_remaining(self): |
||||
return _time_remaining(self._handler) |
||||
|
||||
def add_callback(self, callback): |
||||
return _add_callback(self._handler, callback) |
||||
|
||||
def initial_metadata(self): |
||||
return _initial_metadata(self._handler) |
||||
|
||||
def trailing_metadata(self): |
||||
return _trailing_metadata(self._handler) |
||||
|
||||
def code(self): |
||||
return _code(self._handler) |
||||
|
||||
def details(self): |
||||
return _details(self._handler) |
@ -0,0 +1,115 @@ |
||||
# Copyright 2017 gRPC authors. |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
|
||||
import grpc |
||||
from grpc_testing import _common |
||||
from grpc_testing._channel import _invocation |
||||
|
||||
# All per-call credentials parameters are unused by this test infrastructure. |
||||
# pylint: disable=unused-argument |
||||
class UnaryUnary(grpc.UnaryUnaryMultiCallable): |
||||
|
||||
def __init__(self, method_full_rpc_name, channel_handler): |
||||
self._method_full_rpc_name = method_full_rpc_name |
||||
self._channel_handler = channel_handler |
||||
|
||||
def __call__(self, request, timeout=None, metadata=None, credentials=None): |
||||
rpc_handler = self._channel_handler.invoke_rpc( |
||||
self._method_full_rpc_name, _common.fuss_with_metadata(metadata), |
||||
[request], True, timeout) |
||||
return _invocation.blocking_unary_response(rpc_handler) |
||||
|
||||
def with_call(self, request, timeout=None, metadata=None, credentials=None): |
||||
rpc_handler = self._channel_handler.invoke_rpc( |
||||
self._method_full_rpc_name, _common.fuss_with_metadata(metadata), |
||||
[request], True, timeout) |
||||
return _invocation.blocking_unary_response_with_call(rpc_handler) |
||||
|
||||
def future(self, request, timeout=None, metadata=None, credentials=None): |
||||
rpc_handler = self._channel_handler.invoke_rpc( |
||||
self._method_full_rpc_name, _common.fuss_with_metadata(metadata), |
||||
[request], True, timeout) |
||||
return _invocation.future_call(rpc_handler) |
||||
|
||||
|
||||
class UnaryStream(grpc.StreamStreamMultiCallable): |
||||
|
||||
def __init__(self, method_full_rpc_name, channel_handler): |
||||
self._method_full_rpc_name = method_full_rpc_name |
||||
self._channel_handler = channel_handler |
||||
|
||||
def __call__(self, request, timeout=None, metadata=None, credentials=None): |
||||
rpc_handler = self._channel_handler.invoke_rpc( |
||||
self._method_full_rpc_name, |
||||
_common.fuss_with_metadata(metadata), [request], True, timeout) |
||||
return _invocation.ResponseIteratorCall(rpc_handler) |
||||
|
||||
|
||||
class StreamUnary(grpc.StreamUnaryMultiCallable): |
||||
|
||||
def __init__(self, method_full_rpc_name, channel_handler): |
||||
self._method_full_rpc_name = method_full_rpc_name |
||||
self._channel_handler = channel_handler |
||||
|
||||
def __call__(self, |
||||
request_iterator, |
||||
timeout=None, |
||||
metadata=None, |
||||
credentials=None): |
||||
rpc_handler = self._channel_handler.invoke_rpc( |
||||
self._method_full_rpc_name, |
||||
_common.fuss_with_metadata(metadata), [], False, timeout) |
||||
_invocation.consume_requests(request_iterator, rpc_handler) |
||||
return _invocation.blocking_unary_response(rpc_handler) |
||||
|
||||
def with_call(self, |
||||
request_iterator, |
||||
timeout=None, |
||||
metadata=None, |
||||
credentials=None): |
||||
rpc_handler = self._channel_handler.invoke_rpc( |
||||
self._method_full_rpc_name, |
||||
_common.fuss_with_metadata(metadata), [], False, timeout) |
||||
_invocation.consume_requests(request_iterator, rpc_handler) |
||||
return _invocation.blocking_unary_response_with_call(rpc_handler) |
||||
|
||||
def future(self, |
||||
request_iterator, |
||||
timeout=None, |
||||
metadata=None, |
||||
credentials=None): |
||||
rpc_handler = self._channel_handler.invoke_rpc( |
||||
self._method_full_rpc_name, |
||||
_common.fuss_with_metadata(metadata), [], False, timeout) |
||||
_invocation.consume_requests(request_iterator, rpc_handler) |
||||
return _invocation.future_call(rpc_handler) |
||||
|
||||
|
||||
class StreamStream(grpc.StreamStreamMultiCallable): |
||||
|
||||
def __init__(self, method_full_rpc_name, channel_handler): |
||||
self._method_full_rpc_name = method_full_rpc_name |
||||
self._channel_handler = channel_handler |
||||
|
||||
def __call__(self, |
||||
request_iterator, |
||||
timeout=None, |
||||
metadata=None, |
||||
credentials=None): |
||||
rpc_handler = self._channel_handler.invoke_rpc( |
||||
self._method_full_rpc_name, |
||||
_common.fuss_with_metadata(metadata), [], False, timeout) |
||||
_invocation.consume_requests(request_iterator, rpc_handler) |
||||
return _invocation.ResponseIteratorCall(rpc_handler) |
||||
# pylint: enable=unused-argument |
@ -0,0 +1,193 @@ |
||||
# Copyright 2017 gRPC authors. |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
|
||||
import threading |
||||
|
||||
import grpc |
||||
from grpc_testing import _common |
||||
|
||||
|
||||
class State(_common.ChannelRpcHandler): |
||||
|
||||
def __init__(self, invocation_metadata, requests, requests_closed): |
||||
self._condition = threading.Condition() |
||||
self._invocation_metadata = invocation_metadata |
||||
self._requests = requests |
||||
self._requests_closed = requests_closed |
||||
self._initial_metadata = None |
||||
self._responses = [] |
||||
self._trailing_metadata = None |
||||
self._code = None |
||||
self._details = None |
||||
|
||||
def initial_metadata(self): |
||||
with self._condition: |
||||
while True: |
||||
if self._initial_metadata is None: |
||||
if self._code is None: |
||||
self._condition.wait() |
||||
else: |
||||
return _common.FUSSED_EMPTY_METADATA |
||||
else: |
||||
return self._initial_metadata |
||||
|
||||
def add_request(self, request): |
||||
with self._condition: |
||||
if self._code is None and not self._requests_closed: |
||||
self._requests.append(request) |
||||
self._condition.notify_all() |
||||
return True |
||||
else: |
||||
return False |
||||
|
||||
def close_requests(self): |
||||
with self._condition: |
||||
if self._code is None and not self._requests_closed: |
||||
self._requests_closed = True |
||||
self._condition.notify_all() |
||||
|
||||
def take_response(self): |
||||
with self._condition: |
||||
while True: |
||||
if self._code is grpc.StatusCode.OK: |
||||
if self._responses: |
||||
response = self._responses.pop(0) |
||||
return _common.ChannelRpcRead( |
||||
response, None, None, None) |
||||
else: |
||||
return _common.ChannelRpcRead( |
||||
None, self._trailing_metadata, |
||||
grpc.StatusCode.OK, self._details) |
||||
elif self._code is None: |
||||
if self._responses: |
||||
response = self._responses.pop(0) |
||||
return _common.ChannelRpcRead( |
||||
response, None, None, None) |
||||
else: |
||||
self._condition.wait() |
||||
else: |
||||
return _common.ChannelRpcRead( |
||||
None, self._trailing_metadata, self._code, |
||||
self._details) |
||||
|
||||
def termination(self): |
||||
with self._condition: |
||||
while True: |
||||
if self._code is None: |
||||
self._condition.wait() |
||||
else: |
||||
return self._trailing_metadata, self._code, self._details |
||||
|
||||
def cancel(self, code, details): |
||||
with self._condition: |
||||
if self._code is None: |
||||
if self._initial_metadata is None: |
||||
self._initial_metadata = _common.FUSSED_EMPTY_METADATA |
||||
self._trailing_metadata = _common.FUSSED_EMPTY_METADATA |
||||
self._code = code |
||||
self._details = details |
||||
self._condition.notify_all() |
||||
return True |
||||
else: |
||||
return False |
||||
|
||||
def take_invocation_metadata(self): |
||||
with self._condition: |
||||
if self._invocation_metadata is None: |
||||
raise ValueError('Expected invocation metadata!') |
||||
else: |
||||
invocation_metadata = self._invocation_metadata |
||||
self._invocation_metadata = None |
||||
return invocation_metadata |
||||
|
||||
def take_invocation_metadata_and_request(self): |
||||
with self._condition: |
||||
if self._invocation_metadata is None: |
||||
raise ValueError('Expected invocation metadata!') |
||||
elif not self._requests: |
||||
raise ValueError('Expected at least one request!') |
||||
else: |
||||
invocation_metadata = self._invocation_metadata |
||||
self._invocation_metadata = None |
||||
return invocation_metadata, self._requests.pop(0) |
||||
|
||||
def send_initial_metadata(self, initial_metadata): |
||||
with self._condition: |
||||
self._initial_metadata = _common.fuss_with_metadata( |
||||
initial_metadata) |
||||
self._condition.notify_all() |
||||
|
||||
def take_request(self): |
||||
with self._condition: |
||||
while True: |
||||
if self._requests: |
||||
return self._requests.pop(0) |
||||
else: |
||||
self._condition.wait() |
||||
|
||||
def requests_closed(self): |
||||
with self._condition: |
||||
while True: |
||||
if self._requests_closed: |
||||
return |
||||
else: |
||||
self._condition.wait() |
||||
|
||||
def send_response(self, response): |
||||
with self._condition: |
||||
if self._code is None: |
||||
self._responses.append(response) |
||||
self._condition.notify_all() |
||||
|
||||
def terminate_with_response( |
||||
self, response, trailing_metadata, code, details): |
||||
with self._condition: |
||||
if self._initial_metadata is None: |
||||
self._initial_metadata = _common.FUSSED_EMPTY_METADATA |
||||
self._responses.append(response) |
||||
self._trailing_metadata = _common.fuss_with_metadata( |
||||
trailing_metadata) |
||||
self._code = code |
||||
self._details = details |
||||
self._condition.notify_all() |
||||
|
||||
def terminate(self, trailing_metadata, code, details): |
||||
with self._condition: |
||||
if self._initial_metadata is None: |
||||
self._initial_metadata = _common.FUSSED_EMPTY_METADATA |
||||
self._trailing_metadata = _common.fuss_with_metadata( |
||||
trailing_metadata) |
||||
self._code = code |
||||
self._details = details |
||||
self._condition.notify_all() |
||||
|
||||
def cancelled(self): |
||||
with self._condition: |
||||
while True: |
||||
if self._code is grpc.StatusCode.CANCELLED: |
||||
return |
||||
elif self._code is None: |
||||
self._condition.wait() |
||||
else: |
||||
raise ValueError( |
||||
'Status code unexpectedly {}!'.format(self._code)) |
||||
|
||||
def is_active(self): |
||||
raise NotImplementedError() |
||||
|
||||
def time_remaining(self): |
||||
raise NotImplementedError() |
||||
|
||||
def add_callback(self, callback): |
||||
raise NotImplementedError() |
@ -0,0 +1,92 @@ |
||||
# Copyright 2017 gRPC authors. |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
"""Common interfaces and implementation.""" |
||||
|
||||
import abc |
||||
import collections |
||||
|
||||
import six |
||||
|
||||
|
||||
def _fuss(tuplified_metadata): |
||||
return tuplified_metadata + ( |
||||
( |
||||
'grpc.metadata_added_by_runtime', |
||||
'gRPC is allowed to add metadata in transmission and does so.', |
||||
), |
||||
) |
||||
|
||||
FUSSED_EMPTY_METADATA = _fuss(()) |
||||
|
||||
|
||||
def fuss_with_metadata(metadata): |
||||
if metadata is None: |
||||
return FUSSED_EMPTY_METADATA |
||||
else: |
||||
return _fuss(tuple(metadata)) |
||||
|
||||
|
||||
class ChannelRpcRead( |
||||
collections.namedtuple( |
||||
'ChannelRpcRead', |
||||
('response', 'trailing_metadata', 'code', 'details',))): |
||||
pass |
||||
|
||||
|
||||
class ChannelRpcHandler(six.with_metaclass(abc.ABCMeta)): |
||||
|
||||
@abc.abstractmethod |
||||
def initial_metadata(self): |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def add_request(self, request): |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def close_requests(self): |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def take_response(self): |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def cancel(self, code, details): |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def termination(self): |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def is_active(self): |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def time_remaining(self): |
||||
raise NotImplementedError() |
||||
|
||||
@abc.abstractmethod |
||||
def add_callback(self, callback): |
||||
raise NotImplementedError() |
||||
|
||||
|
||||
class ChannelHandler(six.with_metaclass(abc.ABCMeta)): |
||||
|
||||
@abc.abstractmethod |
||||
def invoke_rpc( |
||||
self, method_full_rpc_name, invocation_metadata, requests, |
||||
requests_closed, timeout): |
||||
raise NotImplementedError() |
@ -0,0 +1,36 @@ |
||||
# Copyright 2017 gRPC authors. |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
"""An example gRPC Python-using application's common code elements.""" |
||||
|
||||
from tests.testing.proto import requests_pb2 |
||||
from tests.testing.proto import services_pb2 |
||||
|
||||
SERVICE_NAME = 'tests_of_grpc_testing.FirstService' |
||||
UNARY_UNARY_METHOD_NAME = 'UnUn' |
||||
UNARY_STREAM_METHOD_NAME = 'UnStre' |
||||
STREAM_UNARY_METHOD_NAME = 'StreUn' |
||||
STREAM_STREAM_METHOD_NAME = 'StreStre' |
||||
|
||||
UNARY_UNARY_REQUEST = requests_pb2.Up(first_up_field=2) |
||||
ERRONEOUS_UNARY_UNARY_REQUEST = requests_pb2.Up(first_up_field=3) |
||||
UNARY_UNARY_RESPONSE = services_pb2.Down(first_down_field=5) |
||||
ERRONEOUS_UNARY_UNARY_RESPONSE = services_pb2.Down(first_down_field=7) |
||||
UNARY_STREAM_REQUEST = requests_pb2.Charm(first_charm_field=11) |
||||
STREAM_UNARY_REQUEST = requests_pb2.Charm(first_charm_field=13) |
||||
STREAM_UNARY_RESPONSE = services_pb2.Strange(first_strange_field=17) |
||||
STREAM_STREAM_REQUEST = requests_pb2.Top(first_top_field=19) |
||||
STREAM_STREAM_RESPONSE = services_pb2.Bottom(first_bottom_field=23) |
||||
TWO_STREAM_STREAM_RESPONSES = (STREAM_STREAM_RESPONSE,) * 2 |
||||
|
||||
INFINITE_REQUEST_STREAM_TIMEOUT = 0.2 |
@ -0,0 +1,33 @@ |
||||
# Copyright 2017 gRPC authors. |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
|
||||
import grpc_testing |
||||
|
||||
from tests.testing.proto import requests_pb2 |
||||
from tests.testing.proto import services_pb2 |
||||
|
||||
# TODO(https://github.com/grpc/grpc/issues/11657): Eliminate this entirely. |
||||
# TODO(https://github.com/google/protobuf/issues/3452): Eliminate this if/else. |
||||
if services_pb2.DESCRIPTOR.services_by_name.get('FirstService') is None: |
||||
FIRST_SERVICE = 'Fix protobuf issue 3452!' |
||||
FIRST_SERVICE_UNUN = 'Fix protobuf issue 3452!' |
||||
FIRST_SERVICE_UNSTRE = 'Fix protobuf issue 3452!' |
||||
FIRST_SERVICE_STREUN = 'Fix protobuf issue 3452!' |
||||
FIRST_SERVICE_STRESTRE = 'Fix protobuf issue 3452!' |
||||
else: |
||||
FIRST_SERVICE = services_pb2.DESCRIPTOR.services_by_name['FirstService'] |
||||
FIRST_SERVICE_UNUN = FIRST_SERVICE.methods_by_name['UnUn'] |
||||
FIRST_SERVICE_UNSTRE = FIRST_SERVICE.methods_by_name['UnStre'] |
||||
FIRST_SERVICE_STREUN = FIRST_SERVICE.methods_by_name['StreUn'] |
||||
FIRST_SERVICE_STRESTRE = FIRST_SERVICE.methods_by_name['StreStre'] |
@ -0,0 +1,260 @@ |
||||
# Copyright 2017 gRPC authors. |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
"""An example gRPC Python-using client-side application.""" |
||||
|
||||
import collections |
||||
import enum |
||||
import threading |
||||
import time |
||||
|
||||
import grpc |
||||
from tests.unit.framework.common import test_constants |
||||
|
||||
from tests.testing.proto import requests_pb2 |
||||
from tests.testing.proto import services_pb2 |
||||
from tests.testing.proto import services_pb2_grpc |
||||
|
||||
from tests.testing import _application_common |
||||
|
||||
|
||||
@enum.unique |
||||
class Scenario(enum.Enum): |
||||
UNARY_UNARY = 'unary unary' |
||||
UNARY_STREAM = 'unary stream' |
||||
STREAM_UNARY = 'stream unary' |
||||
STREAM_STREAM = 'stream stream' |
||||
CONCURRENT_STREAM_UNARY = 'concurrent stream unary' |
||||
CONCURRENT_STREAM_STREAM = 'concurrent stream stream' |
||||
CANCEL_UNARY_UNARY = 'cancel unary unary' |
||||
CANCEL_UNARY_STREAM = 'cancel unary stream' |
||||
INFINITE_REQUEST_STREAM = 'infinite request stream' |
||||
|
||||
|
||||
class Outcome(collections.namedtuple('Outcome', ('kind', 'code', 'details'))): |
||||
"""Outcome of a client application scenario. |
||||
|
||||
Attributes: |
||||
kind: A Kind value describing the overall kind of scenario execution. |
||||
code: A grpc.StatusCode value. Only valid if kind is Kind.RPC_ERROR. |
||||
details: A status details string. Only valid if kind is Kind.RPC_ERROR. |
||||
""" |
||||
|
||||
@enum.unique |
||||
class Kind(enum.Enum): |
||||
SATISFACTORY = 'satisfactory' |
||||
UNSATISFACTORY = 'unsatisfactory' |
||||
RPC_ERROR = 'rpc error' |
||||
|
||||
|
||||
_SATISFACTORY_OUTCOME = Outcome(Outcome.Kind.SATISFACTORY, None, None) |
||||
_UNSATISFACTORY_OUTCOME = Outcome(Outcome.Kind.UNSATISFACTORY, None, None) |
||||
|
||||
|
||||
class _Pipe(object): |
||||
|
||||
def __init__(self): |
||||
self._condition = threading.Condition() |
||||
self._values = [] |
||||
self._open = True |
||||
|
||||
def __iter__(self): |
||||
return self |
||||
|
||||
def _next(self): |
||||
with self._condition: |
||||
while True: |
||||
if self._values: |
||||
return self._values.pop(0) |
||||
elif not self._open: |
||||
raise StopIteration() |
||||
else: |
||||
self._condition.wait() |
||||
|
||||
def __next__(self): # (Python 3 Iterator Protocol) |
||||
return self._next() |
||||
|
||||
def next(self): # (Python 2 Iterator Protocol) |
||||
return self._next() |
||||
|
||||
def add(self, value): |
||||
with self._condition: |
||||
self._values.append(value) |
||||
self._condition.notify_all() |
||||
|
||||
def close(self): |
||||
with self._condition: |
||||
self._open = False |
||||
self._condition.notify_all() |
||||
|
||||
|
||||
def _run_unary_unary(stub): |
||||
response = stub.UnUn(_application_common.UNARY_UNARY_REQUEST) |
||||
if _application_common.UNARY_UNARY_RESPONSE == response: |
||||
return _SATISFACTORY_OUTCOME |
||||
else: |
||||
return _UNSATISFACTORY_OUTCOME |
||||
|
||||
|
||||
def _run_unary_stream(stub): |
||||
response_iterator = stub.UnStre(_application_common.UNARY_STREAM_REQUEST) |
||||
try: |
||||
next(response_iterator) |
||||
except StopIteration: |
||||
return _SATISFACTORY_OUTCOME |
||||
else: |
||||
return _UNSATISFACTORY_OUTCOME |
||||
|
||||
|
||||
def _run_stream_unary(stub): |
||||
response, call = stub.StreUn.with_call( |
||||
iter((_application_common.STREAM_UNARY_REQUEST,) * 3)) |
||||
if (_application_common.STREAM_UNARY_RESPONSE == response and |
||||
call.code() is grpc.StatusCode.OK): |
||||
return _SATISFACTORY_OUTCOME |
||||
else: |
||||
return _UNSATISFACTORY_OUTCOME |
||||
|
||||
|
||||
def _run_stream_stream(stub): |
||||
request_pipe = _Pipe() |
||||
response_iterator = stub.StreStre(iter(request_pipe)) |
||||
request_pipe.add(_application_common.STREAM_STREAM_REQUEST) |
||||
first_responses = next(response_iterator), next(response_iterator), |
||||
request_pipe.add(_application_common.STREAM_STREAM_REQUEST) |
||||
second_responses = next(response_iterator), next(response_iterator), |
||||
request_pipe.close() |
||||
try: |
||||
next(response_iterator) |
||||
except StopIteration: |
||||
unexpected_extra_response = False |
||||
else: |
||||
unexpected_extra_response = True |
||||
if (first_responses == _application_common.TWO_STREAM_STREAM_RESPONSES and |
||||
second_responses == _application_common.TWO_STREAM_STREAM_RESPONSES |
||||
and not unexpected_extra_response): |
||||
return _SATISFACTORY_OUTCOME |
||||
else: |
||||
return _UNSATISFACTORY_OUTCOME |
||||
|
||||
|
||||
def _run_concurrent_stream_unary(stub): |
||||
future_calls = tuple( |
||||
stub.StreUn.future( |
||||
iter((_application_common.STREAM_UNARY_REQUEST,) * 3)) |
||||
for _ in range(test_constants.THREAD_CONCURRENCY)) |
||||
for future_call in future_calls: |
||||
if future_call.code() is grpc.StatusCode.OK: |
||||
response = future_call.result() |
||||
if _application_common.STREAM_UNARY_RESPONSE != response: |
||||
return _UNSATISFACTORY_OUTCOME |
||||
else: |
||||
return _UNSATISFACTORY_OUTCOME |
||||
else: |
||||
return _SATISFACTORY_OUTCOME |
||||
|
||||
|
||||
def _run_concurrent_stream_stream(stub): |
||||
condition = threading.Condition() |
||||
outcomes = [None] * test_constants.RPC_CONCURRENCY |
||||
|
||||
def run_stream_stream(index): |
||||
outcome = _run_stream_stream(stub) |
||||
with condition: |
||||
outcomes[index] = outcome |
||||
condition.notify() |
||||
|
||||
for index in range(test_constants.RPC_CONCURRENCY): |
||||
thread = threading.Thread(target=run_stream_stream, args=(index,)) |
||||
thread.start() |
||||
with condition: |
||||
while True: |
||||
if all(outcomes): |
||||
for outcome in outcomes: |
||||
if outcome.kind is not Outcome.Kind.SATISFACTORY: |
||||
return _UNSATISFACTORY_OUTCOME |
||||
else: |
||||
return _SATISFACTORY_OUTCOME |
||||
else: |
||||
condition.wait() |
||||
|
||||
|
||||
def _run_cancel_unary_unary(stub): |
||||
response_future_call = stub.UnUn.future( |
||||
_application_common.UNARY_UNARY_REQUEST) |
||||
initial_metadata = response_future_call.initial_metadata() |
||||
cancelled = response_future_call.cancel() |
||||
if initial_metadata is not None and cancelled: |
||||
return _SATISFACTORY_OUTCOME |
||||
else: |
||||
return _UNSATISFACTORY_OUTCOME |
||||
|
||||
|
||||
def _run_infinite_request_stream(stub): |
||||
|
||||
def infinite_request_iterator(): |
||||
while True: |
||||
yield _application_common.STREAM_UNARY_REQUEST |
||||
|
||||
response_future_call = stub.StreUn.future( |
||||
infinite_request_iterator(), |
||||
timeout=_application_common.INFINITE_REQUEST_STREAM_TIMEOUT) |
||||
if response_future_call.code() is grpc.StatusCode.DEADLINE_EXCEEDED: |
||||
return _SATISFACTORY_OUTCOME |
||||
else: |
||||
return _UNSATISFACTORY_OUTCOME |
||||
|
||||
|
||||
def run(scenario, channel): |
||||
stub = services_pb2_grpc.FirstServiceStub(channel) |
||||
try: |
||||
if scenario is Scenario.UNARY_UNARY: |
||||
return _run_unary_unary(stub) |
||||
elif scenario is Scenario.UNARY_STREAM: |
||||
return _run_unary_stream(stub) |
||||
elif scenario is Scenario.STREAM_UNARY: |
||||
return _run_stream_unary(stub) |
||||
elif scenario is Scenario.STREAM_STREAM: |
||||
return _run_stream_stream(stub) |
||||
elif scenario is Scenario.CONCURRENT_STREAM_UNARY: |
||||
return _run_concurrent_stream_unary(stub) |
||||
elif scenario is Scenario.CONCURRENT_STREAM_STREAM: |
||||
return _run_concurrent_stream_stream(stub) |
||||
elif scenario is Scenario.CANCEL_UNARY_UNARY: |
||||
return _run_cancel_unary_unary(stub) |
||||
elif scenario is Scenario.INFINITE_REQUEST_STREAM: |
||||
return _run_infinite_request_stream(stub) |
||||
except grpc.RpcError as rpc_error: |
||||
return Outcome(Outcome.Kind.RPC_ERROR, |
||||
rpc_error.code(), rpc_error.details()) |
||||
|
||||
|
||||
_IMPLEMENTATIONS = { |
||||
Scenario.UNARY_UNARY: _run_unary_unary, |
||||
Scenario.UNARY_STREAM: _run_unary_stream, |
||||
Scenario.STREAM_UNARY: _run_stream_unary, |
||||
Scenario.STREAM_STREAM: _run_stream_stream, |
||||
Scenario.CONCURRENT_STREAM_UNARY: _run_concurrent_stream_unary, |
||||
Scenario.CONCURRENT_STREAM_STREAM: _run_concurrent_stream_stream, |
||||
Scenario.CANCEL_UNARY_UNARY: _run_cancel_unary_unary, |
||||
Scenario.INFINITE_REQUEST_STREAM: _run_infinite_request_stream, |
||||
} |
||||
|
||||
|
||||
def run(scenario, channel): |
||||
stub = services_pb2_grpc.FirstServiceStub(channel) |
||||
try: |
||||
return _IMPLEMENTATIONS[scenario](stub) |
||||
except grpc.RpcError as rpc_error: |
||||
return Outcome(Outcome.Kind.RPC_ERROR, |
||||
rpc_error.code(), rpc_error.details()) |
@ -0,0 +1,306 @@ |
||||
# Copyright 2017 gRPC authors. |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
|
||||
from concurrent import futures |
||||
import time |
||||
import unittest |
||||
|
||||
import grpc |
||||
from grpc.framework.foundation import logging_pool |
||||
from tests.unit.framework.common import test_constants |
||||
import grpc_testing |
||||
|
||||
from tests.testing import _application_common |
||||
from tests.testing import _application_testing_common |
||||
from tests.testing import _client_application |
||||
from tests.testing.proto import requests_pb2 |
||||
from tests.testing.proto import services_pb2 |
||||
|
||||
|
||||
# TODO(https://github.com/google/protobuf/issues/3452): Drop this skip. |
||||
@unittest.skipIf( |
||||
services_pb2.DESCRIPTOR.services_by_name.get('FirstService') is None, |
||||
'Fix protobuf issue 3452!') |
||||
class ClientTest(unittest.TestCase): |
||||
|
||||
def setUp(self): |
||||
# In this test the client-side application under test executes in |
||||
# a separate thread while we retain use of the test thread to "play |
||||
# server". |
||||
self._client_execution_thread_pool = logging_pool.pool(1) |
||||
|
||||
self._fake_time = grpc_testing.strict_fake_time(time.time()) |
||||
self._real_time = grpc_testing.strict_real_time() |
||||
self._fake_time_channel = grpc_testing.channel( |
||||
services_pb2.DESCRIPTOR.services_by_name.values(), self._fake_time) |
||||
self._real_time_channel = grpc_testing.channel( |
||||
services_pb2.DESCRIPTOR.services_by_name.values(), self._real_time) |
||||
|
||||
def tearDown(self): |
||||
self._client_execution_thread_pool.shutdown(wait=True) |
||||
|
||||
def test_successful_unary_unary(self): |
||||
application_future = self._client_execution_thread_pool.submit( |
||||
_client_application.run, _client_application.Scenario.UNARY_UNARY, |
||||
self._real_time_channel) |
||||
invocation_metadata, request, rpc = ( |
||||
self._real_time_channel.take_unary_unary( |
||||
_application_testing_common.FIRST_SERVICE_UNUN)) |
||||
rpc.send_initial_metadata(()) |
||||
rpc.terminate(_application_common.UNARY_UNARY_RESPONSE, (), |
||||
grpc.StatusCode.OK, '') |
||||
application_return_value = application_future.result() |
||||
|
||||
self.assertEqual(_application_common.UNARY_UNARY_REQUEST, request) |
||||
self.assertIs(application_return_value.kind, |
||||
_client_application.Outcome.Kind.SATISFACTORY) |
||||
|
||||
def test_successful_unary_stream(self): |
||||
application_future = self._client_execution_thread_pool.submit( |
||||
_client_application.run, _client_application.Scenario.UNARY_STREAM, |
||||
self._fake_time_channel) |
||||
invocation_metadata, request, rpc = ( |
||||
self._fake_time_channel.take_unary_stream( |
||||
_application_testing_common.FIRST_SERVICE_UNSTRE)) |
||||
rpc.send_initial_metadata(()) |
||||
rpc.terminate((), grpc.StatusCode.OK, '') |
||||
application_return_value = application_future.result() |
||||
|
||||
self.assertEqual(_application_common.UNARY_STREAM_REQUEST, request) |
||||
self.assertIs(application_return_value.kind, |
||||
_client_application.Outcome.Kind.SATISFACTORY) |
||||
|
||||
def test_successful_stream_unary(self): |
||||
application_future = self._client_execution_thread_pool.submit( |
||||
_client_application.run, _client_application.Scenario.STREAM_UNARY, |
||||
self._real_time_channel) |
||||
invocation_metadata, rpc = self._real_time_channel.take_stream_unary( |
||||
_application_testing_common.FIRST_SERVICE_STREUN) |
||||
rpc.send_initial_metadata(()) |
||||
first_request = rpc.take_request() |
||||
second_request = rpc.take_request() |
||||
third_request = rpc.take_request() |
||||
rpc.requests_closed() |
||||
rpc.terminate(_application_common.STREAM_UNARY_RESPONSE, (), |
||||
grpc.StatusCode.OK, '') |
||||
application_return_value = application_future.result() |
||||
|
||||
self.assertEqual(_application_common.STREAM_UNARY_REQUEST, |
||||
first_request) |
||||
self.assertEqual(_application_common.STREAM_UNARY_REQUEST, |
||||
second_request) |
||||
self.assertEqual(_application_common.STREAM_UNARY_REQUEST, |
||||
third_request) |
||||
self.assertIs(application_return_value.kind, |
||||
_client_application.Outcome.Kind.SATISFACTORY) |
||||
|
||||
def test_successful_stream_stream(self): |
||||
application_future = self._client_execution_thread_pool.submit( |
||||
_client_application.run, _client_application.Scenario.STREAM_STREAM, |
||||
self._fake_time_channel) |
||||
invocation_metadata, rpc = self._fake_time_channel.take_stream_stream( |
||||
_application_testing_common.FIRST_SERVICE_STRESTRE) |
||||
first_request = rpc.take_request() |
||||
rpc.send_response(_application_common.STREAM_STREAM_RESPONSE) |
||||
rpc.send_response(_application_common.STREAM_STREAM_RESPONSE) |
||||
second_request = rpc.take_request() |
||||
rpc.send_response(_application_common.STREAM_STREAM_RESPONSE) |
||||
rpc.send_response(_application_common.STREAM_STREAM_RESPONSE) |
||||
rpc.requests_closed() |
||||
rpc.terminate((), grpc.StatusCode.OK, '') |
||||
application_return_value = application_future.result() |
||||
|
||||
self.assertEqual(_application_common.STREAM_STREAM_REQUEST, |
||||
first_request) |
||||
self.assertEqual(_application_common.STREAM_STREAM_REQUEST, |
||||
second_request) |
||||
self.assertIs(application_return_value.kind, |
||||
_client_application.Outcome.Kind.SATISFACTORY) |
||||
|
||||
def test_concurrent_stream_stream(self): |
||||
application_future = self._client_execution_thread_pool.submit( |
||||
_client_application.run, |
||||
_client_application.Scenario.CONCURRENT_STREAM_STREAM, |
||||
self._real_time_channel) |
||||
rpcs = [] |
||||
for _ in range(test_constants.RPC_CONCURRENCY): |
||||
invocation_metadata, rpc = ( |
||||
self._real_time_channel.take_stream_stream( |
||||
_application_testing_common.FIRST_SERVICE_STRESTRE)) |
||||
rpcs.append(rpc) |
||||
requests = {} |
||||
for rpc in rpcs: |
||||
requests[rpc] = [rpc.take_request()] |
||||
for rpc in rpcs: |
||||
rpc.send_response(_application_common.STREAM_STREAM_RESPONSE) |
||||
rpc.send_response(_application_common.STREAM_STREAM_RESPONSE) |
||||
for rpc in rpcs: |
||||
requests[rpc].append(rpc.take_request()) |
||||
for rpc in rpcs: |
||||
rpc.send_response(_application_common.STREAM_STREAM_RESPONSE) |
||||
rpc.send_response(_application_common.STREAM_STREAM_RESPONSE) |
||||
for rpc in rpcs: |
||||
rpc.requests_closed() |
||||
for rpc in rpcs: |
||||
rpc.terminate((), grpc.StatusCode.OK, '') |
||||
application_return_value = application_future.result() |
||||
|
||||
for requests_of_one_rpc in requests.values(): |
||||
for request in requests_of_one_rpc: |
||||
self.assertEqual(_application_common.STREAM_STREAM_REQUEST, |
||||
request) |
||||
self.assertIs(application_return_value.kind, |
||||
_client_application.Outcome.Kind.SATISFACTORY) |
||||
|
||||
def test_cancelled_unary_unary(self): |
||||
application_future = self._client_execution_thread_pool.submit( |
||||
_client_application.run, |
||||
_client_application.Scenario.CANCEL_UNARY_UNARY, |
||||
self._fake_time_channel) |
||||
invocation_metadata, request, rpc = ( |
||||
self._fake_time_channel.take_unary_unary( |
||||
_application_testing_common.FIRST_SERVICE_UNUN)) |
||||
rpc.send_initial_metadata(()) |
||||
rpc.cancelled() |
||||
application_return_value = application_future.result() |
||||
|
||||
self.assertEqual(_application_common.UNARY_UNARY_REQUEST, request) |
||||
self.assertIs(application_return_value.kind, |
||||
_client_application.Outcome.Kind.SATISFACTORY) |
||||
|
||||
def test_status_stream_unary(self): |
||||
application_future = self._client_execution_thread_pool.submit( |
||||
_client_application.run, |
||||
_client_application.Scenario.CONCURRENT_STREAM_UNARY, |
||||
self._fake_time_channel) |
||||
rpcs = tuple( |
||||
self._fake_time_channel.take_stream_unary( |
||||
_application_testing_common.FIRST_SERVICE_STREUN)[1] |
||||
for _ in range(test_constants.THREAD_CONCURRENCY)) |
||||
for rpc in rpcs: |
||||
rpc.take_request() |
||||
rpc.take_request() |
||||
rpc.take_request() |
||||
rpc.requests_closed() |
||||
rpc.send_initial_metadata(( |
||||
('my_metadata_key', 'My Metadata Value!',),)) |
||||
for rpc in rpcs[:-1]: |
||||
rpc.terminate(_application_common.STREAM_UNARY_RESPONSE, (), |
||||
grpc.StatusCode.OK, '') |
||||
rpcs[-1].terminate(_application_common.STREAM_UNARY_RESPONSE, (), |
||||
grpc.StatusCode.RESOURCE_EXHAUSTED, |
||||
'nope; not able to handle all those RPCs!') |
||||
application_return_value = application_future.result() |
||||
|
||||
self.assertIs(application_return_value.kind, |
||||
_client_application.Outcome.Kind.UNSATISFACTORY) |
||||
|
||||
def test_status_stream_stream(self): |
||||
code = grpc.StatusCode.DEADLINE_EXCEEDED |
||||
details = 'test deadline exceeded!' |
||||
|
||||
application_future = self._client_execution_thread_pool.submit( |
||||
_client_application.run, _client_application.Scenario.STREAM_STREAM, |
||||
self._real_time_channel) |
||||
invocation_metadata, rpc = self._real_time_channel.take_stream_stream( |
||||
_application_testing_common.FIRST_SERVICE_STRESTRE) |
||||
first_request = rpc.take_request() |
||||
rpc.send_response(_application_common.STREAM_STREAM_RESPONSE) |
||||
rpc.send_response(_application_common.STREAM_STREAM_RESPONSE) |
||||
second_request = rpc.take_request() |
||||
rpc.send_response(_application_common.STREAM_STREAM_RESPONSE) |
||||
rpc.send_response(_application_common.STREAM_STREAM_RESPONSE) |
||||
rpc.requests_closed() |
||||
rpc.terminate((), code, details) |
||||
application_return_value = application_future.result() |
||||
|
||||
self.assertEqual(_application_common.STREAM_STREAM_REQUEST, |
||||
first_request) |
||||
self.assertEqual(_application_common.STREAM_STREAM_REQUEST, |
||||
second_request) |
||||
self.assertIs(application_return_value.kind, |
||||
_client_application.Outcome.Kind.RPC_ERROR) |
||||
self.assertIs(application_return_value.code, code) |
||||
self.assertEqual(application_return_value.details, details) |
||||
|
||||
def test_misbehaving_server_unary_unary(self): |
||||
application_future = self._client_execution_thread_pool.submit( |
||||
_client_application.run, _client_application.Scenario.UNARY_UNARY, |
||||
self._fake_time_channel) |
||||
invocation_metadata, request, rpc = ( |
||||
self._fake_time_channel.take_unary_unary( |
||||
_application_testing_common.FIRST_SERVICE_UNUN)) |
||||
rpc.send_initial_metadata(()) |
||||
rpc.terminate(_application_common.ERRONEOUS_UNARY_UNARY_RESPONSE, (), |
||||
grpc.StatusCode.OK, '') |
||||
application_return_value = application_future.result() |
||||
|
||||
self.assertEqual(_application_common.UNARY_UNARY_REQUEST, request) |
||||
self.assertIs(application_return_value.kind, |
||||
_client_application.Outcome.Kind.UNSATISFACTORY) |
||||
|
||||
def test_misbehaving_server_stream_stream(self): |
||||
application_future = self._client_execution_thread_pool.submit( |
||||
_client_application.run, _client_application.Scenario.STREAM_STREAM, |
||||
self._real_time_channel) |
||||
invocation_metadata, rpc = self._real_time_channel.take_stream_stream( |
||||
_application_testing_common.FIRST_SERVICE_STRESTRE) |
||||
first_request = rpc.take_request() |
||||
rpc.send_response(_application_common.STREAM_STREAM_RESPONSE) |
||||
rpc.send_response(_application_common.STREAM_STREAM_RESPONSE) |
||||
rpc.send_response(_application_common.STREAM_STREAM_RESPONSE) |
||||
second_request = rpc.take_request() |
||||
rpc.send_response(_application_common.STREAM_STREAM_RESPONSE) |
||||
rpc.send_response(_application_common.STREAM_STREAM_RESPONSE) |
||||
rpc.send_response(_application_common.STREAM_STREAM_RESPONSE) |
||||
rpc.requests_closed() |
||||
rpc.terminate((), grpc.StatusCode.OK, '') |
||||
application_return_value = application_future.result() |
||||
|
||||
self.assertEqual(_application_common.STREAM_STREAM_REQUEST, |
||||
first_request) |
||||
self.assertEqual(_application_common.STREAM_STREAM_REQUEST, |
||||
second_request) |
||||
self.assertIs(application_return_value.kind, |
||||
_client_application.Outcome.Kind.UNSATISFACTORY) |
||||
|
||||
def test_infinite_request_stream_real_time(self): |
||||
application_future = self._client_execution_thread_pool.submit( |
||||
_client_application.run, |
||||
_client_application.Scenario.INFINITE_REQUEST_STREAM, |
||||
self._real_time_channel) |
||||
invocation_metadata, rpc = self._real_time_channel.take_stream_unary( |
||||
_application_testing_common.FIRST_SERVICE_STREUN) |
||||
rpc.send_initial_metadata(()) |
||||
first_request = rpc.take_request() |
||||
second_request = rpc.take_request() |
||||
third_request = rpc.take_request() |
||||
self._real_time.sleep_for( |
||||
_application_common.INFINITE_REQUEST_STREAM_TIMEOUT) |
||||
rpc.terminate(_application_common.STREAM_UNARY_RESPONSE, (), |
||||
grpc.StatusCode.DEADLINE_EXCEEDED, '') |
||||
application_return_value = application_future.result() |
||||
|
||||
self.assertEqual(_application_common.STREAM_UNARY_REQUEST, |
||||
first_request) |
||||
self.assertEqual(_application_common.STREAM_UNARY_REQUEST, |
||||
second_request) |
||||
self.assertEqual(_application_common.STREAM_UNARY_REQUEST, |
||||
third_request) |
||||
self.assertIs(application_return_value.kind, |
||||
_client_application.Outcome.Kind.SATISFACTORY) |
||||
|
||||
|
||||
if __name__ == '__main__': |
||||
unittest.main(verbosity=2) |
@ -0,0 +1,13 @@ |
||||
# Copyright 2017 gRPC authors. |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
@ -0,0 +1,29 @@ |
||||
// Copyright 2015 gRPC authors. |
||||
// |
||||
// Licensed under the Apache License, Version 2.0 (the "License"); |
||||
// you may not use this file except in compliance with the License. |
||||
// You may obtain a copy of the License at |
||||
// |
||||
// http://www.apache.org/licenses/LICENSE-2.0 |
||||
// |
||||
// Unless required by applicable law or agreed to in writing, software |
||||
// distributed under the License is distributed on an "AS IS" BASIS, |
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
// See the License for the specific language governing permissions and |
||||
// limitations under the License. |
||||
|
||||
syntax = "proto3"; |
||||
|
||||
package tests_of_grpc_testing; |
||||
|
||||
message Up { |
||||
int32 first_up_field = 1; |
||||
} |
||||
|
||||
message Charm { |
||||
int32 first_charm_field = 1; |
||||
} |
||||
|
||||
message Top { |
||||
int32 first_top_field = 1; |
||||
} |
@ -0,0 +1,42 @@ |
||||
// Copyright 2017 gRPC authors. |
||||
// |
||||
// Licensed under the Apache License, Version 2.0 (the "License"); |
||||
// you may not use this file except in compliance with the License. |
||||
// You may obtain a copy of the License at |
||||
// |
||||
// http://www.apache.org/licenses/LICENSE-2.0 |
||||
// |
||||
// Unless required by applicable law or agreed to in writing, software |
||||
// distributed under the License is distributed on an "AS IS" BASIS, |
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
// See the License for the specific language governing permissions and |
||||
// limitations under the License. |
||||
|
||||
syntax = "proto3"; |
||||
|
||||
import "tests/testing/proto/requests.proto"; |
||||
|
||||
package tests_of_grpc_testing; |
||||
|
||||
message Down { |
||||
int32 first_down_field = 1; |
||||
} |
||||
|
||||
message Strange { |
||||
int32 first_strange_field = 1; |
||||
} |
||||
|
||||
message Bottom { |
||||
int32 first_bottom_field = 1; |
||||
} |
||||
|
||||
service FirstService { |
||||
rpc UnUn(Up) returns (Down); |
||||
rpc UnStre(Charm) returns (stream Strange); |
||||
rpc StreUn(stream Charm) returns (Strange); |
||||
rpc StreStre(stream Top) returns (stream Bottom); |
||||
} |
||||
|
||||
service SecondService { |
||||
rpc UnStre(Strange) returns (stream Charm); |
||||
} |
Loading…
Reference in new issue