From 02a7d4272aee5aa6e539bc69c81c7b99a393eb73 Mon Sep 17 00:00:00 2001
From: ncteisen <ncteisen@gmail.com>
Date: Tue, 13 Dec 2016 13:48:14 -0800
Subject: [PATCH] Add python client to test negative http2 conditions

Documentation for the new tests can be found https://github.com/grpc/grpc/blob/master/doc/negative-http2-interop-test-descriptions.md
---
 .../tests/http2/_negative_http2_client.py     | 153 ++++++++++++++++++
 1 file changed, 153 insertions(+)
 create mode 100644 src/python/grpcio_tests/tests/http2/_negative_http2_client.py

diff --git a/src/python/grpcio_tests/tests/http2/_negative_http2_client.py b/src/python/grpcio_tests/tests/http2/_negative_http2_client.py
new file mode 100644
index 0000000000..f8604683b3
--- /dev/null
+++ b/src/python/grpcio_tests/tests/http2/_negative_http2_client.py
@@ -0,0 +1,153 @@
+# Copyright 2016, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""The Python client used to test negative http2 conditions."""
+
+import argparse
+
+import grpc
+from src.proto.grpc.testing import test_pb2
+from src.proto.grpc.testing import messages_pb2
+
+def _validate_payload_type_and_length(response, expected_type, expected_length):
+  if response.payload.type is not expected_type:
+    raise ValueError(
+      'expected payload type %s, got %s' %
+          (expected_type, type(response.payload.type)))
+  elif len(response.payload.body) != expected_length:
+    raise ValueError(
+      'expected payload body size %d, got %d' %
+          (expected_length, len(response.payload.body)))
+
+def _expect_status_code(call, expected_code):
+  if call.code() != expected_code:
+    raise ValueError(
+      'expected code %s, got %s' % (expected_code, call.code()))
+
+def _expect_status_details(call, expected_details):
+  if call.details() != expected_details:
+    raise ValueError(
+      'expected message %s, got %s' % (expected_details, call.details()))
+
+def _validate_status_code_and_details(call, expected_code, expected_details):
+  _expect_status_code(call, expected_code)
+  _expect_status_details(call, expected_details)
+
+# common requests
+_REQUEST_SIZE = 314159
+_RESPONSE_SIZE = 271828
+
+_SIMPLE_REQUEST = messages_pb2.SimpleRequest(
+    response_type=messages_pb2.COMPRESSABLE,
+    response_size=_RESPONSE_SIZE,
+    payload=messages_pb2.Payload(body=b'\x00' * _REQUEST_SIZE))
+
+def _goaway(stub):
+  first_response = stub.UnaryCall(_SIMPLE_REQUEST)
+  _validate_payload_type_and_length(first_response, 
+      messages_pb2.COMPRESSABLE, _RESPONSE_SIZE)
+  second_response = stub.UnaryCall(_SIMPLE_REQUEST)
+  _validate_payload_type_and_length(second_response, 
+      messages_pb2.COMPRESSABLE, _RESPONSE_SIZE)
+
+def _rst_after_header(stub):
+  resp_future = stub.UnaryCall.future(_SIMPLE_REQUEST)
+  _validate_status_code_and_details(resp_future, grpc.StatusCode.UNAVAILABLE, "")
+
+def _rst_during_data(stub):
+  resp_future = stub.UnaryCall.future(_SIMPLE_REQUEST)
+  _validate_status_code_and_details(resp_future, grpc.StatusCode.UNKNOWN, "")
+
+def _rst_after_data(stub):
+  resp_future = stub.UnaryCall.future(_SIMPLE_REQUEST)
+  _validate_payload_type_and_length(next(resp_future),
+      messages_pb2.COMPRESSABLE, _RESPONSE_SIZE)
+  _validate_status_code_and_details(resp_future, grpc.StatusCode.UNKNOWN, "")
+
+def _ping(stub):
+  response = stub.UnaryCall(_SIMPLE_REQUEST)
+  _validate_payload_type_and_length(response, 
+      messages_pb2.COMPRESSABLE, _RESPONSE_SIZE)
+
+def _max_streams(stub):
+  # send one req to ensure server sets MAX_STREAMS
+  response = stub.UnaryCall(_SIMPLE_REQUEST)
+  _validate_payload_type_and_length(response, 
+      messages_pb2.COMPRESSABLE, _RESPONSE_SIZE)
+
+  # give the streams a workout
+  futures = []
+  for _ in range(15):
+    futures.append(stub.UnaryCall.future(_SIMPLE_REQUEST))
+  for future in futures:
+    _validate_payload_type_and_length(future.result(),
+        messages_pb2.COMPRESSABLE, _RESPONSE_SIZE)
+
+def _run_test_case(test_case, stub):
+  if test_case == 'goaway':
+    _goaway(stub)
+  elif test_case == 'rst_after_header':
+    _rst_after_header(stub)
+  elif test_case == 'rst_during_data':
+    _rst_during_data(stub)
+  elif test_case == 'rst_after_data':
+    _rst_after_data(stub)
+  elif test_case =='ping':
+    _ping(stub)
+  elif test_case == 'max_streams':
+    _max_streams(stub)
+  else:
+    raise ValueError("Invalid test case: %s" % test_case)
+
+def _args():
+  parser = argparse.ArgumentParser()
+  parser.add_argument(
+      '--server_host', help='the host to which to connect', type=str,
+      default="127.0.0.1")
+  parser.add_argument(
+      '--server_port', help='the port to which to connect', type=int,
+      default="8080")
+  parser.add_argument(
+      '--test_case', help='the test case to execute', type=str,
+      default="goaway")
+  return parser.parse_args()
+
+def _stub(server_host, server_port):
+  target = '{}:{}'.format(server_host, server_port)
+  channel = grpc.insecure_channel(target)
+  return test_pb2.TestServiceStub(channel)
+
+def main():
+  args = _args()
+  stub = _stub(args.server_host, args.server_port)
+  _run_test_case(args.test_case, stub)
+
+
+if __name__ == '__main__':
+  main()
-- 
GitLab