aboutsummaryrefslogtreecommitdiffhomepage
path: root/test/core
diff options
context:
space:
mode:
Diffstat (limited to 'test/core')
-rw-r--r--test/core/channel/channel_stack_test.c139
-rw-r--r--test/core/channel/metadata_buffer_test.c194
-rw-r--r--test/core/compression/message_compress_test.c193
-rw-r--r--test/core/echo/client.c139
-rw-r--r--test/core/echo/echo_test.c106
-rw-r--r--test/core/echo/server.c149
-rw-r--r--test/core/end2end/README10
-rw-r--r--test/core/end2end/cq_verifier.c473
-rw-r--r--test/core/end2end/cq_verifier.h73
-rw-r--r--test/core/end2end/data/ca_cert.c102
-rw-r--r--test/core/end2end/data/server1_cert.c116
-rw-r--r--test/core/end2end/data/server1_key.c109
-rw-r--r--test/core/end2end/data/ssl_test_data.h44
-rw-r--r--test/core/end2end/end2end_tests.c1285
-rw-r--r--test/core/end2end/end2end_tests.h66
-rw-r--r--test/core/end2end/fixtures/chttp2_fake_security.c139
-rw-r--r--test/core/end2end/fixtures/chttp2_fullstack.c123
-rw-r--r--test/core/end2end/fixtures/chttp2_simple_ssl_fullstack.c146
-rw-r--r--test/core/end2end/fixtures/chttp2_simple_ssl_with_oauth2_fullstack.c146
-rw-r--r--test/core/end2end/fixtures/chttp2_socket_pair.c169
-rwxr-xr-xtest/core/end2end/gen_build_json.py82
-rw-r--r--test/core/end2end/no_server_test.c83
-rw-r--r--test/core/end2end/tests/cancel_after_accept.c166
-rw-r--r--test/core/end2end/tests/cancel_after_accept_and_writes_closed.c173
-rw-r--r--test/core/end2end/tests/cancel_after_invoke.c150
-rw-r--r--test/core/end2end/tests/cancel_before_invoke.c138
-rw-r--r--test/core/end2end/tests/cancel_in_a_vacuum.c138
-rw-r--r--test/core/end2end/tests/early_server_shutdown_finishes_inflight_calls.c158
-rw-r--r--test/core/end2end/tests/early_server_shutdown_finishes_tags.c127
-rw-r--r--test/core/end2end/tests/invoke_large_request.c184
-rw-r--r--test/core/end2end/tests/max_concurrent_streams.c257
-rw-r--r--test/core/end2end/tests/no_op.c112
-rw-r--r--test/core/end2end/tests/ping_pong_streaming.c201
-rw-r--r--test/core/end2end/tests/request_response_with_metadata_and_payload.c208
-rw-r--r--test/core/end2end/tests/request_response_with_payload.c207
-rw-r--r--test/core/end2end/tests/request_with_large_metadata.c172
-rw-r--r--test/core/end2end/tests/request_with_payload.c171
-rw-r--r--test/core/end2end/tests/simple_delayed_request.c174
-rw-r--r--test/core/end2end/tests/simple_request.c231
-rw-r--r--test/core/end2end/tests/thread_stress_test.c325
-rw-r--r--test/core/end2end/tests/writes_done_hangs_with_pending_read.c198
-rw-r--r--test/core/endpoint/endpoint_tests.c433
-rw-r--r--test/core/endpoint/endpoint_tests.h57
-rw-r--r--test/core/endpoint/resolve_address_test.c135
-rw-r--r--test/core/endpoint/secure_endpoint_test.c222
-rw-r--r--test/core/endpoint/tcp_client_test.c177
-rw-r--r--test/core/endpoint/tcp_server_test.c168
-rw-r--r--test/core/endpoint/tcp_test.c517
-rw-r--r--test/core/eventmanager/em_pipe_test.c200
-rw-r--r--test/core/eventmanager/em_test.c725
-rw-r--r--test/core/fling/client.c196
-rw-r--r--test/core/fling/fling_stream_test.c103
-rw-r--r--test/core/fling/fling_test.c103
-rw-r--r--test/core/fling/server.c159
-rw-r--r--test/core/httpcli/format_request_test.c166
-rw-r--r--test/core/httpcli/httpcli_test.c101
-rw-r--r--test/core/httpcli/parser_test.c155
-rw-r--r--test/core/network_benchmarks/low_level_ping_pong.c626
-rw-r--r--test/core/security/credentials_test.c167
-rw-r--r--test/core/statistics/census_stub_test.c75
-rw-r--r--test/core/statistics/hash_table_test.c288
-rw-r--r--test/core/statistics/log_tests.c568
-rw-r--r--test/core/statistics/log_tests.h50
-rw-r--r--test/core/statistics/multiple_writers_circular_buffer_test.c46
-rw-r--r--test/core/statistics/multiple_writers_test.c46
-rw-r--r--test/core/statistics/performance_test.c46
-rw-r--r--test/core/statistics/quick_test.c54
-rw-r--r--test/core/statistics/window_stats_test.c317
-rw-r--r--test/core/support/cancellable_test.c160
-rw-r--r--test/core/support/cmdline_test.c293
-rw-r--r--test/core/support/histogram_test.c178
-rw-r--r--test/core/support/host_port_test.c72
-rw-r--r--test/core/support/log_test.c47
-rw-r--r--test/core/support/murmur_hash_test.c87
-rw-r--r--test/core/support/slice_buffer_test.c70
-rw-r--r--test/core/support/slice_test.c227
-rw-r--r--test/core/support/string_test.c158
-rw-r--r--test/core/support/sync_test.c451
-rw-r--r--test/core/support/thd_test.c90
-rw-r--r--test/core/support/time_test.c255
-rw-r--r--test/core/surface/byte_buffer_reader_test.c111
-rw-r--r--test/core/surface/completion_queue_benchmark.c168
-rw-r--r--test/core/surface/completion_queue_test.c435
-rw-r--r--test/core/surface/lame_client_test.c82
-rw-r--r--test/core/transport/chttp2/hpack_parser_test.c223
-rw-r--r--test/core/transport/chttp2/hpack_table_test.c269
-rw-r--r--test/core/transport/chttp2/status_conversion_test.c138
-rw-r--r--test/core/transport/chttp2/stream_encoder_test.c320
-rw-r--r--test/core/transport/chttp2/stream_map_test.c228
-rw-r--r--test/core/transport/chttp2/timeout_encoding_test.c140
-rw-r--r--test/core/transport/chttp2_transport_end2end_test.c139
-rw-r--r--test/core/transport/metadata_test.c258
-rw-r--r--test/core/transport/stream_op_test.c125
-rw-r--r--test/core/transport/transport_end2end_tests.c926
-rw-r--r--test/core/transport/transport_end2end_tests.h68
-rw-r--r--test/core/util/grpc_profiler.c38
-rw-r--r--test/core/util/grpc_profiler.h48
-rw-r--r--test/core/util/parse_hexstring.c70
-rw-r--r--test/core/util/parse_hexstring.h41
-rw-r--r--test/core/util/port.c145
-rw-r--r--test/core/util/port.h44
-rw-r--r--test/core/util/slice_splitter.c138
-rw-r--r--test/core/util/slice_splitter.h68
-rw-r--r--test/core/util/test_config.c36
-rw-r--r--test/core/util/test_config.h47
105 files changed, 19869 insertions, 0 deletions
diff --git a/test/core/channel/channel_stack_test.c b/test/core/channel/channel_stack_test.c
new file mode 100644
index 0000000000..44ede2f1d9
--- /dev/null
+++ b/test/core/channel/channel_stack_test.c
@@ -0,0 +1,139 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include "src/core/channel/channel_stack.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include "test/core/util/test_config.h"
+
+#define LOG_TEST_NAME() gpr_log(GPR_INFO, "%s", __FUNCTION__)
+
+static void channel_init_func(grpc_channel_element *elem,
+ const grpc_channel_args *args,
+ grpc_mdctx *metadata_context, int is_first,
+ int is_last) {
+ GPR_ASSERT(args->num_args == 1);
+ GPR_ASSERT(args->args[0].type == GRPC_ARG_INTEGER);
+ GPR_ASSERT(0 == strcmp(args->args[0].key, "test_key"));
+ GPR_ASSERT(args->args[0].value.integer == 42);
+ GPR_ASSERT(is_first);
+ GPR_ASSERT(is_last);
+ *(int *)(elem->channel_data) = 0;
+}
+
+static void call_init_func(grpc_call_element *elem,
+ const void *server_transport_data) {
+ ++*(int *)(elem->channel_data);
+ *(int *)(elem->call_data) = 0;
+}
+
+static void channel_destroy_func(grpc_channel_element *elem) {}
+
+static void call_destroy_func(grpc_call_element *elem) {
+ ++*(int *)(elem->channel_data);
+}
+
+static void call_func(grpc_call_element *elem, grpc_call_op *op) {
+ ++*(int *)(elem->call_data);
+}
+
+static void channel_func(grpc_channel_element *elem, grpc_channel_op *op) {
+ ++*(int *)(elem->channel_data);
+}
+
+static void test_create_channel_stack() {
+ const grpc_channel_filter filter = {
+ call_func, channel_func,
+
+ sizeof(int), call_init_func, call_destroy_func,
+
+ sizeof(int), channel_init_func, channel_destroy_func,
+ };
+ const grpc_channel_filter *filters = &filter;
+ grpc_channel_stack *channel_stack;
+ grpc_call_stack *call_stack;
+ grpc_channel_element *channel_elem;
+ grpc_call_element *call_elem;
+ grpc_arg arg;
+ grpc_channel_args chan_args;
+ grpc_mdctx *metadata_context;
+ int *channel_data;
+ int *call_data;
+
+ LOG_TEST_NAME();
+
+ metadata_context = grpc_mdctx_create();
+
+ arg.type = GRPC_ARG_INTEGER;
+ arg.key = "test_key";
+ arg.value.integer = 42;
+
+ chan_args.num_args = 1;
+ chan_args.args = &arg;
+
+ channel_stack = gpr_malloc(grpc_channel_stack_size(&filters, 1));
+ grpc_channel_stack_init(&filters, 1, &chan_args, metadata_context,
+ channel_stack);
+ GPR_ASSERT(channel_stack->count == 1);
+ channel_elem = grpc_channel_stack_element(channel_stack, 0);
+ channel_data = (int *)channel_elem->channel_data;
+ GPR_ASSERT(*channel_data == 0);
+
+ call_stack = gpr_malloc(channel_stack->call_stack_size);
+ grpc_call_stack_init(channel_stack, NULL, call_stack);
+ GPR_ASSERT(call_stack->count == 1);
+ call_elem = grpc_call_stack_element(call_stack, 0);
+ GPR_ASSERT(call_elem->filter == channel_elem->filter);
+ GPR_ASSERT(call_elem->channel_data == channel_elem->channel_data);
+ call_data = (int *)call_elem->call_data;
+ GPR_ASSERT(*call_data == 0);
+ GPR_ASSERT(*channel_data == 1);
+
+ grpc_call_stack_destroy(call_stack);
+ gpr_free(call_stack);
+ GPR_ASSERT(*channel_data == 2);
+
+ grpc_channel_stack_destroy(channel_stack);
+ gpr_free(channel_stack);
+
+ grpc_mdctx_orphan(metadata_context);
+}
+
+int main(int argc, char **argv) {
+ grpc_test_init(argc, argv);
+ test_create_channel_stack();
+ return 0;
+}
diff --git a/test/core/channel/metadata_buffer_test.c b/test/core/channel/metadata_buffer_test.c
new file mode 100644
index 0000000000..4a60b53f81
--- /dev/null
+++ b/test/core/channel/metadata_buffer_test.c
@@ -0,0 +1,194 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include "src/core/channel/metadata_buffer.h"
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include "test/core/util/test_config.h"
+
+#include <string.h>
+#include <stdio.h>
+
+/* construct a buffer with some prefix followed by an integer converted to
+ a string */
+static gpr_slice construct_buffer(size_t prefix_length, size_t index) {
+ gpr_slice buffer = gpr_slice_malloc(prefix_length + 32);
+ memset(GPR_SLICE_START_PTR(buffer), 'a', prefix_length);
+ GPR_SLICE_SET_LENGTH(
+ buffer, prefix_length +
+ sprintf((char *)GPR_SLICE_START_PTR(buffer) + prefix_length,
+ "%d", (int)index));
+ return buffer;
+}
+
+static void do_nothing(void *ignored, grpc_op_error also_ignored) {}
+
+/* we need a fake channel & call stack, which is defined here */
+
+/* a fake channel needs to track some information about the test */
+typedef struct {
+ size_t key_prefix_len;
+ size_t value_prefix_len;
+} channel_data;
+
+static void fail_call_op(grpc_call_element *elem, grpc_call_op *op) { abort(); }
+
+/* verify that the metadata passed on during flush is the same as we expect */
+static void expect_call_op(grpc_call_element *elem, grpc_call_op *op) {
+ size_t *n = elem->call_data;
+ channel_data *cd = elem->channel_data;
+ gpr_slice key = construct_buffer(cd->key_prefix_len, *n);
+ gpr_slice value = construct_buffer(cd->value_prefix_len, *n);
+
+ GPR_ASSERT(op->type == GRPC_SEND_METADATA);
+ GPR_ASSERT(op->dir == GRPC_CALL_DOWN);
+ GPR_ASSERT(op->flags == *n);
+ GPR_ASSERT(op->done_cb == do_nothing);
+ GPR_ASSERT(op->user_data == (void *)(gpr_uintptr) * n);
+ GPR_ASSERT(0 == gpr_slice_cmp(op->data.metadata->key->slice, key));
+ GPR_ASSERT(0 == gpr_slice_cmp(op->data.metadata->value->slice, value));
+
+ ++*n;
+
+ gpr_slice_unref(key);
+ gpr_slice_unref(value);
+ grpc_mdelem_unref(op->data.metadata);
+}
+
+static void fail_channel_op(grpc_channel_element *elem, grpc_channel_op *op) {
+ abort();
+}
+
+static void init_call_elem(grpc_call_element *elem,
+ const void *transport_server_data) {
+ *(size_t *)elem->call_data = 0;
+}
+
+static void destroy_call_elem(grpc_call_element *elem) {}
+
+static void init_channel_elem(grpc_channel_element *elem,
+ const grpc_channel_args *args, grpc_mdctx *mdctx,
+ int is_first, int is_last) {}
+
+static void destroy_channel_elem(grpc_channel_element *elem) {}
+
+static const grpc_channel_filter top_filter = {
+ fail_call_op, fail_channel_op, sizeof(size_t),
+ init_call_elem, destroy_call_elem, sizeof(channel_data),
+ init_channel_elem, destroy_channel_elem};
+
+static const grpc_channel_filter bottom_filter = {
+ expect_call_op, fail_channel_op, sizeof(size_t),
+ init_call_elem, destroy_call_elem, sizeof(channel_data),
+ init_channel_elem, destroy_channel_elem};
+
+static const grpc_channel_filter *filters[2] = {&top_filter, &bottom_filter};
+
+/* run a test with differently sized keys, and values, some number of times. */
+static void test_case(size_t key_prefix_len, size_t value_prefix_len,
+ size_t num_calls) {
+ size_t i;
+ size_t got_calls;
+ grpc_metadata_buffer buffer;
+ grpc_channel_stack *stk;
+ grpc_call_stack *call;
+ grpc_mdctx *mdctx;
+
+ gpr_log(GPR_INFO, "Test %d calls, {key,value}_prefix_len = {%d, %d}",
+ (int)num_calls, (int)key_prefix_len, (int)value_prefix_len);
+
+ mdctx = grpc_mdctx_create();
+
+ grpc_metadata_buffer_init(&buffer);
+
+ /* queue metadata elements */
+ for (i = 0; i < num_calls; i++) {
+ grpc_call_op op;
+ gpr_slice key = construct_buffer(key_prefix_len, i);
+ gpr_slice value = construct_buffer(value_prefix_len, i);
+
+ op.type = GRPC_SEND_METADATA;
+ op.dir = GRPC_CALL_DOWN;
+ op.flags = i;
+ op.data.metadata = grpc_mdelem_from_slices(mdctx, key, value);
+ op.done_cb = do_nothing;
+ op.user_data = (void *)(gpr_uintptr) i;
+
+ grpc_metadata_buffer_queue(&buffer, &op);
+ }
+
+ /* construct a test channel, call stack */
+ stk = gpr_malloc(grpc_channel_stack_size(filters, 2));
+ grpc_channel_stack_init(filters, 2, NULL, mdctx, stk);
+
+ for (i = 0; i < 2; i++) {
+ channel_data *cd =
+ (channel_data *)grpc_channel_stack_element(stk, i)->channel_data;
+ cd->key_prefix_len = key_prefix_len;
+ cd->value_prefix_len = value_prefix_len;
+ }
+
+ call = gpr_malloc(stk->call_stack_size);
+ grpc_call_stack_init(stk, NULL, call);
+
+ /* flush out metadata, verifying each element (see expect_call_op) */
+ grpc_metadata_buffer_flush(&buffer, grpc_call_stack_element(call, 0));
+
+ /* verify expect_call_op was called an appropriate number of times */
+ got_calls = *(size_t *)grpc_call_stack_element(call, 1)->call_data;
+ GPR_ASSERT(num_calls == got_calls);
+
+ /* clean up the things */
+ grpc_call_stack_destroy(call);
+ gpr_free(call);
+ grpc_channel_stack_destroy(stk);
+ gpr_free(stk);
+
+ grpc_metadata_buffer_destroy(&buffer, GRPC_OP_OK);
+ grpc_mdctx_orphan(mdctx);
+}
+
+int main(int argc, char **argv) {
+ grpc_test_init(argc, argv);
+ test_case(0, 0, 0);
+ test_case(0, 0, 1);
+ test_case(0, 0, 2);
+ test_case(0, 0, 10000);
+ test_case(10, 10, 1);
+ test_case(10, 10, 2);
+ test_case(10, 10, 10000);
+ test_case(100, 100, 1);
+ test_case(100, 100, 2);
+ test_case(100, 100, 10000);
+ return 0;
+}
diff --git a/test/core/compression/message_compress_test.c b/test/core/compression/message_compress_test.c
new file mode 100644
index 0000000000..583f187a23
--- /dev/null
+++ b/test/core/compression/message_compress_test.c
@@ -0,0 +1,193 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include "src/core/compression/message_compress.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "test/core/util/test_config.h"
+#include "src/core/support/murmur_hash.h"
+#include <grpc/support/log.h>
+#include <grpc/support/useful.h>
+#include "test/core/util/slice_splitter.h"
+
+typedef enum { ONE_A = 0, ONE_KB_A, ONE_MB_A, TEST_VALUE_COUNT } test_value;
+
+typedef enum {
+ SHOULD_NOT_COMPRESS,
+ SHOULD_COMPRESS,
+ MAYBE_COMPRESSES
+} compressability;
+
+static void assert_passthrough(gpr_slice value,
+ grpc_compression_algorithm algorithm,
+ grpc_slice_split_mode uncompressed_split_mode,
+ grpc_slice_split_mode compressed_split_mode,
+ compressability compress_result_check) {
+ gpr_slice_buffer input;
+ gpr_slice_buffer compressed_raw;
+ gpr_slice_buffer compressed;
+ gpr_slice_buffer output;
+ gpr_slice final;
+ int was_compressed;
+
+ gpr_log(GPR_INFO,
+ "assert_passthrough: value_length=%d value_hash=0x%08x "
+ "algorithm='%s' uncompressed_split='%s' compressed_split='%s'",
+ GPR_SLICE_LENGTH(value), gpr_murmur_hash3(GPR_SLICE_START_PTR(value),
+ GPR_SLICE_LENGTH(value), 0),
+ grpc_compression_algorithm_name(algorithm),
+ grpc_slice_split_mode_name(uncompressed_split_mode),
+ grpc_slice_split_mode_name(compressed_split_mode));
+
+ gpr_slice_buffer_init(&input);
+ gpr_slice_buffer_init(&compressed_raw);
+ gpr_slice_buffer_init(&compressed);
+ gpr_slice_buffer_init(&output);
+
+ grpc_split_slices_to_buffer(uncompressed_split_mode, &value, 1, &input);
+
+ was_compressed = grpc_msg_compress(algorithm, &input, &compressed_raw);
+ GPR_ASSERT(input.count > 0);
+
+ switch (compress_result_check) {
+ case SHOULD_NOT_COMPRESS:
+ GPR_ASSERT(was_compressed == 0);
+ break;
+ case SHOULD_COMPRESS:
+ GPR_ASSERT(was_compressed == 1);
+ break;
+ case MAYBE_COMPRESSES:
+ /* no check */
+ break;
+ }
+
+ grpc_split_slice_buffer(compressed_split_mode, &compressed_raw, &compressed);
+
+ GPR_ASSERT(grpc_msg_decompress(
+ was_compressed ? algorithm : GRPC_COMPRESS_NONE, &compressed, &output));
+
+ final = grpc_slice_merge(output.slices, output.count);
+ GPR_ASSERT(0 == gpr_slice_cmp(value, final));
+
+ gpr_slice_buffer_destroy(&input);
+ gpr_slice_buffer_destroy(&compressed);
+ gpr_slice_buffer_destroy(&compressed_raw);
+ gpr_slice_buffer_destroy(&output);
+ gpr_slice_unref(final);
+}
+
+static gpr_slice repeated(char c, size_t length) {
+ gpr_slice out = gpr_slice_malloc(length);
+ memset(GPR_SLICE_START_PTR(out), c, length);
+ return out;
+}
+
+static compressability get_compressability(
+ test_value id, grpc_compression_algorithm algorithm) {
+ if (algorithm == GRPC_COMPRESS_NONE) return SHOULD_NOT_COMPRESS;
+ switch (id) {
+ case ONE_A:
+ return SHOULD_NOT_COMPRESS;
+ case ONE_KB_A:
+ case ONE_MB_A:
+ return SHOULD_COMPRESS;
+ case TEST_VALUE_COUNT:
+ abort();
+ break;
+ }
+ return MAYBE_COMPRESSES;
+}
+
+static gpr_slice create_test_value(test_value id) {
+ switch (id) {
+ case ONE_A:
+ return gpr_slice_from_copied_string("a");
+ case ONE_KB_A:
+ return repeated('a', 1024);
+ case ONE_MB_A:
+ return repeated('a', 1024 * 1024);
+ case TEST_VALUE_COUNT:
+ abort();
+ break;
+ }
+ return gpr_slice_from_copied_string("bad value");
+}
+
+static void test_bad_data() {
+ gpr_slice_buffer input;
+ gpr_slice_buffer output;
+ int i;
+
+ gpr_slice_buffer_init(&input);
+ gpr_slice_buffer_init(&output);
+ gpr_slice_buffer_add(&input, gpr_slice_from_copied_string(
+ "this is not valid compressed input"));
+
+ for (i = 0; i < GRPC_COMPRESS_ALGORITHMS_COUNT; i++) {
+ if (i == GRPC_COMPRESS_NONE) continue;
+ GPR_ASSERT(0 == grpc_msg_decompress(i, &input, &output));
+ GPR_ASSERT(0 == output.count);
+ }
+
+ gpr_slice_buffer_destroy(&input);
+ gpr_slice_buffer_destroy(&output);
+}
+
+int main(int argc, char **argv) {
+ int i, j, k, m;
+ grpc_slice_split_mode uncompressed_split_modes[] = {
+ GRPC_SLICE_SPLIT_IDENTITY, GRPC_SLICE_SPLIT_ONE_BYTE};
+ grpc_slice_split_mode compressed_split_modes[] = {GRPC_SLICE_SPLIT_MERGE_ALL,
+ GRPC_SLICE_SPLIT_IDENTITY,
+ GRPC_SLICE_SPLIT_ONE_BYTE};
+
+ grpc_test_init(argc, argv);
+
+ for (i = 0; i < GRPC_COMPRESS_ALGORITHMS_COUNT; i++) {
+ for (j = 0; j < GPR_ARRAY_SIZE(uncompressed_split_modes); j++) {
+ for (k = 0; k < GPR_ARRAY_SIZE(compressed_split_modes); k++) {
+ for (m = 0; m < TEST_VALUE_COUNT; m++) {
+ gpr_slice slice = create_test_value(m);
+ assert_passthrough(slice, i, j, k, get_compressability(m, i));
+ gpr_slice_unref(slice);
+ }
+ }
+ }
+ }
+
+ test_bad_data();
+
+ return 0;
+}
diff --git a/test/core/echo/client.c b/test/core/echo/client.c
new file mode 100644
index 0000000000..1905863e11
--- /dev/null
+++ b/test/core/echo/client.c
@@ -0,0 +1,139 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include <grpc/grpc.h>
+
+#include <string.h>
+
+#include <grpc/support/log.h>
+#include <grpc/support/time.h>
+#include <grpc/byte_buffer.h>
+#include "test/core/util/test_config.h"
+
+enum { WRITE_SLICE_LENGTH = 1024, TOTAL_BYTES = 102400 };
+
+/* Start write the next slice, fill slice.data[0..length - 1] with first % 256,
+ (first + 1) % 256, ... (first + length - 1) % 256.
+ Produce a GRPC_WRITE_ACCEPTED event */
+static void start_write_next_slice(grpc_call *call, int first, int length) {
+ int i = 0;
+ grpc_byte_buffer *byte_buffer = NULL;
+ gpr_slice slice = gpr_slice_malloc(length);
+ for (i = 0; i < length; i++)
+ GPR_SLICE_START_PTR(slice)[i] = (first + i) % 256;
+ byte_buffer = grpc_byte_buffer_create(&slice, 1);
+ GPR_ASSERT(grpc_call_start_write(call, byte_buffer, (void *)1, 0) ==
+ GRPC_CALL_OK);
+ gpr_slice_unref(slice);
+ grpc_byte_buffer_destroy(byte_buffer);
+}
+
+int main(int argc, char **argv) {
+ grpc_channel *channel = NULL;
+ grpc_call *call = NULL;
+ grpc_event *ev = NULL;
+ grpc_byte_buffer_reader *bb_reader = NULL;
+ grpc_completion_queue *cq = NULL;
+ int bytes_written = 0;
+ int bytes_read = 0;
+ int i = 0;
+ int waiting_finishes;
+ gpr_slice read_slice;
+
+ grpc_test_init(argc, argv);
+
+ grpc_init();
+
+ cq = grpc_completion_queue_create();
+
+ GPR_ASSERT(argc == 2);
+ channel = grpc_channel_create(argv[1], NULL);
+ call = grpc_channel_create_call(channel, "/foo", "localhost", gpr_inf_future);
+ GPR_ASSERT(grpc_call_start_invoke(call, cq, (void *)1, (void *)1, (void *)1,
+ 0) == GRPC_CALL_OK);
+ ev = grpc_completion_queue_next(cq, gpr_inf_future);
+ GPR_ASSERT(ev->data.invoke_accepted == GRPC_OP_OK);
+ grpc_event_finish(ev);
+
+ start_write_next_slice(call, bytes_written, WRITE_SLICE_LENGTH);
+ bytes_written += WRITE_SLICE_LENGTH;
+ GPR_ASSERT(grpc_call_start_read(call, (void *)1) == GRPC_CALL_OK);
+ waiting_finishes = 2;
+ while (waiting_finishes) {
+ ev = grpc_completion_queue_next(cq, gpr_inf_future);
+ switch (ev->type) {
+ case GRPC_WRITE_ACCEPTED:
+ if (bytes_written < TOTAL_BYTES) {
+ start_write_next_slice(call, bytes_written, WRITE_SLICE_LENGTH);
+ bytes_written += WRITE_SLICE_LENGTH;
+ } else {
+ GPR_ASSERT(grpc_call_writes_done(call, (void *)1) == GRPC_CALL_OK);
+ }
+ break;
+ case GRPC_CLIENT_METADATA_READ:
+ break;
+ case GRPC_READ:
+ bb_reader = grpc_byte_buffer_reader_create(ev->data.read);
+ while (grpc_byte_buffer_reader_next(bb_reader, &read_slice)) {
+ for (i = 0; i < GPR_SLICE_LENGTH(read_slice); i++) {
+ GPR_ASSERT(GPR_SLICE_START_PTR(read_slice)[i] == bytes_read % 256);
+ bytes_read++;
+ }
+ gpr_slice_unref(read_slice);
+ }
+ grpc_byte_buffer_reader_destroy(bb_reader);
+ if (bytes_read < TOTAL_BYTES) {
+ GPR_ASSERT(grpc_call_start_read(call, (void *)1) == GRPC_CALL_OK);
+ }
+ break;
+ case GRPC_FINISHED:
+ case GRPC_FINISH_ACCEPTED:
+ waiting_finishes--;
+ break;
+ default:
+ GPR_ASSERT(0 && "unexpected event");
+ break;
+ }
+ grpc_event_finish(ev);
+ }
+ GPR_ASSERT(bytes_read == TOTAL_BYTES);
+ gpr_log(GPR_INFO, "All data have been successfully echoed");
+
+ grpc_call_destroy(call);
+ grpc_channel_destroy(channel);
+ grpc_completion_queue_destroy(cq);
+
+ grpc_shutdown();
+
+ return 0;
+}
diff --git a/test/core/echo/echo_test.c b/test/core/echo/echo_test.c
new file mode 100644
index 0000000000..c699f1e83e
--- /dev/null
+++ b/test/core/echo/echo_test.c
@@ -0,0 +1,106 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#define _POSIX_SOURCE
+#include <unistd.h>
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/host_port.h>
+#include <grpc/support/string.h>
+#include "test/core/util/port.h"
+
+static const char *const kHosts[] = {
+ "127.0.0.1", "::1", "::ffff:127.0.0.1", "localhost",
+};
+
+int main(int argc, char **argv) {
+ char *me = argv[0];
+ char *lslash = strrchr(me, '/');
+ char root[1024];
+ int port = grpc_pick_unused_port_or_die();
+ char *args[3];
+ int status;
+ pid_t svr, cli;
+ int i;
+ /* figure out where we are */
+ if (lslash) {
+ memcpy(root, me, lslash - me);
+ root[lslash - me] = 0;
+ } else {
+ strcpy(root, ".");
+ }
+ /* start the server */
+ svr = fork();
+ if (svr == 0) {
+ gpr_asprintf(&args[0], "%s/echo_server", root);
+ gpr_join_host_port(&args[1], "::", port);
+ args[2] = 0;
+ execv(args[0], args);
+
+ gpr_free(args[0]);
+ gpr_free(args[1]);
+ return 1;
+ }
+ /* wait a little */
+ sleep(2);
+ /* start the clients */
+ for (i = 0; i < sizeof(kHosts) / sizeof(*kHosts); i++) {
+ cli = fork();
+ if (cli == 0) {
+ gpr_asprintf(&args[0], "%s/echo_client", root);
+ gpr_join_host_port(&args[1], kHosts[i], port);
+ args[2] = 0;
+ execv(args[0], args);
+
+ gpr_free(args[0]);
+ gpr_free(args[1]);
+ return 1;
+ }
+ /* wait for client */
+ printf("waiting for client: %s\n", kHosts[i]);
+ if (waitpid(cli, &status, 0) == -1) return 2;
+ if (!WIFEXITED(status)) return 4;
+ if (WEXITSTATUS(status)) return WEXITSTATUS(status);
+ }
+ /* wait for server */
+ printf("checking server\n");
+ if (waitpid(svr, &status, WNOHANG) != 0) return 2;
+ kill(svr, SIGKILL);
+ return 0;
+}
diff --git a/test/core/echo/server.c b/test/core/echo/server.c
new file mode 100644
index 0000000000..77383f838e
--- /dev/null
+++ b/test/core/echo/server.c
@@ -0,0 +1,149 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include <grpc/grpc.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "test/core/util/test_config.h"
+#include <grpc/support/alloc.h>
+#include <grpc/support/host_port.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string.h>
+#include <grpc/support/time.h>
+#include "test/core/util/port.h"
+
+static grpc_completion_queue *cq;
+static grpc_server *server;
+
+static const grpc_status status_ok = {GRPC_STATUS_OK, NULL};
+
+typedef struct {
+ gpr_refcount pending_ops;
+ gpr_intmax bytes_read;
+} call_state;
+
+static void request_call() {
+ call_state *tag = gpr_malloc(sizeof(*tag));
+ gpr_ref_init(&tag->pending_ops, 2);
+ tag->bytes_read = 0;
+ grpc_server_request_call(server, tag);
+}
+
+static void assert_read_ok(call_state *s, grpc_byte_buffer *b) {
+ grpc_byte_buffer_reader *bb_reader = NULL;
+ gpr_slice read_slice;
+ int i;
+
+ bb_reader = grpc_byte_buffer_reader_create(b);
+ while (grpc_byte_buffer_reader_next(bb_reader, &read_slice)) {
+ for (i = 0; i < GPR_SLICE_LENGTH(read_slice); i++) {
+ GPR_ASSERT(GPR_SLICE_START_PTR(read_slice)[i] == s->bytes_read % 256);
+ s->bytes_read++;
+ }
+ gpr_slice_unref(read_slice);
+ }
+ grpc_byte_buffer_reader_destroy(bb_reader);
+}
+
+int main(int argc, char **argv) {
+ grpc_event *ev;
+ char *addr;
+ call_state *s;
+
+ grpc_test_init(argc, argv);
+
+ grpc_init();
+ srand(clock());
+
+ if (argc == 2) {
+ addr = gpr_strdup(argv[1]);
+ } else {
+ gpr_join_host_port(&addr, "::", grpc_pick_unused_port_or_die());
+ }
+ gpr_log(GPR_INFO, "creating server on: %s", addr);
+
+ cq = grpc_completion_queue_create();
+ server = grpc_server_create(cq, NULL);
+ GPR_ASSERT(grpc_server_add_http2_port(server, addr));
+ gpr_free(addr);
+ grpc_server_start(server);
+
+ request_call();
+
+ for (;;) {
+ ev = grpc_completion_queue_next(cq, gpr_inf_future);
+ GPR_ASSERT(ev);
+ s = ev->tag;
+ switch (ev->type) {
+ case GRPC_SERVER_RPC_NEW:
+ /* initial ops are already started in request_call */
+ grpc_call_accept(ev->call, cq, s, GRPC_WRITE_BUFFER_HINT);
+ GPR_ASSERT(grpc_call_start_read(ev->call, s) == GRPC_CALL_OK);
+ request_call();
+ break;
+ case GRPC_WRITE_ACCEPTED:
+ GPR_ASSERT(ev->data.write_accepted == GRPC_OP_OK);
+ GPR_ASSERT(grpc_call_start_read(ev->call, s) == GRPC_CALL_OK);
+ break;
+ case GRPC_READ:
+ if (ev->data.read) {
+ assert_read_ok(ev->tag, ev->data.read);
+ GPR_ASSERT(grpc_call_start_write(ev->call, ev->data.read, s,
+ GRPC_WRITE_BUFFER_HINT) ==
+ GRPC_CALL_OK);
+ } else {
+ GPR_ASSERT(grpc_call_start_write_status(ev->call, status_ok, s) ==
+ GRPC_CALL_OK);
+ }
+ break;
+ case GRPC_FINISH_ACCEPTED:
+ case GRPC_FINISHED:
+ if (gpr_unref(&s->pending_ops)) {
+ grpc_call_destroy(ev->call);
+ gpr_free(s);
+ }
+ break;
+ default:
+ abort();
+ }
+ grpc_event_finish(ev);
+ }
+
+ grpc_shutdown();
+
+ return 0;
+}
diff --git a/test/core/end2end/README b/test/core/end2end/README
new file mode 100644
index 0000000000..31598b61b8
--- /dev/null
+++ b/test/core/end2end/README
@@ -0,0 +1,10 @@
+Each fixture (under fixtures/) is paired with each test (under tests/) and
+forms a complete end-to-end test.
+
+To add a new test or fixture:
+- add the code to the relevant directory
+- update gen_build_json.py to reflect the change
+- regenerate projects
+// MOE:begin_strip
+- update net/grpc/c/BUILD to reflect the change
+// MOE:end_strip \ No newline at end of file
diff --git a/test/core/end2end/cq_verifier.c b/test/core/end2end/cq_verifier.c
new file mode 100644
index 0000000000..aebb8b4c41
--- /dev/null
+++ b/test/core/end2end/cq_verifier.c
@@ -0,0 +1,473 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include "test/core/end2end/cq_verifier.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "src/core/surface/event_string.h"
+#include <grpc/byte_buffer.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string.h>
+#include <grpc/support/time.h>
+#include <grpc/support/useful.h>
+
+/* a set of metadata we expect to find on an event */
+typedef struct metadata {
+ size_t count;
+ size_t cap;
+ const char **keys;
+ const char **values;
+} metadata;
+
+/* details what we expect to find on a single event - and forms a linked
+ list to detail other expectations */
+typedef struct expectation {
+ struct expectation *next;
+ struct expectation *prev;
+ grpc_completion_type type;
+ void *tag;
+ union {
+ grpc_op_error finish_accepted;
+ grpc_op_error write_accepted;
+ grpc_op_error invoke_accepted;
+ struct {
+ const char *method;
+ const char *host;
+ gpr_timespec deadline;
+ grpc_call **output_call;
+ metadata *metadata;
+ } server_rpc_new;
+ metadata *client_metadata_read;
+ struct {
+ grpc_status status;
+ metadata *metadata;
+ } finished;
+ gpr_slice *read;
+ } data;
+} expectation;
+
+/* the verifier itself */
+struct cq_verifier {
+ /* bound completion queue */
+ grpc_completion_queue *cq;
+ /* the root/sentinal expectation */
+ expectation expect;
+};
+
+cq_verifier *cq_verifier_create(grpc_completion_queue *cq) {
+ cq_verifier *v = gpr_malloc(sizeof(cq_verifier));
+ v->expect.type = GRPC_COMPLETION_DO_NOT_USE;
+ v->expect.tag = NULL;
+ v->expect.next = &v->expect;
+ v->expect.prev = &v->expect;
+ v->cq = cq;
+ return v;
+}
+
+void cq_verifier_destroy(cq_verifier *v) {
+ cq_verify(v);
+ gpr_free(v);
+}
+
+static int has_metadata(const grpc_metadata *md, size_t count, const char *key,
+ const char *value) {
+ size_t i;
+ for (i = 0; i < count; i++) {
+ if (0 == strcmp(key, md[i].key) && strlen(value) == md[i].value_length &&
+ 0 == memcmp(md[i].value, value, md[i].value_length)) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static void verify_and_destroy_metadata(metadata *md, grpc_metadata *elems,
+ size_t count) {
+ size_t i;
+ for (i = 0; i < md->count; i++) {
+ GPR_ASSERT(has_metadata(elems, count, md->keys[i], md->values[i]));
+ }
+ gpr_free(md->keys);
+ gpr_free(md->values);
+ gpr_free(md);
+}
+
+static gpr_slice merge_slices(gpr_slice *slices, int nslices) {
+ size_t i;
+ size_t len = 0;
+ gpr_uint8 *cursor;
+ gpr_slice out;
+
+ for (i = 0; i < nslices; i++) {
+ len += GPR_SLICE_LENGTH(slices[i]);
+ }
+
+ out = gpr_slice_malloc(len);
+ cursor = GPR_SLICE_START_PTR(out);
+
+ for (i = 0; i < nslices; i++) {
+ memcpy(cursor, GPR_SLICE_START_PTR(slices[i]), GPR_SLICE_LENGTH(slices[i]));
+ cursor += GPR_SLICE_LENGTH(slices[i]);
+ }
+
+ return out;
+}
+
+static int byte_buffer_eq_slice(grpc_byte_buffer *bb, gpr_slice b) {
+ gpr_slice a =
+ merge_slices(bb->data.slice_buffer.slices, bb->data.slice_buffer.count);
+ int ok = GPR_SLICE_LENGTH(a) == GPR_SLICE_LENGTH(b) &&
+ 0 == memcmp(GPR_SLICE_START_PTR(a), GPR_SLICE_START_PTR(b),
+ GPR_SLICE_LENGTH(a));
+ gpr_slice_unref(a);
+ gpr_slice_unref(b);
+ return ok;
+}
+
+static int string_equivalent(const char *a, const char *b) {
+ if (a == NULL) return b == NULL || b[0] == 0;
+ if (b == NULL) return a[0] == 0;
+ return strcmp(a, b) == 0;
+}
+
+static void verify_matches(expectation *e, grpc_event *ev) {
+ GPR_ASSERT(e->type == ev->type);
+ switch (e->type) {
+ case GRPC_FINISH_ACCEPTED:
+ GPR_ASSERT(e->data.finish_accepted == ev->data.finish_accepted);
+ break;
+ case GRPC_WRITE_ACCEPTED:
+ GPR_ASSERT(e->data.write_accepted == ev->data.write_accepted);
+ break;
+ case GRPC_INVOKE_ACCEPTED:
+ GPR_ASSERT(e->data.invoke_accepted == ev->data.invoke_accepted);
+ break;
+ case GRPC_SERVER_RPC_NEW:
+ GPR_ASSERT(string_equivalent(e->data.server_rpc_new.method,
+ ev->data.server_rpc_new.method));
+ GPR_ASSERT(string_equivalent(e->data.server_rpc_new.host,
+ ev->data.server_rpc_new.host));
+ GPR_ASSERT(gpr_time_cmp(e->data.server_rpc_new.deadline,
+ ev->data.server_rpc_new.deadline) <= 0);
+ *e->data.server_rpc_new.output_call = ev->call;
+ verify_and_destroy_metadata(e->data.server_rpc_new.metadata,
+ ev->data.server_rpc_new.metadata_elements,
+ ev->data.server_rpc_new.metadata_count);
+ break;
+ case GRPC_CLIENT_METADATA_READ:
+ verify_and_destroy_metadata(e->data.client_metadata_read,
+ ev->data.client_metadata_read.elements,
+ ev->data.client_metadata_read.count);
+ break;
+ case GRPC_FINISHED:
+ if (e->data.finished.status.code != GRPC_STATUS__DO_NOT_USE) {
+ GPR_ASSERT(e->data.finished.status.code == ev->data.finished.code);
+ GPR_ASSERT(string_equivalent(e->data.finished.status.details,
+ ev->data.finished.details));
+ }
+ verify_and_destroy_metadata(e->data.finished.metadata, NULL, 0);
+ break;
+ case GRPC_QUEUE_SHUTDOWN:
+ gpr_log(GPR_ERROR, "premature queue shutdown");
+ abort();
+ break;
+ case GRPC_READ:
+ if (e->data.read) {
+ GPR_ASSERT(byte_buffer_eq_slice(ev->data.read, *e->data.read));
+ gpr_free(e->data.read);
+ } else {
+ GPR_ASSERT(ev->data.read == NULL);
+ }
+ break;
+ case GRPC_COMPLETION_DO_NOT_USE:
+ gpr_log(GPR_ERROR, "not implemented");
+ abort();
+ break;
+ }
+}
+
+static char *metadata_expectation_string(metadata *md) {
+ size_t len;
+ size_t i;
+ char *out;
+ char *p;
+
+ if (!md) return gpr_strdup("nil");
+
+ for (len = 0, i = 0; i < md->count; i++) {
+ len += strlen(md->keys[i]);
+ len += strlen(md->values[i]);
+ }
+ len += 3 + md->count;
+
+ p = out = gpr_malloc(len);
+ *p++ = '{';
+ for (i = 0; i < md->count; i++) {
+ if (i) *p++ = ',';
+ p += sprintf(p, "%s:%s", md->keys[i], md->values[i]);
+ }
+ *p++ = '}';
+ *p++ = 0;
+ return out;
+}
+
+static size_t expectation_to_string(char *out, expectation *e) {
+ gpr_timespec timeout;
+ char *str = NULL;
+ size_t len;
+
+ switch (e->type) {
+ case GRPC_FINISH_ACCEPTED:
+ return sprintf(out, "GRPC_FINISH_ACCEPTED result=%d",
+ e->data.finish_accepted);
+ case GRPC_WRITE_ACCEPTED:
+ return sprintf(out, "GRPC_WRITE_ACCEPTED result=%d",
+ e->data.write_accepted);
+ case GRPC_INVOKE_ACCEPTED:
+ return sprintf(out, "GRPC_INVOKE_ACCEPTED result=%d",
+ e->data.invoke_accepted);
+ case GRPC_SERVER_RPC_NEW:
+ timeout = gpr_time_sub(e->data.server_rpc_new.deadline, gpr_now());
+ return sprintf(out, "GRPC_SERVER_RPC_NEW method=%s host=%s timeout=%fsec",
+ e->data.server_rpc_new.method, e->data.server_rpc_new.host,
+ timeout.tv_sec + 1e-9 * timeout.tv_nsec);
+ case GRPC_CLIENT_METADATA_READ:
+ str = metadata_expectation_string(e->data.client_metadata_read);
+ len = sprintf(out, "GRPC_CLIENT_METADATA_READ %s", str);
+ gpr_free(str);
+ return len;
+ case GRPC_FINISHED:
+ str = metadata_expectation_string(e->data.finished.metadata);
+ len = sprintf(out, "GRPC_FINISHED code=%d details=%s %s",
+ e->data.finished.status.code,
+ e->data.finished.status.details, str);
+ gpr_free(str);
+ return len;
+ case GRPC_READ:
+ if (e->data.read) {
+ str =
+ gpr_hexdump((char *)GPR_SLICE_START_PTR(*e->data.read),
+ GPR_SLICE_LENGTH(*e->data.read), GPR_HEXDUMP_PLAINTEXT);
+ }
+ len = sprintf(out, "GRPC_READ data=%s", str);
+ gpr_free(str);
+ return len;
+ case GRPC_COMPLETION_DO_NOT_USE:
+ case GRPC_QUEUE_SHUTDOWN:
+ gpr_log(GPR_ERROR, "not implemented");
+ abort();
+ break;
+ }
+ return 0;
+}
+
+static char *expectations_to_string(cq_verifier *v) {
+ /* allocate a large buffer: we're about to crash anyway */
+ char *buffer = gpr_malloc(32 * 1024 * 1024);
+ char *p = buffer;
+ expectation *e;
+
+ for (e = v->expect.next; e != &v->expect; e = e->next) {
+ p += expectation_to_string(p, e);
+ *p++ = '\n';
+ }
+
+ *p = 0;
+ return buffer;
+}
+
+static void fail_no_event_received(cq_verifier *v) {
+ char *expectations = expectations_to_string(v);
+ gpr_log(GPR_ERROR, "no event received, but expected:\n%s", expectations);
+ gpr_free(expectations);
+ abort();
+}
+
+void cq_verify(cq_verifier *v) {
+ gpr_timespec deadline =
+ gpr_time_add(gpr_now(), gpr_time_from_micros(10 * GPR_US_PER_SEC));
+ grpc_event *ev;
+ expectation *e;
+
+ char have_tags[512] = {0};
+ char *phave = have_tags;
+
+ while (v->expect.next != &v->expect) {
+ ev = grpc_completion_queue_next(v->cq, deadline);
+ if (!ev) {
+ fail_no_event_received(v);
+ }
+
+ for (e = v->expect.next; e != &v->expect; e = e->next) {
+ phave += sprintf(phave, " %p", e->tag);
+ if (e->tag == ev->tag) {
+ verify_matches(e, ev);
+ e->next->prev = e->prev;
+ e->prev->next = e->next;
+ gpr_free(e);
+ break;
+ }
+ }
+ if (e == &v->expect) {
+ char *s = grpc_event_string(ev);
+ gpr_log(GPR_ERROR, "event not found: %s", s);
+ gpr_log(GPR_ERROR, "have tags:%s", have_tags);
+ gpr_free(s);
+ abort();
+ }
+
+ grpc_event_finish(ev);
+ }
+}
+
+void cq_verify_empty(cq_verifier *v) {
+ gpr_timespec deadline =
+ gpr_time_add(gpr_now(), gpr_time_from_micros(3000000));
+ grpc_event *ev;
+
+ GPR_ASSERT(v->expect.next == &v->expect && "expectation queue must be empty");
+
+ ev = grpc_completion_queue_next(v->cq, deadline);
+ GPR_ASSERT(ev == NULL);
+}
+
+static expectation *add(cq_verifier *v, grpc_completion_type type, void *tag) {
+ expectation *e = gpr_malloc(sizeof(expectation));
+ e->type = type;
+ e->tag = tag;
+ e->next = &v->expect;
+ e->prev = e->next->prev;
+ e->next->prev = e->prev->next = e;
+ return e;
+}
+
+static metadata *metadata_from_args(va_list args) {
+ metadata *md = gpr_malloc(sizeof(metadata));
+ const char *key, *value;
+ md->count = 0;
+ md->cap = 0;
+ md->keys = NULL;
+ md->values = NULL;
+
+ for (;;) {
+ key = va_arg(args, const char *);
+ if (!key) return md;
+ value = va_arg(args, const char *);
+ GPR_ASSERT(value);
+
+ if (md->cap == md->count) {
+ md->cap = GPR_MAX(md->cap + 1, md->cap * 3 / 2);
+ md->keys = gpr_realloc(md->keys, sizeof(const char *) * md->cap);
+ md->values = gpr_realloc(md->values, sizeof(const char *) * md->cap);
+ }
+ md->keys[md->count] = key;
+ md->values[md->count] = value;
+ md->count++;
+ }
+}
+
+void cq_expect_invoke_accepted(cq_verifier *v, void *tag,
+ grpc_op_error result) {
+ add(v, GRPC_INVOKE_ACCEPTED, tag)->data.invoke_accepted = result;
+}
+
+void cq_expect_write_accepted(cq_verifier *v, void *tag, grpc_op_error result) {
+ add(v, GRPC_WRITE_ACCEPTED, tag)->data.write_accepted = result;
+}
+
+void cq_expect_finish_accepted(cq_verifier *v, void *tag,
+ grpc_op_error result) {
+ add(v, GRPC_FINISH_ACCEPTED, tag)->data.finish_accepted = result;
+}
+
+void cq_expect_read(cq_verifier *v, void *tag, gpr_slice bytes) {
+ expectation *e = add(v, GRPC_READ, tag);
+ e->data.read = gpr_malloc(sizeof(gpr_slice));
+ *e->data.read = bytes;
+}
+
+void cq_expect_empty_read(cq_verifier *v, void *tag) {
+ expectation *e = add(v, GRPC_READ, tag);
+ e->data.read = NULL;
+}
+
+void cq_expect_server_rpc_new(cq_verifier *v, grpc_call **output_call,
+ void *tag, const char *method, const char *host,
+ gpr_timespec deadline, ...) {
+ va_list args;
+ expectation *e = add(v, GRPC_SERVER_RPC_NEW, tag);
+ e->data.server_rpc_new.method = method;
+ e->data.server_rpc_new.host = host;
+ e->data.server_rpc_new.deadline = deadline;
+ e->data.server_rpc_new.output_call = output_call;
+
+ va_start(args, deadline);
+ e->data.server_rpc_new.metadata = metadata_from_args(args);
+ va_end(args);
+}
+
+void cq_expect_client_metadata_read(cq_verifier *v, void *tag, ...) {
+ va_list args;
+ expectation *e = add(v, GRPC_CLIENT_METADATA_READ, tag);
+
+ va_start(args, tag);
+ e->data.client_metadata_read = metadata_from_args(args);
+ va_end(args);
+}
+
+static void finished_internal(cq_verifier *v, void *tag, grpc_status status,
+ va_list args) {
+ expectation *e = add(v, GRPC_FINISHED, tag);
+ e->data.finished.status = status;
+ e->data.finished.metadata = metadata_from_args(args);
+}
+
+void cq_expect_finished_with_status(cq_verifier *v, void *tag,
+ grpc_status status, ...) {
+ va_list args;
+ va_start(args, status);
+ finished_internal(v, tag, status, args);
+ va_end(args);
+}
+
+void cq_expect_finished(cq_verifier *v, void *tag, ...) {
+ va_list args;
+ grpc_status status = {GRPC_STATUS__DO_NOT_USE, NULL};
+ va_start(args, tag);
+ finished_internal(v, tag, status, args);
+ va_end(args);
+}
diff --git a/test/core/end2end/cq_verifier.h b/test/core/end2end/cq_verifier.h
new file mode 100644
index 0000000000..60f56bde67
--- /dev/null
+++ b/test/core/end2end/cq_verifier.h
@@ -0,0 +1,73 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#ifndef __GRPC_TEST_END2END_CQ_VERIFIER_H__
+#define __GRPC_TEST_END2END_CQ_VERIFIER_H__
+
+#include <grpc/grpc.h>
+
+/* A cq_verifier can verify that expected events arrive in a timely fashion
+ on a single completion queue */
+
+typedef struct cq_verifier cq_verifier;
+
+/* construct/destroy a cq_verifier */
+cq_verifier *cq_verifier_create(grpc_completion_queue *cq);
+void cq_verifier_destroy(cq_verifier *v);
+
+/* ensure all expected events (and only those events) are present on the
+ bound completion queue */
+void cq_verify(cq_verifier *v);
+
+/* ensure that the completion queue is empty */
+void cq_verify_empty(cq_verifier *v);
+
+/* Various expectation matchers
+ Any functions taking ... expect a NULL terminated list of key/value pairs
+ (each pair using two parameter slots) of metadata that MUST be present in
+ the event. */
+void cq_expect_invoke_accepted(cq_verifier *v, void *tag, grpc_op_error result);
+void cq_expect_write_accepted(cq_verifier *v, void *tag, grpc_op_error result);
+void cq_expect_finish_accepted(cq_verifier *v, void *tag, grpc_op_error result);
+void cq_expect_read(cq_verifier *v, void *tag, gpr_slice bytes);
+void cq_expect_empty_read(cq_verifier *v, void *tag);
+/* *output_call is set the the server call instance */
+void cq_expect_server_rpc_new(cq_verifier *v, grpc_call **output_call,
+ void *tag, const char *method, const char *host,
+ gpr_timespec deadline, ...);
+void cq_expect_client_metadata_read(cq_verifier *v, void *tag, ...);
+void cq_expect_finished_with_status(cq_verifier *v, void *tag,
+ grpc_status status, ...);
+void cq_expect_finished(cq_verifier *v, void *tag, ...);
+
+#endif /* __GRPC_TEST_END2END_CQ_VERIFIER_H__ */
diff --git a/test/core/end2end/data/ca_cert.c b/test/core/end2end/data/ca_cert.c
new file mode 100644
index 0000000000..458b7f04b5
--- /dev/null
+++ b/test/core/end2end/data/ca_cert.c
@@ -0,0 +1,102 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+unsigned char test_ca_cert[] = {
+ 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x20, 0x43,
+ 0x45, 0x52, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x45, 0x2d, 0x2d,
+ 0x2d, 0x2d, 0x2d, 0x0a, 0x4d, 0x49, 0x49, 0x43, 0x49, 0x7a, 0x43, 0x43,
+ 0x41, 0x59, 0x77, 0x43, 0x43, 0x51, 0x43, 0x46, 0x54, 0x62, 0x46, 0x37,
+ 0x58, 0x4e, 0x53, 0x76, 0x76, 0x6a, 0x41, 0x4e, 0x42, 0x67, 0x6b, 0x71,
+ 0x68, 0x6b, 0x69, 0x47, 0x39, 0x77, 0x30, 0x42, 0x41, 0x51, 0x55, 0x46,
+ 0x41, 0x44, 0x42, 0x57, 0x4d, 0x51, 0x73, 0x77, 0x43, 0x51, 0x59, 0x44,
+ 0x56, 0x51, 0x51, 0x47, 0x45, 0x77, 0x4a, 0x42, 0x0a, 0x56, 0x54, 0x45,
+ 0x54, 0x4d, 0x42, 0x45, 0x47, 0x41, 0x31, 0x55, 0x45, 0x43, 0x41, 0x77,
+ 0x4b, 0x55, 0x32, 0x39, 0x74, 0x5a, 0x53, 0x31, 0x54, 0x64, 0x47, 0x46,
+ 0x30, 0x5a, 0x54, 0x45, 0x68, 0x4d, 0x42, 0x38, 0x47, 0x41, 0x31, 0x55,
+ 0x45, 0x43, 0x67, 0x77, 0x59, 0x53, 0x57, 0x35, 0x30, 0x5a, 0x58, 0x4a,
+ 0x75, 0x5a, 0x58, 0x51, 0x67, 0x56, 0x32, 0x6c, 0x6b, 0x5a, 0x32, 0x6c,
+ 0x30, 0x0a, 0x63, 0x79, 0x42, 0x51, 0x64, 0x48, 0x6b, 0x67, 0x54, 0x48,
+ 0x52, 0x6b, 0x4d, 0x51, 0x38, 0x77, 0x44, 0x51, 0x59, 0x44, 0x56, 0x51,
+ 0x51, 0x44, 0x44, 0x41, 0x5a, 0x30, 0x5a, 0x58, 0x4e, 0x30, 0x59, 0x32,
+ 0x45, 0x77, 0x48, 0x68, 0x63, 0x4e, 0x4d, 0x54, 0x51, 0x77, 0x4e, 0x7a,
+ 0x45, 0x33, 0x4d, 0x6a, 0x4d, 0x78, 0x4e, 0x7a, 0x55, 0x78, 0x57, 0x68,
+ 0x63, 0x4e, 0x4d, 0x6a, 0x51, 0x77, 0x0a, 0x4e, 0x7a, 0x45, 0x30, 0x4d,
+ 0x6a, 0x4d, 0x78, 0x4e, 0x7a, 0x55, 0x78, 0x57, 0x6a, 0x42, 0x57, 0x4d,
+ 0x51, 0x73, 0x77, 0x43, 0x51, 0x59, 0x44, 0x56, 0x51, 0x51, 0x47, 0x45,
+ 0x77, 0x4a, 0x42, 0x56, 0x54, 0x45, 0x54, 0x4d, 0x42, 0x45, 0x47, 0x41,
+ 0x31, 0x55, 0x45, 0x43, 0x41, 0x77, 0x4b, 0x55, 0x32, 0x39, 0x74, 0x5a,
+ 0x53, 0x31, 0x54, 0x64, 0x47, 0x46, 0x30, 0x5a, 0x54, 0x45, 0x68, 0x0a,
+ 0x4d, 0x42, 0x38, 0x47, 0x41, 0x31, 0x55, 0x45, 0x43, 0x67, 0x77, 0x59,
+ 0x53, 0x57, 0x35, 0x30, 0x5a, 0x58, 0x4a, 0x75, 0x5a, 0x58, 0x51, 0x67,
+ 0x56, 0x32, 0x6c, 0x6b, 0x5a, 0x32, 0x6c, 0x30, 0x63, 0x79, 0x42, 0x51,
+ 0x64, 0x48, 0x6b, 0x67, 0x54, 0x48, 0x52, 0x6b, 0x4d, 0x51, 0x38, 0x77,
+ 0x44, 0x51, 0x59, 0x44, 0x56, 0x51, 0x51, 0x44, 0x44, 0x41, 0x5a, 0x30,
+ 0x5a, 0x58, 0x4e, 0x30, 0x0a, 0x59, 0x32, 0x45, 0x77, 0x67, 0x5a, 0x38,
+ 0x77, 0x44, 0x51, 0x59, 0x4a, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x63,
+ 0x4e, 0x41, 0x51, 0x45, 0x42, 0x42, 0x51, 0x41, 0x44, 0x67, 0x59, 0x30,
+ 0x41, 0x4d, 0x49, 0x47, 0x4a, 0x41, 0x6f, 0x47, 0x42, 0x41, 0x4d, 0x42,
+ 0x41, 0x33, 0x77, 0x56, 0x65, 0x54, 0x47, 0x48, 0x5a, 0x52, 0x31, 0x52,
+ 0x79, 0x65, 0x2f, 0x69, 0x2b, 0x4a, 0x38, 0x61, 0x32, 0x0a, 0x63, 0x75,
+ 0x35, 0x67, 0x58, 0x77, 0x46, 0x56, 0x36, 0x54, 0x6e, 0x4f, 0x62, 0x7a,
+ 0x47, 0x4d, 0x37, 0x62, 0x4c, 0x46, 0x43, 0x4f, 0x35, 0x69, 0x39, 0x76,
+ 0x34, 0x6d, 0x4c, 0x6f, 0x34, 0x69, 0x46, 0x7a, 0x50, 0x73, 0x48, 0x6d,
+ 0x57, 0x44, 0x55, 0x78, 0x4b, 0x53, 0x33, 0x59, 0x38, 0x69, 0x58, 0x62,
+ 0x75, 0x30, 0x65, 0x59, 0x42, 0x6c, 0x4c, 0x6f, 0x4e, 0x59, 0x30, 0x6c,
+ 0x53, 0x76, 0x0a, 0x78, 0x44, 0x78, 0x33, 0x33, 0x4f, 0x2b, 0x44, 0x75,
+ 0x77, 0x4d, 0x6d, 0x56, 0x4e, 0x2b, 0x44, 0x7a, 0x53, 0x44, 0x2b, 0x45,
+ 0x6f, 0x64, 0x39, 0x7a, 0x66, 0x76, 0x77, 0x4f, 0x57, 0x48, 0x73, 0x61,
+ 0x7a, 0x59, 0x43, 0x5a, 0x54, 0x32, 0x50, 0x68, 0x4e, 0x78, 0x6e, 0x56,
+ 0x57, 0x49, 0x75, 0x4a, 0x58, 0x56, 0x69, 0x59, 0x34, 0x4a, 0x41, 0x48,
+ 0x55, 0x47, 0x6f, 0x64, 0x6a, 0x78, 0x2b, 0x0a, 0x51, 0x41, 0x69, 0x36,
+ 0x79, 0x43, 0x41, 0x75, 0x72, 0x55, 0x5a, 0x47, 0x76, 0x59, 0x58, 0x47,
+ 0x67, 0x5a, 0x53, 0x42, 0x41, 0x67, 0x4d, 0x42, 0x41, 0x41, 0x45, 0x77,
+ 0x44, 0x51, 0x59, 0x4a, 0x4b, 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x63, 0x4e,
+ 0x41, 0x51, 0x45, 0x46, 0x42, 0x51, 0x41, 0x44, 0x67, 0x59, 0x45, 0x41,
+ 0x51, 0x6f, 0x51, 0x56, 0x44, 0x38, 0x62, 0x77, 0x64, 0x74, 0x57, 0x4a,
+ 0x0a, 0x41, 0x6e, 0x69, 0x47, 0x42, 0x77, 0x63, 0x43, 0x66, 0x71, 0x59,
+ 0x79, 0x48, 0x2b, 0x2f, 0x4b, 0x70, 0x41, 0x31, 0x30, 0x41, 0x63, 0x65,
+ 0x62, 0x4a, 0x56, 0x56, 0x54, 0x79, 0x59, 0x62, 0x59, 0x76, 0x49, 0x39,
+ 0x51, 0x38, 0x64, 0x36, 0x52, 0x53, 0x56, 0x75, 0x34, 0x50, 0x5a, 0x79,
+ 0x39, 0x4f, 0x41, 0x4c, 0x48, 0x52, 0x2f, 0x51, 0x72, 0x57, 0x42, 0x64,
+ 0x59, 0x54, 0x41, 0x79, 0x7a, 0x0a, 0x66, 0x4e, 0x41, 0x6d, 0x63, 0x32,
+ 0x63, 0x6d, 0x64, 0x6b, 0x53, 0x52, 0x4a, 0x7a, 0x6a, 0x68, 0x49, 0x61,
+ 0x4f, 0x73, 0x74, 0x6e, 0x51, 0x79, 0x31, 0x4a, 0x2b, 0x46, 0x6b, 0x30,
+ 0x54, 0x39, 0x58, 0x79, 0x76, 0x51, 0x74, 0x71, 0x34, 0x39, 0x39, 0x79,
+ 0x46, 0x62, 0x71, 0x39, 0x78, 0x6f, 0x67, 0x55, 0x56, 0x6c, 0x45, 0x47,
+ 0x48, 0x36, 0x32, 0x78, 0x50, 0x36, 0x76, 0x48, 0x30, 0x59, 0x0a, 0x35,
+ 0x75, 0x6b, 0x4b, 0x2f, 0x2f, 0x64, 0x43, 0x50, 0x41, 0x7a, 0x41, 0x31,
+ 0x31, 0x59, 0x75, 0x58, 0x32, 0x72, 0x6e, 0x65, 0x78, 0x30, 0x4a, 0x68,
+ 0x75, 0x54, 0x51, 0x66, 0x63, 0x49, 0x3d, 0x0a, 0x2d, 0x2d, 0x2d, 0x2d,
+ 0x2d, 0x45, 0x4e, 0x44, 0x20, 0x43, 0x45, 0x52, 0x54, 0x49, 0x46, 0x49,
+ 0x43, 0x41, 0x54, 0x45, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a};
+unsigned int test_ca_cert_size = 802;
diff --git a/test/core/end2end/data/server1_cert.c b/test/core/end2end/data/server1_cert.c
new file mode 100644
index 0000000000..da1d36653c
--- /dev/null
+++ b/test/core/end2end/data/server1_cert.c
@@ -0,0 +1,116 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+unsigned char test_server1_cert[] = {
+ 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x20, 0x43,
+ 0x45, 0x52, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x45, 0x2d, 0x2d,
+ 0x2d, 0x2d, 0x2d, 0x0a, 0x4d, 0x49, 0x49, 0x43, 0x6d, 0x7a, 0x43, 0x43,
+ 0x41, 0x67, 0x53, 0x67, 0x41, 0x77, 0x49, 0x42, 0x41, 0x67, 0x49, 0x42,
+ 0x41, 0x7a, 0x41, 0x4e, 0x42, 0x67, 0x6b, 0x71, 0x68, 0x6b, 0x69, 0x47,
+ 0x39, 0x77, 0x30, 0x42, 0x41, 0x51, 0x55, 0x46, 0x41, 0x44, 0x42, 0x57,
+ 0x4d, 0x51, 0x73, 0x77, 0x43, 0x51, 0x59, 0x44, 0x56, 0x51, 0x51, 0x47,
+ 0x45, 0x77, 0x4a, 0x42, 0x56, 0x54, 0x45, 0x54, 0x0a, 0x4d, 0x42, 0x45,
+ 0x47, 0x41, 0x31, 0x55, 0x45, 0x43, 0x41, 0x77, 0x4b, 0x55, 0x32, 0x39,
+ 0x74, 0x5a, 0x53, 0x31, 0x54, 0x64, 0x47, 0x46, 0x30, 0x5a, 0x54, 0x45,
+ 0x68, 0x4d, 0x42, 0x38, 0x47, 0x41, 0x31, 0x55, 0x45, 0x43, 0x67, 0x77,
+ 0x59, 0x53, 0x57, 0x35, 0x30, 0x5a, 0x58, 0x4a, 0x75, 0x5a, 0x58, 0x51,
+ 0x67, 0x56, 0x32, 0x6c, 0x6b, 0x5a, 0x32, 0x6c, 0x30, 0x63, 0x79, 0x42,
+ 0x51, 0x0a, 0x64, 0x48, 0x6b, 0x67, 0x54, 0x48, 0x52, 0x6b, 0x4d, 0x51,
+ 0x38, 0x77, 0x44, 0x51, 0x59, 0x44, 0x56, 0x51, 0x51, 0x44, 0x44, 0x41,
+ 0x5a, 0x30, 0x5a, 0x58, 0x4e, 0x30, 0x59, 0x32, 0x45, 0x77, 0x48, 0x68,
+ 0x63, 0x4e, 0x4d, 0x54, 0x51, 0x77, 0x4e, 0x7a, 0x49, 0x79, 0x4d, 0x44,
+ 0x59, 0x77, 0x4d, 0x44, 0x55, 0x33, 0x57, 0x68, 0x63, 0x4e, 0x4d, 0x6a,
+ 0x51, 0x77, 0x4e, 0x7a, 0x45, 0x35, 0x0a, 0x4d, 0x44, 0x59, 0x77, 0x4d,
+ 0x44, 0x55, 0x33, 0x57, 0x6a, 0x42, 0x6b, 0x4d, 0x51, 0x73, 0x77, 0x43,
+ 0x51, 0x59, 0x44, 0x56, 0x51, 0x51, 0x47, 0x45, 0x77, 0x4a, 0x56, 0x55,
+ 0x7a, 0x45, 0x52, 0x4d, 0x41, 0x38, 0x47, 0x41, 0x31, 0x55, 0x45, 0x43,
+ 0x42, 0x4d, 0x49, 0x53, 0x57, 0x78, 0x73, 0x61, 0x57, 0x35, 0x76, 0x61,
+ 0x58, 0x4d, 0x78, 0x45, 0x44, 0x41, 0x4f, 0x42, 0x67, 0x4e, 0x56, 0x0a,
+ 0x42, 0x41, 0x63, 0x54, 0x42, 0x30, 0x4e, 0x6f, 0x61, 0x57, 0x4e, 0x68,
+ 0x5a, 0x32, 0x38, 0x78, 0x46, 0x44, 0x41, 0x53, 0x42, 0x67, 0x4e, 0x56,
+ 0x42, 0x41, 0x6f, 0x54, 0x43, 0x30, 0x64, 0x76, 0x62, 0x32, 0x64, 0x73,
+ 0x5a, 0x53, 0x42, 0x4a, 0x62, 0x6d, 0x4d, 0x75, 0x4d, 0x52, 0x6f, 0x77,
+ 0x47, 0x41, 0x59, 0x44, 0x56, 0x51, 0x51, 0x44, 0x46, 0x42, 0x45, 0x71,
+ 0x4c, 0x6e, 0x52, 0x6c, 0x0a, 0x63, 0x33, 0x51, 0x75, 0x5a, 0x32, 0x39,
+ 0x76, 0x5a, 0x32, 0x78, 0x6c, 0x4c, 0x6d, 0x4e, 0x76, 0x62, 0x54, 0x43,
+ 0x42, 0x6e, 0x7a, 0x41, 0x4e, 0x42, 0x67, 0x6b, 0x71, 0x68, 0x6b, 0x69,
+ 0x47, 0x39, 0x77, 0x30, 0x42, 0x41, 0x51, 0x45, 0x46, 0x41, 0x41, 0x4f,
+ 0x42, 0x6a, 0x51, 0x41, 0x77, 0x67, 0x59, 0x6b, 0x43, 0x67, 0x59, 0x45,
+ 0x41, 0x34, 0x63, 0x4d, 0x56, 0x4a, 0x79, 0x67, 0x73, 0x0a, 0x4a, 0x55,
+ 0x6d, 0x6c, 0x67, 0x4d, 0x4d, 0x7a, 0x67, 0x64, 0x69, 0x30, 0x68, 0x31,
+ 0x58, 0x6f, 0x43, 0x52, 0x37, 0x2b, 0x77, 0x77, 0x31, 0x70, 0x6f, 0x70,
+ 0x30, 0x34, 0x4f, 0x4d, 0x4d, 0x79, 0x79, 0x37, 0x48, 0x2f, 0x69, 0x30,
+ 0x50, 0x4a, 0x32, 0x57, 0x36, 0x59, 0x33, 0x35, 0x2b, 0x62, 0x34, 0x43,
+ 0x4d, 0x38, 0x51, 0x72, 0x6b, 0x59, 0x65, 0x45, 0x61, 0x66, 0x55, 0x47,
+ 0x44, 0x4f, 0x0a, 0x52, 0x59, 0x58, 0x36, 0x79, 0x56, 0x2f, 0x63, 0x48,
+ 0x47, 0x47, 0x73, 0x44, 0x2f, 0x78, 0x30, 0x32, 0x79, 0x65, 0x36, 0x65,
+ 0x79, 0x31, 0x55, 0x44, 0x74, 0x6b, 0x47, 0x41, 0x44, 0x2f, 0x6d, 0x70,
+ 0x44, 0x45, 0x78, 0x38, 0x59, 0x43, 0x72, 0x6a, 0x41, 0x63, 0x31, 0x56,
+ 0x66, 0x76, 0x74, 0x38, 0x46, 0x6b, 0x36, 0x43, 0x6e, 0x31, 0x57, 0x56,
+ 0x49, 0x78, 0x56, 0x2f, 0x4a, 0x33, 0x30, 0x0a, 0x33, 0x78, 0x6a, 0x42,
+ 0x73, 0x46, 0x67, 0x42, 0x79, 0x51, 0x35, 0x35, 0x52, 0x42, 0x70, 0x31,
+ 0x4f, 0x4c, 0x5a, 0x66, 0x56, 0x4c, 0x6f, 0x36, 0x41, 0x6c, 0x65, 0x42,
+ 0x44, 0x53, 0x62, 0x63, 0x78, 0x61, 0x45, 0x43, 0x41, 0x77, 0x45, 0x41,
+ 0x41, 0x61, 0x4e, 0x72, 0x4d, 0x47, 0x6b, 0x77, 0x43, 0x51, 0x59, 0x44,
+ 0x56, 0x52, 0x30, 0x54, 0x42, 0x41, 0x49, 0x77, 0x41, 0x44, 0x41, 0x4c,
+ 0x0a, 0x42, 0x67, 0x4e, 0x56, 0x48, 0x51, 0x38, 0x45, 0x42, 0x41, 0x4d,
+ 0x43, 0x42, 0x65, 0x41, 0x77, 0x54, 0x77, 0x59, 0x44, 0x56, 0x52, 0x30,
+ 0x52, 0x42, 0x45, 0x67, 0x77, 0x52, 0x6f, 0x49, 0x51, 0x4b, 0x69, 0x35,
+ 0x30, 0x5a, 0x58, 0x4e, 0x30, 0x4c, 0x6d, 0x64, 0x76, 0x62, 0x32, 0x64,
+ 0x73, 0x5a, 0x53, 0x35, 0x6d, 0x63, 0x6f, 0x49, 0x59, 0x64, 0x32, 0x46,
+ 0x30, 0x5a, 0x58, 0x4a, 0x36, 0x0a, 0x62, 0x32, 0x39, 0x70, 0x4c, 0x6e,
+ 0x52, 0x6c, 0x63, 0x33, 0x51, 0x75, 0x5a, 0x32, 0x39, 0x76, 0x5a, 0x32,
+ 0x78, 0x6c, 0x4c, 0x6d, 0x4a, 0x6c, 0x67, 0x68, 0x49, 0x71, 0x4c, 0x6e,
+ 0x52, 0x6c, 0x63, 0x33, 0x51, 0x75, 0x65, 0x57, 0x39, 0x31, 0x64, 0x48,
+ 0x56, 0x69, 0x5a, 0x53, 0x35, 0x6a, 0x62, 0x32, 0x32, 0x48, 0x42, 0x4d,
+ 0x43, 0x6f, 0x41, 0x51, 0x4d, 0x77, 0x44, 0x51, 0x59, 0x4a, 0x0a, 0x4b,
+ 0x6f, 0x5a, 0x49, 0x68, 0x76, 0x63, 0x4e, 0x41, 0x51, 0x45, 0x46, 0x42,
+ 0x51, 0x41, 0x44, 0x67, 0x59, 0x45, 0x41, 0x4d, 0x32, 0x49, 0x69, 0x30,
+ 0x4c, 0x67, 0x54, 0x47, 0x62, 0x4a, 0x31, 0x6a, 0x34, 0x6f, 0x71, 0x58,
+ 0x39, 0x62, 0x78, 0x56, 0x63, 0x78, 0x6d, 0x2b, 0x2f, 0x52, 0x35, 0x59,
+ 0x66, 0x38, 0x6f, 0x69, 0x30, 0x61, 0x5a, 0x71, 0x54, 0x4a, 0x6c, 0x6e,
+ 0x4c, 0x59, 0x53, 0x0a, 0x77, 0x58, 0x63, 0x42, 0x79, 0x6b, 0x78, 0x54,
+ 0x78, 0x31, 0x38, 0x31, 0x73, 0x37, 0x57, 0x79, 0x66, 0x4a, 0x34, 0x39,
+ 0x57, 0x77, 0x72, 0x59, 0x58, 0x6f, 0x37, 0x38, 0x7a, 0x54, 0x44, 0x41,
+ 0x6e, 0x66, 0x31, 0x6d, 0x61, 0x30, 0x66, 0x50, 0x71, 0x33, 0x65, 0x34,
+ 0x6d, 0x70, 0x73, 0x70, 0x76, 0x79, 0x6e, 0x64, 0x4c, 0x68, 0x31, 0x61,
+ 0x2b, 0x4f, 0x61, 0x72, 0x48, 0x61, 0x31, 0x65, 0x0a, 0x61, 0x54, 0x30,
+ 0x44, 0x49, 0x49, 0x59, 0x6b, 0x37, 0x71, 0x65, 0x45, 0x61, 0x31, 0x59,
+ 0x63, 0x56, 0x6c, 0x6a, 0x78, 0x32, 0x4b, 0x79, 0x4c, 0x64, 0x30, 0x72,
+ 0x31, 0x42, 0x42, 0x41, 0x66, 0x72, 0x77, 0x79, 0x47, 0x61, 0x45, 0x50,
+ 0x56, 0x65, 0x4a, 0x51, 0x56, 0x59, 0x57, 0x61, 0x4f, 0x4a, 0x52, 0x55,
+ 0x32, 0x77, 0x65, 0x2f, 0x4b, 0x44, 0x34, 0x6f, 0x6a, 0x66, 0x39, 0x73,
+ 0x3d, 0x0a, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x45, 0x4e, 0x44, 0x20, 0x43,
+ 0x45, 0x52, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x45, 0x2d, 0x2d,
+ 0x2d, 0x2d, 0x2d, 0x0a};
+unsigned int test_server1_cert_size = 964;
diff --git a/test/core/end2end/data/server1_key.c b/test/core/end2end/data/server1_key.c
new file mode 100644
index 0000000000..3540505467
--- /dev/null
+++ b/test/core/end2end/data/server1_key.c
@@ -0,0 +1,109 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+unsigned char test_server1_key[] = {
+ 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x20, 0x52,
+ 0x53, 0x41, 0x20, 0x50, 0x52, 0x49, 0x56, 0x41, 0x54, 0x45, 0x20, 0x4b,
+ 0x45, 0x59, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a, 0x4d, 0x49, 0x49, 0x43,
+ 0x57, 0x77, 0x49, 0x42, 0x41, 0x41, 0x4b, 0x42, 0x67, 0x51, 0x44, 0x68,
+ 0x77, 0x78, 0x55, 0x6e, 0x4b, 0x43, 0x77, 0x6c, 0x53, 0x61, 0x57, 0x41,
+ 0x77, 0x7a, 0x4f, 0x42, 0x32, 0x4c, 0x53, 0x48, 0x56, 0x65, 0x67, 0x4a,
+ 0x48, 0x76, 0x37, 0x44, 0x44, 0x57, 0x6d, 0x69, 0x6e, 0x54, 0x67, 0x34,
+ 0x77, 0x7a, 0x4c, 0x4c, 0x73, 0x66, 0x2b, 0x4c, 0x51, 0x38, 0x6e, 0x5a,
+ 0x0a, 0x62, 0x70, 0x6a, 0x66, 0x6e, 0x35, 0x76, 0x67, 0x49, 0x7a, 0x78,
+ 0x43, 0x75, 0x52, 0x68, 0x34, 0x52, 0x70, 0x39, 0x51, 0x59, 0x4d, 0x35,
+ 0x46, 0x68, 0x66, 0x72, 0x4a, 0x58, 0x39, 0x77, 0x63, 0x59, 0x61, 0x77,
+ 0x50, 0x2f, 0x48, 0x54, 0x62, 0x4a, 0x37, 0x70, 0x37, 0x4c, 0x56, 0x51,
+ 0x4f, 0x32, 0x51, 0x59, 0x41, 0x50, 0x2b, 0x61, 0x6b, 0x4d, 0x54, 0x48,
+ 0x78, 0x67, 0x4b, 0x75, 0x4d, 0x0a, 0x42, 0x7a, 0x56, 0x56, 0x2b, 0x2b,
+ 0x33, 0x77, 0x57, 0x54, 0x6f, 0x4b, 0x66, 0x56, 0x5a, 0x55, 0x6a, 0x46,
+ 0x58, 0x38, 0x6e, 0x66, 0x54, 0x66, 0x47, 0x4d, 0x47, 0x77, 0x57, 0x41,
+ 0x48, 0x4a, 0x44, 0x6e, 0x6c, 0x45, 0x47, 0x6e, 0x55, 0x34, 0x74, 0x6c,
+ 0x39, 0x55, 0x75, 0x6a, 0x6f, 0x43, 0x56, 0x34, 0x45, 0x4e, 0x4a, 0x74,
+ 0x7a, 0x46, 0x6f, 0x51, 0x49, 0x44, 0x41, 0x51, 0x41, 0x42, 0x0a, 0x41,
+ 0x6f, 0x47, 0x41, 0x4a, 0x2b, 0x36, 0x68, 0x70, 0x7a, 0x4e, 0x72, 0x32,
+ 0x34, 0x79, 0x54, 0x51, 0x5a, 0x74, 0x46, 0x57, 0x51, 0x70, 0x44, 0x70,
+ 0x45, 0x79, 0x46, 0x70, 0x6c, 0x64, 0x64, 0x4b, 0x4a, 0x4d, 0x4f, 0x78,
+ 0x44, 0x79, 0x61, 0x33, 0x53, 0x39, 0x70, 0x70, 0x4b, 0x33, 0x76, 0x54,
+ 0x57, 0x72, 0x49, 0x49, 0x54, 0x56, 0x32, 0x78, 0x4e, 0x63, 0x75, 0x63,
+ 0x77, 0x37, 0x49, 0x0a, 0x63, 0x65, 0x54, 0x62, 0x64, 0x79, 0x72, 0x47,
+ 0x73, 0x79, 0x6a, 0x73, 0x55, 0x30, 0x2f, 0x48, 0x64, 0x43, 0x63, 0x49,
+ 0x66, 0x39, 0x79, 0x6d, 0x32, 0x6a, 0x66, 0x6d, 0x47, 0x4c, 0x55, 0x77,
+ 0x6d, 0x79, 0x68, 0x6c, 0x74, 0x4b, 0x56, 0x77, 0x30, 0x51, 0x59, 0x63,
+ 0x46, 0x42, 0x30, 0x58, 0x4c, 0x6b, 0x63, 0x30, 0x6e, 0x49, 0x35, 0x59,
+ 0x76, 0x45, 0x59, 0x6f, 0x65, 0x56, 0x44, 0x67, 0x0a, 0x6f, 0x6d, 0x5a,
+ 0x49, 0x58, 0x6e, 0x31, 0x45, 0x33, 0x45, 0x57, 0x2b, 0x73, 0x53, 0x49,
+ 0x57, 0x53, 0x62, 0x6b, 0x4d, 0x75, 0x39, 0x62, 0x59, 0x32, 0x6b, 0x73,
+ 0x74, 0x4b, 0x58, 0x52, 0x32, 0x55, 0x5a, 0x6d, 0x4d, 0x67, 0x57, 0x44,
+ 0x74, 0x6d, 0x42, 0x45, 0x50, 0x4d, 0x61, 0x45, 0x43, 0x51, 0x51, 0x44,
+ 0x36, 0x79, 0x54, 0x34, 0x54, 0x41, 0x5a, 0x4d, 0x35, 0x68, 0x47, 0x42,
+ 0x62, 0x0a, 0x63, 0x69, 0x42, 0x4b, 0x67, 0x4d, 0x55, 0x50, 0x36, 0x50,
+ 0x77, 0x4f, 0x68, 0x50, 0x68, 0x4f, 0x4d, 0x50, 0x49, 0x76, 0x69, 0x6a,
+ 0x4f, 0x35, 0x30, 0x41, 0x69, 0x75, 0x36, 0x69, 0x75, 0x43, 0x56, 0x38,
+ 0x38, 0x6c, 0x31, 0x51, 0x49, 0x79, 0x33, 0x38, 0x67, 0x57, 0x56, 0x68,
+ 0x78, 0x6a, 0x4e, 0x72, 0x71, 0x36, 0x50, 0x33, 0x34, 0x36, 0x6a, 0x34,
+ 0x49, 0x42, 0x67, 0x2b, 0x6b, 0x42, 0x0a, 0x39, 0x61, 0x6c, 0x77, 0x70,
+ 0x43, 0x4f, 0x44, 0x41, 0x6b, 0x45, 0x41, 0x35, 0x6e, 0x53, 0x6e, 0x6d,
+ 0x39, 0x6b, 0x36, 0x79, 0x6b, 0x59, 0x65, 0x51, 0x57, 0x4e, 0x53, 0x30,
+ 0x66, 0x4e, 0x57, 0x69, 0x52, 0x69, 0x6e, 0x43, 0x64, 0x6c, 0x32, 0x33,
+ 0x41, 0x37, 0x75, 0x73, 0x44, 0x47, 0x53, 0x75, 0x4b, 0x4b, 0x6c, 0x6d,
+ 0x30, 0x31, 0x39, 0x69, 0x6f, 0x6d, 0x4a, 0x2f, 0x52, 0x67, 0x64, 0x0a,
+ 0x4d, 0x4b, 0x44, 0x4f, 0x70, 0x30, 0x71, 0x2f, 0x32, 0x4f, 0x6f, 0x73,
+ 0x74, 0x62, 0x74, 0x65, 0x4f, 0x57, 0x4d, 0x32, 0x4d, 0x52, 0x46, 0x66,
+ 0x34, 0x6a, 0x4d, 0x48, 0x33, 0x77, 0x79, 0x56, 0x43, 0x77, 0x4a, 0x41,
+ 0x66, 0x41, 0x64, 0x6a, 0x4a, 0x38, 0x73, 0x7a, 0x6f, 0x4e, 0x4b, 0x54,
+ 0x52, 0x53, 0x61, 0x67, 0x53, 0x62, 0x68, 0x39, 0x76, 0x57, 0x79, 0x67,
+ 0x6e, 0x42, 0x32, 0x76, 0x0a, 0x49, 0x42, 0x79, 0x63, 0x36, 0x6c, 0x34,
+ 0x54, 0x54, 0x75, 0x5a, 0x51, 0x4a, 0x52, 0x47, 0x7a, 0x43, 0x76, 0x65,
+ 0x61, 0x66, 0x7a, 0x39, 0x6c, 0x6f, 0x76, 0x75, 0x42, 0x33, 0x57, 0x6f,
+ 0x68, 0x43, 0x41, 0x42, 0x64, 0x51, 0x52, 0x64, 0x39, 0x75, 0x6b, 0x43,
+ 0x58, 0x4c, 0x32, 0x43, 0x70, 0x73, 0x45, 0x70, 0x71, 0x7a, 0x6b, 0x61,
+ 0x66, 0x4f, 0x51, 0x4a, 0x41, 0x4a, 0x55, 0x6a, 0x63, 0x0a, 0x55, 0x53,
+ 0x65, 0x64, 0x44, 0x6c, 0x71, 0x33, 0x7a, 0x47, 0x5a, 0x77, 0x59, 0x4d,
+ 0x31, 0x59, 0x77, 0x38, 0x64, 0x38, 0x52, 0x75, 0x69, 0x72, 0x42, 0x55,
+ 0x46, 0x5a, 0x4e, 0x71, 0x4a, 0x65, 0x6c, 0x59, 0x61, 0x69, 0x2b, 0x6e,
+ 0x52, 0x59, 0x43, 0x6c, 0x44, 0x6b, 0x52, 0x56, 0x46, 0x67, 0x62, 0x35,
+ 0x79, 0x6b, 0x73, 0x6f, 0x59, 0x79, 0x63, 0x62, 0x71, 0x35, 0x54, 0x78,
+ 0x47, 0x6f, 0x0a, 0x56, 0x65, 0x71, 0x4b, 0x4f, 0x76, 0x67, 0x50, 0x70,
+ 0x6a, 0x34, 0x52, 0x57, 0x50, 0x48, 0x6c, 0x4c, 0x77, 0x4a, 0x41, 0x47,
+ 0x55, 0x4d, 0x6b, 0x33, 0x62, 0x71, 0x54, 0x39, 0x31, 0x78, 0x42, 0x55,
+ 0x43, 0x6e, 0x4c, 0x52, 0x73, 0x2f, 0x76, 0x66, 0x6f, 0x43, 0x70, 0x48,
+ 0x70, 0x67, 0x36, 0x65, 0x79, 0x77, 0x51, 0x54, 0x42, 0x44, 0x41, 0x56,
+ 0x36, 0x78, 0x6b, 0x79, 0x7a, 0x34, 0x61, 0x0a, 0x52, 0x48, 0x33, 0x49,
+ 0x37, 0x2f, 0x2b, 0x79, 0x6a, 0x33, 0x5a, 0x78, 0x52, 0x32, 0x4a, 0x6f,
+ 0x57, 0x48, 0x67, 0x55, 0x77, 0x5a, 0x37, 0x6c, 0x5a, 0x6b, 0x31, 0x56,
+ 0x6e, 0x68, 0x66, 0x66, 0x46, 0x79, 0x65, 0x37, 0x53, 0x42, 0x58, 0x79,
+ 0x61, 0x67, 0x3d, 0x3d, 0x0a, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x45, 0x4e,
+ 0x44, 0x20, 0x52, 0x53, 0x41, 0x20, 0x50, 0x52, 0x49, 0x56, 0x41, 0x54,
+ 0x45, 0x20, 0x4b, 0x45, 0x59, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a};
+unsigned int test_server1_key_size = 887;
diff --git a/test/core/end2end/data/ssl_test_data.h b/test/core/end2end/data/ssl_test_data.h
new file mode 100644
index 0000000000..75263b85d2
--- /dev/null
+++ b/test/core/end2end/data/ssl_test_data.h
@@ -0,0 +1,44 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#ifndef __GRPC_TEST_END2END_DATA_SSL_TEST_DATA_H__
+#define __GRPC_TEST_END2END_DATA_SSL_TEST_DATA_H__
+
+extern unsigned char test_ca_cert[];
+extern unsigned int test_ca_cert_size;
+extern unsigned char test_server1_cert[];
+extern unsigned int test_server1_cert_size;
+extern unsigned char test_server1_key[];
+extern unsigned int test_server1_key_size;
+
+#endif /* __GRPC_TEST_END2END_DATA_SSL_TEST_DATA_H__ */
diff --git a/test/core/end2end/end2end_tests.c b/test/core/end2end/end2end_tests.c
new file mode 100644
index 0000000000..7174d09866
--- /dev/null
+++ b/test/core/end2end/end2end_tests.c
@@ -0,0 +1,1285 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include "test/core/end2end/end2end_tests.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <grpc/byte_buffer.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/time.h>
+#include <grpc/support/useful.h>
+#include "test/core/end2end/cq_verifier.h"
+
+enum { TIMEOUT = 200000 };
+
+static void *tag(gpr_intptr t) { return (void *)t; }
+
+static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
+ const char *test_name,
+ grpc_channel_args *client_args,
+ grpc_channel_args *server_args) {
+ grpc_end2end_test_fixture f;
+ gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+ f = config.create_fixture(client_args, server_args);
+ config.init_client(&f, client_args);
+ config.init_server(&f, server_args);
+ return f;
+}
+
+static gpr_timespec n_seconds_time(int n) {
+ return gpr_time_add(gpr_now(), gpr_time_from_micros(GPR_US_PER_SEC * n));
+}
+
+static gpr_timespec five_seconds_time() { return n_seconds_time(5); }
+
+static void drain_cq(grpc_completion_queue *cq) {
+ grpc_event *ev;
+ grpc_completion_type type;
+ do {
+ ev = grpc_completion_queue_next(cq, five_seconds_time());
+ GPR_ASSERT(ev);
+ type = ev->type;
+ grpc_event_finish(ev);
+ } while (type != GRPC_QUEUE_SHUTDOWN);
+}
+
+static void shutdown_server(grpc_end2end_test_fixture *f) {
+ if (!f->server) return;
+ grpc_server_shutdown(f->server);
+ grpc_server_destroy(f->server);
+ f->server = NULL;
+}
+
+static void shutdown_client(grpc_end2end_test_fixture *f) {
+ if (!f->client) return;
+ grpc_channel_destroy(f->client);
+ f->client = NULL;
+}
+
+static void end_test(grpc_end2end_test_fixture *f) {
+ shutdown_server(f);
+ shutdown_client(f);
+
+ grpc_completion_queue_shutdown(f->server_cq);
+ drain_cq(f->server_cq);
+ grpc_completion_queue_destroy(f->server_cq);
+ grpc_completion_queue_shutdown(f->client_cq);
+ drain_cq(f->client_cq);
+ grpc_completion_queue_destroy(f->client_cq);
+}
+
+static void test_no_op(grpc_end2end_test_config config) {
+ grpc_end2end_test_fixture f = begin_test(config, __FUNCTION__, NULL, NULL);
+ end_test(&f);
+ config.tear_down_data(&f);
+}
+
+static void simple_request_body(grpc_end2end_test_fixture f) {
+ grpc_call *c;
+ grpc_call *s;
+ grpc_status send_status = {GRPC_STATUS_UNIMPLEMENTED, "xyz"};
+ gpr_timespec deadline = five_seconds_time();
+ cq_verifier *v_client = cq_verifier_create(f.client_cq);
+ cq_verifier *v_server = cq_verifier_create(f.server_cq);
+
+ c = grpc_channel_create_call(f.client, "/foo", "test.google.com", deadline);
+ GPR_ASSERT(c);
+
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_invoke(c, f.client_cq, tag(1), tag(2), tag(3), 0));
+ cq_expect_invoke_accepted(v_client, tag(1), GRPC_OP_OK);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_writes_done(c, tag(4)));
+ cq_expect_finish_accepted(v_client, tag(4), GRPC_OP_OK);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_server_request_call(f.server, tag(100)));
+ cq_expect_server_rpc_new(v_server, &s, tag(100), "/foo", "test.google.com",
+ deadline, NULL);
+ cq_verify(v_server);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_accept(s, f.server_cq, tag(102), 0));
+ cq_expect_client_metadata_read(v_client, tag(2), NULL);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_write_status(s, send_status, tag(5)));
+ cq_expect_finished_with_status(v_client, tag(3), send_status, NULL);
+ cq_verify(v_client);
+
+ cq_expect_finish_accepted(v_server, tag(5), GRPC_OP_OK);
+ cq_verify(v_server);
+ cq_expect_finished(v_server, tag(102), NULL);
+ cq_verify(v_server);
+
+ grpc_call_destroy(c);
+ grpc_call_destroy(s);
+
+ cq_verifier_destroy(v_client);
+ cq_verifier_destroy(v_server);
+}
+
+/* an alternative ordering of the simple request body */
+static void simple_request_body2(grpc_end2end_test_fixture f) {
+ grpc_call *c;
+ grpc_call *s;
+ grpc_status send_status = {GRPC_STATUS_UNIMPLEMENTED, "xyz"};
+ gpr_timespec deadline = five_seconds_time();
+ cq_verifier *v_client = cq_verifier_create(f.client_cq);
+ cq_verifier *v_server = cq_verifier_create(f.server_cq);
+
+ c = grpc_channel_create_call(f.client, "/foo", "test.google.com", deadline);
+ GPR_ASSERT(c);
+
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_invoke(c, f.client_cq, tag(1), tag(2), tag(3), 0));
+ cq_expect_invoke_accepted(v_client, tag(1), GRPC_OP_OK);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_writes_done(c, tag(4)));
+ cq_expect_finish_accepted(v_client, tag(4), GRPC_OP_OK);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_server_request_call(f.server, tag(100)));
+ cq_expect_server_rpc_new(v_server, &s, tag(100), "/foo", "test.google.com",
+ deadline, NULL);
+ cq_verify(v_server);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_accept(s, f.server_cq, tag(102), 0));
+
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_write_status(s, send_status, tag(5)));
+ cq_expect_finish_accepted(v_server, tag(5), GRPC_OP_OK);
+ cq_verify(v_server);
+
+ cq_expect_client_metadata_read(v_client, tag(2), NULL);
+ cq_verify(v_client);
+
+ cq_expect_finished_with_status(v_client, tag(3), send_status, NULL);
+ cq_verify(v_client);
+
+ cq_expect_finished(v_server, tag(102), NULL);
+ cq_verify(v_server);
+
+ grpc_call_destroy(c);
+ grpc_call_destroy(s);
+
+ cq_verifier_destroy(v_client);
+ cq_verifier_destroy(v_server);
+}
+
+static void test_invoke_simple_request(
+ grpc_end2end_test_config config, const char *name,
+ void (*body)(grpc_end2end_test_fixture f)) {
+ char fullname[64];
+ grpc_end2end_test_fixture f;
+
+ sprintf(fullname, "%s/%s", __FUNCTION__, name);
+
+ f = begin_test(config, fullname, NULL, NULL);
+ body(f);
+ end_test(&f);
+ config.tear_down_data(&f);
+}
+
+static void test_invoke_10_simple_requests(grpc_end2end_test_config config) {
+ int i;
+ grpc_end2end_test_fixture f = begin_test(config, __FUNCTION__, NULL, NULL);
+ for (i = 0; i < 10; i++) {
+ simple_request_body(f);
+ gpr_log(GPR_INFO, "Passed simple request %d", i);
+ }
+ end_test(&f);
+ config.tear_down_data(&f);
+}
+
+static void simple_delayed_request_body(grpc_end2end_test_config config,
+ grpc_end2end_test_fixture *f,
+ grpc_channel_args *client_args,
+ grpc_channel_args *server_args,
+ long delay_us) {
+ grpc_call *c;
+ grpc_call *s;
+ grpc_status send_status = {GRPC_STATUS_UNIMPLEMENTED, "xyz"};
+ gpr_timespec deadline = five_seconds_time();
+ cq_verifier *v_client = cq_verifier_create(f->client_cq);
+ cq_verifier *v_server = cq_verifier_create(f->server_cq);
+
+ config.init_client(f, client_args);
+
+ c = grpc_channel_create_call(f->client, "/foo", "test.google.com", deadline);
+ GPR_ASSERT(c);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_invoke(c, f->client_cq, tag(1),
+ tag(2), tag(3), 0));
+ gpr_sleep_until(gpr_time_add(gpr_now(), gpr_time_from_micros(delay_us)));
+ cq_expect_invoke_accepted(v_client, tag(1), GRPC_OP_OK);
+
+ config.init_server(f, server_args);
+
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_writes_done(c, tag(4)));
+ cq_expect_finish_accepted(v_client, tag(4), GRPC_OP_OK);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_server_request_call(f->server, tag(100)));
+ cq_expect_server_rpc_new(v_server, &s, tag(100), "/foo", "test.google.com",
+ deadline, NULL);
+ cq_verify(v_server);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_accept(s, f->server_cq, tag(102), 0));
+ cq_expect_client_metadata_read(v_client, tag(2), NULL);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_write_status(s, send_status, tag(5)));
+ cq_expect_finished_with_status(v_client, tag(3), send_status, NULL);
+ cq_verify(v_client);
+
+ cq_expect_finish_accepted(v_server, tag(5), GRPC_OP_OK);
+ cq_expect_finished(v_server, tag(102), NULL);
+ cq_verify(v_server);
+
+ grpc_call_destroy(c);
+ grpc_call_destroy(s);
+
+ cq_verifier_destroy(v_client);
+ cq_verifier_destroy(v_server);
+}
+
+static void test_simple_delayed_request_short(grpc_end2end_test_config config) {
+ grpc_end2end_test_fixture f;
+
+ gpr_log(GPR_INFO, "%s/%s", __FUNCTION__, config.name);
+ f = config.create_fixture(NULL, NULL);
+ simple_delayed_request_body(config, &f, NULL, NULL, 100000);
+ end_test(&f);
+ config.tear_down_data(&f);
+}
+
+static void test_simple_delayed_request_long(grpc_end2end_test_config config) {
+ grpc_end2end_test_fixture f;
+
+ gpr_log(GPR_INFO, "%s/%s", __FUNCTION__, config.name);
+ f = config.create_fixture(NULL, NULL);
+ /* This timeout should be longer than a single retry */
+ simple_delayed_request_body(config, &f, NULL, NULL, 1500000);
+ end_test(&f);
+ config.tear_down_data(&f);
+}
+
+/* Client sends a request with payload, server reads then returns status. */
+static void test_invoke_request_with_payload(grpc_end2end_test_config config) {
+ grpc_call *c;
+ grpc_call *s;
+ grpc_status send_status = {GRPC_STATUS_UNIMPLEMENTED, "xyz"};
+ gpr_slice payload_slice = gpr_slice_from_copied_string("hello world");
+ grpc_byte_buffer *payload = grpc_byte_buffer_create(&payload_slice, 1);
+ gpr_timespec deadline = five_seconds_time();
+ grpc_end2end_test_fixture f = begin_test(config, __FUNCTION__, NULL, NULL);
+ cq_verifier *v_client = cq_verifier_create(f.client_cq);
+ cq_verifier *v_server = cq_verifier_create(f.server_cq);
+
+ /* byte buffer holds the slice, we can unref it already */
+ gpr_slice_unref(payload_slice);
+
+ c = grpc_channel_create_call(f.client, "/foo", "test.google.com", deadline);
+ GPR_ASSERT(c);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_server_request_call(f.server, tag(100)));
+
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_invoke(c, f.client_cq, tag(1), tag(2), tag(3), 0));
+ cq_expect_invoke_accepted(v_client, tag(1), GRPC_OP_OK);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_write(c, payload, tag(4), 0));
+ /* destroy byte buffer early to ensure async code keeps track of its contents
+ correctly */
+ grpc_byte_buffer_destroy(payload);
+ cq_expect_write_accepted(v_client, tag(4), GRPC_OP_OK);
+ cq_verify(v_client);
+
+ cq_expect_server_rpc_new(v_server, &s, tag(100), "/foo", "test.google.com",
+ deadline, NULL);
+ cq_verify(v_server);
+
+ grpc_call_accept(s, f.server_cq, tag(102), 0);
+ cq_expect_client_metadata_read(v_client, tag(2), NULL);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_read(s, tag(4)));
+ cq_expect_read(v_server, tag(4), gpr_slice_from_copied_string("hello world"));
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_writes_done(c, tag(5)));
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_write_status(s, send_status, tag(6)));
+ cq_expect_finish_accepted(v_client, tag(5), GRPC_OP_OK);
+ cq_expect_finished_with_status(v_client, tag(3), send_status, NULL);
+ cq_verify(v_client);
+
+ cq_expect_finish_accepted(v_server, tag(6), GRPC_OP_OK);
+ cq_expect_finished(v_server, tag(102), NULL);
+ cq_verify(v_server);
+
+ grpc_call_destroy(c);
+ grpc_call_destroy(s);
+
+ end_test(&f);
+ config.tear_down_data(&f);
+
+ cq_verifier_destroy(v_client);
+ cq_verifier_destroy(v_server);
+}
+
+/* test the case when there is a pending message at the client side,
+ writes_done should not return a status without a start_read.
+ Note: this test will last for 3s. Do not run in a loop. */
+static void test_writes_done_hangs_with_pending_read(
+ grpc_end2end_test_config config) {
+ grpc_call *c;
+ grpc_call *s;
+ grpc_status send_status = {GRPC_STATUS_UNIMPLEMENTED, "xyz"};
+ gpr_slice request_payload_slice = gpr_slice_from_copied_string("hello world");
+ gpr_slice response_payload_slice = gpr_slice_from_copied_string("hello you");
+ grpc_byte_buffer *request_payload =
+ grpc_byte_buffer_create(&request_payload_slice, 1);
+ grpc_byte_buffer *response_payload =
+ grpc_byte_buffer_create(&response_payload_slice, 1);
+ gpr_timespec deadline = five_seconds_time();
+ grpc_end2end_test_fixture f = begin_test(config, __FUNCTION__, NULL, NULL);
+ cq_verifier *v_client = cq_verifier_create(f.client_cq);
+ cq_verifier *v_server = cq_verifier_create(f.server_cq);
+
+ /* byte buffer holds the slice, we can unref it already */
+ gpr_slice_unref(request_payload_slice);
+ gpr_slice_unref(response_payload_slice);
+
+ c = grpc_channel_create_call(f.client, "/foo", "test.google.com", deadline);
+ GPR_ASSERT(c);
+
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_invoke(c, f.client_cq, tag(1), tag(2), tag(3), 0));
+ cq_expect_invoke_accepted(v_client, tag(1), GRPC_OP_OK);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_write(c, request_payload, tag(4), 0));
+ /* destroy byte buffer early to ensure async code keeps track of its contents
+ correctly */
+ grpc_byte_buffer_destroy(request_payload);
+ cq_expect_write_accepted(v_client, tag(4), GRPC_OP_OK);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_server_request_call(f.server, tag(100)));
+ cq_expect_server_rpc_new(v_server, &s, tag(100), "/foo", "test.google.com",
+ deadline, NULL);
+ cq_verify(v_server);
+
+ grpc_call_accept(s, f.server_cq, tag(102), 0);
+ cq_expect_client_metadata_read(v_client, tag(2), NULL);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_read(s, tag(5)));
+ cq_expect_read(v_server, tag(5), gpr_slice_from_copied_string("hello world"));
+ cq_verify(v_server);
+
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_write(s, response_payload, tag(6), 0));
+ /* destroy byte buffer early to ensure async code keeps track of its contents
+ correctly */
+ grpc_byte_buffer_destroy(response_payload);
+ cq_expect_write_accepted(v_server, tag(6), GRPC_OP_OK);
+ cq_verify(v_server);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_writes_done(c, tag(6)));
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_write_status(s, send_status, tag(7)));
+
+ cq_expect_finish_accepted(v_client, tag(6), GRPC_OP_OK);
+ cq_verify(v_client);
+
+ /* does not return status because there is a pending message to be read */
+ cq_verify_empty(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_read(c, tag(8)));
+ cq_expect_read(v_client, tag(8), gpr_slice_from_copied_string("hello you"));
+ cq_verify(v_client);
+
+ cq_expect_finished_with_status(v_client, tag(3), send_status, NULL);
+ cq_verify(v_client);
+
+ cq_expect_finish_accepted(v_server, tag(7), GRPC_OP_OK);
+ cq_expect_finished(v_server, tag(102), NULL);
+ cq_verify(v_server);
+
+ grpc_call_destroy(c);
+ grpc_call_destroy(s);
+
+ end_test(&f);
+ config.tear_down_data(&f);
+
+ cq_verifier_destroy(v_client);
+ cq_verifier_destroy(v_server);
+}
+
+static void request_response_with_payload(grpc_end2end_test_fixture f) {
+ grpc_call *c;
+ grpc_call *s;
+ grpc_status send_status = {GRPC_STATUS_UNIMPLEMENTED, "xyz"};
+ gpr_slice request_payload_slice = gpr_slice_from_copied_string("hello world");
+ gpr_slice response_payload_slice = gpr_slice_from_copied_string("hello you");
+ grpc_byte_buffer *request_payload =
+ grpc_byte_buffer_create(&request_payload_slice, 1);
+ grpc_byte_buffer *response_payload =
+ grpc_byte_buffer_create(&response_payload_slice, 1);
+ gpr_timespec deadline = five_seconds_time();
+ cq_verifier *v_client = cq_verifier_create(f.client_cq);
+ cq_verifier *v_server = cq_verifier_create(f.server_cq);
+
+ /* byte buffer holds the slice, we can unref it already */
+ gpr_slice_unref(request_payload_slice);
+ gpr_slice_unref(response_payload_slice);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_server_request_call(f.server, tag(100)));
+
+ c = grpc_channel_create_call(f.client, "/foo", "test.google.com", deadline);
+ GPR_ASSERT(c);
+
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_invoke(c, f.client_cq, tag(1), tag(2), tag(3), 0));
+ cq_expect_invoke_accepted(v_client, tag(1), GRPC_OP_OK);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_write(c, request_payload, tag(4), 0));
+ /* destroy byte buffer early to ensure async code keeps track of its contents
+ correctly */
+ grpc_byte_buffer_destroy(request_payload);
+ cq_expect_write_accepted(v_client, tag(4), GRPC_OP_OK);
+ cq_verify(v_client);
+
+ cq_expect_server_rpc_new(v_server, &s, tag(100), "/foo", "test.google.com",
+ deadline, NULL);
+ cq_verify(v_server);
+
+ grpc_call_accept(s, f.server_cq, tag(102), 0);
+ cq_expect_client_metadata_read(v_client, tag(2), NULL);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_read(s, tag(5)));
+ cq_expect_read(v_server, tag(5), gpr_slice_from_copied_string("hello world"));
+ cq_verify(v_server);
+
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_write(s, response_payload, tag(6), 0));
+ /* destroy byte buffer early to ensure async code keeps track of its contents
+ correctly */
+ grpc_byte_buffer_destroy(response_payload);
+ cq_expect_write_accepted(v_server, tag(6), GRPC_OP_OK);
+ cq_verify(v_server);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_read(c, tag(7)));
+ cq_expect_read(v_client, tag(7), gpr_slice_from_copied_string("hello you"));
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_writes_done(c, tag(8)));
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_write_status(s, send_status, tag(9)));
+
+ cq_expect_finish_accepted(v_client, tag(8), GRPC_OP_OK);
+ cq_expect_finished_with_status(v_client, tag(3), send_status, NULL);
+ cq_verify(v_client);
+
+ cq_expect_finish_accepted(v_server, tag(9), GRPC_OP_OK);
+ cq_expect_finished(v_server, tag(102), NULL);
+ cq_verify(v_server);
+
+ grpc_call_destroy(c);
+ grpc_call_destroy(s);
+
+ cq_verifier_destroy(v_client);
+ cq_verifier_destroy(v_server);
+}
+
+/* Client sends a request with payload, server reads then returns a response
+ payload and status. */
+static void test_invoke_request_response_with_payload(
+ grpc_end2end_test_config config) {
+ grpc_end2end_test_fixture f = begin_test(config, __FUNCTION__, NULL, NULL);
+ request_response_with_payload(f);
+ end_test(&f);
+ config.tear_down_data(&f);
+}
+
+static void test_invoke_10_request_response_with_payload(
+ grpc_end2end_test_config config) {
+ int i;
+ grpc_end2end_test_fixture f = begin_test(config, __FUNCTION__, NULL, NULL);
+ for (i = 0; i < 10; i++) {
+ request_response_with_payload(f);
+ }
+ end_test(&f);
+ config.tear_down_data(&f);
+}
+
+/* allow cancellation by either grpc_call_cancel, or by wait_for_deadline (which
+ * does nothing) */
+typedef grpc_call_error (*canceller)(grpc_call *call);
+
+static grpc_call_error wait_for_deadline(grpc_call *call) {
+ return GRPC_CALL_OK;
+}
+
+/* Cancel and do nothing */
+static void test_cancel_in_a_vacuum(grpc_end2end_test_config config,
+ canceller call_cancel) {
+ grpc_call *c;
+ grpc_end2end_test_fixture f = begin_test(config, __FUNCTION__, NULL, NULL);
+ gpr_timespec deadline = five_seconds_time();
+ cq_verifier *v_client = cq_verifier_create(f.client_cq);
+
+ c = grpc_channel_create_call(f.client, "/foo", "test.google.com", deadline);
+ GPR_ASSERT(c);
+
+ GPR_ASSERT(GRPC_CALL_OK == call_cancel(c));
+
+ grpc_call_destroy(c);
+
+ cq_verifier_destroy(v_client);
+ end_test(&f);
+ config.tear_down_data(&f);
+}
+
+/* Cancel before invoke */
+static void test_cancel_before_invoke(grpc_end2end_test_config config) {
+ grpc_call *c;
+ grpc_end2end_test_fixture f = begin_test(config, __FUNCTION__, NULL, NULL);
+ gpr_timespec deadline = five_seconds_time();
+ cq_verifier *v_client = cq_verifier_create(f.client_cq);
+ grpc_status chk_status = {GRPC_STATUS_CANCELLED, NULL};
+
+ c = grpc_channel_create_call(f.client, "/foo", "test.google.com", deadline);
+ GPR_ASSERT(c);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_cancel(c));
+
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_invoke(c, f.client_cq, tag(1), tag(2), tag(3), 0));
+ cq_expect_invoke_accepted(v_client, tag(1), GRPC_OP_ERROR);
+ cq_expect_client_metadata_read(v_client, tag(2), NULL);
+ cq_expect_finished_with_status(v_client, tag(3), chk_status, NULL);
+ cq_verify(v_client);
+
+ grpc_call_destroy(c);
+
+ cq_verifier_destroy(v_client);
+ end_test(&f);
+ config.tear_down_data(&f);
+}
+
+/* Cancel after invoke, no payload */
+static void test_cancel_after_invoke(grpc_end2end_test_config config,
+ canceller call_cancel) {
+ grpc_call *c;
+ grpc_end2end_test_fixture f = begin_test(config, __FUNCTION__, NULL, NULL);
+ gpr_timespec deadline = five_seconds_time();
+ cq_verifier *v_client = cq_verifier_create(f.client_cq);
+ grpc_status chk_status = {GRPC_STATUS_CANCELLED, NULL};
+
+ c = grpc_channel_create_call(f.client, "/foo", "test.google.com", deadline);
+ GPR_ASSERT(c);
+
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_invoke(c, f.client_cq, tag(1), tag(2), tag(3), 0));
+ cq_expect_invoke_accepted(v_client, tag(1), GRPC_OP_OK);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK == call_cancel(c));
+
+ cq_expect_client_metadata_read(v_client, tag(2), NULL);
+ cq_expect_finished_with_status(v_client, tag(3), chk_status, NULL);
+ cq_verify(v_client);
+
+ grpc_call_destroy(c);
+
+ cq_verifier_destroy(v_client);
+ end_test(&f);
+ config.tear_down_data(&f);
+}
+
+/* Cancel after accept, no payload */
+static void test_cancel_after_accept(grpc_end2end_test_config config,
+ canceller call_cancel) {
+ grpc_call *c;
+ grpc_call *s;
+ grpc_end2end_test_fixture f = begin_test(config, __FUNCTION__, NULL, NULL);
+ gpr_timespec deadline = five_seconds_time();
+ cq_verifier *v_client = cq_verifier_create(f.client_cq);
+ cq_verifier *v_server = cq_verifier_create(f.server_cq);
+ grpc_status chk_status = {GRPC_STATUS_CANCELLED, NULL};
+
+ c = grpc_channel_create_call(f.client, "/foo", "test.google.com", deadline);
+ GPR_ASSERT(c);
+
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_invoke(c, f.client_cq, tag(1), tag(2), tag(3), 0));
+ cq_expect_invoke_accepted(v_client, tag(1), GRPC_OP_OK);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_server_request_call(f.server, tag(100)));
+ cq_expect_server_rpc_new(v_server, &s, tag(100), "/foo", "test.google.com",
+ deadline, NULL);
+ cq_verify(v_server);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_accept(s, f.server_cq, tag(102), 0));
+ cq_expect_client_metadata_read(v_client, tag(2), NULL);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK == call_cancel(c));
+
+ cq_expect_finished_with_status(v_client, tag(3), chk_status, NULL);
+ cq_verify(v_client);
+
+ cq_expect_finished_with_status(v_server, tag(102), chk_status, NULL);
+ cq_verify(v_server);
+
+ grpc_call_destroy(c);
+ grpc_call_destroy(s);
+
+ cq_verifier_destroy(v_client);
+ cq_verifier_destroy(v_server);
+ end_test(&f);
+ config.tear_down_data(&f);
+}
+
+/* Cancel after accept with a writes closed, no payload */
+static void test_cancel_after_accept_and_writes_closed(
+ grpc_end2end_test_config config, canceller call_cancel) {
+ grpc_call *c;
+ grpc_call *s;
+ grpc_end2end_test_fixture f = begin_test(config, __FUNCTION__, NULL, NULL);
+ gpr_timespec deadline = five_seconds_time();
+ cq_verifier *v_client = cq_verifier_create(f.client_cq);
+ cq_verifier *v_server = cq_verifier_create(f.server_cq);
+ grpc_status chk_status = {GRPC_STATUS_CANCELLED, NULL};
+
+ c = grpc_channel_create_call(f.client, "/foo", "test.google.com", deadline);
+ GPR_ASSERT(c);
+
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_invoke(c, f.client_cq, tag(1), tag(2), tag(3), 0));
+ cq_expect_invoke_accepted(v_client, tag(1), GRPC_OP_OK);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_server_request_call(f.server, tag(100)));
+ cq_expect_server_rpc_new(v_server, &s, tag(100), "/foo", "test.google.com",
+ deadline, NULL);
+ cq_verify(v_server);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_accept(s, f.server_cq, tag(102), 0));
+ cq_expect_client_metadata_read(v_client, tag(2), NULL);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_writes_done(c, tag(4)));
+ cq_expect_finish_accepted(v_client, tag(4), GRPC_OP_OK);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_read(s, tag(101)));
+ cq_expect_empty_read(v_server, tag(101));
+ cq_verify(v_server);
+
+ GPR_ASSERT(GRPC_CALL_OK == call_cancel(c));
+
+ cq_expect_finished_with_status(v_client, tag(3), chk_status, NULL);
+ cq_verify(v_client);
+
+ cq_expect_finished_with_status(v_server, tag(102), chk_status, NULL);
+ cq_verify(v_server);
+
+ grpc_call_destroy(c);
+ grpc_call_destroy(s);
+
+ cq_verifier_destroy(v_client);
+ cq_verifier_destroy(v_server);
+ end_test(&f);
+ config.tear_down_data(&f);
+}
+
+/* Request/response with metadata and payload.*/
+static void test_request_response_with_metadata_and_payload(
+ grpc_end2end_test_config config) {
+ grpc_call *c;
+ grpc_call *s;
+ grpc_status send_status = {GRPC_STATUS_UNIMPLEMENTED, "xyz"};
+ gpr_slice request_payload_slice = gpr_slice_from_copied_string("hello world");
+ gpr_slice response_payload_slice = gpr_slice_from_copied_string("hello you");
+ grpc_byte_buffer *request_payload =
+ grpc_byte_buffer_create(&request_payload_slice, 1);
+ grpc_byte_buffer *response_payload =
+ grpc_byte_buffer_create(&response_payload_slice, 1);
+ gpr_timespec deadline = five_seconds_time();
+ grpc_metadata meta1 = {"key1", "val1", 4};
+ grpc_metadata meta2 = {"key2", "val2", 4};
+ grpc_metadata meta3 = {"key3", "val3", 4};
+ grpc_metadata meta4 = {"key4", "val4", 4};
+ grpc_end2end_test_fixture f = begin_test(config, __FUNCTION__, NULL, NULL);
+ cq_verifier *v_client = cq_verifier_create(f.client_cq);
+ cq_verifier *v_server = cq_verifier_create(f.server_cq);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_server_request_call(f.server, tag(100)));
+
+ /* byte buffer holds the slice, we can unref it already */
+ gpr_slice_unref(request_payload_slice);
+ gpr_slice_unref(response_payload_slice);
+
+ c = grpc_channel_create_call(f.client, "/foo", "test.google.com", deadline);
+ GPR_ASSERT(c);
+
+ /* add multiple metadata */
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_add_metadata(c, &meta1, 0));
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_add_metadata(c, &meta2, 0));
+
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_invoke(c, f.client_cq, tag(1), tag(2), tag(3), 0));
+ cq_expect_invoke_accepted(v_client, tag(1), GRPC_OP_OK);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_write(c, request_payload, tag(4), 0));
+ /* destroy byte buffer early to ensure async code keeps track of its contents
+ correctly */
+ grpc_byte_buffer_destroy(request_payload);
+ cq_expect_write_accepted(v_client, tag(4), GRPC_OP_OK);
+ cq_verify(v_client);
+
+ cq_expect_server_rpc_new(v_server, &s, tag(100), "/foo", "test.google.com",
+ deadline, "key1", "val1", "key2", "val2", NULL);
+ cq_verify(v_server);
+
+ /* add multiple metadata */
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_add_metadata(s, &meta3, 0));
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_add_metadata(s, &meta4, 0));
+
+ grpc_call_accept(s, f.server_cq, tag(102), 0);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_read(s, tag(5)));
+ cq_expect_read(v_server, tag(5), gpr_slice_from_copied_string("hello world"));
+ cq_verify(v_server);
+
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_write(s, response_payload, tag(6), 0));
+ /* destroy byte buffer early to ensure async code keeps track of its contents
+ correctly */
+ grpc_byte_buffer_destroy(response_payload);
+ cq_expect_write_accepted(v_server, tag(6), GRPC_OP_OK);
+ cq_verify(v_server);
+
+ /* fetch metadata.. */
+ cq_expect_client_metadata_read(v_client, tag(2), "key3", "val3", "key4",
+ "val4", NULL);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_read(c, tag(7)));
+ cq_expect_read(v_client, tag(7), gpr_slice_from_copied_string("hello you"));
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_writes_done(c, tag(8)));
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_write_status(s, send_status, tag(9)));
+
+ cq_expect_finish_accepted(v_client, tag(8), GRPC_OP_OK);
+ cq_expect_finished_with_status(v_client, tag(3), send_status, NULL);
+ cq_verify(v_client);
+
+ cq_expect_finish_accepted(v_server, tag(9), GRPC_OP_OK);
+ cq_expect_finished(v_server, tag(102), NULL);
+ cq_verify(v_server);
+
+ grpc_call_destroy(c);
+ grpc_call_destroy(s);
+
+ end_test(&f);
+ config.tear_down_data(&f);
+
+ cq_verifier_destroy(v_client);
+ cq_verifier_destroy(v_server);
+}
+
+/* Request with a large amount of metadata.*/
+static void test_request_with_large_metadata(grpc_end2end_test_config config) {
+ grpc_call *c;
+ grpc_call *s;
+ grpc_status send_status = {GRPC_STATUS_OK, NULL};
+ gpr_timespec deadline = five_seconds_time();
+ grpc_metadata meta;
+ grpc_end2end_test_fixture f = begin_test(config, __FUNCTION__, NULL, NULL);
+ cq_verifier *v_client = cq_verifier_create(f.client_cq);
+ cq_verifier *v_server = cq_verifier_create(f.server_cq);
+ const int large_size = 64 * 1024;
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_server_request_call(f.server, tag(100)));
+
+ meta.key = "key";
+ meta.value = gpr_malloc(large_size + 1);
+ memset(meta.value, 'a', large_size);
+ meta.value[large_size] = 0;
+ meta.value_length = large_size;
+
+ c = grpc_channel_create_call(f.client, "/foo", "test.google.com", deadline);
+ GPR_ASSERT(c);
+
+ /* add the metadata */
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_add_metadata(c, &meta, 0));
+
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_invoke(c, f.client_cq, tag(1), tag(2), tag(3), 0));
+ cq_expect_invoke_accepted(v_client, tag(1), GRPC_OP_OK);
+ cq_verify(v_client);
+
+ cq_expect_server_rpc_new(v_server, &s, tag(100), "/foo", "test.google.com",
+ deadline, "key", meta.value, NULL);
+ cq_verify(v_server);
+
+ grpc_call_accept(s, f.server_cq, tag(102), 0);
+
+ /* fetch metadata.. */
+ cq_expect_client_metadata_read(v_client, tag(2), NULL);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_writes_done(c, tag(8)));
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_write_status(s, send_status, tag(9)));
+
+ cq_expect_finish_accepted(v_client, tag(8), GRPC_OP_OK);
+ cq_expect_finished_with_status(v_client, tag(3), send_status, NULL);
+ cq_verify(v_client);
+
+ cq_expect_finish_accepted(v_server, tag(9), GRPC_OP_OK);
+ cq_expect_finished(v_server, tag(102), NULL);
+ cq_verify(v_server);
+
+ grpc_call_destroy(c);
+ grpc_call_destroy(s);
+
+ end_test(&f);
+ config.tear_down_data(&f);
+
+ cq_verifier_destroy(v_client);
+ cq_verifier_destroy(v_server);
+
+ gpr_free(meta.value);
+}
+
+/* Client pings and server pongs. Repeat messages rounds before finishing. */
+static void test_pingpong_streaming(grpc_end2end_test_config config,
+ int messages) {
+ int i;
+ grpc_call *c;
+ grpc_call *s = NULL;
+ grpc_status send_status = {GRPC_STATUS_UNIMPLEMENTED, "xyz"};
+ gpr_slice request_payload_slice = gpr_slice_from_copied_string("hello world");
+ gpr_slice response_payload_slice = gpr_slice_from_copied_string("hello you");
+ grpc_byte_buffer *request_payload = NULL;
+ grpc_byte_buffer *response_payload = NULL;
+ gpr_timespec deadline = n_seconds_time(messages * 5);
+ grpc_end2end_test_fixture f = begin_test(config, __FUNCTION__, NULL, NULL);
+ cq_verifier *v_client = cq_verifier_create(f.client_cq);
+ cq_verifier *v_server = cq_verifier_create(f.server_cq);
+
+ gpr_log(GPR_INFO, "testing with %d message pairs.", messages);
+ c = grpc_channel_create_call(f.client, "/foo", "test.google.com", deadline);
+ GPR_ASSERT(c);
+
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_invoke(c, f.client_cq, tag(1), tag(2), tag(3), 0));
+ cq_expect_invoke_accepted(v_client, tag(1), GRPC_OP_OK);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_server_request_call(f.server, tag(100)));
+
+ cq_expect_server_rpc_new(v_server, &s, tag(100), "/foo", "test.google.com",
+ deadline, NULL);
+ cq_verify(v_server);
+ grpc_call_accept(s, f.server_cq, tag(102), 0);
+
+ cq_expect_client_metadata_read(v_client, tag(2), NULL);
+ cq_verify(v_client);
+
+ for (i = 0; i < messages; i++) {
+ request_payload = grpc_byte_buffer_create(&request_payload_slice, 1);
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_write(c, request_payload, tag(2), 0));
+ /* destroy byte buffer early to ensure async code keeps track of its
+ contents
+ correctly */
+ grpc_byte_buffer_destroy(request_payload);
+ cq_expect_write_accepted(v_client, tag(2), GRPC_OP_OK);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_read(s, tag(3)));
+ cq_expect_read(v_server, tag(3),
+ gpr_slice_from_copied_string("hello world"));
+ cq_verify(v_server);
+
+ response_payload = grpc_byte_buffer_create(&response_payload_slice, 1);
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_write(s, response_payload, tag(4), 0));
+ /* destroy byte buffer early to ensure async code keeps track of its
+ contents
+ correctly */
+ grpc_byte_buffer_destroy(response_payload);
+ cq_expect_write_accepted(v_server, tag(4), GRPC_OP_OK);
+ cq_verify(v_server);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_read(c, tag(5)));
+ cq_expect_read(v_client, tag(5), gpr_slice_from_copied_string("hello you"));
+ cq_verify(v_client);
+ }
+
+ gpr_slice_unref(request_payload_slice);
+ gpr_slice_unref(response_payload_slice);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_writes_done(c, tag(6)));
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_write_status(s, send_status, tag(7)));
+
+ cq_expect_finish_accepted(v_client, tag(6), GRPC_OP_OK);
+ cq_expect_finished_with_status(v_client, tag(3), send_status, NULL);
+ cq_verify(v_client);
+
+ cq_expect_finish_accepted(v_server, tag(7), GRPC_OP_OK);
+ cq_expect_finished(v_server, tag(102), NULL);
+ cq_verify(v_server);
+
+ grpc_call_destroy(c);
+ grpc_call_destroy(s);
+
+ end_test(&f);
+ config.tear_down_data(&f);
+
+ cq_verifier_destroy(v_client);
+ cq_verifier_destroy(v_server);
+}
+
+static void test_early_server_shutdown_finishes_tags(
+ grpc_end2end_test_config config) {
+ grpc_end2end_test_fixture f = begin_test(config, __FUNCTION__, NULL, NULL);
+ cq_verifier *v_server = cq_verifier_create(f.server_cq);
+ grpc_call *s = (void *)1;
+
+ /* upon shutdown, the server should finish all requested calls indicating
+ no new call */
+ grpc_server_request_call(f.server, tag(1000));
+ grpc_server_shutdown(f.server);
+ cq_expect_server_rpc_new(v_server, &s, tag(1000), NULL, NULL, gpr_inf_past,
+ NULL);
+ cq_verify(v_server);
+ GPR_ASSERT(s == NULL);
+
+ end_test(&f);
+ config.tear_down_data(&f);
+ cq_verifier_destroy(v_server);
+}
+
+static void test_early_server_shutdown_finishes_inflight_calls(
+ grpc_end2end_test_config config) {
+ grpc_end2end_test_fixture f = begin_test(config, __FUNCTION__, NULL, NULL);
+ grpc_call *c;
+ grpc_call *s;
+ grpc_status expect_status = {GRPC_STATUS_UNAVAILABLE, NULL};
+ gpr_timespec deadline = five_seconds_time();
+ cq_verifier *v_client = cq_verifier_create(f.client_cq);
+ cq_verifier *v_server = cq_verifier_create(f.server_cq);
+
+ c = grpc_channel_create_call(f.client, "/foo", "test.google.com", deadline);
+ GPR_ASSERT(c);
+
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_invoke(c, f.client_cq, tag(1), tag(2), tag(3), 0));
+ cq_expect_invoke_accepted(v_client, tag(1), GRPC_OP_OK);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_writes_done(c, tag(4)));
+ cq_expect_finish_accepted(v_client, tag(4), GRPC_OP_OK);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_server_request_call(f.server, tag(100)));
+ cq_expect_server_rpc_new(v_server, &s, tag(100), "/foo", "test.google.com",
+ deadline, NULL);
+ cq_verify(v_server);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_accept(s, f.server_cq, tag(102), 0));
+ cq_expect_client_metadata_read(v_client, tag(2), NULL);
+ cq_verify(v_client);
+
+ /* shutdown and destroy the server */
+ shutdown_server(&f);
+
+ cq_expect_finished(v_server, tag(102), NULL);
+ cq_verify(v_server);
+
+ grpc_call_destroy(s);
+
+ cq_expect_finished_with_status(v_client, tag(3), expect_status, NULL);
+ cq_verify(v_client);
+
+ grpc_call_destroy(c);
+
+ cq_verifier_destroy(v_client);
+ cq_verifier_destroy(v_server);
+
+ end_test(&f);
+ config.tear_down_data(&f);
+}
+
+static void test_max_concurrent_streams(grpc_end2end_test_config config) {
+ grpc_end2end_test_fixture f;
+ grpc_arg server_arg;
+ grpc_channel_args server_args;
+ grpc_call *c1;
+ grpc_call *c2;
+ grpc_call *s1;
+ grpc_call *s2;
+ gpr_timespec deadline;
+ grpc_status send_status = {GRPC_STATUS_UNIMPLEMENTED, "xyz"};
+ cq_verifier *v_client;
+ cq_verifier *v_server;
+
+ server_arg.key = GRPC_ARG_MAX_CONCURRENT_STREAMS;
+ server_arg.type = GRPC_ARG_INTEGER;
+ server_arg.value.integer = 1;
+
+ server_args.num_args = 1;
+ server_args.args = &server_arg;
+
+ f = begin_test(config, __FUNCTION__, NULL, &server_args);
+ v_client = cq_verifier_create(f.client_cq);
+ v_server = cq_verifier_create(f.server_cq);
+
+ /* perform a ping-pong to ensure that settings have had a chance to round
+ trip */
+ simple_request_body(f);
+ /* perform another one to make sure that the one stream case still works */
+ simple_request_body(f);
+
+ /* start two requests - ensuring that the second is not accepted until
+ the first completes */
+ deadline = five_seconds_time();
+ c1 = grpc_channel_create_call(f.client, "/foo", "test.google.com", deadline);
+ GPR_ASSERT(c1);
+ c2 = grpc_channel_create_call(f.client, "/bar", "test.google.com", deadline);
+ GPR_ASSERT(c1);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_server_request_call(f.server, tag(100)));
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_invoke(c1, f.client_cq, tag(300),
+ tag(301), tag(302), 0));
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_invoke(c2, f.client_cq, tag(400),
+ tag(401), tag(402), 0));
+ cq_expect_invoke_accepted(v_client, tag(300), GRPC_OP_OK);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_writes_done(c1, tag(303)));
+ cq_expect_finish_accepted(v_client, tag(303), GRPC_OP_OK);
+ cq_verify(v_client);
+
+ cq_expect_server_rpc_new(v_server, &s1, tag(100), "/foo", "test.google.com",
+ deadline, NULL);
+ cq_verify(v_server);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_accept(s1, f.server_cq, tag(102), 0));
+ cq_expect_client_metadata_read(v_client, tag(301), NULL);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_write_status(s1, send_status, tag(103)));
+ cq_expect_finish_accepted(v_server, tag(103), GRPC_OP_OK);
+ cq_expect_finished(v_server, tag(102), NULL);
+ cq_verify(v_server);
+
+ /* first request is finished, we should be able to start the second */
+ cq_expect_finished_with_status(v_client, tag(302), send_status, NULL);
+ cq_expect_invoke_accepted(v_client, tag(400), GRPC_OP_OK);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_writes_done(c2, tag(403)));
+ cq_expect_finish_accepted(v_client, tag(403), GRPC_OP_OK);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_server_request_call(f.server, tag(200)));
+ cq_expect_server_rpc_new(v_server, &s2, tag(200), "/bar", "test.google.com",
+ deadline, NULL);
+ cq_verify(v_server);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_accept(s2, f.server_cq, tag(202), 0));
+ cq_expect_client_metadata_read(v_client, tag(401), NULL);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_write_status(s2, send_status, tag(203)));
+ cq_expect_finish_accepted(v_server, tag(203), GRPC_OP_OK);
+ cq_expect_finished(v_server, tag(202), NULL);
+ cq_verify(v_server);
+
+ cq_expect_finished_with_status(v_client, tag(402), send_status, NULL);
+ cq_verify(v_client);
+
+ cq_verifier_destroy(v_client);
+ cq_verifier_destroy(v_server);
+
+ grpc_call_destroy(c1);
+ grpc_call_destroy(s1);
+ grpc_call_destroy(c2);
+ grpc_call_destroy(s2);
+
+ end_test(&f);
+ config.tear_down_data(&f);
+}
+
+static gpr_slice large_slice() {
+ gpr_slice slice = gpr_slice_malloc(1000000);
+ memset(GPR_SLICE_START_PTR(slice), 0xab, GPR_SLICE_LENGTH(slice));
+ return slice;
+}
+
+static void test_invoke_large_request(grpc_end2end_test_config config) {
+ grpc_call *c;
+ grpc_call *s;
+ grpc_status send_status = {GRPC_STATUS_UNIMPLEMENTED, "xyz"};
+ gpr_slice request_payload_slice = large_slice();
+ grpc_byte_buffer *request_payload =
+ grpc_byte_buffer_create(&request_payload_slice, 1);
+ gpr_timespec deadline = five_seconds_time();
+ grpc_end2end_test_fixture f = begin_test(config, __FUNCTION__, NULL, NULL);
+ cq_verifier *v_client = cq_verifier_create(f.client_cq);
+ cq_verifier *v_server = cq_verifier_create(f.server_cq);
+
+ /* byte buffer holds the slice, we can unref it already */
+ gpr_slice_unref(request_payload_slice);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_server_request_call(f.server, tag(100)));
+
+ c = grpc_channel_create_call(f.client, "/foo", "test.google.com", deadline);
+ GPR_ASSERT(c);
+
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_invoke(c, f.client_cq, tag(1), tag(2), tag(3), 0));
+ cq_expect_invoke_accepted(v_client, tag(1), GRPC_OP_OK);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_write(c, request_payload, tag(4), 0));
+ /* destroy byte buffer early to ensure async code keeps track of its contents
+ correctly */
+ grpc_byte_buffer_destroy(request_payload);
+ /* write should not be accepted until the server is willing to read the
+ request (as this request is very large) */
+ cq_verify_empty(v_client);
+
+ cq_expect_server_rpc_new(v_server, &s, tag(100), "/foo", "test.google.com",
+ deadline, NULL);
+ cq_verify(v_server);
+
+ grpc_call_accept(s, f.server_cq, tag(102), 0);
+ cq_expect_client_metadata_read(v_client, tag(2), NULL);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_read(s, tag(5)));
+ /* now the write can be accepted */
+ cq_expect_write_accepted(v_client, tag(4), GRPC_OP_OK);
+ cq_verify(v_client);
+ cq_expect_read(v_server, tag(5), large_slice());
+ cq_verify(v_server);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_writes_done(c, tag(8)));
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_write_status(s, send_status, tag(9)));
+
+ cq_expect_finish_accepted(v_client, tag(8), GRPC_OP_OK);
+ cq_expect_finished_with_status(v_client, tag(3), send_status, NULL);
+ cq_verify(v_client);
+
+ cq_expect_finish_accepted(v_server, tag(9), GRPC_OP_OK);
+ cq_expect_finished(v_server, tag(102), NULL);
+ cq_verify(v_server);
+
+ grpc_call_destroy(c);
+ grpc_call_destroy(s);
+
+ cq_verifier_destroy(v_client);
+ cq_verifier_destroy(v_server);
+
+ end_test(&f);
+ config.tear_down_data(&f);
+}
+
+void grpc_end2end_tests(grpc_end2end_test_config config) {
+ int i;
+ canceller cancellers[2] = {grpc_call_cancel, wait_for_deadline};
+
+ test_no_op(config);
+ test_invoke_simple_request(config, "simple_request_body",
+ simple_request_body);
+ test_invoke_simple_request(config, "simple_request_body2",
+ simple_request_body2);
+ test_invoke_10_simple_requests(config);
+ if (config.feature_mask & FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION) {
+ test_simple_delayed_request_short(config);
+ test_simple_delayed_request_long(config);
+ }
+ test_invoke_request_with_payload(config);
+ test_request_response_with_metadata_and_payload(config);
+ test_request_with_large_metadata(config);
+ test_writes_done_hangs_with_pending_read(config);
+ test_invoke_request_response_with_payload(config);
+ test_invoke_10_request_response_with_payload(config);
+ test_early_server_shutdown_finishes_tags(config);
+ test_early_server_shutdown_finishes_inflight_calls(config);
+ test_max_concurrent_streams(config);
+ test_invoke_large_request(config);
+ for (i = 0; i < GPR_ARRAY_SIZE(cancellers); i++) {
+ test_cancel_in_a_vacuum(config, cancellers[i]);
+ test_cancel_after_invoke(config, cancellers[i]);
+ test_cancel_after_accept(config, cancellers[i]);
+ test_cancel_after_accept_and_writes_closed(config, cancellers[i]);
+ }
+ test_cancel_before_invoke(config);
+ for (i = 1; i < 10; i++) {
+ test_pingpong_streaming(config, i);
+ }
+}
diff --git a/test/core/end2end/end2end_tests.h b/test/core/end2end/end2end_tests.h
new file mode 100644
index 0000000000..b509b1ac9d
--- /dev/null
+++ b/test/core/end2end/end2end_tests.h
@@ -0,0 +1,66 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#ifndef __GRPC_TEST_END2END_END2END_TESTS_H__
+#define __GRPC_TEST_END2END_END2END_TESTS_H__
+
+#include <grpc/grpc.h>
+
+typedef struct grpc_end2end_test_fixture grpc_end2end_test_fixture;
+typedef struct grpc_end2end_test_config grpc_end2end_test_config;
+
+#define FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION 1
+
+struct grpc_end2end_test_fixture {
+ grpc_completion_queue *server_cq;
+ grpc_completion_queue *client_cq;
+ grpc_server *server;
+ grpc_channel *client;
+ void *fixture_data;
+};
+
+struct grpc_end2end_test_config {
+ const char *name;
+ gpr_uint32 feature_mask;
+ grpc_end2end_test_fixture (*create_fixture)(grpc_channel_args *client_args,
+ grpc_channel_args *server_args);
+ void (*init_client)(grpc_end2end_test_fixture *f,
+ grpc_channel_args *client_args);
+ void (*init_server)(grpc_end2end_test_fixture *f,
+ grpc_channel_args *server_args);
+ void (*tear_down_data)(grpc_end2end_test_fixture *f);
+};
+
+void grpc_end2end_tests(grpc_end2end_test_config config);
+
+#endif /* __GRPC_TEST_END2END_END2END_TESTS_H__ */
diff --git a/test/core/end2end/fixtures/chttp2_fake_security.c b/test/core/end2end/fixtures/chttp2_fake_security.c
new file mode 100644
index 0000000000..a1f29de4dd
--- /dev/null
+++ b/test/core/end2end/fixtures/chttp2_fake_security.c
@@ -0,0 +1,139 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include "test/core/end2end/end2end_tests.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include "src/core/channel/channel_args.h"
+#include "src/core/eventmanager/em.h"
+#include "src/core/security/credentials.h"
+#include "src/core/security/security_context.h"
+#include <grpc/support/alloc.h>
+#include <grpc/support/host_port.h>
+#include <grpc/support/log.h>
+#include "test/core/util/test_config.h"
+#include "test/core/util/port.h"
+#include "test/core/end2end/data/ssl_test_data.h"
+
+
+static grpc_em em;
+
+typedef struct fullstack_secure_fixture_data {
+ char *localaddr;
+} fullstack_secure_fixture_data;
+
+static grpc_end2end_test_fixture chttp2_create_fixture_secure_fullstack(
+ grpc_channel_args *client_args, grpc_channel_args *server_args) {
+ grpc_end2end_test_fixture f;
+ int port = grpc_pick_unused_port_or_die();
+ fullstack_secure_fixture_data *ffd =
+ gpr_malloc(sizeof(fullstack_secure_fixture_data));
+
+ gpr_join_host_port(&ffd->localaddr, "localhost", port);
+
+ f.fixture_data = ffd;
+ f.client_cq = grpc_completion_queue_create();
+ f.server_cq = grpc_completion_queue_create();
+
+ return f;
+}
+
+static void chttp2_init_client_secure_fullstack(grpc_end2end_test_fixture *f,
+ grpc_channel_args *client_args,
+ grpc_credentials *creds) {
+ fullstack_secure_fixture_data *ffd = f->fixture_data;
+ f->client = grpc_secure_channel_create(creds, ffd->localaddr, client_args);
+ GPR_ASSERT(f->client != NULL);
+ grpc_credentials_release(creds);
+}
+
+static void chttp2_init_server_secure_fullstack(
+ grpc_end2end_test_fixture *f, grpc_channel_args *server_args,
+ grpc_server_credentials *server_creds) {
+ fullstack_secure_fixture_data *ffd = f->fixture_data;
+ f->server =
+ grpc_secure_server_create(server_creds, f->server_cq, server_args);
+ grpc_server_credentials_release(server_creds);
+ grpc_server_add_secure_http2_port(f->server, ffd->localaddr);
+ grpc_server_start(f->server);
+}
+
+void chttp2_tear_down_secure_fullstack(grpc_end2end_test_fixture *f) {
+ fullstack_secure_fixture_data *ffd = f->fixture_data;
+ gpr_free(ffd->localaddr);
+ gpr_free(ffd);
+}
+
+static void chttp2_init_client_fake_secure_fullstack(
+ grpc_end2end_test_fixture *f, grpc_channel_args *client_args) {
+ grpc_credentials *fake_ts_creds =
+ grpc_fake_transport_security_credentials_create();
+ chttp2_init_client_secure_fullstack(f, client_args, fake_ts_creds);
+}
+
+static void chttp2_init_server_fake_secure_fullstack(
+ grpc_end2end_test_fixture *f, grpc_channel_args *server_args) {
+ grpc_server_credentials *fake_ts_creds =
+ grpc_fake_transport_security_server_credentials_create();
+ chttp2_init_server_secure_fullstack(f, server_args, fake_ts_creds);
+}
+
+/* All test configurations */
+
+static grpc_end2end_test_config configs[] = {
+ {"chttp2/fake_secure_fullstack", FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION,
+ chttp2_create_fixture_secure_fullstack,
+ chttp2_init_client_fake_secure_fullstack,
+ chttp2_init_server_fake_secure_fullstack,
+ chttp2_tear_down_secure_fullstack},
+};
+
+int main(int argc, char **argv) {
+ size_t i;
+ grpc_test_init(argc, argv);
+
+
+ grpc_init();
+ grpc_em_init(&em);
+
+ for (i = 0; i < sizeof(configs) / sizeof(*configs); i++) {
+ grpc_end2end_tests(configs[i]);
+ }
+
+ GPR_ASSERT(grpc_em_destroy(&em) == GRPC_EM_OK);
+ grpc_shutdown();
+
+ return 0;
+}
diff --git a/test/core/end2end/fixtures/chttp2_fullstack.c b/test/core/end2end/fixtures/chttp2_fullstack.c
new file mode 100644
index 0000000000..da75d61e66
--- /dev/null
+++ b/test/core/end2end/fixtures/chttp2_fullstack.c
@@ -0,0 +1,123 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include "test/core/end2end/end2end_tests.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "src/core/channel/client_channel.h"
+#include "src/core/channel/connected_channel.h"
+#include "src/core/channel/http_filter.h"
+#include "src/core/channel/http_server_filter.h"
+#include "src/core/eventmanager/em.h"
+#include "src/core/surface/channel.h"
+#include "src/core/surface/client.h"
+#include "src/core/surface/server.h"
+#include "src/core/surface/surface_em.h"
+#include "src/core/transport/chttp2_transport.h"
+#include <grpc/support/alloc.h>
+#include <grpc/support/host_port.h>
+#include <grpc/support/log.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/thd.h>
+#include <grpc/support/useful.h>
+#include "test/core/util/port.h"
+#include "test/core/util/test_config.h"
+
+typedef struct fullstack_fixture_data {
+ char *localaddr;
+} fullstack_fixture_data;
+
+static grpc_end2end_test_fixture chttp2_create_fixture_fullstack(
+ grpc_channel_args *client_args, grpc_channel_args *server_args) {
+ grpc_end2end_test_fixture f;
+ int port = grpc_pick_unused_port_or_die();
+ fullstack_fixture_data *ffd = gpr_malloc(sizeof(fullstack_fixture_data));
+
+ gpr_join_host_port(&ffd->localaddr, "localhost", port);
+
+ f.fixture_data = ffd;
+ f.client_cq = grpc_completion_queue_create();
+ f.server_cq = grpc_completion_queue_create();
+
+ return f;
+}
+
+void chttp2_init_client_fullstack(grpc_end2end_test_fixture *f,
+ grpc_channel_args *client_args) {
+ fullstack_fixture_data *ffd = f->fixture_data;
+ f->client = grpc_channel_create(ffd->localaddr, client_args);
+}
+
+void chttp2_init_server_fullstack(grpc_end2end_test_fixture *f,
+ grpc_channel_args *server_args) {
+ fullstack_fixture_data *ffd = f->fixture_data;
+ f->server = grpc_server_create(f->server_cq, server_args);
+ grpc_server_add_http2_port(f->server, ffd->localaddr);
+ grpc_server_start(f->server);
+}
+
+void chttp2_tear_down_fullstack(grpc_end2end_test_fixture *f) {
+ fullstack_fixture_data *ffd = f->fixture_data;
+ gpr_free(ffd->localaddr);
+ gpr_free(ffd);
+}
+
+/* All test configurations */
+static grpc_end2end_test_config configs[] = {
+ {"chttp2/fullstack", FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION,
+ chttp2_create_fixture_fullstack, chttp2_init_client_fullstack,
+ chttp2_init_server_fullstack, chttp2_tear_down_fullstack},
+};
+
+int main(int argc, char **argv) {
+ size_t i;
+
+ grpc_test_init(argc, argv);
+ grpc_init();
+
+ for (i = 0; i < sizeof(configs) / sizeof(*configs); i++) {
+ grpc_end2end_tests(configs[i]);
+ }
+
+ grpc_shutdown();
+
+ return 0;
+}
diff --git a/test/core/end2end/fixtures/chttp2_simple_ssl_fullstack.c b/test/core/end2end/fixtures/chttp2_simple_ssl_fullstack.c
new file mode 100644
index 0000000000..2dd3ce9a26
--- /dev/null
+++ b/test/core/end2end/fixtures/chttp2_simple_ssl_fullstack.c
@@ -0,0 +1,146 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include "test/core/end2end/end2end_tests.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include "src/core/channel/channel_args.h"
+#include "src/core/eventmanager/em.h"
+#include "src/core/security/credentials.h"
+#include "src/core/security/security_context.h"
+#include <grpc/support/alloc.h>
+#include <grpc/support/host_port.h>
+#include <grpc/support/log.h>
+#include "test/core/util/test_config.h"
+#include "test/core/util/port.h"
+#include "test/core/end2end/data/ssl_test_data.h"
+
+
+static grpc_em em;
+
+typedef struct fullstack_secure_fixture_data {
+ char *localaddr;
+} fullstack_secure_fixture_data;
+
+static grpc_end2end_test_fixture chttp2_create_fixture_secure_fullstack(
+ grpc_channel_args *client_args, grpc_channel_args *server_args) {
+ grpc_end2end_test_fixture f;
+ int port = grpc_pick_unused_port_or_die();
+ fullstack_secure_fixture_data *ffd =
+ gpr_malloc(sizeof(fullstack_secure_fixture_data));
+
+ gpr_join_host_port(&ffd->localaddr, "localhost", port);
+
+ f.fixture_data = ffd;
+ f.client_cq = grpc_completion_queue_create();
+ f.server_cq = grpc_completion_queue_create();
+
+ return f;
+}
+
+static void chttp2_init_client_secure_fullstack(grpc_end2end_test_fixture *f,
+ grpc_channel_args *client_args,
+ grpc_credentials *creds) {
+ fullstack_secure_fixture_data *ffd = f->fixture_data;
+ f->client = grpc_secure_channel_create(creds, ffd->localaddr, client_args);
+ GPR_ASSERT(f->client != NULL);
+ grpc_credentials_release(creds);
+}
+
+static void chttp2_init_server_secure_fullstack(
+ grpc_end2end_test_fixture *f, grpc_channel_args *server_args,
+ grpc_server_credentials *server_creds) {
+ fullstack_secure_fixture_data *ffd = f->fixture_data;
+ f->server =
+ grpc_secure_server_create(server_creds, f->server_cq, server_args);
+ grpc_server_credentials_release(server_creds);
+ grpc_server_add_secure_http2_port(f->server, ffd->localaddr);
+ grpc_server_start(f->server);
+}
+
+void chttp2_tear_down_secure_fullstack(grpc_end2end_test_fixture *f) {
+ fullstack_secure_fixture_data *ffd = f->fixture_data;
+ gpr_free(ffd->localaddr);
+ gpr_free(ffd);
+}
+
+static void chttp2_init_client_simple_ssl_secure_fullstack(
+ grpc_end2end_test_fixture *f, grpc_channel_args *client_args) {
+ grpc_credentials *ssl_creds = grpc_ssl_credentials_create(
+ test_ca_cert, test_ca_cert_size, NULL, 0, NULL, 0);
+ grpc_arg ssl_name_override = {GRPC_ARG_STRING,
+ GRPC_SSL_TARGET_NAME_OVERRIDE_ARG,
+ {"foo.test.google.com"}};
+ grpc_channel_args *new_client_args =
+ grpc_channel_args_copy_and_add(client_args, &ssl_name_override);
+ chttp2_init_client_secure_fullstack(f, new_client_args, ssl_creds);
+ grpc_channel_args_destroy(new_client_args);
+}
+
+static void chttp2_init_server_simple_ssl_secure_fullstack(
+ grpc_end2end_test_fixture *f, grpc_channel_args *server_args) {
+ grpc_server_credentials *ssl_creds = grpc_ssl_server_credentials_create(
+ NULL, 0, test_server1_key, test_server1_key_size, test_server1_cert,
+ test_server1_cert_size);
+ chttp2_init_server_secure_fullstack(f, server_args, ssl_creds);
+}
+
+/* All test configurations */
+
+static grpc_end2end_test_config configs[] = {
+ {"chttp2/simple_ssl_fullstack", FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION,
+ chttp2_create_fixture_secure_fullstack,
+ chttp2_init_client_simple_ssl_secure_fullstack,
+ chttp2_init_server_simple_ssl_secure_fullstack,
+ chttp2_tear_down_secure_fullstack},
+};
+
+int main(int argc, char **argv) {
+ size_t i;
+ grpc_test_init(argc, argv);
+
+
+ grpc_init();
+ grpc_em_init(&em);
+
+ for (i = 0; i < sizeof(configs) / sizeof(*configs); i++) {
+ grpc_end2end_tests(configs[i]);
+ }
+
+ GPR_ASSERT(grpc_em_destroy(&em) == GRPC_EM_OK);
+ grpc_shutdown();
+
+ return 0;
+}
diff --git a/test/core/end2end/fixtures/chttp2_simple_ssl_with_oauth2_fullstack.c b/test/core/end2end/fixtures/chttp2_simple_ssl_with_oauth2_fullstack.c
new file mode 100644
index 0000000000..22720c5b5f
--- /dev/null
+++ b/test/core/end2end/fixtures/chttp2_simple_ssl_with_oauth2_fullstack.c
@@ -0,0 +1,146 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include "test/core/end2end/end2end_tests.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include "src/core/channel/channel_args.h"
+#include "src/core/eventmanager/em.h"
+#include "src/core/security/credentials.h"
+#include "src/core/security/security_context.h"
+#include <grpc/support/alloc.h>
+#include <grpc/support/host_port.h>
+#include <grpc/support/log.h>
+#include "test/core/util/test_config.h"
+#include "test/core/util/port.h"
+#include "test/core/end2end/data/ssl_test_data.h"
+
+
+static grpc_em em;
+
+typedef struct fullstack_secure_fixture_data {
+ char *localaddr;
+} fullstack_secure_fixture_data;
+
+static grpc_end2end_test_fixture chttp2_create_fixture_secure_fullstack(
+ grpc_channel_args *client_args, grpc_channel_args *server_args) {
+ grpc_end2end_test_fixture f;
+ int port = grpc_pick_unused_port_or_die();
+ fullstack_secure_fixture_data *ffd =
+ gpr_malloc(sizeof(fullstack_secure_fixture_data));
+
+ gpr_join_host_port(&ffd->localaddr, "localhost", port);
+
+ f.fixture_data = ffd;
+ f.client_cq = grpc_completion_queue_create();
+ f.server_cq = grpc_completion_queue_create();
+
+ return f;
+}
+
+static void chttp2_init_server_secure_fullstack(
+ grpc_end2end_test_fixture *f, grpc_channel_args *server_args,
+ grpc_server_credentials *server_creds) {
+ fullstack_secure_fixture_data *ffd = f->fixture_data;
+ f->server =
+ grpc_secure_server_create(server_creds, f->server_cq, server_args);
+ grpc_server_credentials_release(server_creds);
+ grpc_server_add_secure_http2_port(f->server, ffd->localaddr);
+ grpc_server_start(f->server);
+}
+
+void chttp2_tear_down_secure_fullstack(grpc_end2end_test_fixture *f) {
+ fullstack_secure_fixture_data *ffd = f->fixture_data;
+ gpr_free(ffd->localaddr);
+ gpr_free(ffd);
+}
+
+static void chttp2_init_client_simple_ssl_with_oauth2_secure_fullstack(
+ grpc_end2end_test_fixture *f, grpc_channel_args *client_args) {
+ /* TODO(jboeuf): Replace with composite credentials when those are
+ implemented. */
+ grpc_channel_security_context *client_ctx = NULL;
+ fullstack_secure_fixture_data *ffd = f->fixture_data;
+ grpc_ssl_config config;
+ grpc_credentials *oauth2 =
+ grpc_fake_oauth2_credentials_create("Bearer aaslkfjs424535asdf", 1);
+ memset(&config, 0, sizeof(grpc_ssl_config));
+ config.pem_root_certs = test_ca_cert;
+ config.pem_root_certs_size = test_ca_cert_size;
+ GPR_ASSERT(grpc_ssl_channel_security_context_create(
+ oauth2, &config, "foo.test.google.com", &client_ctx) ==
+ GRPC_SECURITY_OK);
+ f->client = grpc_secure_channel_create_internal(ffd->localaddr, client_args,
+ client_ctx);
+ grpc_security_context_unref(&client_ctx->base);
+ grpc_credentials_unref(oauth2);
+}
+
+static void chttp2_init_server_simple_ssl_secure_fullstack(
+ grpc_end2end_test_fixture *f, grpc_channel_args *server_args) {
+ grpc_server_credentials *ssl_creds = grpc_ssl_server_credentials_create(
+ NULL, 0, test_server1_key, test_server1_key_size, test_server1_cert,
+ test_server1_cert_size);
+ chttp2_init_server_secure_fullstack(f, server_args, ssl_creds);
+}
+
+/* All test configurations */
+
+static grpc_end2end_test_config configs[] = {
+ {"chttp2/simple_ssl_with_oauth2_fullstack",
+ FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION,
+ chttp2_create_fixture_secure_fullstack,
+ chttp2_init_client_simple_ssl_with_oauth2_secure_fullstack,
+ chttp2_init_server_simple_ssl_secure_fullstack,
+ chttp2_tear_down_secure_fullstack},
+};
+
+int main(int argc, char **argv) {
+ size_t i;
+ grpc_test_init(argc, argv);
+
+
+ grpc_init();
+ grpc_em_init(&em);
+
+ for (i = 0; i < sizeof(configs) / sizeof(*configs); i++) {
+ grpc_end2end_tests(configs[i]);
+ }
+
+ GPR_ASSERT(grpc_em_destroy(&em) == GRPC_EM_OK);
+ grpc_shutdown();
+
+ return 0;
+}
diff --git a/test/core/end2end/fixtures/chttp2_socket_pair.c b/test/core/end2end/fixtures/chttp2_socket_pair.c
new file mode 100644
index 0000000000..593ff78ba8
--- /dev/null
+++ b/test/core/end2end/fixtures/chttp2_socket_pair.c
@@ -0,0 +1,169 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include "test/core/end2end/end2end_tests.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "src/core/channel/client_channel.h"
+#include "src/core/channel/connected_channel.h"
+#include "src/core/channel/http_filter.h"
+#include "src/core/channel/http_server_filter.h"
+#include "src/core/eventmanager/em.h"
+#include "src/core/surface/channel.h"
+#include "src/core/surface/client.h"
+#include "src/core/surface/server.h"
+#include "src/core/surface/surface_em.h"
+#include "src/core/transport/chttp2_transport.h"
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/thd.h>
+#include <grpc/support/useful.h>
+#include "test/core/util/port.h"
+#include "test/core/util/test_config.h"
+
+static void create_sockets(int sv[2]) {
+ int flags;
+ GPR_ASSERT(socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == 0);
+ flags = fcntl(sv[0], F_GETFL, 0);
+ GPR_ASSERT(fcntl(sv[0], F_SETFL, flags | O_NONBLOCK) == 0);
+ flags = fcntl(sv[1], F_GETFL, 0);
+ GPR_ASSERT(fcntl(sv[1], F_SETFL, flags | O_NONBLOCK) == 0);
+}
+
+/* chttp2 transport that is immediately available (used for testing
+ connected_channel without a client_channel */
+
+static grpc_transport_setup_result server_setup_transport(
+ void *ts, grpc_transport *transport, grpc_mdctx *mdctx) {
+ grpc_end2end_test_fixture *f = ts;
+ static grpc_channel_filter const *extra_filters[] = {&grpc_http_server_filter,
+ &grpc_http_filter};
+ return grpc_server_setup_transport(f->server, transport, extra_filters,
+ GPR_ARRAY_SIZE(extra_filters), mdctx);
+}
+
+typedef struct {
+ grpc_end2end_test_fixture *f;
+ grpc_channel_args *client_args;
+} sp_client_setup;
+
+static grpc_transport_setup_result client_setup_transport(
+ void *ts, grpc_transport *transport, grpc_mdctx *mdctx) {
+ sp_client_setup *cs = ts;
+
+ const grpc_channel_filter *filters[] = {&grpc_client_surface_filter,
+ &grpc_connected_channel_filter};
+ size_t nfilters = sizeof(filters) / sizeof(*filters);
+ grpc_channel *channel = grpc_channel_create_from_filters(
+ filters, nfilters, cs->client_args, mdctx, 1);
+
+ cs->f->client = channel;
+
+ return grpc_connected_channel_bind_transport(
+ grpc_channel_get_channel_stack(channel), transport);
+}
+
+typedef struct socketpair_fixture_data { int sv[2]; } socketpair_fixture_data;
+
+static grpc_end2end_test_fixture chttp2_create_fixture_socketpair(
+ grpc_channel_args *client_args, grpc_channel_args *server_args) {
+ socketpair_fixture_data *sfd = gpr_malloc(sizeof(socketpair_fixture_data));
+
+ grpc_end2end_test_fixture f;
+ f.fixture_data = sfd;
+ f.client_cq = grpc_completion_queue_create();
+ f.server_cq = grpc_completion_queue_create();
+ f.server = grpc_server_create_from_filters(f.server_cq, NULL, 0, server_args);
+ f.client = NULL;
+
+ create_sockets(sfd->sv);
+
+ return f;
+}
+
+static void chttp2_init_client_socketpair(grpc_end2end_test_fixture *f,
+ grpc_channel_args *client_args) {
+ socketpair_fixture_data *sfd = f->fixture_data;
+ grpc_endpoint *cli_tcp;
+ sp_client_setup cs;
+ cs.client_args = client_args;
+ cs.f = f;
+ cli_tcp = grpc_tcp_create_dbg(sfd->sv[0], grpc_surface_em(), 65536);
+ grpc_create_chttp2_transport(client_setup_transport, &cs, client_args,
+ cli_tcp, NULL, 0, grpc_mdctx_create(), 1);
+ GPR_ASSERT(f->client);
+}
+
+static void chttp2_init_server_socketpair(grpc_end2end_test_fixture *f,
+ grpc_channel_args *server_args) {
+ socketpair_fixture_data *sfd = f->fixture_data;
+ grpc_endpoint *svr_tcp;
+ svr_tcp = grpc_tcp_create_dbg(sfd->sv[1], grpc_surface_em(), 65536);
+ grpc_create_chttp2_transport(server_setup_transport, f, server_args, svr_tcp,
+ NULL, 0, grpc_mdctx_create(), 0);
+}
+
+static void chttp2_tear_down_socketpair(grpc_end2end_test_fixture *f) {
+ gpr_free(f->fixture_data);
+}
+
+/* All test configurations */
+static grpc_end2end_test_config configs[] = {
+ {"chttp2/socketpair", 0, chttp2_create_fixture_socketpair,
+ chttp2_init_client_socketpair, chttp2_init_server_socketpair,
+ chttp2_tear_down_socketpair},
+};
+
+int main(int argc, char **argv) {
+ size_t i;
+
+ grpc_test_init(argc, argv);
+ grpc_init();
+
+ for (i = 0; i < sizeof(configs) / sizeof(*configs); i++) {
+ grpc_end2end_tests(configs[i]);
+ }
+
+ grpc_shutdown();
+
+ return 0;
+}
diff --git a/test/core/end2end/gen_build_json.py b/test/core/end2end/gen_build_json.py
new file mode 100755
index 0000000000..0bcccbeae4
--- /dev/null
+++ b/test/core/end2end/gen_build_json.py
@@ -0,0 +1,82 @@
+import simplejson
+
+END2END_FIXTURES = [
+ 'chttp2_fake_security',
+ 'chttp2_fullstack',
+ 'chttp2_simple_ssl_fullstack',
+ 'chttp2_simple_ssl_with_oauth2_fullstack',
+ 'chttp2_socket_pair',
+]
+
+
+END2END_TESTS = [
+ 'cancel_after_accept',
+ 'cancel_after_accept_and_writes_closed',
+ 'cancel_after_invoke',
+ 'cancel_before_invoke',
+ 'cancel_in_a_vacuum',
+ 'early_server_shutdown_finishes_inflight_calls',
+ 'early_server_shutdown_finishes_tags',
+ 'invoke_large_request',
+ 'max_concurrent_streams',
+ 'no_op',
+ 'ping_pong_streaming',
+ 'request_response_with_metadata_and_payload',
+ 'request_response_with_payload',
+ 'simple_delayed_request',
+ 'simple_request',
+ 'thread_stress_test',
+ 'writes_done_hangs_with_pending_read',
+]
+
+
+def main():
+ json = {
+ '#': 'generated with test/end2end/gen_build_json.py',
+ 'libs': [
+ {
+ 'name': 'end2end_fixture_%s' % f,
+ 'build': 'private',
+ 'secure': True,
+ 'src': ['test/core/end2end/fixtures/%s.c' % f]
+ }
+ for f in END2END_FIXTURES] + [
+ {
+ 'name': 'end2end_test_%s' % t,
+ 'build': 'private',
+ 'secure': False,
+ 'src': ['test/core/end2end/tests/%s.c' % t]
+ }
+ for t in END2END_TESTS] + [
+ {
+ 'name': 'end2end_certs',
+ 'build': 'private',
+ 'src': [
+ "test/core/end2end/data/ca_cert.c",
+ "test/core/end2end/data/server1_cert.c",
+ "test/core/end2end/data/server1_key.c"
+ ]
+ }
+ ],
+ 'targets': [
+ {
+ 'name': '%s_%s_test' % (f, t),
+ 'build': 'test',
+ 'src': [],
+ 'deps': [
+ 'end2end_fixture_%s' % f,
+ 'end2end_test_%s' % t,
+ 'end2end_certs',
+ 'grpc_test_util',
+ 'grpc',
+ 'gpr'
+ ]
+ }
+ for f in END2END_FIXTURES
+ for t in END2END_TESTS]}
+ print simplejson.dumps(json, sort_keys=True, indent=2 * ' ')
+
+
+if __name__ == '__main__':
+ main()
+
diff --git a/test/core/end2end/no_server_test.c b/test/core/end2end/no_server_test.c
new file mode 100644
index 0000000000..f0810563a7
--- /dev/null
+++ b/test/core/end2end/no_server_test.c
@@ -0,0 +1,83 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include <grpc/grpc.h>
+#include <grpc/support/log.h>
+#include "test/core/end2end/cq_verifier.h"
+#include "test/core/util/test_config.h"
+
+static void *tag(gpr_intptr i) { return (void *)i; }
+
+int main(int argc, char **argv) {
+ grpc_channel *chan;
+ grpc_call *call;
+ gpr_timespec timeout = gpr_time_from_micros(4000000);
+ gpr_timespec deadline = gpr_time_add(gpr_now(), timeout);
+ grpc_completion_queue *cq;
+ cq_verifier *cqv;
+ grpc_event *ev;
+ int done;
+ grpc_status expect_status = {GRPC_STATUS_CANCELLED, NULL};
+
+ grpc_test_init(argc, argv);
+ grpc_init();
+
+ cq = grpc_completion_queue_create();
+ cqv = cq_verifier_create(cq);
+
+ /* create a call, channel to a non existant server */
+ chan = grpc_channel_create("nonexistant:54321", NULL);
+ call = grpc_channel_create_call(chan, "/foo", "nonexistant", deadline);
+ GPR_ASSERT(grpc_call_start_invoke(call, cq, tag(1), tag(2), tag(3), 0) ==
+ GRPC_CALL_OK);
+ /* verify that all tags get completed */
+ cq_expect_invoke_accepted(cqv, tag(1), GRPC_OP_ERROR);
+ cq_expect_client_metadata_read(cqv, tag(2), NULL);
+ cq_expect_finished_with_status(cqv, tag(3), expect_status, NULL);
+ cq_verify(cqv);
+
+ grpc_completion_queue_shutdown(cq);
+ for (done = 0; !done;) {
+ ev = grpc_completion_queue_next(cq, gpr_inf_future);
+ done = ev->type == GRPC_QUEUE_SHUTDOWN;
+ grpc_event_finish(ev);
+ }
+ grpc_completion_queue_destroy(cq);
+ grpc_call_destroy(call);
+ grpc_channel_destroy(chan);
+ cq_verifier_destroy(cqv);
+
+ grpc_shutdown();
+
+ return 0;
+}
diff --git a/test/core/end2end/tests/cancel_after_accept.c b/test/core/end2end/tests/cancel_after_accept.c
new file mode 100644
index 0000000000..c9c3a18923
--- /dev/null
+++ b/test/core/end2end/tests/cancel_after_accept.c
@@ -0,0 +1,166 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include "test/core/end2end/end2end_tests.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <grpc/byte_buffer.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/time.h>
+#include <grpc/support/useful.h>
+#include "test/core/end2end/cq_verifier.h"
+
+/* allow cancellation by either grpc_call_cancel, or by wait_for_deadline (which
+ * does nothing) */
+typedef grpc_call_error (*canceller)(grpc_call *call);
+
+static grpc_call_error wait_for_deadline(grpc_call *call) {
+ return GRPC_CALL_OK;
+}
+
+enum { TIMEOUT = 200000 };
+
+static void *tag(gpr_intptr t) { return (void *)t; }
+
+static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
+ const char *test_name,
+ grpc_channel_args *client_args,
+ grpc_channel_args *server_args) {
+ grpc_end2end_test_fixture f;
+ gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+ f = config.create_fixture(client_args, server_args);
+ config.init_client(&f, client_args);
+ config.init_server(&f, server_args);
+ return f;
+}
+
+static gpr_timespec n_seconds_time(int n) {
+ return gpr_time_add(gpr_now(), gpr_time_from_micros(GPR_US_PER_SEC * n));
+}
+
+static gpr_timespec five_seconds_time() { return n_seconds_time(5); }
+
+static void drain_cq(grpc_completion_queue *cq) {
+ grpc_event *ev;
+ grpc_completion_type type;
+ do {
+ ev = grpc_completion_queue_next(cq, five_seconds_time());
+ GPR_ASSERT(ev);
+ type = ev->type;
+ grpc_event_finish(ev);
+ } while (type != GRPC_QUEUE_SHUTDOWN);
+}
+
+static void shutdown_server(grpc_end2end_test_fixture *f) {
+ if (!f->server) return;
+ grpc_server_shutdown(f->server);
+ grpc_server_destroy(f->server);
+ f->server = NULL;
+}
+
+static void shutdown_client(grpc_end2end_test_fixture *f) {
+ if (!f->client) return;
+ grpc_channel_destroy(f->client);
+ f->client = NULL;
+}
+
+static void end_test(grpc_end2end_test_fixture *f) {
+ shutdown_server(f);
+ shutdown_client(f);
+
+ grpc_completion_queue_shutdown(f->server_cq);
+ drain_cq(f->server_cq);
+ grpc_completion_queue_destroy(f->server_cq);
+ grpc_completion_queue_shutdown(f->client_cq);
+ drain_cq(f->client_cq);
+ grpc_completion_queue_destroy(f->client_cq);
+}
+
+/* Cancel after accept, no payload */
+static void test_cancel_after_accept(grpc_end2end_test_config config,
+ canceller call_cancel) {
+ grpc_call *c;
+ grpc_call *s;
+ grpc_end2end_test_fixture f = begin_test(config, __FUNCTION__, NULL, NULL);
+ gpr_timespec deadline = five_seconds_time();
+ cq_verifier *v_client = cq_verifier_create(f.client_cq);
+ cq_verifier *v_server = cq_verifier_create(f.server_cq);
+ grpc_status chk_status = {GRPC_STATUS_CANCELLED, NULL};
+
+ c = grpc_channel_create_call(f.client, "/foo", "test.google.com", deadline);
+ GPR_ASSERT(c);
+
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_invoke(c, f.client_cq, tag(1), tag(2), tag(3), 0));
+ cq_expect_invoke_accepted(v_client, tag(1), GRPC_OP_OK);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_server_request_call(f.server, tag(100)));
+ cq_expect_server_rpc_new(v_server, &s, tag(100), "/foo", "test.google.com",
+ deadline, NULL);
+ cq_verify(v_server);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_accept(s, f.server_cq, tag(102), 0));
+ cq_expect_client_metadata_read(v_client, tag(2), NULL);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK == call_cancel(c));
+
+ cq_expect_finished_with_status(v_client, tag(3), chk_status, NULL);
+ cq_verify(v_client);
+
+ cq_expect_finished_with_status(v_server, tag(102), chk_status, NULL);
+ cq_verify(v_server);
+
+ grpc_call_destroy(c);
+ grpc_call_destroy(s);
+
+ cq_verifier_destroy(v_client);
+ cq_verifier_destroy(v_server);
+ end_test(&f);
+ config.tear_down_data(&f);
+}
+
+
+void grpc_end2end_tests(grpc_end2end_test_config config) {
+ int i;
+ canceller cancellers[2] = {grpc_call_cancel, wait_for_deadline};
+
+ for (i = 0; i < GPR_ARRAY_SIZE(cancellers); i++) {
+ test_cancel_after_accept(config, cancellers[i]);
+ }
+}
diff --git a/test/core/end2end/tests/cancel_after_accept_and_writes_closed.c b/test/core/end2end/tests/cancel_after_accept_and_writes_closed.c
new file mode 100644
index 0000000000..904dabe231
--- /dev/null
+++ b/test/core/end2end/tests/cancel_after_accept_and_writes_closed.c
@@ -0,0 +1,173 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include "test/core/end2end/end2end_tests.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <grpc/byte_buffer.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/time.h>
+#include <grpc/support/useful.h>
+#include "test/core/end2end/cq_verifier.h"
+
+/* allow cancellation by either grpc_call_cancel, or by wait_for_deadline (which
+ * does nothing) */
+typedef grpc_call_error (*canceller)(grpc_call *call);
+
+static grpc_call_error wait_for_deadline(grpc_call *call) {
+ return GRPC_CALL_OK;
+}
+
+enum { TIMEOUT = 200000 };
+
+static void *tag(gpr_intptr t) { return (void *)t; }
+
+static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
+ const char *test_name,
+ grpc_channel_args *client_args,
+ grpc_channel_args *server_args) {
+ grpc_end2end_test_fixture f;
+ gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+ f = config.create_fixture(client_args, server_args);
+ config.init_client(&f, client_args);
+ config.init_server(&f, server_args);
+ return f;
+}
+
+static gpr_timespec n_seconds_time(int n) {
+ return gpr_time_add(gpr_now(), gpr_time_from_micros(GPR_US_PER_SEC * n));
+}
+
+static gpr_timespec five_seconds_time() { return n_seconds_time(5); }
+
+static void drain_cq(grpc_completion_queue *cq) {
+ grpc_event *ev;
+ grpc_completion_type type;
+ do {
+ ev = grpc_completion_queue_next(cq, five_seconds_time());
+ GPR_ASSERT(ev);
+ type = ev->type;
+ grpc_event_finish(ev);
+ } while (type != GRPC_QUEUE_SHUTDOWN);
+}
+
+static void shutdown_server(grpc_end2end_test_fixture *f) {
+ if (!f->server) return;
+ grpc_server_shutdown(f->server);
+ grpc_server_destroy(f->server);
+ f->server = NULL;
+}
+
+static void shutdown_client(grpc_end2end_test_fixture *f) {
+ if (!f->client) return;
+ grpc_channel_destroy(f->client);
+ f->client = NULL;
+}
+
+static void end_test(grpc_end2end_test_fixture *f) {
+ shutdown_server(f);
+ shutdown_client(f);
+
+ grpc_completion_queue_shutdown(f->server_cq);
+ drain_cq(f->server_cq);
+ grpc_completion_queue_destroy(f->server_cq);
+ grpc_completion_queue_shutdown(f->client_cq);
+ drain_cq(f->client_cq);
+ grpc_completion_queue_destroy(f->client_cq);
+}
+
+/* Cancel after accept with a writes closed, no payload */
+static void test_cancel_after_accept_and_writes_closed(
+ grpc_end2end_test_config config, canceller call_cancel) {
+ grpc_call *c;
+ grpc_call *s;
+ grpc_end2end_test_fixture f = begin_test(config, __FUNCTION__, NULL, NULL);
+ gpr_timespec deadline = five_seconds_time();
+ cq_verifier *v_client = cq_verifier_create(f.client_cq);
+ cq_verifier *v_server = cq_verifier_create(f.server_cq);
+ grpc_status chk_status = {GRPC_STATUS_CANCELLED, NULL};
+
+ c = grpc_channel_create_call(f.client, "/foo", "test.google.com", deadline);
+ GPR_ASSERT(c);
+
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_invoke(c, f.client_cq, tag(1), tag(2), tag(3), 0));
+ cq_expect_invoke_accepted(v_client, tag(1), GRPC_OP_OK);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_server_request_call(f.server, tag(100)));
+ cq_expect_server_rpc_new(v_server, &s, tag(100), "/foo", "test.google.com",
+ deadline, NULL);
+ cq_verify(v_server);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_accept(s, f.server_cq, tag(102), 0));
+ cq_expect_client_metadata_read(v_client, tag(2), NULL);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_writes_done(c, tag(4)));
+ cq_expect_finish_accepted(v_client, tag(4), GRPC_OP_OK);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_read(s, tag(101)));
+ cq_expect_empty_read(v_server, tag(101));
+ cq_verify(v_server);
+
+ GPR_ASSERT(GRPC_CALL_OK == call_cancel(c));
+
+ cq_expect_finished_with_status(v_client, tag(3), chk_status, NULL);
+ cq_verify(v_client);
+
+ cq_expect_finished_with_status(v_server, tag(102), chk_status, NULL);
+ cq_verify(v_server);
+
+ grpc_call_destroy(c);
+ grpc_call_destroy(s);
+
+ cq_verifier_destroy(v_client);
+ cq_verifier_destroy(v_server);
+ end_test(&f);
+ config.tear_down_data(&f);
+}
+
+void grpc_end2end_tests(grpc_end2end_test_config config) {
+ int i;
+ canceller cancellers[2] = {grpc_call_cancel, wait_for_deadline};
+
+ for (i = 0; i < GPR_ARRAY_SIZE(cancellers); i++) {
+ test_cancel_after_accept_and_writes_closed(config, cancellers[i]);
+ }
+}
diff --git a/test/core/end2end/tests/cancel_after_invoke.c b/test/core/end2end/tests/cancel_after_invoke.c
new file mode 100644
index 0000000000..59208134a4
--- /dev/null
+++ b/test/core/end2end/tests/cancel_after_invoke.c
@@ -0,0 +1,150 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include "test/core/end2end/end2end_tests.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <grpc/byte_buffer.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/time.h>
+#include <grpc/support/useful.h>
+#include "test/core/end2end/cq_verifier.h"
+
+/* allow cancellation by either grpc_call_cancel, or by wait_for_deadline (which
+ * does nothing) */
+typedef grpc_call_error (*canceller)(grpc_call *call);
+
+static grpc_call_error wait_for_deadline(grpc_call *call) {
+ return GRPC_CALL_OK;
+}
+
+enum { TIMEOUT = 200000 };
+
+static void *tag(gpr_intptr t) { return (void *)t; }
+
+static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
+ const char *test_name,
+ grpc_channel_args *client_args,
+ grpc_channel_args *server_args) {
+ grpc_end2end_test_fixture f;
+ gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+ f = config.create_fixture(client_args, server_args);
+ config.init_client(&f, client_args);
+ config.init_server(&f, server_args);
+ return f;
+}
+
+static gpr_timespec n_seconds_time(int n) {
+ return gpr_time_add(gpr_now(), gpr_time_from_micros(GPR_US_PER_SEC * n));
+}
+
+static gpr_timespec five_seconds_time() { return n_seconds_time(5); }
+
+static void drain_cq(grpc_completion_queue *cq) {
+ grpc_event *ev;
+ grpc_completion_type type;
+ do {
+ ev = grpc_completion_queue_next(cq, five_seconds_time());
+ GPR_ASSERT(ev);
+ type = ev->type;
+ grpc_event_finish(ev);
+ } while (type != GRPC_QUEUE_SHUTDOWN);
+}
+
+static void shutdown_server(grpc_end2end_test_fixture *f) {
+ if (!f->server) return;
+ grpc_server_shutdown(f->server);
+ grpc_server_destroy(f->server);
+ f->server = NULL;
+}
+
+static void shutdown_client(grpc_end2end_test_fixture *f) {
+ if (!f->client) return;
+ grpc_channel_destroy(f->client);
+ f->client = NULL;
+}
+
+static void end_test(grpc_end2end_test_fixture *f) {
+ shutdown_server(f);
+ shutdown_client(f);
+
+ grpc_completion_queue_shutdown(f->server_cq);
+ drain_cq(f->server_cq);
+ grpc_completion_queue_destroy(f->server_cq);
+ grpc_completion_queue_shutdown(f->client_cq);
+ drain_cq(f->client_cq);
+ grpc_completion_queue_destroy(f->client_cq);
+}
+
+/* Cancel after invoke, no payload */
+static void test_cancel_after_invoke(grpc_end2end_test_config config,
+ canceller call_cancel) {
+ grpc_call *c;
+ grpc_end2end_test_fixture f = begin_test(config, __FUNCTION__, NULL, NULL);
+ gpr_timespec deadline = five_seconds_time();
+ cq_verifier *v_client = cq_verifier_create(f.client_cq);
+ grpc_status chk_status = {GRPC_STATUS_CANCELLED, NULL};
+
+ c = grpc_channel_create_call(f.client, "/foo", "test.google.com", deadline);
+ GPR_ASSERT(c);
+
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_invoke(c, f.client_cq, tag(1), tag(2), tag(3), 0));
+ cq_expect_invoke_accepted(v_client, tag(1), GRPC_OP_OK);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK == call_cancel(c));
+
+ cq_expect_client_metadata_read(v_client, tag(2), NULL);
+ cq_expect_finished_with_status(v_client, tag(3), chk_status, NULL);
+ cq_verify(v_client);
+
+ grpc_call_destroy(c);
+
+ cq_verifier_destroy(v_client);
+ end_test(&f);
+ config.tear_down_data(&f);
+}
+
+void grpc_end2end_tests(grpc_end2end_test_config config) {
+ int i;
+ canceller cancellers[2] = {grpc_call_cancel, wait_for_deadline};
+
+ for (i = 0; i < GPR_ARRAY_SIZE(cancellers); i++) {
+ test_cancel_after_invoke(config, cancellers[i]);
+ }
+}
diff --git a/test/core/end2end/tests/cancel_before_invoke.c b/test/core/end2end/tests/cancel_before_invoke.c
new file mode 100644
index 0000000000..afffc2fba4
--- /dev/null
+++ b/test/core/end2end/tests/cancel_before_invoke.c
@@ -0,0 +1,138 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include "test/core/end2end/end2end_tests.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <grpc/byte_buffer.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/time.h>
+#include <grpc/support/useful.h>
+#include "test/core/end2end/cq_verifier.h"
+
+/* allow cancellation by either grpc_call_cancel, or by wait_for_deadline (which
+ * does nothing) */
+typedef grpc_call_error (*canceller)(grpc_call *call);
+
+enum { TIMEOUT = 200000 };
+
+static void *tag(gpr_intptr t) { return (void *)t; }
+
+static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
+ const char *test_name,
+ grpc_channel_args *client_args,
+ grpc_channel_args *server_args) {
+ grpc_end2end_test_fixture f;
+ gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+ f = config.create_fixture(client_args, server_args);
+ config.init_client(&f, client_args);
+ config.init_server(&f, server_args);
+ return f;
+}
+
+static gpr_timespec n_seconds_time(int n) {
+ return gpr_time_add(gpr_now(), gpr_time_from_micros(GPR_US_PER_SEC * n));
+}
+
+static gpr_timespec five_seconds_time() { return n_seconds_time(5); }
+
+static void drain_cq(grpc_completion_queue *cq) {
+ grpc_event *ev;
+ grpc_completion_type type;
+ do {
+ ev = grpc_completion_queue_next(cq, five_seconds_time());
+ GPR_ASSERT(ev);
+ type = ev->type;
+ grpc_event_finish(ev);
+ } while (type != GRPC_QUEUE_SHUTDOWN);
+}
+
+static void shutdown_server(grpc_end2end_test_fixture *f) {
+ if (!f->server) return;
+ grpc_server_shutdown(f->server);
+ grpc_server_destroy(f->server);
+ f->server = NULL;
+}
+
+static void shutdown_client(grpc_end2end_test_fixture *f) {
+ if (!f->client) return;
+ grpc_channel_destroy(f->client);
+ f->client = NULL;
+}
+
+static void end_test(grpc_end2end_test_fixture *f) {
+ shutdown_server(f);
+ shutdown_client(f);
+
+ grpc_completion_queue_shutdown(f->server_cq);
+ drain_cq(f->server_cq);
+ grpc_completion_queue_destroy(f->server_cq);
+ grpc_completion_queue_shutdown(f->client_cq);
+ drain_cq(f->client_cq);
+ grpc_completion_queue_destroy(f->client_cq);
+}
+
+/* Cancel before invoke */
+static void test_cancel_before_invoke(grpc_end2end_test_config config) {
+ grpc_call *c;
+ grpc_end2end_test_fixture f = begin_test(config, __FUNCTION__, NULL, NULL);
+ gpr_timespec deadline = five_seconds_time();
+ cq_verifier *v_client = cq_verifier_create(f.client_cq);
+ grpc_status chk_status = {GRPC_STATUS_CANCELLED, NULL};
+
+ c = grpc_channel_create_call(f.client, "/foo", "test.google.com", deadline);
+ GPR_ASSERT(c);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_cancel(c));
+
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_invoke(c, f.client_cq, tag(1), tag(2), tag(3), 0));
+ cq_expect_invoke_accepted(v_client, tag(1), GRPC_OP_ERROR);
+ cq_expect_client_metadata_read(v_client, tag(2), NULL);
+ cq_expect_finished_with_status(v_client, tag(3), chk_status, NULL);
+ cq_verify(v_client);
+
+ grpc_call_destroy(c);
+
+ cq_verifier_destroy(v_client);
+ end_test(&f);
+ config.tear_down_data(&f);
+}
+
+void grpc_end2end_tests(grpc_end2end_test_config config) {
+ test_cancel_before_invoke(config);
+}
diff --git a/test/core/end2end/tests/cancel_in_a_vacuum.c b/test/core/end2end/tests/cancel_in_a_vacuum.c
new file mode 100644
index 0000000000..1a4dfa7b73
--- /dev/null
+++ b/test/core/end2end/tests/cancel_in_a_vacuum.c
@@ -0,0 +1,138 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include "test/core/end2end/end2end_tests.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <grpc/byte_buffer.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/time.h>
+#include <grpc/support/useful.h>
+#include "test/core/end2end/cq_verifier.h"
+
+/* allow cancellation by either grpc_call_cancel, or by wait_for_deadline (which
+ * does nothing) */
+typedef grpc_call_error (*canceller)(grpc_call *call);
+
+static grpc_call_error wait_for_deadline(grpc_call *call) {
+ return GRPC_CALL_OK;
+}
+
+enum { TIMEOUT = 200000 };
+
+static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
+ const char *test_name,
+ grpc_channel_args *client_args,
+ grpc_channel_args *server_args) {
+ grpc_end2end_test_fixture f;
+ gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+ f = config.create_fixture(client_args, server_args);
+ config.init_client(&f, client_args);
+ config.init_server(&f, server_args);
+ return f;
+}
+
+static gpr_timespec n_seconds_time(int n) {
+ return gpr_time_add(gpr_now(), gpr_time_from_micros(GPR_US_PER_SEC * n));
+}
+
+static gpr_timespec five_seconds_time() { return n_seconds_time(5); }
+
+static void drain_cq(grpc_completion_queue *cq) {
+ grpc_event *ev;
+ grpc_completion_type type;
+ do {
+ ev = grpc_completion_queue_next(cq, five_seconds_time());
+ GPR_ASSERT(ev);
+ type = ev->type;
+ grpc_event_finish(ev);
+ } while (type != GRPC_QUEUE_SHUTDOWN);
+}
+
+static void shutdown_server(grpc_end2end_test_fixture *f) {
+ if (!f->server) return;
+ grpc_server_shutdown(f->server);
+ grpc_server_destroy(f->server);
+ f->server = NULL;
+}
+
+static void shutdown_client(grpc_end2end_test_fixture *f) {
+ if (!f->client) return;
+ grpc_channel_destroy(f->client);
+ f->client = NULL;
+}
+
+static void end_test(grpc_end2end_test_fixture *f) {
+ shutdown_server(f);
+ shutdown_client(f);
+
+ grpc_completion_queue_shutdown(f->server_cq);
+ drain_cq(f->server_cq);
+ grpc_completion_queue_destroy(f->server_cq);
+ grpc_completion_queue_shutdown(f->client_cq);
+ drain_cq(f->client_cq);
+ grpc_completion_queue_destroy(f->client_cq);
+}
+
+/* Cancel and do nothing */
+static void test_cancel_in_a_vacuum(grpc_end2end_test_config config,
+ canceller call_cancel) {
+ grpc_call *c;
+ grpc_end2end_test_fixture f = begin_test(config, __FUNCTION__, NULL, NULL);
+ gpr_timespec deadline = five_seconds_time();
+ cq_verifier *v_client = cq_verifier_create(f.client_cq);
+
+ c = grpc_channel_create_call(f.client, "/foo", "test.google.com", deadline);
+ GPR_ASSERT(c);
+
+ GPR_ASSERT(GRPC_CALL_OK == call_cancel(c));
+
+ grpc_call_destroy(c);
+
+ cq_verifier_destroy(v_client);
+ end_test(&f);
+ config.tear_down_data(&f);
+}
+
+void grpc_end2end_tests(grpc_end2end_test_config config) {
+ int i;
+ canceller cancellers[2] = {grpc_call_cancel, wait_for_deadline};
+
+ for (i = 0; i < GPR_ARRAY_SIZE(cancellers); i++) {
+ test_cancel_in_a_vacuum(config, cancellers[i]);
+ }
+}
diff --git a/test/core/end2end/tests/early_server_shutdown_finishes_inflight_calls.c b/test/core/end2end/tests/early_server_shutdown_finishes_inflight_calls.c
new file mode 100644
index 0000000000..8d73e2c15e
--- /dev/null
+++ b/test/core/end2end/tests/early_server_shutdown_finishes_inflight_calls.c
@@ -0,0 +1,158 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include "test/core/end2end/end2end_tests.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <grpc/byte_buffer.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/time.h>
+#include <grpc/support/useful.h>
+#include "test/core/end2end/cq_verifier.h"
+
+enum { TIMEOUT = 200000 };
+
+static void *tag(gpr_intptr t) { return (void *)t; }
+
+static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
+ const char *test_name,
+ grpc_channel_args *client_args,
+ grpc_channel_args *server_args) {
+ grpc_end2end_test_fixture f;
+ gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+ f = config.create_fixture(client_args, server_args);
+ config.init_client(&f, client_args);
+ config.init_server(&f, server_args);
+ return f;
+}
+
+static gpr_timespec n_seconds_time(int n) {
+ return gpr_time_add(gpr_now(), gpr_time_from_micros(GPR_US_PER_SEC * n));
+}
+
+static gpr_timespec five_seconds_time() { return n_seconds_time(5); }
+
+static void drain_cq(grpc_completion_queue *cq) {
+ grpc_event *ev;
+ grpc_completion_type type;
+ do {
+ ev = grpc_completion_queue_next(cq, five_seconds_time());
+ GPR_ASSERT(ev);
+ type = ev->type;
+ grpc_event_finish(ev);
+ } while (type != GRPC_QUEUE_SHUTDOWN);
+}
+
+static void shutdown_server(grpc_end2end_test_fixture *f) {
+ if (!f->server) return;
+ grpc_server_shutdown(f->server);
+ grpc_server_destroy(f->server);
+ f->server = NULL;
+}
+
+static void shutdown_client(grpc_end2end_test_fixture *f) {
+ if (!f->client) return;
+ grpc_channel_destroy(f->client);
+ f->client = NULL;
+}
+
+static void end_test(grpc_end2end_test_fixture *f) {
+ shutdown_server(f);
+ shutdown_client(f);
+
+ grpc_completion_queue_shutdown(f->server_cq);
+ drain_cq(f->server_cq);
+ grpc_completion_queue_destroy(f->server_cq);
+ grpc_completion_queue_shutdown(f->client_cq);
+ drain_cq(f->client_cq);
+ grpc_completion_queue_destroy(f->client_cq);
+}
+
+static void test_early_server_shutdown_finishes_inflight_calls(
+ grpc_end2end_test_config config) {
+ grpc_end2end_test_fixture f = begin_test(config, __FUNCTION__, NULL, NULL);
+ grpc_call *c;
+ grpc_call *s;
+ grpc_status expect_status = {GRPC_STATUS_UNAVAILABLE, NULL};
+ gpr_timespec deadline = five_seconds_time();
+ cq_verifier *v_client = cq_verifier_create(f.client_cq);
+ cq_verifier *v_server = cq_verifier_create(f.server_cq);
+
+ c = grpc_channel_create_call(f.client, "/foo", "test.google.com", deadline);
+ GPR_ASSERT(c);
+
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_invoke(c, f.client_cq, tag(1), tag(2), tag(3), 0));
+ cq_expect_invoke_accepted(v_client, tag(1), GRPC_OP_OK);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_writes_done(c, tag(4)));
+ cq_expect_finish_accepted(v_client, tag(4), GRPC_OP_OK);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_server_request_call(f.server, tag(100)));
+ cq_expect_server_rpc_new(v_server, &s, tag(100), "/foo", "test.google.com",
+ deadline, NULL);
+ cq_verify(v_server);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_accept(s, f.server_cq, tag(102), 0));
+ cq_expect_client_metadata_read(v_client, tag(2), NULL);
+ cq_verify(v_client);
+
+ /* shutdown and destroy the server */
+ shutdown_server(&f);
+
+ cq_expect_finished(v_server, tag(102), NULL);
+ cq_verify(v_server);
+
+ grpc_call_destroy(s);
+
+ cq_expect_finished_with_status(v_client, tag(3), expect_status, NULL);
+ cq_verify(v_client);
+
+ grpc_call_destroy(c);
+
+ cq_verifier_destroy(v_client);
+ cq_verifier_destroy(v_server);
+
+ end_test(&f);
+ config.tear_down_data(&f);
+}
+
+void grpc_end2end_tests(grpc_end2end_test_config config) {
+ test_early_server_shutdown_finishes_inflight_calls(config);
+}
diff --git a/test/core/end2end/tests/early_server_shutdown_finishes_tags.c b/test/core/end2end/tests/early_server_shutdown_finishes_tags.c
new file mode 100644
index 0000000000..eb3eb7c07a
--- /dev/null
+++ b/test/core/end2end/tests/early_server_shutdown_finishes_tags.c
@@ -0,0 +1,127 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include "test/core/end2end/end2end_tests.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <grpc/byte_buffer.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/time.h>
+#include <grpc/support/useful.h>
+#include "test/core/end2end/cq_verifier.h"
+
+enum { TIMEOUT = 200000 };
+
+static void *tag(gpr_intptr t) { return (void *)t; }
+
+static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
+ const char *test_name,
+ grpc_channel_args *client_args,
+ grpc_channel_args *server_args) {
+ grpc_end2end_test_fixture f;
+ gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+ f = config.create_fixture(client_args, server_args);
+ config.init_client(&f, client_args);
+ config.init_server(&f, server_args);
+ return f;
+}
+
+static gpr_timespec n_seconds_time(int n) {
+ return gpr_time_add(gpr_now(), gpr_time_from_micros(GPR_US_PER_SEC * n));
+}
+
+static gpr_timespec five_seconds_time() { return n_seconds_time(5); }
+
+static void drain_cq(grpc_completion_queue *cq) {
+ grpc_event *ev;
+ grpc_completion_type type;
+ do {
+ ev = grpc_completion_queue_next(cq, five_seconds_time());
+ GPR_ASSERT(ev);
+ type = ev->type;
+ grpc_event_finish(ev);
+ } while (type != GRPC_QUEUE_SHUTDOWN);
+}
+
+static void shutdown_server(grpc_end2end_test_fixture *f) {
+ if (!f->server) return;
+ grpc_server_shutdown(f->server);
+ grpc_server_destroy(f->server);
+ f->server = NULL;
+}
+
+static void shutdown_client(grpc_end2end_test_fixture *f) {
+ if (!f->client) return;
+ grpc_channel_destroy(f->client);
+ f->client = NULL;
+}
+
+static void end_test(grpc_end2end_test_fixture *f) {
+ shutdown_server(f);
+ shutdown_client(f);
+
+ grpc_completion_queue_shutdown(f->server_cq);
+ drain_cq(f->server_cq);
+ grpc_completion_queue_destroy(f->server_cq);
+ grpc_completion_queue_shutdown(f->client_cq);
+ drain_cq(f->client_cq);
+ grpc_completion_queue_destroy(f->client_cq);
+}
+
+static void test_early_server_shutdown_finishes_tags(
+ grpc_end2end_test_config config) {
+ grpc_end2end_test_fixture f = begin_test(config, __FUNCTION__, NULL, NULL);
+ cq_verifier *v_server = cq_verifier_create(f.server_cq);
+ grpc_call *s = (void *)1;
+
+ /* upon shutdown, the server should finish all requested calls indicating
+ no new call */
+ grpc_server_request_call(f.server, tag(1000));
+ grpc_server_shutdown(f.server);
+ cq_expect_server_rpc_new(v_server, &s, tag(1000), NULL, NULL, gpr_inf_past,
+ NULL);
+ cq_verify(v_server);
+ GPR_ASSERT(s == NULL);
+
+ end_test(&f);
+ config.tear_down_data(&f);
+ cq_verifier_destroy(v_server);
+}
+
+void grpc_end2end_tests(grpc_end2end_test_config config) {
+ test_early_server_shutdown_finishes_tags(config);
+}
diff --git a/test/core/end2end/tests/invoke_large_request.c b/test/core/end2end/tests/invoke_large_request.c
new file mode 100644
index 0000000000..7a3cab3f82
--- /dev/null
+++ b/test/core/end2end/tests/invoke_large_request.c
@@ -0,0 +1,184 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include "test/core/end2end/end2end_tests.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <grpc/byte_buffer.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/time.h>
+#include <grpc/support/useful.h>
+#include "test/core/end2end/cq_verifier.h"
+
+enum { TIMEOUT = 200000 };
+
+static void *tag(gpr_intptr t) { return (void *)t; }
+
+static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
+ const char *test_name,
+ grpc_channel_args *client_args,
+ grpc_channel_args *server_args) {
+ grpc_end2end_test_fixture f;
+ gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+ f = config.create_fixture(client_args, server_args);
+ config.init_client(&f, client_args);
+ config.init_server(&f, server_args);
+ return f;
+}
+
+static gpr_timespec n_seconds_time(int n) {
+ return gpr_time_add(gpr_now(), gpr_time_from_micros(GPR_US_PER_SEC * n));
+}
+
+static gpr_timespec five_seconds_time() { return n_seconds_time(5); }
+
+static void drain_cq(grpc_completion_queue *cq) {
+ grpc_event *ev;
+ grpc_completion_type type;
+ do {
+ ev = grpc_completion_queue_next(cq, five_seconds_time());
+ GPR_ASSERT(ev);
+ type = ev->type;
+ grpc_event_finish(ev);
+ } while (type != GRPC_QUEUE_SHUTDOWN);
+}
+
+static void shutdown_server(grpc_end2end_test_fixture *f) {
+ if (!f->server) return;
+ grpc_server_shutdown(f->server);
+ grpc_server_destroy(f->server);
+ f->server = NULL;
+}
+
+static void shutdown_client(grpc_end2end_test_fixture *f) {
+ if (!f->client) return;
+ grpc_channel_destroy(f->client);
+ f->client = NULL;
+}
+
+static void end_test(grpc_end2end_test_fixture *f) {
+ shutdown_server(f);
+ shutdown_client(f);
+
+ grpc_completion_queue_shutdown(f->server_cq);
+ drain_cq(f->server_cq);
+ grpc_completion_queue_destroy(f->server_cq);
+ grpc_completion_queue_shutdown(f->client_cq);
+ drain_cq(f->client_cq);
+ grpc_completion_queue_destroy(f->client_cq);
+}
+
+static gpr_slice large_slice() {
+ gpr_slice slice = gpr_slice_malloc(1000000);
+ memset(GPR_SLICE_START_PTR(slice), 0xab, GPR_SLICE_LENGTH(slice));
+ return slice;
+}
+
+static void test_invoke_large_request(grpc_end2end_test_config config) {
+ grpc_call *c;
+ grpc_call *s;
+ grpc_status send_status = {GRPC_STATUS_UNIMPLEMENTED, "xyz"};
+ gpr_slice request_payload_slice = large_slice();
+ grpc_byte_buffer *request_payload =
+ grpc_byte_buffer_create(&request_payload_slice, 1);
+ gpr_timespec deadline = five_seconds_time();
+ grpc_end2end_test_fixture f = begin_test(config, __FUNCTION__, NULL, NULL);
+ cq_verifier *v_client = cq_verifier_create(f.client_cq);
+ cq_verifier *v_server = cq_verifier_create(f.server_cq);
+
+ /* byte buffer holds the slice, we can unref it already */
+ gpr_slice_unref(request_payload_slice);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_server_request_call(f.server, tag(100)));
+
+ c = grpc_channel_create_call(f.client, "/foo", "test.google.com", deadline);
+ GPR_ASSERT(c);
+
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_invoke(c, f.client_cq, tag(1), tag(2), tag(3), 0));
+ cq_expect_invoke_accepted(v_client, tag(1), GRPC_OP_OK);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_write(c, request_payload, tag(4), 0));
+ /* destroy byte buffer early to ensure async code keeps track of its contents
+ correctly */
+ grpc_byte_buffer_destroy(request_payload);
+ /* write should not be accepted until the server is willing to read the
+ request (as this request is very large) */
+ cq_verify_empty(v_client);
+
+ cq_expect_server_rpc_new(v_server, &s, tag(100), "/foo", "test.google.com",
+ deadline, NULL);
+ cq_verify(v_server);
+
+ grpc_call_accept(s, f.server_cq, tag(102), 0);
+ cq_expect_client_metadata_read(v_client, tag(2), NULL);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_read(s, tag(5)));
+ /* now the write can be accepted */
+ cq_expect_write_accepted(v_client, tag(4), GRPC_OP_OK);
+ cq_verify(v_client);
+ cq_expect_read(v_server, tag(5), large_slice());
+ cq_verify(v_server);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_writes_done(c, tag(8)));
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_write_status(s, send_status, tag(9)));
+
+ cq_expect_finish_accepted(v_client, tag(8), GRPC_OP_OK);
+ cq_expect_finished_with_status(v_client, tag(3), send_status, NULL);
+ cq_verify(v_client);
+
+ cq_expect_finish_accepted(v_server, tag(9), GRPC_OP_OK);
+ cq_expect_finished(v_server, tag(102), NULL);
+ cq_verify(v_server);
+
+ grpc_call_destroy(c);
+ grpc_call_destroy(s);
+
+ cq_verifier_destroy(v_client);
+ cq_verifier_destroy(v_server);
+
+ end_test(&f);
+ config.tear_down_data(&f);
+}
+
+void grpc_end2end_tests(grpc_end2end_test_config config) {
+ test_invoke_large_request(config);
+}
diff --git a/test/core/end2end/tests/max_concurrent_streams.c b/test/core/end2end/tests/max_concurrent_streams.c
new file mode 100644
index 0000000000..6b331a550c
--- /dev/null
+++ b/test/core/end2end/tests/max_concurrent_streams.c
@@ -0,0 +1,257 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include "test/core/end2end/end2end_tests.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <grpc/byte_buffer.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/time.h>
+#include <grpc/support/useful.h>
+#include "test/core/end2end/cq_verifier.h"
+
+enum { TIMEOUT = 200000 };
+
+static void *tag(gpr_intptr t) { return (void *)t; }
+
+static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
+ const char *test_name,
+ grpc_channel_args *client_args,
+ grpc_channel_args *server_args) {
+ grpc_end2end_test_fixture f;
+ gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+ f = config.create_fixture(client_args, server_args);
+ config.init_client(&f, client_args);
+ config.init_server(&f, server_args);
+ return f;
+}
+
+static gpr_timespec n_seconds_time(int n) {
+ return gpr_time_add(gpr_now(), gpr_time_from_micros(GPR_US_PER_SEC * n));
+}
+
+static gpr_timespec five_seconds_time() { return n_seconds_time(5); }
+
+static void drain_cq(grpc_completion_queue *cq) {
+ grpc_event *ev;
+ grpc_completion_type type;
+ do {
+ ev = grpc_completion_queue_next(cq, five_seconds_time());
+ GPR_ASSERT(ev);
+ type = ev->type;
+ grpc_event_finish(ev);
+ } while (type != GRPC_QUEUE_SHUTDOWN);
+}
+
+static void shutdown_server(grpc_end2end_test_fixture *f) {
+ if (!f->server) return;
+ grpc_server_shutdown(f->server);
+ grpc_server_destroy(f->server);
+ f->server = NULL;
+}
+
+static void shutdown_client(grpc_end2end_test_fixture *f) {
+ if (!f->client) return;
+ grpc_channel_destroy(f->client);
+ f->client = NULL;
+}
+
+static void end_test(grpc_end2end_test_fixture *f) {
+ shutdown_server(f);
+ shutdown_client(f);
+
+ grpc_completion_queue_shutdown(f->server_cq);
+ drain_cq(f->server_cq);
+ grpc_completion_queue_destroy(f->server_cq);
+ grpc_completion_queue_shutdown(f->client_cq);
+ drain_cq(f->client_cq);
+ grpc_completion_queue_destroy(f->client_cq);
+}
+
+static void simple_request_body(grpc_end2end_test_fixture f) {
+ grpc_call *c;
+ grpc_call *s;
+ grpc_status send_status = {GRPC_STATUS_UNIMPLEMENTED, "xyz"};
+ gpr_timespec deadline = five_seconds_time();
+ cq_verifier *v_client = cq_verifier_create(f.client_cq);
+ cq_verifier *v_server = cq_verifier_create(f.server_cq);
+
+ c = grpc_channel_create_call(f.client, "/foo", "test.google.com", deadline);
+ GPR_ASSERT(c);
+
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_invoke(c, f.client_cq, tag(1), tag(2), tag(3), 0));
+ cq_expect_invoke_accepted(v_client, tag(1), GRPC_OP_OK);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_writes_done(c, tag(4)));
+ cq_expect_finish_accepted(v_client, tag(4), GRPC_OP_OK);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_server_request_call(f.server, tag(100)));
+ cq_expect_server_rpc_new(v_server, &s, tag(100), "/foo", "test.google.com",
+ deadline, NULL);
+ cq_verify(v_server);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_accept(s, f.server_cq, tag(102), 0));
+ cq_expect_client_metadata_read(v_client, tag(2), NULL);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_write_status(s, send_status, tag(5)));
+ cq_expect_finished_with_status(v_client, tag(3), send_status, NULL);
+ cq_verify(v_client);
+
+ cq_expect_finish_accepted(v_server, tag(5), GRPC_OP_OK);
+ cq_verify(v_server);
+ cq_expect_finished(v_server, tag(102), NULL);
+ cq_verify(v_server);
+
+ grpc_call_destroy(c);
+ grpc_call_destroy(s);
+
+ cq_verifier_destroy(v_client);
+ cq_verifier_destroy(v_server);
+}
+
+static void test_max_concurrent_streams(grpc_end2end_test_config config) {
+ grpc_end2end_test_fixture f;
+ grpc_arg server_arg;
+ grpc_channel_args server_args;
+ grpc_call *c1;
+ grpc_call *c2;
+ grpc_call *s1;
+ grpc_call *s2;
+ gpr_timespec deadline;
+ grpc_status send_status = {GRPC_STATUS_UNIMPLEMENTED, "xyz"};
+ cq_verifier *v_client;
+ cq_verifier *v_server;
+
+ server_arg.key = GRPC_ARG_MAX_CONCURRENT_STREAMS;
+ server_arg.type = GRPC_ARG_INTEGER;
+ server_arg.value.integer = 1;
+
+ server_args.num_args = 1;
+ server_args.args = &server_arg;
+
+ f = begin_test(config, __FUNCTION__, NULL, &server_args);
+ v_client = cq_verifier_create(f.client_cq);
+ v_server = cq_verifier_create(f.server_cq);
+
+ /* perform a ping-pong to ensure that settings have had a chance to round
+ trip */
+ simple_request_body(f);
+ /* perform another one to make sure that the one stream case still works */
+ simple_request_body(f);
+
+ /* start two requests - ensuring that the second is not accepted until
+ the first completes */
+ deadline = five_seconds_time();
+ c1 = grpc_channel_create_call(f.client, "/foo", "test.google.com", deadline);
+ GPR_ASSERT(c1);
+ c2 = grpc_channel_create_call(f.client, "/bar", "test.google.com", deadline);
+ GPR_ASSERT(c1);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_server_request_call(f.server, tag(100)));
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_invoke(c1, f.client_cq, tag(300),
+ tag(301), tag(302), 0));
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_invoke(c2, f.client_cq, tag(400),
+ tag(401), tag(402), 0));
+ cq_expect_invoke_accepted(v_client, tag(300), GRPC_OP_OK);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_writes_done(c1, tag(303)));
+ cq_expect_finish_accepted(v_client, tag(303), GRPC_OP_OK);
+ cq_verify(v_client);
+
+ cq_expect_server_rpc_new(v_server, &s1, tag(100), "/foo", "test.google.com",
+ deadline, NULL);
+ cq_verify(v_server);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_accept(s1, f.server_cq, tag(102), 0));
+ cq_expect_client_metadata_read(v_client, tag(301), NULL);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_write_status(s1, send_status, tag(103)));
+ cq_expect_finish_accepted(v_server, tag(103), GRPC_OP_OK);
+ cq_expect_finished(v_server, tag(102), NULL);
+ cq_verify(v_server);
+
+ /* first request is finished, we should be able to start the second */
+ cq_expect_finished_with_status(v_client, tag(302), send_status, NULL);
+ cq_expect_invoke_accepted(v_client, tag(400), GRPC_OP_OK);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_writes_done(c2, tag(403)));
+ cq_expect_finish_accepted(v_client, tag(403), GRPC_OP_OK);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_server_request_call(f.server, tag(200)));
+ cq_expect_server_rpc_new(v_server, &s2, tag(200), "/bar", "test.google.com",
+ deadline, NULL);
+ cq_verify(v_server);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_accept(s2, f.server_cq, tag(202), 0));
+ cq_expect_client_metadata_read(v_client, tag(401), NULL);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_write_status(s2, send_status, tag(203)));
+ cq_expect_finish_accepted(v_server, tag(203), GRPC_OP_OK);
+ cq_expect_finished(v_server, tag(202), NULL);
+ cq_verify(v_server);
+
+ cq_expect_finished_with_status(v_client, tag(402), send_status, NULL);
+ cq_verify(v_client);
+
+ cq_verifier_destroy(v_client);
+ cq_verifier_destroy(v_server);
+
+ grpc_call_destroy(c1);
+ grpc_call_destroy(s1);
+ grpc_call_destroy(c2);
+ grpc_call_destroy(s2);
+
+ end_test(&f);
+ config.tear_down_data(&f);
+}
+
+void grpc_end2end_tests(grpc_end2end_test_config config) {
+ test_max_concurrent_streams(config);
+}
diff --git a/test/core/end2end/tests/no_op.c b/test/core/end2end/tests/no_op.c
new file mode 100644
index 0000000000..ca03103438
--- /dev/null
+++ b/test/core/end2end/tests/no_op.c
@@ -0,0 +1,112 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include "test/core/end2end/end2end_tests.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <grpc/byte_buffer.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/time.h>
+#include <grpc/support/useful.h>
+#include "test/core/end2end/cq_verifier.h"
+
+enum { TIMEOUT = 200000 };
+
+static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
+ const char *test_name,
+ grpc_channel_args *client_args,
+ grpc_channel_args *server_args) {
+ grpc_end2end_test_fixture f;
+ gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+ f = config.create_fixture(client_args, server_args);
+ config.init_client(&f, client_args);
+ config.init_server(&f, server_args);
+ return f;
+}
+
+static gpr_timespec n_seconds_time(int n) {
+ return gpr_time_add(gpr_now(), gpr_time_from_micros(GPR_US_PER_SEC * n));
+}
+
+static gpr_timespec five_seconds_time() { return n_seconds_time(5); }
+
+static void drain_cq(grpc_completion_queue *cq) {
+ grpc_event *ev;
+ grpc_completion_type type;
+ do {
+ ev = grpc_completion_queue_next(cq, five_seconds_time());
+ GPR_ASSERT(ev);
+ type = ev->type;
+ grpc_event_finish(ev);
+ } while (type != GRPC_QUEUE_SHUTDOWN);
+}
+
+static void shutdown_server(grpc_end2end_test_fixture *f) {
+ if (!f->server) return;
+ grpc_server_shutdown(f->server);
+ grpc_server_destroy(f->server);
+ f->server = NULL;
+}
+
+static void shutdown_client(grpc_end2end_test_fixture *f) {
+ if (!f->client) return;
+ grpc_channel_destroy(f->client);
+ f->client = NULL;
+}
+
+static void end_test(grpc_end2end_test_fixture *f) {
+ shutdown_server(f);
+ shutdown_client(f);
+
+ grpc_completion_queue_shutdown(f->server_cq);
+ drain_cq(f->server_cq);
+ grpc_completion_queue_destroy(f->server_cq);
+ grpc_completion_queue_shutdown(f->client_cq);
+ drain_cq(f->client_cq);
+ grpc_completion_queue_destroy(f->client_cq);
+}
+
+static void test_no_op(grpc_end2end_test_config config) {
+ grpc_end2end_test_fixture f = begin_test(config, __FUNCTION__, NULL, NULL);
+ end_test(&f);
+ config.tear_down_data(&f);
+}
+
+
+void grpc_end2end_tests(grpc_end2end_test_config config) {
+ test_no_op(config);
+}
diff --git a/test/core/end2end/tests/ping_pong_streaming.c b/test/core/end2end/tests/ping_pong_streaming.c
new file mode 100644
index 0000000000..84e7bb41b8
--- /dev/null
+++ b/test/core/end2end/tests/ping_pong_streaming.c
@@ -0,0 +1,201 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include "test/core/end2end/end2end_tests.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <grpc/byte_buffer.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/time.h>
+#include <grpc/support/useful.h>
+#include "test/core/end2end/cq_verifier.h"
+
+enum { TIMEOUT = 200000 };
+
+static void *tag(gpr_intptr t) { return (void *)t; }
+
+static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
+ const char *test_name,
+ grpc_channel_args *client_args,
+ grpc_channel_args *server_args) {
+ grpc_end2end_test_fixture f;
+ gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+ f = config.create_fixture(client_args, server_args);
+ config.init_client(&f, client_args);
+ config.init_server(&f, server_args);
+ return f;
+}
+
+static gpr_timespec n_seconds_time(int n) {
+ return gpr_time_add(gpr_now(), gpr_time_from_micros(GPR_US_PER_SEC * n));
+}
+
+static gpr_timespec five_seconds_time() { return n_seconds_time(5); }
+
+static void drain_cq(grpc_completion_queue *cq) {
+ grpc_event *ev;
+ grpc_completion_type type;
+ do {
+ ev = grpc_completion_queue_next(cq, five_seconds_time());
+ GPR_ASSERT(ev);
+ type = ev->type;
+ grpc_event_finish(ev);
+ } while (type != GRPC_QUEUE_SHUTDOWN);
+}
+
+static void shutdown_server(grpc_end2end_test_fixture *f) {
+ if (!f->server) return;
+ grpc_server_shutdown(f->server);
+ grpc_server_destroy(f->server);
+ f->server = NULL;
+}
+
+static void shutdown_client(grpc_end2end_test_fixture *f) {
+ if (!f->client) return;
+ grpc_channel_destroy(f->client);
+ f->client = NULL;
+}
+
+static void end_test(grpc_end2end_test_fixture *f) {
+ shutdown_server(f);
+ shutdown_client(f);
+
+ grpc_completion_queue_shutdown(f->server_cq);
+ drain_cq(f->server_cq);
+ grpc_completion_queue_destroy(f->server_cq);
+ grpc_completion_queue_shutdown(f->client_cq);
+ drain_cq(f->client_cq);
+ grpc_completion_queue_destroy(f->client_cq);
+}
+
+/* Client pings and server pongs. Repeat messages rounds before finishing. */
+static void test_pingpong_streaming(grpc_end2end_test_config config,
+ int messages) {
+ int i;
+ grpc_call *c;
+ grpc_call *s = NULL;
+ grpc_status send_status = {GRPC_STATUS_UNIMPLEMENTED, "xyz"};
+ gpr_slice request_payload_slice = gpr_slice_from_copied_string("hello world");
+ gpr_slice response_payload_slice = gpr_slice_from_copied_string("hello you");
+ grpc_byte_buffer *request_payload = NULL;
+ grpc_byte_buffer *response_payload = NULL;
+ gpr_timespec deadline = n_seconds_time(messages * 5);
+ grpc_end2end_test_fixture f = begin_test(config, __FUNCTION__, NULL, NULL);
+ cq_verifier *v_client = cq_verifier_create(f.client_cq);
+ cq_verifier *v_server = cq_verifier_create(f.server_cq);
+
+ gpr_log(GPR_INFO, "testing with %d message pairs.", messages);
+ c = grpc_channel_create_call(f.client, "/foo", "test.google.com", deadline);
+ GPR_ASSERT(c);
+
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_invoke(c, f.client_cq, tag(1), tag(2), tag(3), 0));
+ cq_expect_invoke_accepted(v_client, tag(1), GRPC_OP_OK);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_server_request_call(f.server, tag(100)));
+
+ cq_expect_server_rpc_new(v_server, &s, tag(100), "/foo", "test.google.com",
+ deadline, NULL);
+ cq_verify(v_server);
+ grpc_call_accept(s, f.server_cq, tag(102), 0);
+
+ cq_expect_client_metadata_read(v_client, tag(2), NULL);
+ cq_verify(v_client);
+
+ for (i = 0; i < messages; i++) {
+ request_payload = grpc_byte_buffer_create(&request_payload_slice, 1);
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_write(c, request_payload, tag(2), 0));
+ /* destroy byte buffer early to ensure async code keeps track of its
+ contents
+ correctly */
+ grpc_byte_buffer_destroy(request_payload);
+ cq_expect_write_accepted(v_client, tag(2), GRPC_OP_OK);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_read(s, tag(3)));
+ cq_expect_read(v_server, tag(3),
+ gpr_slice_from_copied_string("hello world"));
+ cq_verify(v_server);
+
+ response_payload = grpc_byte_buffer_create(&response_payload_slice, 1);
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_write(s, response_payload, tag(4), 0));
+ /* destroy byte buffer early to ensure async code keeps track of its
+ contents
+ correctly */
+ grpc_byte_buffer_destroy(response_payload);
+ cq_expect_write_accepted(v_server, tag(4), GRPC_OP_OK);
+ cq_verify(v_server);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_read(c, tag(5)));
+ cq_expect_read(v_client, tag(5), gpr_slice_from_copied_string("hello you"));
+ cq_verify(v_client);
+ }
+
+ gpr_slice_unref(request_payload_slice);
+ gpr_slice_unref(response_payload_slice);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_writes_done(c, tag(6)));
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_write_status(s, send_status, tag(7)));
+
+ cq_expect_finish_accepted(v_client, tag(6), GRPC_OP_OK);
+ cq_expect_finished_with_status(v_client, tag(3), send_status, NULL);
+ cq_verify(v_client);
+
+ cq_expect_finish_accepted(v_server, tag(7), GRPC_OP_OK);
+ cq_expect_finished(v_server, tag(102), NULL);
+ cq_verify(v_server);
+
+ grpc_call_destroy(c);
+ grpc_call_destroy(s);
+
+ end_test(&f);
+ config.tear_down_data(&f);
+
+ cq_verifier_destroy(v_client);
+ cq_verifier_destroy(v_server);
+}
+
+void grpc_end2end_tests(grpc_end2end_test_config config) {
+ int i;
+
+ for (i = 1; i < 10; i++) {
+ test_pingpong_streaming(config, i);
+ }
+}
diff --git a/test/core/end2end/tests/request_response_with_metadata_and_payload.c b/test/core/end2end/tests/request_response_with_metadata_and_payload.c
new file mode 100644
index 0000000000..4cb5679c6b
--- /dev/null
+++ b/test/core/end2end/tests/request_response_with_metadata_and_payload.c
@@ -0,0 +1,208 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include "test/core/end2end/end2end_tests.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <grpc/byte_buffer.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/time.h>
+#include <grpc/support/useful.h>
+#include "test/core/end2end/cq_verifier.h"
+
+enum { TIMEOUT = 200000 };
+
+static void *tag(gpr_intptr t) { return (void *)t; }
+
+static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
+ const char *test_name,
+ grpc_channel_args *client_args,
+ grpc_channel_args *server_args) {
+ grpc_end2end_test_fixture f;
+ gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+ f = config.create_fixture(client_args, server_args);
+ config.init_client(&f, client_args);
+ config.init_server(&f, server_args);
+ return f;
+}
+
+static gpr_timespec n_seconds_time(int n) {
+ return gpr_time_add(gpr_now(), gpr_time_from_micros(GPR_US_PER_SEC * n));
+}
+
+static gpr_timespec five_seconds_time() { return n_seconds_time(5); }
+
+static void drain_cq(grpc_completion_queue *cq) {
+ grpc_event *ev;
+ grpc_completion_type type;
+ do {
+ ev = grpc_completion_queue_next(cq, five_seconds_time());
+ GPR_ASSERT(ev);
+ type = ev->type;
+ grpc_event_finish(ev);
+ } while (type != GRPC_QUEUE_SHUTDOWN);
+}
+
+static void shutdown_server(grpc_end2end_test_fixture *f) {
+ if (!f->server) return;
+ grpc_server_shutdown(f->server);
+ grpc_server_destroy(f->server);
+ f->server = NULL;
+}
+
+static void shutdown_client(grpc_end2end_test_fixture *f) {
+ if (!f->client) return;
+ grpc_channel_destroy(f->client);
+ f->client = NULL;
+}
+
+static void end_test(grpc_end2end_test_fixture *f) {
+ shutdown_server(f);
+ shutdown_client(f);
+
+ grpc_completion_queue_shutdown(f->server_cq);
+ drain_cq(f->server_cq);
+ grpc_completion_queue_destroy(f->server_cq);
+ grpc_completion_queue_shutdown(f->client_cq);
+ drain_cq(f->client_cq);
+ grpc_completion_queue_destroy(f->client_cq);
+}
+
+/* Request/response with metadata and payload.*/
+static void test_request_response_with_metadata_and_payload(
+ grpc_end2end_test_config config) {
+ grpc_call *c;
+ grpc_call *s;
+ grpc_status send_status = {GRPC_STATUS_UNIMPLEMENTED, "xyz"};
+ gpr_slice request_payload_slice = gpr_slice_from_copied_string("hello world");
+ gpr_slice response_payload_slice = gpr_slice_from_copied_string("hello you");
+ grpc_byte_buffer *request_payload =
+ grpc_byte_buffer_create(&request_payload_slice, 1);
+ grpc_byte_buffer *response_payload =
+ grpc_byte_buffer_create(&response_payload_slice, 1);
+ gpr_timespec deadline = five_seconds_time();
+ grpc_metadata meta1 = {"key1", "val1", 4};
+ grpc_metadata meta2 = {"key2", "val2", 4};
+ grpc_metadata meta3 = {"key3", "val3", 4};
+ grpc_metadata meta4 = {"key4", "val4", 4};
+ grpc_end2end_test_fixture f = begin_test(config, __FUNCTION__, NULL, NULL);
+ cq_verifier *v_client = cq_verifier_create(f.client_cq);
+ cq_verifier *v_server = cq_verifier_create(f.server_cq);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_server_request_call(f.server, tag(100)));
+
+ /* byte buffer holds the slice, we can unref it already */
+ gpr_slice_unref(request_payload_slice);
+ gpr_slice_unref(response_payload_slice);
+
+ c = grpc_channel_create_call(f.client, "/foo", "test.google.com", deadline);
+ GPR_ASSERT(c);
+
+ /* add multiple metadata */
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_add_metadata(c, &meta1, 0));
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_add_metadata(c, &meta2, 0));
+
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_invoke(c, f.client_cq, tag(1), tag(2), tag(3), 0));
+ cq_expect_invoke_accepted(v_client, tag(1), GRPC_OP_OK);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_write(c, request_payload, tag(4), 0));
+ /* destroy byte buffer early to ensure async code keeps track of its contents
+ correctly */
+ grpc_byte_buffer_destroy(request_payload);
+ cq_expect_write_accepted(v_client, tag(4), GRPC_OP_OK);
+ cq_verify(v_client);
+
+ cq_expect_server_rpc_new(v_server, &s, tag(100), "/foo", "test.google.com",
+ deadline, "key1", "val1", "key2", "val2", NULL);
+ cq_verify(v_server);
+
+ /* add multiple metadata */
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_add_metadata(s, &meta3, 0));
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_add_metadata(s, &meta4, 0));
+
+ grpc_call_accept(s, f.server_cq, tag(102), 0);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_read(s, tag(5)));
+ cq_expect_read(v_server, tag(5), gpr_slice_from_copied_string("hello world"));
+ cq_verify(v_server);
+
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_write(s, response_payload, tag(6), 0));
+ /* destroy byte buffer early to ensure async code keeps track of its contents
+ correctly */
+ grpc_byte_buffer_destroy(response_payload);
+ cq_expect_write_accepted(v_server, tag(6), GRPC_OP_OK);
+ cq_verify(v_server);
+
+ /* fetch metadata.. */
+ cq_expect_client_metadata_read(v_client, tag(2), "key3", "val3", "key4",
+ "val4", NULL);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_read(c, tag(7)));
+ cq_expect_read(v_client, tag(7), gpr_slice_from_copied_string("hello you"));
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_writes_done(c, tag(8)));
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_write_status(s, send_status, tag(9)));
+
+ cq_expect_finish_accepted(v_client, tag(8), GRPC_OP_OK);
+ cq_expect_finished_with_status(v_client, tag(3), send_status, NULL);
+ cq_verify(v_client);
+
+ cq_expect_finish_accepted(v_server, tag(9), GRPC_OP_OK);
+ cq_expect_finished(v_server, tag(102), NULL);
+ cq_verify(v_server);
+
+ grpc_call_destroy(c);
+ grpc_call_destroy(s);
+
+ end_test(&f);
+ config.tear_down_data(&f);
+
+ cq_verifier_destroy(v_client);
+ cq_verifier_destroy(v_server);
+}
+
+
+void grpc_end2end_tests(grpc_end2end_test_config config) {
+ test_request_response_with_metadata_and_payload(config);
+}
diff --git a/test/core/end2end/tests/request_response_with_payload.c b/test/core/end2end/tests/request_response_with_payload.c
new file mode 100644
index 0000000000..fb9b59a5ab
--- /dev/null
+++ b/test/core/end2end/tests/request_response_with_payload.c
@@ -0,0 +1,207 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include "test/core/end2end/end2end_tests.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <grpc/byte_buffer.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/time.h>
+#include <grpc/support/useful.h>
+#include "test/core/end2end/cq_verifier.h"
+
+enum { TIMEOUT = 200000 };
+
+static void *tag(gpr_intptr t) { return (void *)t; }
+
+static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
+ const char *test_name,
+ grpc_channel_args *client_args,
+ grpc_channel_args *server_args) {
+ grpc_end2end_test_fixture f;
+ gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+ f = config.create_fixture(client_args, server_args);
+ config.init_client(&f, client_args);
+ config.init_server(&f, server_args);
+ return f;
+}
+
+static gpr_timespec n_seconds_time(int n) {
+ return gpr_time_add(gpr_now(), gpr_time_from_micros(GPR_US_PER_SEC * n));
+}
+
+static gpr_timespec five_seconds_time() { return n_seconds_time(5); }
+
+static void drain_cq(grpc_completion_queue *cq) {
+ grpc_event *ev;
+ grpc_completion_type type;
+ do {
+ ev = grpc_completion_queue_next(cq, five_seconds_time());
+ GPR_ASSERT(ev);
+ type = ev->type;
+ grpc_event_finish(ev);
+ } while (type != GRPC_QUEUE_SHUTDOWN);
+}
+
+static void shutdown_server(grpc_end2end_test_fixture *f) {
+ if (!f->server) return;
+ grpc_server_shutdown(f->server);
+ grpc_server_destroy(f->server);
+ f->server = NULL;
+}
+
+static void shutdown_client(grpc_end2end_test_fixture *f) {
+ if (!f->client) return;
+ grpc_channel_destroy(f->client);
+ f->client = NULL;
+}
+
+static void end_test(grpc_end2end_test_fixture *f) {
+ shutdown_server(f);
+ shutdown_client(f);
+
+ grpc_completion_queue_shutdown(f->server_cq);
+ drain_cq(f->server_cq);
+ grpc_completion_queue_destroy(f->server_cq);
+ grpc_completion_queue_shutdown(f->client_cq);
+ drain_cq(f->client_cq);
+ grpc_completion_queue_destroy(f->client_cq);
+}
+
+static void request_response_with_payload(grpc_end2end_test_fixture f) {
+ grpc_call *c;
+ grpc_call *s;
+ grpc_status send_status = {GRPC_STATUS_UNIMPLEMENTED, "xyz"};
+ gpr_slice request_payload_slice = gpr_slice_from_copied_string("hello world");
+ gpr_slice response_payload_slice = gpr_slice_from_copied_string("hello you");
+ grpc_byte_buffer *request_payload =
+ grpc_byte_buffer_create(&request_payload_slice, 1);
+ grpc_byte_buffer *response_payload =
+ grpc_byte_buffer_create(&response_payload_slice, 1);
+ gpr_timespec deadline = five_seconds_time();
+ cq_verifier *v_client = cq_verifier_create(f.client_cq);
+ cq_verifier *v_server = cq_verifier_create(f.server_cq);
+
+ /* byte buffer holds the slice, we can unref it already */
+ gpr_slice_unref(request_payload_slice);
+ gpr_slice_unref(response_payload_slice);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_server_request_call(f.server, tag(100)));
+
+ c = grpc_channel_create_call(f.client, "/foo", "test.google.com", deadline);
+ GPR_ASSERT(c);
+
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_invoke(c, f.client_cq, tag(1), tag(2), tag(3), 0));
+ cq_expect_invoke_accepted(v_client, tag(1), GRPC_OP_OK);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_write(c, request_payload, tag(4), 0));
+ /* destroy byte buffer early to ensure async code keeps track of its contents
+ correctly */
+ grpc_byte_buffer_destroy(request_payload);
+ cq_expect_write_accepted(v_client, tag(4), GRPC_OP_OK);
+ cq_verify(v_client);
+
+ cq_expect_server_rpc_new(v_server, &s, tag(100), "/foo", "test.google.com",
+ deadline, NULL);
+ cq_verify(v_server);
+
+ grpc_call_accept(s, f.server_cq, tag(102), 0);
+ cq_expect_client_metadata_read(v_client, tag(2), NULL);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_read(s, tag(5)));
+ cq_expect_read(v_server, tag(5), gpr_slice_from_copied_string("hello world"));
+ cq_verify(v_server);
+
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_write(s, response_payload, tag(6), 0));
+ /* destroy byte buffer early to ensure async code keeps track of its contents
+ correctly */
+ grpc_byte_buffer_destroy(response_payload);
+ cq_expect_write_accepted(v_server, tag(6), GRPC_OP_OK);
+ cq_verify(v_server);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_read(c, tag(7)));
+ cq_expect_read(v_client, tag(7), gpr_slice_from_copied_string("hello you"));
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_writes_done(c, tag(8)));
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_write_status(s, send_status, tag(9)));
+
+ cq_expect_finish_accepted(v_client, tag(8), GRPC_OP_OK);
+ cq_expect_finished_with_status(v_client, tag(3), send_status, NULL);
+ cq_verify(v_client);
+
+ cq_expect_finish_accepted(v_server, tag(9), GRPC_OP_OK);
+ cq_expect_finished(v_server, tag(102), NULL);
+ cq_verify(v_server);
+
+ grpc_call_destroy(c);
+ grpc_call_destroy(s);
+
+ cq_verifier_destroy(v_client);
+ cq_verifier_destroy(v_server);
+}
+
+/* Client sends a request with payload, server reads then returns a response
+ payload and status. */
+static void test_invoke_request_response_with_payload(
+ grpc_end2end_test_config config) {
+ grpc_end2end_test_fixture f = begin_test(config, __FUNCTION__, NULL, NULL);
+ request_response_with_payload(f);
+ end_test(&f);
+ config.tear_down_data(&f);
+}
+
+static void test_invoke_10_request_response_with_payload(
+ grpc_end2end_test_config config) {
+ int i;
+ grpc_end2end_test_fixture f = begin_test(config, __FUNCTION__, NULL, NULL);
+ for (i = 0; i < 10; i++) {
+ request_response_with_payload(f);
+ }
+ end_test(&f);
+ config.tear_down_data(&f);
+}
+
+void grpc_end2end_tests(grpc_end2end_test_config config) {
+ test_invoke_request_response_with_payload(config);
+ test_invoke_10_request_response_with_payload(config);
+}
diff --git a/test/core/end2end/tests/request_with_large_metadata.c b/test/core/end2end/tests/request_with_large_metadata.c
new file mode 100644
index 0000000000..ccdee4025e
--- /dev/null
+++ b/test/core/end2end/tests/request_with_large_metadata.c
@@ -0,0 +1,172 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include "test/core/end2end/end2end_tests.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <grpc/byte_buffer.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/time.h>
+#include <grpc/support/useful.h>
+#include "test/core/end2end/cq_verifier.h"
+
+enum { TIMEOUT = 200000 };
+
+static void *tag(gpr_intptr t) { return (void *)t; }
+
+static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
+ const char *test_name,
+ grpc_channel_args *client_args,
+ grpc_channel_args *server_args) {
+ grpc_end2end_test_fixture f;
+ gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+ f = config.create_fixture(client_args, server_args);
+ config.init_client(&f, client_args);
+ config.init_server(&f, server_args);
+ return f;
+}
+
+static gpr_timespec n_seconds_time(int n) {
+ return gpr_time_add(gpr_now(), gpr_time_from_micros(GPR_US_PER_SEC * n));
+}
+
+static gpr_timespec five_seconds_time() { return n_seconds_time(5); }
+
+static void drain_cq(grpc_completion_queue *cq) {
+ grpc_event *ev;
+ grpc_completion_type type;
+ do {
+ ev = grpc_completion_queue_next(cq, five_seconds_time());
+ GPR_ASSERT(ev);
+ type = ev->type;
+ grpc_event_finish(ev);
+ } while (type != GRPC_QUEUE_SHUTDOWN);
+}
+
+static void shutdown_server(grpc_end2end_test_fixture *f) {
+ if (!f->server) return;
+ grpc_server_shutdown(f->server);
+ grpc_server_destroy(f->server);
+ f->server = NULL;
+}
+
+static void shutdown_client(grpc_end2end_test_fixture *f) {
+ if (!f->client) return;
+ grpc_channel_destroy(f->client);
+ f->client = NULL;
+}
+
+static void end_test(grpc_end2end_test_fixture *f) {
+ shutdown_server(f);
+ shutdown_client(f);
+
+ grpc_completion_queue_shutdown(f->server_cq);
+ drain_cq(f->server_cq);
+ grpc_completion_queue_destroy(f->server_cq);
+ grpc_completion_queue_shutdown(f->client_cq);
+ drain_cq(f->client_cq);
+ grpc_completion_queue_destroy(f->client_cq);
+}
+
+/* Request with a large amount of metadata.*/
+static void test_request_with_large_metadata(grpc_end2end_test_config config) {
+ grpc_call *c;
+ grpc_call *s;
+ grpc_status send_status = {GRPC_STATUS_OK, NULL};
+ gpr_timespec deadline = five_seconds_time();
+ grpc_metadata meta;
+ grpc_end2end_test_fixture f = begin_test(config, __FUNCTION__, NULL, NULL);
+ cq_verifier *v_client = cq_verifier_create(f.client_cq);
+ cq_verifier *v_server = cq_verifier_create(f.server_cq);
+ const int large_size = 64 * 1024;
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_server_request_call(f.server, tag(100)));
+
+ meta.key = "key";
+ meta.value = gpr_malloc(large_size + 1);
+ memset(meta.value, 'a', large_size);
+ meta.value[large_size] = 0;
+ meta.value_length = large_size;
+
+ c = grpc_channel_create_call(f.client, "/foo", "test.google.com", deadline);
+ GPR_ASSERT(c);
+
+ /* add the metadata */
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_add_metadata(c, &meta, 0));
+
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_invoke(c, f.client_cq, tag(1), tag(2), tag(3), 0));
+ cq_expect_invoke_accepted(v_client, tag(1), GRPC_OP_OK);
+ cq_verify(v_client);
+
+ cq_expect_server_rpc_new(v_server, &s, tag(100), "/foo", "test.google.com",
+ deadline, "key", meta.value, NULL);
+ cq_verify(v_server);
+
+ grpc_call_accept(s, f.server_cq, tag(102), 0);
+
+ /* fetch metadata.. */
+ cq_expect_client_metadata_read(v_client, tag(2), NULL);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_writes_done(c, tag(8)));
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_write_status(s, send_status, tag(9)));
+
+ cq_expect_finish_accepted(v_client, tag(8), GRPC_OP_OK);
+ cq_expect_finished_with_status(v_client, tag(3), send_status, NULL);
+ cq_verify(v_client);
+
+ cq_expect_finish_accepted(v_server, tag(9), GRPC_OP_OK);
+ cq_expect_finished(v_server, tag(102), NULL);
+ cq_verify(v_server);
+
+ grpc_call_destroy(c);
+ grpc_call_destroy(s);
+
+ end_test(&f);
+ config.tear_down_data(&f);
+
+ cq_verifier_destroy(v_client);
+ cq_verifier_destroy(v_server);
+
+ gpr_free(meta.value);
+}
+
+void grpc_end2end_tests(grpc_end2end_test_config config) {
+ test_request_with_large_metadata(config);
+}
diff --git a/test/core/end2end/tests/request_with_payload.c b/test/core/end2end/tests/request_with_payload.c
new file mode 100644
index 0000000000..38a29e379e
--- /dev/null
+++ b/test/core/end2end/tests/request_with_payload.c
@@ -0,0 +1,171 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include "test/core/end2end/end2end_tests.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <grpc/byte_buffer.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/time.h>
+#include <grpc/support/useful.h>
+#include "test/core/end2end/cq_verifier.h"
+
+enum { TIMEOUT = 200000 };
+
+static void *tag(gpr_intptr t) { return (void *)t; }
+
+static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
+ const char *test_name,
+ grpc_channel_args *client_args,
+ grpc_channel_args *server_args) {
+ grpc_end2end_test_fixture f;
+ gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+ f = config.create_fixture(client_args, server_args);
+ config.init_client(&f, client_args);
+ config.init_server(&f, server_args);
+ return f;
+}
+
+static gpr_timespec n_seconds_time(int n) {
+ return gpr_time_add(gpr_now(), gpr_time_from_micros(GPR_US_PER_SEC * n));
+}
+
+static gpr_timespec five_seconds_time() { return n_seconds_time(5); }
+
+static void drain_cq(grpc_completion_queue *cq) {
+ grpc_event *ev;
+ grpc_completion_type type;
+ do {
+ ev = grpc_completion_queue_next(cq, five_seconds_time());
+ GPR_ASSERT(ev);
+ type = ev->type;
+ grpc_event_finish(ev);
+ } while (type != GRPC_QUEUE_SHUTDOWN);
+}
+
+static void shutdown_server(grpc_end2end_test_fixture *f) {
+ if (!f->server) return;
+ grpc_server_shutdown(f->server);
+ grpc_server_destroy(f->server);
+ f->server = NULL;
+}
+
+static void shutdown_client(grpc_end2end_test_fixture *f) {
+ if (!f->client) return;
+ grpc_channel_destroy(f->client);
+ f->client = NULL;
+}
+
+static void end_test(grpc_end2end_test_fixture *f) {
+ shutdown_server(f);
+ shutdown_client(f);
+
+ grpc_completion_queue_shutdown(f->server_cq);
+ drain_cq(f->server_cq);
+ grpc_completion_queue_destroy(f->server_cq);
+ grpc_completion_queue_shutdown(f->client_cq);
+ drain_cq(f->client_cq);
+ grpc_completion_queue_destroy(f->client_cq);
+}
+
+/* Client sends a request with payload, server reads then returns status. */
+static void test_invoke_request_with_payload(grpc_end2end_test_config config) {
+ grpc_call *c;
+ grpc_call *s;
+ grpc_status send_status = {GRPC_STATUS_UNIMPLEMENTED, "xyz"};
+ gpr_slice payload_slice = gpr_slice_from_copied_string("hello world");
+ grpc_byte_buffer *payload = grpc_byte_buffer_create(&payload_slice, 1);
+ gpr_timespec deadline = five_seconds_time();
+ grpc_end2end_test_fixture f = begin_test(config, __FUNCTION__, NULL, NULL);
+ cq_verifier *v_client = cq_verifier_create(f.client_cq);
+ cq_verifier *v_server = cq_verifier_create(f.server_cq);
+
+ /* byte buffer holds the slice, we can unref it already */
+ gpr_slice_unref(payload_slice);
+
+ c = grpc_channel_create_call(f.client, "/foo", "test.google.com", deadline);
+ GPR_ASSERT(c);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_server_request_call(f.server, tag(100)));
+
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_invoke(c, f.client_cq, tag(1), tag(2), tag(3), 0));
+ cq_expect_invoke_accepted(v_client, tag(1), GRPC_OP_OK);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_write(c, payload, tag(4), 0));
+ /* destroy byte buffer early to ensure async code keeps track of its contents
+ correctly */
+ grpc_byte_buffer_destroy(payload);
+ cq_expect_write_accepted(v_client, tag(4), GRPC_OP_OK);
+ cq_verify(v_client);
+
+ cq_expect_server_rpc_new(v_server, &s, tag(100), "/foo", "test.google.com",
+ deadline, NULL);
+ cq_verify(v_server);
+
+ grpc_call_accept(s, f.server_cq, tag(102), 0);
+ cq_expect_client_metadata_read(v_client, tag(2), NULL);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_read(s, tag(4)));
+ cq_expect_read(v_server, tag(4), gpr_slice_from_copied_string("hello world"));
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_writes_done(c, tag(5)));
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_write_status(s, send_status, tag(6)));
+ cq_expect_finish_accepted(v_client, tag(5), GRPC_OP_OK);
+ cq_expect_finished_with_status(v_client, tag(3), send_status, NULL);
+ cq_verify(v_client);
+
+ cq_expect_finish_accepted(v_server, tag(6), GRPC_OP_OK);
+ cq_expect_finished(v_server, tag(102), NULL);
+ cq_verify(v_server);
+
+ grpc_call_destroy(c);
+ grpc_call_destroy(s);
+
+ end_test(&f);
+ config.tear_down_data(&f);
+
+ cq_verifier_destroy(v_client);
+ cq_verifier_destroy(v_server);
+}
+
+void grpc_end2end_tests(grpc_end2end_test_config config) {
+ test_invoke_request_with_payload(config);
+}
diff --git a/test/core/end2end/tests/simple_delayed_request.c b/test/core/end2end/tests/simple_delayed_request.c
new file mode 100644
index 0000000000..dde6eb6d23
--- /dev/null
+++ b/test/core/end2end/tests/simple_delayed_request.c
@@ -0,0 +1,174 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include "test/core/end2end/end2end_tests.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <grpc/byte_buffer.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/time.h>
+#include <grpc/support/useful.h>
+#include "test/core/end2end/cq_verifier.h"
+
+enum { TIMEOUT = 200000 };
+
+static void *tag(gpr_intptr t) { return (void *)t; }
+
+static gpr_timespec n_seconds_time(int n) {
+ return gpr_time_add(gpr_now(), gpr_time_from_micros(GPR_US_PER_SEC * n));
+}
+
+static gpr_timespec five_seconds_time() { return n_seconds_time(5); }
+
+static void drain_cq(grpc_completion_queue *cq) {
+ grpc_event *ev;
+ grpc_completion_type type;
+ do {
+ ev = grpc_completion_queue_next(cq, five_seconds_time());
+ GPR_ASSERT(ev);
+ type = ev->type;
+ grpc_event_finish(ev);
+ } while (type != GRPC_QUEUE_SHUTDOWN);
+}
+
+static void shutdown_server(grpc_end2end_test_fixture *f) {
+ if (!f->server) return;
+ grpc_server_shutdown(f->server);
+ grpc_server_destroy(f->server);
+ f->server = NULL;
+}
+
+static void shutdown_client(grpc_end2end_test_fixture *f) {
+ if (!f->client) return;
+ grpc_channel_destroy(f->client);
+ f->client = NULL;
+}
+
+static void end_test(grpc_end2end_test_fixture *f) {
+ shutdown_server(f);
+ shutdown_client(f);
+
+ grpc_completion_queue_shutdown(f->server_cq);
+ drain_cq(f->server_cq);
+ grpc_completion_queue_destroy(f->server_cq);
+ grpc_completion_queue_shutdown(f->client_cq);
+ drain_cq(f->client_cq);
+ grpc_completion_queue_destroy(f->client_cq);
+}
+
+static void simple_delayed_request_body(grpc_end2end_test_config config,
+ grpc_end2end_test_fixture *f,
+ grpc_channel_args *client_args,
+ grpc_channel_args *server_args,
+ long delay_us) {
+ grpc_call *c;
+ grpc_call *s;
+ grpc_status send_status = {GRPC_STATUS_UNIMPLEMENTED, "xyz"};
+ gpr_timespec deadline = five_seconds_time();
+ cq_verifier *v_client = cq_verifier_create(f->client_cq);
+ cq_verifier *v_server = cq_verifier_create(f->server_cq);
+
+ config.init_client(f, client_args);
+
+ c = grpc_channel_create_call(f->client, "/foo", "test.google.com", deadline);
+ GPR_ASSERT(c);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_invoke(c, f->client_cq, tag(1),
+ tag(2), tag(3), 0));
+ gpr_sleep_until(gpr_time_add(gpr_now(), gpr_time_from_micros(delay_us)));
+ cq_expect_invoke_accepted(v_client, tag(1), GRPC_OP_OK);
+
+ config.init_server(f, server_args);
+
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_writes_done(c, tag(4)));
+ cq_expect_finish_accepted(v_client, tag(4), GRPC_OP_OK);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_server_request_call(f->server, tag(100)));
+ cq_expect_server_rpc_new(v_server, &s, tag(100), "/foo", "test.google.com",
+ deadline, NULL);
+ cq_verify(v_server);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_accept(s, f->server_cq, tag(102), 0));
+ cq_expect_client_metadata_read(v_client, tag(2), NULL);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_write_status(s, send_status, tag(5)));
+ cq_expect_finished_with_status(v_client, tag(3), send_status, NULL);
+ cq_verify(v_client);
+
+ cq_expect_finish_accepted(v_server, tag(5), GRPC_OP_OK);
+ cq_expect_finished(v_server, tag(102), NULL);
+ cq_verify(v_server);
+
+ grpc_call_destroy(c);
+ grpc_call_destroy(s);
+
+ cq_verifier_destroy(v_client);
+ cq_verifier_destroy(v_server);
+}
+
+static void test_simple_delayed_request_short(grpc_end2end_test_config config) {
+ grpc_end2end_test_fixture f;
+
+ gpr_log(GPR_INFO, "%s/%s", __FUNCTION__, config.name);
+ f = config.create_fixture(NULL, NULL);
+ simple_delayed_request_body(config, &f, NULL, NULL, 100000);
+ end_test(&f);
+ config.tear_down_data(&f);
+}
+
+static void test_simple_delayed_request_long(grpc_end2end_test_config config) {
+ grpc_end2end_test_fixture f;
+
+ gpr_log(GPR_INFO, "%s/%s", __FUNCTION__, config.name);
+ f = config.create_fixture(NULL, NULL);
+ /* This timeout should be longer than a single retry */
+ simple_delayed_request_body(config, &f, NULL, NULL, 1500000);
+ end_test(&f);
+ config.tear_down_data(&f);
+}
+
+void grpc_end2end_tests(grpc_end2end_test_config config) {
+ if (config.feature_mask & FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION) {
+ test_simple_delayed_request_short(config);
+ test_simple_delayed_request_long(config);
+ }
+}
diff --git a/test/core/end2end/tests/simple_request.c b/test/core/end2end/tests/simple_request.c
new file mode 100644
index 0000000000..15214ffb2b
--- /dev/null
+++ b/test/core/end2end/tests/simple_request.c
@@ -0,0 +1,231 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include "test/core/end2end/end2end_tests.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <grpc/byte_buffer.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/time.h>
+#include <grpc/support/useful.h>
+#include "test/core/end2end/cq_verifier.h"
+
+enum { TIMEOUT = 200000 };
+
+static void *tag(gpr_intptr t) { return (void *)t; }
+
+static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
+ const char *test_name,
+ grpc_channel_args *client_args,
+ grpc_channel_args *server_args) {
+ grpc_end2end_test_fixture f;
+ gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+ f = config.create_fixture(client_args, server_args);
+ config.init_client(&f, client_args);
+ config.init_server(&f, server_args);
+ return f;
+}
+
+static gpr_timespec n_seconds_time(int n) {
+ return gpr_time_add(gpr_now(), gpr_time_from_micros(GPR_US_PER_SEC * n));
+}
+
+static gpr_timespec five_seconds_time() { return n_seconds_time(5); }
+
+static void drain_cq(grpc_completion_queue *cq) {
+ grpc_event *ev;
+ grpc_completion_type type;
+ do {
+ ev = grpc_completion_queue_next(cq, five_seconds_time());
+ GPR_ASSERT(ev);
+ type = ev->type;
+ grpc_event_finish(ev);
+ } while (type != GRPC_QUEUE_SHUTDOWN);
+}
+
+static void shutdown_server(grpc_end2end_test_fixture *f) {
+ if (!f->server) return;
+ grpc_server_shutdown(f->server);
+ grpc_server_destroy(f->server);
+ f->server = NULL;
+}
+
+static void shutdown_client(grpc_end2end_test_fixture *f) {
+ if (!f->client) return;
+ grpc_channel_destroy(f->client);
+ f->client = NULL;
+}
+
+static void end_test(grpc_end2end_test_fixture *f) {
+ shutdown_server(f);
+ shutdown_client(f);
+
+ grpc_completion_queue_shutdown(f->server_cq);
+ drain_cq(f->server_cq);
+ grpc_completion_queue_destroy(f->server_cq);
+ grpc_completion_queue_shutdown(f->client_cq);
+ drain_cq(f->client_cq);
+ grpc_completion_queue_destroy(f->client_cq);
+}
+
+static void simple_request_body(grpc_end2end_test_fixture f) {
+ grpc_call *c;
+ grpc_call *s;
+ grpc_status send_status = {GRPC_STATUS_UNIMPLEMENTED, "xyz"};
+ gpr_timespec deadline = five_seconds_time();
+ cq_verifier *v_client = cq_verifier_create(f.client_cq);
+ cq_verifier *v_server = cq_verifier_create(f.server_cq);
+
+ c = grpc_channel_create_call(f.client, "/foo", "test.google.com", deadline);
+ GPR_ASSERT(c);
+
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_invoke(c, f.client_cq, tag(1), tag(2), tag(3), 0));
+ cq_expect_invoke_accepted(v_client, tag(1), GRPC_OP_OK);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_writes_done(c, tag(4)));
+ cq_expect_finish_accepted(v_client, tag(4), GRPC_OP_OK);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_server_request_call(f.server, tag(100)));
+ cq_expect_server_rpc_new(v_server, &s, tag(100), "/foo", "test.google.com",
+ deadline, NULL);
+ cq_verify(v_server);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_accept(s, f.server_cq, tag(102), 0));
+ cq_expect_client_metadata_read(v_client, tag(2), NULL);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_write_status(s, send_status, tag(5)));
+ cq_expect_finished_with_status(v_client, tag(3), send_status, NULL);
+ cq_verify(v_client);
+
+ cq_expect_finish_accepted(v_server, tag(5), GRPC_OP_OK);
+ cq_verify(v_server);
+ cq_expect_finished(v_server, tag(102), NULL);
+ cq_verify(v_server);
+
+ grpc_call_destroy(c);
+ grpc_call_destroy(s);
+
+ cq_verifier_destroy(v_client);
+ cq_verifier_destroy(v_server);
+}
+
+/* an alternative ordering of the simple request body */
+static void simple_request_body2(grpc_end2end_test_fixture f) {
+ grpc_call *c;
+ grpc_call *s;
+ grpc_status send_status = {GRPC_STATUS_UNIMPLEMENTED, "xyz"};
+ gpr_timespec deadline = five_seconds_time();
+ cq_verifier *v_client = cq_verifier_create(f.client_cq);
+ cq_verifier *v_server = cq_verifier_create(f.server_cq);
+
+ c = grpc_channel_create_call(f.client, "/foo", "test.google.com", deadline);
+ GPR_ASSERT(c);
+
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_invoke(c, f.client_cq, tag(1), tag(2), tag(3), 0));
+ cq_expect_invoke_accepted(v_client, tag(1), GRPC_OP_OK);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_writes_done(c, tag(4)));
+ cq_expect_finish_accepted(v_client, tag(4), GRPC_OP_OK);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_server_request_call(f.server, tag(100)));
+ cq_expect_server_rpc_new(v_server, &s, tag(100), "/foo", "test.google.com",
+ deadline, NULL);
+ cq_verify(v_server);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_accept(s, f.server_cq, tag(102), 0));
+
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_write_status(s, send_status, tag(5)));
+ cq_expect_finish_accepted(v_server, tag(5), GRPC_OP_OK);
+ cq_verify(v_server);
+
+ cq_expect_client_metadata_read(v_client, tag(2), NULL);
+ cq_verify(v_client);
+
+ cq_expect_finished_with_status(v_client, tag(3), send_status, NULL);
+ cq_verify(v_client);
+
+ cq_expect_finished(v_server, tag(102), NULL);
+ cq_verify(v_server);
+
+ grpc_call_destroy(c);
+ grpc_call_destroy(s);
+
+ cq_verifier_destroy(v_client);
+ cq_verifier_destroy(v_server);
+}
+
+static void test_invoke_simple_request(
+ grpc_end2end_test_config config, const char *name,
+ void (*body)(grpc_end2end_test_fixture f)) {
+ char fullname[64];
+ grpc_end2end_test_fixture f;
+
+ sprintf(fullname, "%s/%s", __FUNCTION__, name);
+
+ f = begin_test(config, fullname, NULL, NULL);
+ body(f);
+ end_test(&f);
+ config.tear_down_data(&f);
+}
+
+static void test_invoke_10_simple_requests(grpc_end2end_test_config config) {
+ int i;
+ grpc_end2end_test_fixture f = begin_test(config, __FUNCTION__, NULL, NULL);
+ for (i = 0; i < 10; i++) {
+ simple_request_body(f);
+ gpr_log(GPR_INFO, "Passed simple request %d", i);
+ }
+ end_test(&f);
+ config.tear_down_data(&f);
+}
+
+void grpc_end2end_tests(grpc_end2end_test_config config) {
+ test_invoke_simple_request(config, "simple_request_body",
+ simple_request_body);
+ test_invoke_simple_request(config, "simple_request_body2",
+ simple_request_body2);
+ test_invoke_10_simple_requests(config);
+}
diff --git a/test/core/end2end/tests/thread_stress_test.c b/test/core/end2end/tests/thread_stress_test.c
new file mode 100644
index 0000000000..44b250fecb
--- /dev/null
+++ b/test/core/end2end/tests/thread_stress_test.c
@@ -0,0 +1,325 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include "test/core/end2end/end2end_tests.h"
+
+#include <string.h>
+
+#include "src/core/surface/event_string.h"
+#include "src/core/surface/completion_queue.h"
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/time.h>
+#include <grpc/support/thd.h>
+
+#define SERVER_THREADS 16
+#define CLIENT_THREADS 16
+
+static grpc_end2end_test_fixture g_fixture;
+static gpr_timespec g_test_end_time;
+static gpr_event g_client_done[CLIENT_THREADS];
+static gpr_event g_server_done[SERVER_THREADS];
+static gpr_mu g_mu;
+static int g_active_requests;
+
+static gpr_timespec n_seconds_time(int n) {
+ return gpr_time_add(gpr_now(), gpr_time_from_micros(GPR_US_PER_SEC * n));
+}
+
+static gpr_timespec five_seconds_time() { return n_seconds_time(5); }
+
+/* Drain pending events on a completion queue until it's ready to destroy.
+ Does some post-processing to safely release memory on some of the events. */
+static void drain_cq(int client, grpc_completion_queue *cq) {
+ grpc_event *ev;
+ grpc_completion_type type;
+ char *evstr;
+ int done = 0;
+ char *name = client ? "client" : "server";
+ while (!done) {
+ ev = grpc_completion_queue_next(cq, five_seconds_time());
+ if (!ev) {
+ gpr_log(GPR_ERROR, "waiting for %s cq to drain", name);
+ grpc_cq_dump_pending_ops(cq);
+ continue;
+ }
+
+ evstr = grpc_event_string(ev);
+ gpr_log(GPR_INFO, "got late %s event: %s", name, evstr);
+ gpr_free(evstr);
+
+ type = ev->type;
+ switch (type) {
+ case GRPC_SERVER_RPC_NEW:
+ gpr_free(ev->tag);
+ if (ev->call) {
+ grpc_call_destroy(ev->call);
+ }
+ break;
+ case GRPC_FINISHED:
+ grpc_call_destroy(ev->call);
+ break;
+ case GRPC_QUEUE_SHUTDOWN:
+ done = 1;
+ break;
+ case GRPC_READ:
+ case GRPC_WRITE_ACCEPTED:
+ if (!client && gpr_unref(ev->tag)) {
+ gpr_free(ev->tag);
+ }
+ default:
+ break;
+ }
+ grpc_event_finish(ev);
+ }
+}
+
+/* Kick off a new request - assumes g_mu taken */
+static void start_request() {
+ grpc_call *call = grpc_channel_create_call(
+ g_fixture.client, "/Foo", "test.google.com", g_test_end_time);
+ g_active_requests++;
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_invoke(call, g_fixture.client_cq,
+ NULL, NULL, NULL, 0));
+}
+
+/* Async client: handle sending requests, reading responses, and starting
+ new requests when old ones finish */
+static void client_thread(void *p) {
+ int id = (gpr_intptr)p;
+ grpc_event *ev;
+ gpr_slice slice = gpr_slice_malloc(100);
+ grpc_byte_buffer *buf;
+ char *estr;
+ memset(GPR_SLICE_START_PTR(slice), id, GPR_SLICE_LENGTH(slice));
+
+ buf = grpc_byte_buffer_create(&slice, 1);
+ gpr_slice_unref(slice);
+
+ for (;;) {
+ ev = grpc_completion_queue_next(g_fixture.client_cq, n_seconds_time(1));
+ if (ev) {
+ switch (ev->type) {
+ default:
+ estr = grpc_event_string(ev);
+ gpr_log(GPR_ERROR, "unexpected event: %s", estr);
+ gpr_free(estr);
+ break;
+ case GRPC_INVOKE_ACCEPTED:
+ /* better not keep going if the invoke failed */
+ if (ev->data.invoke_accepted == GRPC_OP_OK) {
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_read(ev->call, NULL));
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_write(ev->call, buf, NULL, 0));
+ }
+ break;
+ case GRPC_READ:
+ break;
+ case GRPC_WRITE_ACCEPTED:
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_writes_done(ev->call, NULL));
+ break;
+ case GRPC_FINISH_ACCEPTED:
+ break;
+ case GRPC_CLIENT_METADATA_READ:
+ break;
+ case GRPC_FINISHED:
+ /* kick off a new request if the test should still be running */
+ gpr_mu_lock(&g_mu);
+ g_active_requests--;
+ if (gpr_time_cmp(gpr_now(), g_test_end_time) < 0) {
+ start_request();
+ }
+ gpr_mu_unlock(&g_mu);
+ grpc_call_destroy(ev->call);
+ break;
+ }
+ grpc_event_finish(ev);
+ }
+ gpr_mu_lock(&g_mu);
+ if (g_active_requests == 0) {
+ gpr_mu_unlock(&g_mu);
+ break;
+ }
+ gpr_mu_unlock(&g_mu);
+ }
+
+ grpc_byte_buffer_destroy(buf);
+ gpr_event_set(&g_client_done[id], (void *)1);
+}
+
+/* Request a new server call. We tag them with a ref-count that starts at two,
+ and decrements after each of: a read completes and a write completes.
+ When it drops to zero, we write status */
+static void request_server_call() {
+ gpr_refcount *rc = gpr_malloc(sizeof(gpr_refcount));
+ gpr_ref_init(rc, 2);
+ grpc_server_request_call(g_fixture.server, rc);
+}
+
+static void maybe_end_server_call(grpc_call *call, gpr_refcount *rc) {
+ grpc_status ok_status = {GRPC_STATUS_OK, NULL};
+ if (gpr_unref(rc)) {
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_write_status(call, ok_status, NULL));
+ gpr_free(rc);
+ }
+}
+
+static void server_thread(void *p) {
+ int id = (gpr_intptr)p;
+ grpc_event *ev;
+ gpr_slice slice = gpr_slice_malloc(100);
+ grpc_byte_buffer *buf;
+ char *estr;
+ memset(GPR_SLICE_START_PTR(slice), id, GPR_SLICE_LENGTH(slice));
+
+ request_server_call();
+
+ buf = grpc_byte_buffer_create(&slice, 1);
+ gpr_slice_unref(slice);
+
+ for (;;) {
+ ev = grpc_completion_queue_next(g_fixture.server_cq, n_seconds_time(1));
+ if (ev) {
+ switch (ev->type) {
+ default:
+ estr = grpc_event_string(ev);
+ gpr_log(GPR_ERROR, "unexpected event: %s", estr);
+ gpr_free(estr);
+ break;
+ case GRPC_SERVER_RPC_NEW:
+ if (ev->call) {
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_accept(ev->call,
+ g_fixture.server_cq,
+ ev->tag, 0));
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_read(ev->call, ev->tag));
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_write(ev->call, buf, ev->tag, 0));
+ } else {
+ gpr_free(ev->tag);
+ }
+ break;
+ case GRPC_READ:
+ if (ev->data.read) {
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_read(ev->call, ev->tag));
+ } else {
+ maybe_end_server_call(ev->call, ev->tag);
+ }
+ break;
+ case GRPC_WRITE_ACCEPTED:
+ maybe_end_server_call(ev->call, ev->tag);
+ break;
+ case GRPC_FINISH_ACCEPTED:
+ break;
+ case GRPC_FINISHED:
+ grpc_call_destroy(ev->call);
+ request_server_call();
+ break;
+ }
+ grpc_event_finish(ev);
+ }
+ gpr_mu_lock(&g_mu);
+ if (g_active_requests == 0) {
+ gpr_mu_unlock(&g_mu);
+ break;
+ }
+ gpr_mu_unlock(&g_mu);
+ }
+
+ grpc_byte_buffer_destroy(buf);
+ gpr_event_set(&g_server_done[id], (void *)1);
+}
+
+static void run_test(grpc_end2end_test_config config, int requests_in_flight) {
+ int i;
+ gpr_thd_id thd_id;
+
+ gpr_log(GPR_INFO, "thread_test/%s @ %d requests", config.name,
+ requests_in_flight);
+
+ /* setup client, server */
+ g_fixture = config.create_fixture(NULL, NULL);
+ config.init_client(&g_fixture, NULL);
+ config.init_server(&g_fixture, NULL);
+
+ /* schedule end time */
+ g_test_end_time = n_seconds_time(5);
+
+ g_active_requests = 0;
+ gpr_mu_init(&g_mu);
+
+ /* kick off threads */
+ for (i = 0; i < CLIENT_THREADS; i++) {
+ gpr_event_init(&g_client_done[i]);
+ gpr_thd_new(&thd_id, client_thread, (void *)(gpr_intptr)i, NULL);
+ }
+ for (i = 0; i < SERVER_THREADS; i++) {
+ gpr_event_init(&g_server_done[i]);
+ gpr_thd_new(&thd_id, server_thread, (void *)(gpr_intptr)i, NULL);
+ }
+
+ /* start requests */
+ gpr_mu_lock(&g_mu);
+ for (i = 0; i < requests_in_flight; i++) {
+ start_request();
+ }
+ gpr_mu_unlock(&g_mu);
+
+ /* await completion */
+ for (i = 0; i < CLIENT_THREADS; i++) {
+ gpr_event_wait(&g_client_done[i], gpr_inf_future);
+ }
+ for (i = 0; i < SERVER_THREADS; i++) {
+ gpr_event_wait(&g_server_done[i], gpr_inf_future);
+ }
+
+ /* shutdown the things */
+ grpc_server_shutdown(g_fixture.server);
+ grpc_server_destroy(g_fixture.server);
+ grpc_channel_destroy(g_fixture.client);
+
+ grpc_completion_queue_shutdown(g_fixture.server_cq);
+ drain_cq(0, g_fixture.server_cq);
+ grpc_completion_queue_destroy(g_fixture.server_cq);
+ grpc_completion_queue_shutdown(g_fixture.client_cq);
+ drain_cq(1, g_fixture.client_cq);
+ grpc_completion_queue_destroy(g_fixture.client_cq);
+
+ config.tear_down_data(&g_fixture);
+
+ gpr_mu_destroy(&g_mu);
+}
+
+void grpc_end2end_tests(grpc_end2end_test_config config) {
+ run_test(config, 1000);
+}
diff --git a/test/core/end2end/tests/writes_done_hangs_with_pending_read.c b/test/core/end2end/tests/writes_done_hangs_with_pending_read.c
new file mode 100644
index 0000000000..555d7b92c0
--- /dev/null
+++ b/test/core/end2end/tests/writes_done_hangs_with_pending_read.c
@@ -0,0 +1,198 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include "test/core/end2end/end2end_tests.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <grpc/byte_buffer.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/time.h>
+#include <grpc/support/useful.h>
+#include "test/core/end2end/cq_verifier.h"
+
+enum { TIMEOUT = 200000 };
+
+static void *tag(gpr_intptr t) { return (void *)t; }
+
+static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config,
+ const char *test_name,
+ grpc_channel_args *client_args,
+ grpc_channel_args *server_args) {
+ grpc_end2end_test_fixture f;
+ gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+ f = config.create_fixture(client_args, server_args);
+ config.init_client(&f, client_args);
+ config.init_server(&f, server_args);
+ return f;
+}
+
+static gpr_timespec n_seconds_time(int n) {
+ return gpr_time_add(gpr_now(), gpr_time_from_micros(GPR_US_PER_SEC * n));
+}
+
+static gpr_timespec five_seconds_time() { return n_seconds_time(5); }
+
+static void drain_cq(grpc_completion_queue *cq) {
+ grpc_event *ev;
+ grpc_completion_type type;
+ do {
+ ev = grpc_completion_queue_next(cq, five_seconds_time());
+ GPR_ASSERT(ev);
+ type = ev->type;
+ grpc_event_finish(ev);
+ } while (type != GRPC_QUEUE_SHUTDOWN);
+}
+
+static void shutdown_server(grpc_end2end_test_fixture *f) {
+ if (!f->server) return;
+ grpc_server_shutdown(f->server);
+ grpc_server_destroy(f->server);
+ f->server = NULL;
+}
+
+static void shutdown_client(grpc_end2end_test_fixture *f) {
+ if (!f->client) return;
+ grpc_channel_destroy(f->client);
+ f->client = NULL;
+}
+
+static void end_test(grpc_end2end_test_fixture *f) {
+ shutdown_server(f);
+ shutdown_client(f);
+
+ grpc_completion_queue_shutdown(f->server_cq);
+ drain_cq(f->server_cq);
+ grpc_completion_queue_destroy(f->server_cq);
+ grpc_completion_queue_shutdown(f->client_cq);
+ drain_cq(f->client_cq);
+ grpc_completion_queue_destroy(f->client_cq);
+}
+
+/* test the case when there is a pending message at the client side,
+ writes_done should not return a status without a start_read.
+ Note: this test will last for 3s. Do not run in a loop. */
+static void test_writes_done_hangs_with_pending_read(
+ grpc_end2end_test_config config) {
+ grpc_call *c;
+ grpc_call *s;
+ grpc_status send_status = {GRPC_STATUS_UNIMPLEMENTED, "xyz"};
+ gpr_slice request_payload_slice = gpr_slice_from_copied_string("hello world");
+ gpr_slice response_payload_slice = gpr_slice_from_copied_string("hello you");
+ grpc_byte_buffer *request_payload =
+ grpc_byte_buffer_create(&request_payload_slice, 1);
+ grpc_byte_buffer *response_payload =
+ grpc_byte_buffer_create(&response_payload_slice, 1);
+ gpr_timespec deadline = five_seconds_time();
+ grpc_end2end_test_fixture f = begin_test(config, __FUNCTION__, NULL, NULL);
+ cq_verifier *v_client = cq_verifier_create(f.client_cq);
+ cq_verifier *v_server = cq_verifier_create(f.server_cq);
+
+ /* byte buffer holds the slice, we can unref it already */
+ gpr_slice_unref(request_payload_slice);
+ gpr_slice_unref(response_payload_slice);
+
+ c = grpc_channel_create_call(f.client, "/foo", "test.google.com", deadline);
+ GPR_ASSERT(c);
+
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_invoke(c, f.client_cq, tag(1), tag(2), tag(3), 0));
+ cq_expect_invoke_accepted(v_client, tag(1), GRPC_OP_OK);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_write(c, request_payload, tag(4), 0));
+ /* destroy byte buffer early to ensure async code keeps track of its contents
+ correctly */
+ grpc_byte_buffer_destroy(request_payload);
+ cq_expect_write_accepted(v_client, tag(4), GRPC_OP_OK);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_server_request_call(f.server, tag(100)));
+ cq_expect_server_rpc_new(v_server, &s, tag(100), "/foo", "test.google.com",
+ deadline, NULL);
+ cq_verify(v_server);
+
+ grpc_call_accept(s, f.server_cq, tag(102), 0);
+ cq_expect_client_metadata_read(v_client, tag(2), NULL);
+ cq_verify(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_read(s, tag(5)));
+ cq_expect_read(v_server, tag(5), gpr_slice_from_copied_string("hello world"));
+ cq_verify(v_server);
+
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_write(s, response_payload, tag(6), 0));
+ /* destroy byte buffer early to ensure async code keeps track of its contents
+ correctly */
+ grpc_byte_buffer_destroy(response_payload);
+ cq_expect_write_accepted(v_server, tag(6), GRPC_OP_OK);
+ cq_verify(v_server);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_writes_done(c, tag(6)));
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_write_status(s, send_status, tag(7)));
+
+ cq_expect_finish_accepted(v_client, tag(6), GRPC_OP_OK);
+ cq_verify(v_client);
+
+ /* does not return status because there is a pending message to be read */
+ cq_verify_empty(v_client);
+
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_read(c, tag(8)));
+ cq_expect_read(v_client, tag(8), gpr_slice_from_copied_string("hello you"));
+ cq_verify(v_client);
+
+ cq_expect_finished_with_status(v_client, tag(3), send_status, NULL);
+ cq_verify(v_client);
+
+ cq_expect_finish_accepted(v_server, tag(7), GRPC_OP_OK);
+ cq_expect_finished(v_server, tag(102), NULL);
+ cq_verify(v_server);
+
+ grpc_call_destroy(c);
+ grpc_call_destroy(s);
+
+ end_test(&f);
+ config.tear_down_data(&f);
+
+ cq_verifier_destroy(v_client);
+ cq_verifier_destroy(v_server);
+}
+
+void grpc_end2end_tests(grpc_end2end_test_config config) {
+ test_writes_done_hangs_with_pending_read(config);
+}
diff --git a/test/core/endpoint/endpoint_tests.c b/test/core/endpoint/endpoint_tests.c
new file mode 100644
index 0000000000..9cc0eaa959
--- /dev/null
+++ b/test/core/endpoint/endpoint_tests.c
@@ -0,0 +1,433 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include "test/core/endpoint/endpoint_tests.h"
+
+#include <sys/types.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/slice.h>
+#include <grpc/support/log.h>
+#include <grpc/support/time.h>
+
+/*
+ General test notes:
+
+ All tests which write data into an endpoint write i%256 into byte i, which
+ is verified by readers.
+
+ In general there are a few interesting things to vary which may lead to
+ exercising different codepaths in an implementation:
+ 1. Total amount of data written to the endpoint
+ 2. Size of slice allocations
+ 3. Amount of data we read from or write to the endpoint at once
+
+ The tests here tend to parameterize these where applicable.
+
+*/
+
+ssize_t count_and_unref_slices(gpr_slice *slices, size_t nslices,
+ int *current_data) {
+ ssize_t num_bytes = 0;
+ int i;
+ int j;
+ unsigned char *buf;
+ for (i = 0; i < nslices; ++i) {
+ buf = GPR_SLICE_START_PTR(slices[i]);
+ for (j = 0; j < GPR_SLICE_LENGTH(slices[i]); ++j) {
+ GPR_ASSERT(buf[j] == *current_data);
+ *current_data = (*current_data + 1) % 256;
+ }
+ num_bytes += GPR_SLICE_LENGTH(slices[i]);
+ gpr_slice_unref(slices[i]);
+ }
+ return num_bytes;
+}
+
+static grpc_endpoint_test_fixture begin_test(grpc_endpoint_test_config config,
+ const char *test_name,
+ ssize_t slice_size) {
+ gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
+ return config.create_fixture(slice_size);
+}
+
+static void end_test(grpc_endpoint_test_config config) { config.clean_up(); }
+
+static gpr_slice *allocate_blocks(ssize_t num_bytes, ssize_t slice_size,
+ size_t *num_blocks, int *current_data) {
+ ssize_t nslices = num_bytes / slice_size + (num_bytes % slice_size ? 1 : 0);
+ gpr_slice *slices = malloc(sizeof(gpr_slice) * nslices);
+ ssize_t num_bytes_left = num_bytes;
+ int i;
+ int j;
+ unsigned char *buf;
+ *num_blocks = nslices;
+
+ for (i = 0; i < nslices; ++i) {
+ slices[i] = gpr_slice_malloc(slice_size > num_bytes_left ? num_bytes_left
+ : slice_size);
+ num_bytes_left -= GPR_SLICE_LENGTH(slices[i]);
+ buf = GPR_SLICE_START_PTR(slices[i]);
+ for (j = 0; j < GPR_SLICE_LENGTH(slices[i]); ++j) {
+ buf[j] = *current_data;
+ *current_data = (*current_data + 1) % 256;
+ }
+ }
+ GPR_ASSERT(num_bytes_left == 0);
+ return slices;
+}
+
+struct read_and_write_test_state {
+ grpc_endpoint *read_ep;
+ grpc_endpoint *write_ep;
+ gpr_mu mu;
+ gpr_cv cv;
+ ssize_t target_bytes;
+ ssize_t bytes_read;
+ ssize_t current_write_size;
+ ssize_t bytes_written;
+ int current_read_data;
+ int current_write_data;
+ int read_done;
+ int write_done;
+};
+
+static void read_and_write_test_read_handler(void *data, gpr_slice *slices,
+ size_t nslices,
+ grpc_endpoint_cb_status error) {
+ struct read_and_write_test_state *state = data;
+ GPR_ASSERT(error != GRPC_ENDPOINT_CB_ERROR);
+ if (error == GRPC_ENDPOINT_CB_SHUTDOWN) {
+ gpr_log(GPR_INFO, "Read handler shutdown");
+ gpr_mu_lock(&state->mu);
+ state->read_done = 1;
+ gpr_cv_signal(&state->cv);
+ gpr_mu_unlock(&state->mu);
+ return;
+ }
+
+ state->bytes_read +=
+ count_and_unref_slices(slices, nslices, &state->current_read_data);
+ if (state->bytes_read == state->target_bytes) {
+ gpr_log(GPR_INFO, "Read handler done");
+ gpr_mu_lock(&state->mu);
+ state->read_done = 1;
+ gpr_cv_signal(&state->cv);
+ gpr_mu_unlock(&state->mu);
+ } else {
+ grpc_endpoint_notify_on_read(
+ state->read_ep, read_and_write_test_read_handler, data, gpr_inf_future);
+ }
+}
+
+static void read_and_write_test_write_handler(void *data,
+ grpc_endpoint_cb_status error) {
+ struct read_and_write_test_state *state = data;
+ gpr_slice *slices = NULL;
+ size_t nslices;
+ grpc_endpoint_write_status write_status;
+
+ GPR_ASSERT(error != GRPC_ENDPOINT_CB_ERROR);
+
+ if (error == GRPC_ENDPOINT_CB_SHUTDOWN) {
+ gpr_log(GPR_INFO, "Write handler shutdown");
+ gpr_mu_lock(&state->mu);
+ state->write_done = 1;
+ gpr_cv_signal(&state->cv);
+ gpr_mu_unlock(&state->mu);
+ return;
+ }
+
+ for (;;) {
+ /* Need to do inline writes until they don't succeed synchronously or we
+ finish writing */
+ state->bytes_written += state->current_write_size;
+ if (state->target_bytes - state->bytes_written <
+ state->current_write_size) {
+ state->current_write_size = state->target_bytes - state->bytes_written;
+ }
+ if (state->current_write_size == 0) {
+ break;
+ }
+
+ slices = allocate_blocks(state->current_write_size, 8192, &nslices,
+ &state->current_write_data);
+ write_status = grpc_endpoint_write(state->write_ep, slices, nslices,
+ read_and_write_test_write_handler, state,
+ gpr_inf_future);
+ GPR_ASSERT(write_status != GRPC_ENDPOINT_WRITE_ERROR);
+ free(slices);
+ if (write_status == GRPC_ENDPOINT_WRITE_PENDING) {
+ return;
+ }
+ }
+ GPR_ASSERT(state->bytes_written == state->target_bytes);
+
+ gpr_log(GPR_INFO, "Write handler done");
+ gpr_mu_lock(&state->mu);
+ state->write_done = 1;
+ gpr_cv_signal(&state->cv);
+ gpr_mu_unlock(&state->mu);
+}
+
+/* Do both reading and writing using the grpc_endpoint API.
+
+ This also includes a test of the shutdown behavior.
+ */
+static void read_and_write_test(grpc_endpoint_test_config config,
+ ssize_t num_bytes, ssize_t write_size,
+ ssize_t slice_size, int shutdown) {
+ struct read_and_write_test_state state;
+ gpr_timespec rel_deadline = {20, 0};
+ gpr_timespec deadline = gpr_time_add(gpr_now(), rel_deadline);
+ grpc_endpoint_test_fixture f = begin_test(config, __FUNCTION__, slice_size);
+
+ if (shutdown) {
+ gpr_log(GPR_INFO, "Start read and write shutdown test");
+ } else {
+ gpr_log(GPR_INFO, "Start read and write test with %d bytes, slice size %d",
+ num_bytes, slice_size);
+ }
+
+ gpr_mu_init(&state.mu);
+ gpr_cv_init(&state.cv);
+
+ state.read_ep = f.client_ep;
+ state.write_ep = f.server_ep;
+ state.target_bytes = num_bytes;
+ state.bytes_read = 0;
+ state.current_write_size = write_size;
+ state.bytes_written = 0;
+ state.read_done = 0;
+ state.write_done = 0;
+ state.current_read_data = 0;
+ state.current_write_data = 0;
+
+ /* Get started by pretending an initial write completed */
+ state.bytes_written -= state.current_write_size;
+ read_and_write_test_write_handler(&state, GRPC_ENDPOINT_CB_OK);
+
+ grpc_endpoint_notify_on_read(state.read_ep, read_and_write_test_read_handler,
+ &state, gpr_inf_future);
+
+ if (shutdown) {
+ grpc_endpoint_shutdown(state.read_ep);
+ grpc_endpoint_shutdown(state.write_ep);
+ }
+
+ gpr_mu_lock(&state.mu);
+ while (!state.read_done || !state.write_done) {
+ GPR_ASSERT(gpr_cv_wait(&state.cv, &state.mu, deadline) == 0);
+ }
+ gpr_mu_unlock(&state.mu);
+
+ grpc_endpoint_destroy(state.read_ep);
+ grpc_endpoint_destroy(state.write_ep);
+ gpr_mu_destroy(&state.mu);
+ gpr_cv_destroy(&state.cv);
+ end_test(config);
+}
+
+struct timeout_test_state {
+ gpr_event io_done;
+};
+
+static void read_timeout_test_read_handler(void *data, gpr_slice *slices,
+ size_t nslices,
+ grpc_endpoint_cb_status error) {
+ struct timeout_test_state *state = data;
+ GPR_ASSERT(error == GRPC_ENDPOINT_CB_TIMED_OUT);
+ gpr_event_set(&state->io_done, (void *)1);
+}
+
+static void read_timeout_test(grpc_endpoint_test_config config,
+ ssize_t slice_size) {
+ gpr_timespec timeout = gpr_time_from_micros(10000);
+ gpr_timespec read_deadline = gpr_time_add(gpr_now(), timeout);
+ gpr_timespec test_deadline =
+ gpr_time_add(gpr_now(), gpr_time_from_micros(2000000));
+ struct timeout_test_state state;
+ grpc_endpoint_test_fixture f = begin_test(config, __FUNCTION__, slice_size);
+
+ gpr_event_init(&state.io_done);
+
+ grpc_endpoint_notify_on_read(f.client_ep, read_timeout_test_read_handler,
+ &state, read_deadline);
+ GPR_ASSERT(gpr_event_wait(&state.io_done, test_deadline));
+ grpc_endpoint_destroy(f.client_ep);
+ grpc_endpoint_destroy(f.server_ep);
+ end_test(config);
+}
+
+static void write_timeout_test_write_handler(void *data,
+ grpc_endpoint_cb_status error) {
+ struct timeout_test_state *state = data;
+ GPR_ASSERT(error == GRPC_ENDPOINT_CB_TIMED_OUT);
+ gpr_event_set(&state->io_done, (void *)1);
+}
+
+static void write_timeout_test(grpc_endpoint_test_config config,
+ ssize_t slice_size) {
+ gpr_timespec timeout = gpr_time_from_micros(10000);
+ gpr_timespec write_deadline = gpr_time_add(gpr_now(), timeout);
+ gpr_timespec test_deadline =
+ gpr_time_add(gpr_now(), gpr_time_from_micros(2000000));
+ struct timeout_test_state state;
+ int current_data = 1;
+ gpr_slice *slices;
+ size_t nblocks;
+ size_t size;
+ grpc_endpoint_test_fixture f = begin_test(config, __FUNCTION__, slice_size);
+
+ gpr_event_init(&state.io_done);
+
+ /* TODO(klempner): Factor this out with the equivalent code in tcp_test.c */
+ for (size = 1;; size *= 2) {
+ slices = allocate_blocks(size, 1, &nblocks, &current_data);
+ switch (grpc_endpoint_write(f.client_ep, slices, nblocks,
+ write_timeout_test_write_handler, &state,
+ write_deadline)) {
+ case GRPC_ENDPOINT_WRITE_DONE:
+ break;
+ case GRPC_ENDPOINT_WRITE_ERROR:
+ gpr_log(GPR_ERROR, "error writing");
+ abort();
+ case GRPC_ENDPOINT_WRITE_PENDING:
+ GPR_ASSERT(gpr_event_wait(&state.io_done, test_deadline));
+ gpr_free(slices);
+ goto exit;
+ }
+ gpr_free(slices);
+ }
+exit:
+ grpc_endpoint_destroy(f.client_ep);
+ grpc_endpoint_destroy(f.server_ep);
+ end_test(config);
+}
+
+typedef struct {
+ gpr_event ev;
+ grpc_endpoint *ep;
+} shutdown_during_write_test_state;
+
+static void shutdown_during_write_test_read_handler(
+ void *user_data, gpr_slice *slices, size_t nslices,
+ grpc_endpoint_cb_status error) {
+ size_t i;
+ shutdown_during_write_test_state *st = user_data;
+
+ for (i = 0; i < nslices; i++) {
+ gpr_slice_unref(slices[i]);
+ }
+
+ if (error != GRPC_ENDPOINT_CB_OK) {
+ grpc_endpoint_destroy(st->ep);
+ gpr_event_set(&st->ev, (void *)(gpr_intptr)error);
+ } else {
+ grpc_endpoint_notify_on_read(st->ep,
+ shutdown_during_write_test_read_handler,
+ user_data, gpr_inf_future);
+ }
+}
+
+static void shutdown_during_write_test_write_handler(
+ void *user_data, grpc_endpoint_cb_status error) {
+ shutdown_during_write_test_state *st = user_data;
+ gpr_log(GPR_INFO, "shutdown_during_write_test_write_handler: error = %d",
+ error);
+ grpc_endpoint_destroy(st->ep);
+ gpr_event_set(&st->ev, (void *)(gpr_intptr)error);
+}
+
+static void shutdown_during_write_test(grpc_endpoint_test_config config,
+ ssize_t slice_size) {
+ /* test that shutdown with a pending write creates no leaks */
+ gpr_timespec deadline;
+ size_t size;
+ size_t nblocks;
+ int current_data = 1;
+ shutdown_during_write_test_state read_st;
+ shutdown_during_write_test_state write_st;
+ gpr_slice *slices;
+ grpc_endpoint_test_fixture f = begin_test(config, __FUNCTION__, slice_size);
+
+ gpr_log(GPR_INFO, "testing shutdown during a write");
+
+ read_st.ep = f.client_ep;
+ write_st.ep = f.server_ep;
+ gpr_event_init(&read_st.ev);
+ gpr_event_init(&write_st.ev);
+
+#if 0
+ read_st.ep = grpc_tcp_create(sv[1], &em);
+ write_st.ep = grpc_tcp_create(sv[0], &em);
+#endif
+
+ grpc_endpoint_notify_on_read(read_st.ep,
+ shutdown_during_write_test_read_handler,
+ &read_st, gpr_inf_future);
+ for (size = 1;; size *= 2) {
+ slices = allocate_blocks(size, 1, &nblocks, &current_data);
+ switch (grpc_endpoint_write(write_st.ep, slices, nblocks,
+ shutdown_during_write_test_write_handler,
+ &write_st, gpr_inf_future)) {
+ case GRPC_ENDPOINT_WRITE_DONE:
+ break;
+ case GRPC_ENDPOINT_WRITE_ERROR:
+ gpr_log(GPR_ERROR, "error writing");
+ abort();
+ case GRPC_ENDPOINT_WRITE_PENDING:
+ grpc_endpoint_shutdown(write_st.ep);
+ deadline =
+ gpr_time_add(gpr_now(), gpr_time_from_micros(10 * GPR_US_PER_SEC));
+ GPR_ASSERT(gpr_event_wait(&write_st.ev, deadline));
+ GPR_ASSERT(gpr_event_wait(&read_st.ev, deadline));
+ gpr_free(slices);
+ end_test(config);
+ return;
+ }
+ gpr_free(slices);
+ }
+
+ gpr_log(GPR_ERROR, "should never reach here");
+ abort();
+}
+
+void grpc_endpoint_tests(grpc_endpoint_test_config config) {
+ read_and_write_test(config, 10000000, 100000, 8192, 0);
+ read_and_write_test(config, 1000000, 100000, 1, 0);
+ read_and_write_test(config, 100000000, 100000, 1, 1);
+ read_timeout_test(config, 1000);
+ write_timeout_test(config, 1000);
+ shutdown_during_write_test(config, 1000);
+}
diff --git a/test/core/endpoint/endpoint_tests.h b/test/core/endpoint/endpoint_tests.h
new file mode 100644
index 0000000000..79ee759b45
--- /dev/null
+++ b/test/core/endpoint/endpoint_tests.h
@@ -0,0 +1,57 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#ifndef __GRPC_TEST_ENDPOINT_ENDPOINT_TESTS_H__
+#define __GRPC_TEST_ENDPOINT_ENDPOINT_TESTS_H__
+
+#include <sys/types.h>
+
+#include "src/core/endpoint/endpoint.h"
+
+typedef struct grpc_endpoint_test_config grpc_endpoint_test_config;
+typedef struct grpc_endpoint_test_fixture grpc_endpoint_test_fixture;
+
+struct grpc_endpoint_test_fixture {
+ grpc_endpoint *client_ep;
+ grpc_endpoint *server_ep;
+};
+
+struct grpc_endpoint_test_config {
+ const char *name;
+ grpc_endpoint_test_fixture (*create_fixture)(ssize_t slice_size);
+ void (*clean_up)();
+};
+
+void grpc_endpoint_tests(grpc_endpoint_test_config config);
+
+#endif /* __GRPC_TEST_ENDPOINT_ENDPOINT_TESTS_H__ */
diff --git a/test/core/endpoint/resolve_address_test.c b/test/core/endpoint/resolve_address_test.c
new file mode 100644
index 0000000000..1e208d3699
--- /dev/null
+++ b/test/core/endpoint/resolve_address_test.c
@@ -0,0 +1,135 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include "src/core/endpoint/resolve_address.h"
+#include <grpc/support/log.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/time.h>
+#include "test/core/util/test_config.h"
+
+static gpr_timespec test_deadline() {
+ return gpr_time_add(gpr_now(), gpr_time_from_micros(100000000));
+}
+
+static void must_succeed(void* evp, grpc_resolved_addresses* p) {
+ GPR_ASSERT(p);
+ GPR_ASSERT(p->naddrs >= 1);
+ grpc_resolved_addresses_destroy(p);
+ gpr_event_set(evp, (void*)1);
+}
+
+static void must_fail(void* evp, grpc_resolved_addresses* p) {
+ GPR_ASSERT(!p);
+ gpr_event_set(evp, (void*)1);
+}
+
+static void test_localhost() {
+ gpr_event ev;
+ gpr_event_init(&ev);
+ grpc_resolve_address("localhost:1", NULL, must_succeed, &ev);
+ GPR_ASSERT(gpr_event_wait(&ev, test_deadline()));
+}
+
+static void test_default_port() {
+ gpr_event ev;
+ gpr_event_init(&ev);
+ grpc_resolve_address("localhost", "1", must_succeed, &ev);
+ GPR_ASSERT(gpr_event_wait(&ev, test_deadline()));
+}
+
+static void test_missing_default_port() {
+ gpr_event ev;
+ gpr_event_init(&ev);
+ grpc_resolve_address("localhost", NULL, must_fail, &ev);
+ GPR_ASSERT(gpr_event_wait(&ev, test_deadline()));
+}
+
+static void test_ipv6_with_port() {
+ gpr_event ev;
+ gpr_event_init(&ev);
+ grpc_resolve_address("[2001:db8::1]:1", NULL, must_succeed, &ev);
+ GPR_ASSERT(gpr_event_wait(&ev, test_deadline()));
+}
+
+static void test_ipv6_without_port() {
+ const char* const kCases[] = {
+ "2001:db8::1", "2001:db8::1.2.3.4", "[2001:db8::1]",
+ };
+ int i;
+ for (i = 0; i < sizeof(kCases) / sizeof(*kCases); i++) {
+ gpr_event ev;
+ gpr_event_init(&ev);
+ grpc_resolve_address(kCases[i], "80", must_succeed, &ev);
+ GPR_ASSERT(gpr_event_wait(&ev, test_deadline()));
+ }
+}
+
+static void test_invalid_ip_addresses() {
+ const char* const kCases[] = {
+ "293.283.1238.3:1", "[2001:db8::11111]:1",
+ };
+ int i;
+ for (i = 0; i < sizeof(kCases) / sizeof(*kCases); i++) {
+ gpr_event ev;
+ gpr_event_init(&ev);
+ grpc_resolve_address(kCases[i], NULL, must_fail, &ev);
+ GPR_ASSERT(gpr_event_wait(&ev, test_deadline()));
+ }
+}
+
+static void test_unparseable_hostports() {
+ const char* const kCases[] = {
+ "[", "[::1", "[::1]bad", "[1.2.3.4]", "[localhost]", "[localhost]:1",
+ };
+ int i;
+ for (i = 0; i < sizeof(kCases) / sizeof(*kCases); i++) {
+ gpr_event ev;
+ gpr_event_init(&ev);
+ grpc_resolve_address(kCases[i], "1", must_fail, &ev);
+ GPR_ASSERT(gpr_event_wait(&ev, test_deadline()));
+ }
+}
+
+int main(int argc, char** argv) {
+ grpc_test_init(argc, argv);
+
+ test_localhost();
+ test_default_port();
+ test_missing_default_port();
+ test_ipv6_with_port();
+ test_ipv6_without_port();
+ test_invalid_ip_addresses();
+ test_unparseable_hostports();
+
+ return 0;
+}
diff --git a/test/core/endpoint/secure_endpoint_test.c b/test/core/endpoint/secure_endpoint_test.c
new file mode 100644
index 0000000000..9fc2511763
--- /dev/null
+++ b/test/core/endpoint/secure_endpoint_test.c
@@ -0,0 +1,222 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include "endpoint_tests.h"
+
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include "src/core/endpoint/secure_endpoint.h"
+#include "src/core/endpoint/tcp.h"
+#include "src/core/eventmanager/em.h"
+#include "src/core/tsi/fake_transport_security.h"
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include "test/core/util/test_config.h"
+
+grpc_em g_em;
+
+static void create_sockets(int sv[2]) {
+ int flags;
+ GPR_ASSERT(socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == 0);
+ flags = fcntl(sv[0], F_GETFL, 0);
+ GPR_ASSERT(fcntl(sv[0], F_SETFL, flags | O_NONBLOCK) == 0);
+ flags = fcntl(sv[1], F_GETFL, 0);
+ GPR_ASSERT(fcntl(sv[1], F_SETFL, flags | O_NONBLOCK) == 0);
+}
+
+static grpc_endpoint_test_fixture secure_endpoint_create_fixture_tcp_socketpair(
+ ssize_t slice_size, gpr_slice *leftover_slices, size_t leftover_nslices) {
+ int sv[2];
+ tsi_frame_protector *fake_read_protector = tsi_create_fake_protector(NULL);
+ tsi_frame_protector *fake_write_protector = tsi_create_fake_protector(NULL);
+ grpc_endpoint_test_fixture f;
+ grpc_endpoint *tcp_read;
+ grpc_endpoint *tcp_write;
+
+ create_sockets(sv);
+ grpc_em_init(&g_em);
+ tcp_read = grpc_tcp_create_dbg(sv[0], &g_em, slice_size);
+ tcp_write = grpc_tcp_create(sv[1], &g_em);
+
+ if (leftover_nslices == 0) {
+ f.client_ep =
+ grpc_secure_endpoint_create(fake_read_protector, tcp_read, NULL, 0);
+ } else {
+ int i;
+ tsi_result result;
+ gpr_uint32 still_pending_size;
+ size_t total_buffer_size = 8192;
+ size_t buffer_size = total_buffer_size;
+ gpr_uint8 *encrypted_buffer = gpr_malloc(buffer_size);
+ gpr_uint8 *cur = encrypted_buffer;
+ gpr_slice encrypted_leftover;
+ for (i = 0; i < leftover_nslices; i++) {
+ gpr_slice plain = leftover_slices[i];
+ gpr_uint8 *message_bytes = GPR_SLICE_START_PTR(plain);
+ size_t message_size = GPR_SLICE_LENGTH(plain);
+ while (message_size > 0) {
+ gpr_uint32 protected_buffer_size_to_send = buffer_size;
+ gpr_uint32 processed_message_size = message_size;
+ result = tsi_frame_protector_protect(
+ fake_write_protector, message_bytes, &processed_message_size, cur,
+ &protected_buffer_size_to_send);
+ GPR_ASSERT(result == TSI_OK);
+ message_bytes += processed_message_size;
+ message_size -= processed_message_size;
+ cur += protected_buffer_size_to_send;
+ buffer_size -= protected_buffer_size_to_send;
+
+ GPR_ASSERT(buffer_size >= 0);
+ }
+ gpr_slice_unref(plain);
+ }
+ do {
+ gpr_uint32 protected_buffer_size_to_send = buffer_size;
+ result = tsi_frame_protector_protect_flush(fake_write_protector, cur,
+ &protected_buffer_size_to_send,
+ &still_pending_size);
+ GPR_ASSERT(result == TSI_OK);
+ cur += protected_buffer_size_to_send;
+ buffer_size -= protected_buffer_size_to_send;
+ GPR_ASSERT(buffer_size >= 0);
+ } while (still_pending_size > 0);
+ encrypted_leftover = gpr_slice_from_copied_buffer(
+ (const char *)encrypted_buffer, total_buffer_size - buffer_size);
+ f.client_ep = grpc_secure_endpoint_create(fake_read_protector, tcp_read,
+ &encrypted_leftover, 1);
+ gpr_slice_unref(encrypted_leftover);
+ gpr_free(encrypted_buffer);
+ }
+
+ f.server_ep =
+ grpc_secure_endpoint_create(fake_write_protector, tcp_write, NULL, 0);
+ return f;
+}
+
+static grpc_endpoint_test_fixture
+secure_endpoint_create_fixture_tcp_socketpair_noleftover(ssize_t slice_size) {
+ return secure_endpoint_create_fixture_tcp_socketpair(slice_size, NULL, 0);
+}
+
+static grpc_endpoint_test_fixture
+secure_endpoint_create_fixture_tcp_socketpair_leftover(ssize_t slice_size) {
+ gpr_slice s =
+ gpr_slice_from_copied_string("hello world 12345678900987654321");
+ grpc_endpoint_test_fixture f;
+
+ f = secure_endpoint_create_fixture_tcp_socketpair(slice_size, &s, 1);
+ return f;
+}
+
+static void clean_up() { grpc_em_destroy(&g_em); }
+
+static grpc_endpoint_test_config configs[] = {
+ {"secure_ep/tcp_socketpair",
+ secure_endpoint_create_fixture_tcp_socketpair_noleftover, clean_up},
+ {"secure_ep/tcp_socketpair_leftover",
+ secure_endpoint_create_fixture_tcp_socketpair_leftover, clean_up},
+};
+
+static void verify_leftover(void *user_data, gpr_slice *slices, size_t nslices,
+ grpc_endpoint_cb_status error) {
+ gpr_slice s =
+ gpr_slice_from_copied_string("hello world 12345678900987654321");
+
+ GPR_ASSERT(error == GRPC_ENDPOINT_CB_OK);
+ GPR_ASSERT(nslices == 1);
+
+ GPR_ASSERT(0 == gpr_slice_cmp(s, slices[0]));
+ gpr_slice_unref(slices[0]);
+ gpr_slice_unref(s);
+ *(int *)user_data = 1;
+}
+
+static void test_leftover(grpc_endpoint_test_config config,
+ ssize_t slice_size) {
+ grpc_endpoint_test_fixture f = config.create_fixture(slice_size);
+ int verified = 0;
+ gpr_log(GPR_INFO, "Start test left over");
+
+ grpc_endpoint_notify_on_read(f.client_ep, verify_leftover, &verified,
+ gpr_inf_future);
+ GPR_ASSERT(verified == 1);
+
+ grpc_endpoint_shutdown(f.client_ep);
+ grpc_endpoint_shutdown(f.server_ep);
+ grpc_endpoint_destroy(f.client_ep);
+ grpc_endpoint_destroy(f.server_ep);
+ clean_up();
+}
+
+static void destroy_early(void *user_data, gpr_slice *slices, size_t nslices,
+ grpc_endpoint_cb_status error) {
+ grpc_endpoint_test_fixture *f = user_data;
+ gpr_slice s =
+ gpr_slice_from_copied_string("hello world 12345678900987654321");
+
+ GPR_ASSERT(error == GRPC_ENDPOINT_CB_OK);
+ GPR_ASSERT(nslices == 1);
+
+ grpc_endpoint_shutdown(f->client_ep);
+ grpc_endpoint_destroy(f->client_ep);
+
+ GPR_ASSERT(0 == gpr_slice_cmp(s, slices[0]));
+ gpr_slice_unref(slices[0]);
+ gpr_slice_unref(s);
+}
+
+/* test which destroys the ep before finishing reading */
+static void test_destroy_ep_early(grpc_endpoint_test_config config,
+ ssize_t slice_size) {
+ grpc_endpoint_test_fixture f = config.create_fixture(slice_size);
+ gpr_log(GPR_INFO, "Start test destroy early");
+
+ grpc_endpoint_notify_on_read(f.client_ep, destroy_early, &f, gpr_inf_future);
+
+ grpc_endpoint_shutdown(f.server_ep);
+ grpc_endpoint_destroy(f.server_ep);
+ clean_up();
+}
+
+int main(int argc, char **argv) {
+ grpc_test_init(argc, argv);
+
+ grpc_endpoint_tests(configs[0]);
+ test_leftover(configs[1], 1);
+ test_destroy_ep_early(configs[1], 1);
+
+ return 0;
+}
diff --git a/test/core/endpoint/tcp_client_test.c b/test/core/endpoint/tcp_client_test.c
new file mode 100644
index 0000000000..10138e6af5
--- /dev/null
+++ b/test/core/endpoint/tcp_client_test.c
@@ -0,0 +1,177 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include "src/core/endpoint/tcp_client.h"
+
+#include <errno.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include "src/core/eventmanager/em.h"
+#include <grpc/support/log.h>
+#include <grpc/support/time.h>
+
+static grpc_em em;
+
+static gpr_timespec test_deadline() {
+ return gpr_time_add(gpr_now(), gpr_time_from_micros(1000000));
+}
+
+static void must_succeed(void *arg, grpc_endpoint *tcp) {
+ GPR_ASSERT(tcp);
+ grpc_endpoint_shutdown(tcp);
+ grpc_endpoint_destroy(tcp);
+ gpr_event_set(arg, (void *)1);
+}
+
+static void must_fail(void *arg, grpc_endpoint *tcp) {
+ GPR_ASSERT(!tcp);
+ gpr_event_set(arg, (void *)1);
+}
+
+void test_succeeds() {
+ struct sockaddr_in addr;
+ socklen_t addr_len = sizeof(addr);
+ int svr_fd;
+ int r;
+ gpr_event ev;
+
+ gpr_event_init(&ev);
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+
+ /* create a dummy server */
+ svr_fd = socket(AF_INET, SOCK_STREAM, 0);
+ GPR_ASSERT(svr_fd >= 0);
+ GPR_ASSERT(0 == bind(svr_fd, (struct sockaddr *)&addr, addr_len));
+ GPR_ASSERT(0 == listen(svr_fd, 1));
+
+ /* connect to it */
+ GPR_ASSERT(getsockname(svr_fd, (struct sockaddr *)&addr, &addr_len) == 0);
+ grpc_tcp_client_connect(must_succeed, &ev, &em, (struct sockaddr *)&addr,
+ addr_len, gpr_inf_future);
+
+ /* await the connection */
+ do {
+ addr_len = sizeof(addr);
+ r = accept(svr_fd, (struct sockaddr *)&addr, &addr_len);
+ } while (r == -1 && errno == EINTR);
+ GPR_ASSERT(r >= 0);
+ close(r);
+
+ /* wait for the connection callback to finish */
+ GPR_ASSERT(gpr_event_wait(&ev, test_deadline()));
+}
+
+void test_fails() {
+ struct sockaddr_in addr;
+ socklen_t addr_len = sizeof(addr);
+ gpr_event ev;
+
+ gpr_event_init(&ev);
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+
+ /* connect to a broken address */
+ grpc_tcp_client_connect(must_fail, &ev, &em, (struct sockaddr *)&addr,
+ addr_len, gpr_inf_future);
+
+ /* wait for the connection callback to finish */
+ GPR_ASSERT(gpr_event_wait(&ev, test_deadline()));
+}
+
+void test_times_out() {
+ struct sockaddr_in addr;
+ socklen_t addr_len = sizeof(addr);
+ int svr_fd;
+#define NUM_CLIENT_CONNECTS 10
+ int client_fd[NUM_CLIENT_CONNECTS];
+ int i;
+ int r;
+ gpr_event ev;
+ gpr_timespec connect_deadline;
+
+ gpr_event_init(&ev);
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+
+ /* create a dummy server */
+ svr_fd = socket(AF_INET, SOCK_STREAM, 0);
+ GPR_ASSERT(svr_fd >= 0);
+ GPR_ASSERT(0 == bind(svr_fd, (struct sockaddr *)&addr, addr_len));
+ GPR_ASSERT(0 == listen(svr_fd, 1));
+ /* Get its address */
+ GPR_ASSERT(getsockname(svr_fd, (struct sockaddr *)&addr, &addr_len) == 0);
+
+ /* tie up the listen buffer, which is somewhat arbitrarily sized. */
+ for (i = 0; i < NUM_CLIENT_CONNECTS; ++i) {
+ client_fd[i] = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
+ do {
+ r = connect(client_fd[i], (struct sockaddr *)&addr, addr_len);
+ } while (r == -1 && errno == EINTR);
+ GPR_ASSERT(r < 0);
+ GPR_ASSERT(errno == EWOULDBLOCK || errno == EINPROGRESS);
+ }
+
+ /* connect to dummy server address */
+
+ connect_deadline = gpr_time_add(gpr_now(), gpr_time_from_micros(1000000));
+
+ grpc_tcp_client_connect(must_fail, &ev, &em, (struct sockaddr *)&addr,
+ addr_len, connect_deadline);
+ /* Make sure the event doesn't trigger early */
+ GPR_ASSERT(!gpr_event_wait(
+ &ev, gpr_time_add(gpr_now(), gpr_time_from_micros(500000))));
+ /* Now wait until it should have triggered */
+ sleep(1);
+
+ /* wait for the connection callback to finish */
+ GPR_ASSERT(gpr_event_wait(&ev, test_deadline()));
+ close(svr_fd);
+ for (i = 0; i < NUM_CLIENT_CONNECTS; ++i) {
+ close(client_fd[i]);
+ }
+}
+
+int main(void) {
+ grpc_em_init(&em);
+ test_succeeds();
+ test_fails();
+ test_times_out();
+ return 0;
+}
diff --git a/test/core/endpoint/tcp_server_test.c b/test/core/endpoint/tcp_server_test.c
new file mode 100644
index 0000000000..10e2c36df1
--- /dev/null
+++ b/test/core/endpoint/tcp_server_test.c
@@ -0,0 +1,168 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include "src/core/endpoint/tcp_server.h"
+#include "src/core/eventmanager/em.h"
+#include <grpc/support/log.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/time.h>
+#include "test/core/util/test_config.h"
+
+#include <netinet/in.h>
+#include <string.h>
+#include <unistd.h>
+
+#define LOG_TEST() gpr_log(GPR_INFO, "%s", __FUNCTION__)
+
+static grpc_em em;
+
+static gpr_mu mu;
+static gpr_cv cv;
+static int nconnects = 0;
+
+static void on_connect(void *arg, grpc_endpoint *tcp) {
+ grpc_endpoint_shutdown(tcp);
+ grpc_endpoint_destroy(tcp);
+
+ gpr_mu_lock(&mu);
+ nconnects++;
+ gpr_cv_broadcast(&cv);
+ gpr_mu_unlock(&mu);
+}
+
+static void test_no_op() {
+ grpc_tcp_server *s = grpc_tcp_server_create(&em);
+ grpc_tcp_server_destroy(s);
+}
+
+static void test_no_op_with_start() {
+ grpc_tcp_server *s = grpc_tcp_server_create(&em);
+ LOG_TEST();
+ grpc_tcp_server_start(s, on_connect, NULL);
+ grpc_tcp_server_destroy(s);
+}
+
+static void test_no_op_with_port() {
+ struct sockaddr_in addr;
+ grpc_tcp_server *s = grpc_tcp_server_create(&em);
+ LOG_TEST();
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ GPR_ASSERT(
+ grpc_tcp_server_add_port(s, (struct sockaddr *)&addr, sizeof(addr)) >= 0);
+
+ grpc_tcp_server_destroy(s);
+}
+
+static void test_no_op_with_port_and_start() {
+ struct sockaddr_in addr;
+ grpc_tcp_server *s = grpc_tcp_server_create(&em);
+ LOG_TEST();
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ GPR_ASSERT(
+ grpc_tcp_server_add_port(s, (struct sockaddr *)&addr, sizeof(addr)) >= 0);
+
+ grpc_tcp_server_start(s, on_connect, NULL);
+
+ grpc_tcp_server_destroy(s);
+}
+
+static void test_connect(int n) {
+ struct sockaddr_in addr;
+ socklen_t addr_len = sizeof(addr);
+ int svrfd, clifd;
+ grpc_tcp_server *s = grpc_tcp_server_create(&em);
+ int nconnects_before;
+ gpr_timespec deadline;
+ int i;
+ LOG_TEST();
+ gpr_log(GPR_INFO, "clients=%d", n);
+
+ gpr_mu_lock(&mu);
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ svrfd = grpc_tcp_server_add_port(s, (struct sockaddr *)&addr, addr_len);
+ GPR_ASSERT(svrfd >= 0);
+
+ GPR_ASSERT(getsockname(svrfd, (struct sockaddr *)&addr, &addr_len) == 0);
+ GPR_ASSERT(addr_len == sizeof(addr));
+
+ grpc_tcp_server_start(s, on_connect, NULL);
+
+ for (i = 0; i < n; i++) {
+ deadline = gpr_time_add(gpr_now(), gpr_time_from_micros(10000000));
+
+ nconnects_before = nconnects;
+ clifd = socket(AF_INET, SOCK_STREAM, 0);
+ GPR_ASSERT(clifd >= 0);
+ GPR_ASSERT(connect(clifd, (struct sockaddr *)&addr, addr_len) == 0);
+
+ while (nconnects == nconnects_before) {
+ GPR_ASSERT(gpr_cv_wait(&cv, &mu, deadline) == 0);
+ }
+
+ GPR_ASSERT(nconnects == nconnects_before + 1);
+ close(clifd);
+
+ if (i != n - 1) {
+ sleep(1);
+ }
+ }
+
+ gpr_mu_unlock(&mu);
+
+ grpc_tcp_server_destroy(s);
+}
+
+int main(int argc, char **argv) {
+ grpc_test_init(argc, argv);
+ grpc_em_init(&em);
+ gpr_mu_init(&mu);
+ gpr_cv_init(&cv);
+
+ test_no_op();
+ test_no_op_with_start();
+ test_no_op_with_port();
+ test_no_op_with_port_and_start();
+ test_connect(1);
+ test_connect(10);
+
+ grpc_em_destroy(&em);
+ gpr_mu_destroy(&mu);
+ gpr_cv_destroy(&cv);
+ return 0;
+}
diff --git a/test/core/endpoint/tcp_test.c b/test/core/endpoint/tcp_test.c
new file mode 100644
index 0000000000..7dbc2783e9
--- /dev/null
+++ b/test/core/endpoint/tcp_test.c
@@ -0,0 +1,517 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include "src/core/endpoint/tcp.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include "src/core/eventmanager/em.h"
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/time.h>
+#include <grpc/support/useful.h>
+#include "test/core/util/test_config.h"
+#include "test/core/endpoint/endpoint_tests.h"
+
+/*
+ General test notes:
+
+ All tests which write data into a socket write i%256 into byte i, which is
+ verified by readers.
+
+ In general there are a few interesting things to vary which may lead to
+ exercising different codepaths in an implementation:
+ 1. Total amount of data written to the socket
+ 2. Size of slice allocations
+ 3. Amount of data we read from or write to the socket at once
+
+ The tests here tend to parameterize these where applicable.
+
+ */
+
+grpc_em g_em;
+
+static void create_sockets(int sv[2]) {
+ int flags;
+ GPR_ASSERT(socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == 0);
+ flags = fcntl(sv[0], F_GETFL, 0);
+ GPR_ASSERT(fcntl(sv[0], F_SETFL, flags | O_NONBLOCK) == 0);
+ flags = fcntl(sv[1], F_GETFL, 0);
+ GPR_ASSERT(fcntl(sv[1], F_SETFL, flags | O_NONBLOCK) == 0);
+}
+
+static ssize_t fill_socket(int fd) {
+ ssize_t write_bytes;
+ ssize_t total_bytes = 0;
+ int i;
+ unsigned char buf[256];
+ for (i = 0; i < 256; ++i) {
+ buf[i] = i;
+ }
+ do {
+ write_bytes = write(fd, buf, 256);
+ if (write_bytes > 0) {
+ total_bytes += write_bytes;
+ }
+ } while (write_bytes >= 0 || errno == EINTR);
+ GPR_ASSERT(errno == EAGAIN);
+ return total_bytes;
+}
+
+static size_t fill_socket_partial(int fd, size_t bytes) {
+ ssize_t write_bytes;
+ size_t total_bytes = 0;
+ unsigned char *buf = malloc(bytes);
+ int i;
+ for (i = 0; i < bytes; ++i) {
+ buf[i] = i % 256;
+ }
+
+ do {
+ write_bytes = write(fd, buf, bytes - total_bytes);
+ if (write_bytes > 0) {
+ total_bytes += write_bytes;
+ }
+ } while ((write_bytes >= 0 || errno == EINTR) && bytes > total_bytes);
+
+ gpr_free(buf);
+
+ return total_bytes;
+}
+
+struct read_socket_state {
+ grpc_endpoint *ep;
+ gpr_mu mu;
+ gpr_cv cv;
+ size_t read_bytes;
+ ssize_t target_read_bytes;
+};
+
+static ssize_t count_and_unref_slices(gpr_slice *slices, size_t nslices,
+ int *current_data) {
+ ssize_t num_bytes = 0;
+ int i;
+ int j;
+ unsigned char *buf;
+ for (i = 0; i < nslices; ++i) {
+ buf = GPR_SLICE_START_PTR(slices[i]);
+ for (j = 0; j < GPR_SLICE_LENGTH(slices[i]); ++j) {
+ GPR_ASSERT(buf[j] == *current_data);
+ *current_data = (*current_data + 1) % 256;
+ }
+ num_bytes += GPR_SLICE_LENGTH(slices[i]);
+ gpr_slice_unref(slices[i]);
+ }
+ return num_bytes;
+}
+
+static void read_cb(void *user_data, gpr_slice *slices, size_t nslices,
+ grpc_endpoint_cb_status error) {
+ struct read_socket_state *state = (struct read_socket_state *)user_data;
+ ssize_t read_bytes;
+ int current_data = 0;
+
+ GPR_ASSERT(error == GRPC_ENDPOINT_CB_OK);
+
+ gpr_mu_lock(&state->mu);
+ read_bytes = count_and_unref_slices(slices, nslices, &current_data);
+ state->read_bytes += read_bytes;
+ gpr_log(GPR_INFO, "Read %d bytes of %d", read_bytes,
+ state->target_read_bytes);
+ if (state->read_bytes >= state->target_read_bytes) {
+ gpr_cv_signal(&state->cv);
+ } else {
+ grpc_endpoint_notify_on_read(state->ep, read_cb, state, gpr_inf_future);
+ }
+ gpr_mu_unlock(&state->mu);
+}
+
+/* Write to a socket, then read from it using the grpc_tcp API. */
+static void read_test(ssize_t num_bytes, ssize_t slice_size) {
+ int sv[2];
+ grpc_em em;
+ grpc_endpoint *ep;
+ struct read_socket_state state;
+ ssize_t written_bytes;
+ gpr_timespec rel_deadline = {20, 0};
+ gpr_timespec deadline = gpr_time_add(gpr_now(), rel_deadline);
+
+ gpr_log(GPR_INFO, "Read test of size %d, slice size %d", num_bytes,
+ slice_size);
+
+ create_sockets(sv);
+ grpc_em_init(&em);
+
+ ep = grpc_tcp_create_dbg(sv[1], &em, slice_size);
+ written_bytes = fill_socket_partial(sv[0], num_bytes);
+ gpr_log(GPR_INFO, "Wrote %d bytes", written_bytes);
+
+ gpr_mu_init(&state.mu);
+ gpr_cv_init(&state.cv);
+ state.ep = ep;
+ state.read_bytes = 0;
+ state.target_read_bytes = written_bytes;
+
+ grpc_endpoint_notify_on_read(ep, read_cb, &state, gpr_inf_future);
+
+ gpr_mu_lock(&state.mu);
+ for (;;) {
+ GPR_ASSERT(gpr_cv_wait(&state.cv, &state.mu, deadline) == 0);
+ if (state.read_bytes >= state.target_read_bytes) {
+ break;
+ }
+ }
+ GPR_ASSERT(state.read_bytes == state.target_read_bytes);
+ gpr_mu_unlock(&state.mu);
+
+ grpc_endpoint_destroy(ep);
+
+ grpc_em_destroy(&em);
+ gpr_mu_destroy(&state.mu);
+ gpr_cv_destroy(&state.cv);
+}
+
+/* Write to a socket until it fills up, then read from it using the grpc_tcp
+ API. */
+static void large_read_test(ssize_t slice_size) {
+ int sv[2];
+ grpc_em em;
+ grpc_endpoint *ep;
+ struct read_socket_state state;
+ ssize_t written_bytes;
+ gpr_timespec rel_deadline = {20, 0};
+ gpr_timespec deadline = gpr_time_add(gpr_now(), rel_deadline);
+
+ gpr_log(GPR_INFO, "Start large read test, slice size %d", slice_size);
+
+ create_sockets(sv);
+ grpc_em_init(&em);
+
+ ep = grpc_tcp_create_dbg(sv[1], &em, slice_size);
+ written_bytes = fill_socket(sv[0]);
+ gpr_log(GPR_INFO, "Wrote %d bytes", written_bytes);
+
+ gpr_mu_init(&state.mu);
+ gpr_cv_init(&state.cv);
+ state.ep = ep;
+ state.read_bytes = 0;
+ state.target_read_bytes = written_bytes;
+
+ grpc_endpoint_notify_on_read(ep, read_cb, &state, gpr_inf_future);
+
+ gpr_mu_lock(&state.mu);
+ for (;;) {
+ GPR_ASSERT(gpr_cv_wait(&state.cv, &state.mu, deadline) == 0);
+ if (state.read_bytes >= state.target_read_bytes) {
+ break;
+ }
+ }
+ GPR_ASSERT(state.read_bytes == state.target_read_bytes);
+ gpr_mu_unlock(&state.mu);
+
+ grpc_endpoint_destroy(ep);
+
+ grpc_em_destroy(&em);
+ gpr_mu_destroy(&state.mu);
+ gpr_cv_destroy(&state.cv);
+}
+
+struct write_socket_state {
+ grpc_endpoint *ep;
+ gpr_mu mu;
+ gpr_cv cv;
+ int write_done;
+};
+
+static gpr_slice *allocate_blocks(ssize_t num_bytes, ssize_t slice_size,
+ size_t *num_blocks, int *current_data) {
+ ssize_t nslices = num_bytes / slice_size + (num_bytes % slice_size ? 1 : 0);
+ gpr_slice *slices = gpr_malloc(sizeof(gpr_slice) * nslices);
+ ssize_t num_bytes_left = num_bytes;
+ int i;
+ int j;
+ unsigned char *buf;
+ *num_blocks = nslices;
+
+ for (i = 0; i < nslices; ++i) {
+ slices[i] = gpr_slice_malloc(slice_size > num_bytes_left ? num_bytes_left
+ : slice_size);
+ num_bytes_left -= GPR_SLICE_LENGTH(slices[i]);
+ buf = GPR_SLICE_START_PTR(slices[i]);
+ for (j = 0; j < GPR_SLICE_LENGTH(slices[i]); ++j) {
+ buf[j] = *current_data;
+ *current_data = (*current_data + 1) % 256;
+ }
+ }
+ GPR_ASSERT(num_bytes_left == 0);
+ return slices;
+}
+
+static void write_done(void *user_data /* write_socket_state */,
+ grpc_endpoint_cb_status error) {
+ struct write_socket_state *state = (struct write_socket_state *)user_data;
+ gpr_log(GPR_INFO, "Write done callback called");
+ gpr_mu_lock(&state->mu);
+ gpr_log(GPR_INFO, "Signalling write done");
+ state->write_done = 1;
+ gpr_cv_signal(&state->cv);
+ gpr_mu_unlock(&state->mu);
+}
+
+void drain_socket_blocking(int fd, size_t num_bytes, size_t read_size) {
+ unsigned char *buf = malloc(read_size);
+ ssize_t bytes_read;
+ size_t bytes_left = num_bytes;
+ int flags;
+ int current = 0;
+ int i;
+
+ flags = fcntl(fd, F_GETFL, 0);
+ GPR_ASSERT(fcntl(fd, F_SETFL, flags & ~O_NONBLOCK) == 0);
+
+ for (;;) {
+ do {
+ bytes_read =
+ read(fd, buf, bytes_left > read_size ? read_size : bytes_left);
+ } while (bytes_read < 0 && errno == EINTR);
+ GPR_ASSERT(bytes_read >= 0);
+ for (i = 0; i < bytes_read; ++i) {
+ GPR_ASSERT(buf[i] == current);
+ current = (current + 1) % 256;
+ }
+ bytes_left -= bytes_read;
+ if (bytes_left == 0) break;
+ }
+ flags = fcntl(fd, F_GETFL, 0);
+ GPR_ASSERT(fcntl(fd, F_SETFL, flags | O_NONBLOCK) == 0);
+
+ gpr_free(buf);
+}
+
+static ssize_t drain_socket(int fd) {
+ ssize_t read_bytes;
+ ssize_t total_bytes = 0;
+ unsigned char buf[256];
+ int current = 0;
+ int i;
+ do {
+ read_bytes = read(fd, buf, 256);
+ if (read_bytes > 0) {
+ total_bytes += read_bytes;
+ for (i = 0; i < read_bytes; ++i) {
+ GPR_ASSERT(buf[i] == current);
+ current = (current + 1) % 256;
+ }
+ }
+ } while (read_bytes >= 0 || errno == EINTR);
+ GPR_ASSERT(errno == EAGAIN);
+ return total_bytes;
+}
+
+/* Write to a socket using the grpc_tcp API, then drain it directly.
+ Note that if the write does not complete immediately we need to drain the
+ socket in parallel with the read. */
+static void write_test(ssize_t num_bytes, ssize_t slice_size) {
+ int sv[2];
+ grpc_em em;
+ grpc_endpoint *ep;
+ struct write_socket_state state;
+ ssize_t read_bytes;
+ size_t num_blocks;
+ gpr_slice *slices;
+ int current_data = 0;
+ gpr_timespec rel_deadline = {20, 0};
+ gpr_timespec deadline = gpr_time_add(gpr_now(), rel_deadline);
+
+ gpr_log(GPR_INFO, "Start write test with %d bytes, slice size %d", num_bytes,
+ slice_size);
+
+ create_sockets(sv);
+ grpc_em_init(&em);
+
+ ep = grpc_tcp_create(sv[1], &em);
+
+ gpr_mu_init(&state.mu);
+ gpr_cv_init(&state.cv);
+ state.ep = ep;
+ state.write_done = 0;
+
+ slices = allocate_blocks(num_bytes, slice_size, &num_blocks, &current_data);
+
+ if (grpc_endpoint_write(ep, slices, num_blocks, write_done, &state,
+ gpr_inf_future) == GRPC_ENDPOINT_WRITE_DONE) {
+ /* Write completed immediately */
+ read_bytes = drain_socket(sv[0]);
+ GPR_ASSERT(read_bytes == num_bytes);
+ } else {
+ drain_socket_blocking(sv[0], num_bytes, num_bytes);
+ gpr_mu_lock(&state.mu);
+ for (;;) {
+ if (state.write_done) {
+ break;
+ }
+ GPR_ASSERT(gpr_cv_wait(&state.cv, &state.mu, deadline) == 0);
+ }
+ gpr_mu_unlock(&state.mu);
+ }
+
+ grpc_endpoint_destroy(ep);
+ grpc_em_destroy(&em);
+ gpr_mu_destroy(&state.mu);
+ gpr_cv_destroy(&state.cv);
+ gpr_free(slices);
+}
+
+static void read_done_for_write_error(void *ud, gpr_slice *slices,
+ size_t nslices,
+ grpc_endpoint_cb_status error) {
+ GPR_ASSERT(error != GRPC_ENDPOINT_CB_OK);
+ GPR_ASSERT(nslices == 0);
+}
+
+/* Write to a socket using the grpc_tcp API, then drain it directly.
+ Note that if the write does not complete immediately we need to drain the
+ socket in parallel with the read. */
+static void write_error_test(ssize_t num_bytes, ssize_t slice_size) {
+ int sv[2];
+ grpc_em em;
+ grpc_endpoint *ep;
+ struct write_socket_state state;
+ size_t num_blocks;
+ gpr_slice *slices;
+ int current_data = 0;
+ gpr_timespec rel_deadline = {20, 0};
+ gpr_timespec deadline = gpr_time_add(gpr_now(), rel_deadline);
+
+ gpr_log(GPR_INFO, "Start write error test with %d bytes, slice size %d",
+ num_bytes, slice_size);
+
+ create_sockets(sv);
+ grpc_em_init(&em);
+
+ ep = grpc_tcp_create(sv[1], &em);
+ close(sv[0]);
+
+ gpr_mu_init(&state.mu);
+ gpr_cv_init(&state.cv);
+ state.ep = ep;
+ state.write_done = 0;
+
+ slices = allocate_blocks(num_bytes, slice_size, &num_blocks, &current_data);
+
+ switch (grpc_endpoint_write(ep, slices, num_blocks, write_done, &state,
+ gpr_inf_future)) {
+ case GRPC_ENDPOINT_WRITE_DONE:
+ case GRPC_ENDPOINT_WRITE_ERROR:
+ /* Write completed immediately */
+ break;
+ case GRPC_ENDPOINT_WRITE_PENDING:
+ grpc_endpoint_notify_on_read(ep, read_done_for_write_error, NULL,
+ gpr_inf_future);
+ gpr_mu_lock(&state.mu);
+ for (;;) {
+ if (state.write_done) {
+ break;
+ }
+ GPR_ASSERT(gpr_cv_wait(&state.cv, &state.mu, deadline) == 0);
+ }
+ gpr_mu_unlock(&state.mu);
+ break;
+ }
+
+ grpc_endpoint_destroy(ep);
+ grpc_em_destroy(&em);
+ gpr_mu_destroy(&state.mu);
+ gpr_cv_destroy(&state.cv);
+ free(slices);
+}
+
+void run_tests() {
+ int i = 0;
+
+ read_test(100, 8192);
+ read_test(10000, 8192);
+ read_test(10000, 137);
+ read_test(10000, 1);
+ large_read_test(8192);
+ large_read_test(1);
+
+ write_test(100, 8192);
+ write_test(100, 1);
+ write_test(100000, 8192);
+ write_test(100000, 1);
+ write_test(100000, 137);
+
+ for (i = 1; i < 1000; i = GPR_MAX(i + 1, i * 5 / 4)) {
+ write_error_test(40320, i);
+ }
+
+ for (i = 1; i < 1000; i = GPR_MAX(i + 1, i * 5 / 4)) {
+ write_test(40320, i);
+ }
+}
+
+static void clean_up() { grpc_em_destroy(&g_em); }
+
+static grpc_endpoint_test_fixture create_fixture_tcp_socketpair(
+ ssize_t slice_size) {
+ int sv[2];
+ grpc_endpoint_test_fixture f;
+
+ create_sockets(sv);
+ grpc_em_init(&g_em);
+ f.client_ep = grpc_tcp_create_dbg(sv[0], &g_em, slice_size);
+ f.server_ep = grpc_tcp_create(sv[1], &g_em);
+
+ return f;
+}
+
+static grpc_endpoint_test_config configs[] = {
+ {"tcp/tcp_socketpair", create_fixture_tcp_socketpair, clean_up},
+};
+
+int main(int argc, char **argv) {
+ grpc_test_init(argc, argv);
+ /* disable SIGPIPE */
+ signal(SIGPIPE, SIG_IGN);
+ run_tests();
+ grpc_endpoint_tests(configs[0]);
+
+ return 0;
+}
diff --git a/test/core/eventmanager/em_pipe_test.c b/test/core/eventmanager/em_pipe_test.c
new file mode 100644
index 0000000000..5411142c89
--- /dev/null
+++ b/test/core/eventmanager/em_pipe_test.c
@@ -0,0 +1,200 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+/* Test grpc_em_fd with pipe. The test creates a pipe with non-blocking mode,
+ sends a stream of bytes through the pipe, and verifies that all bytes are
+ received. */
+#include "src/core/eventmanager/em.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <grpc/support/log.h>
+#include "test/core/util/test_config.h"
+
+/* Operation for fcntl() to set pipe buffer size. */
+#ifndef F_SETPIPE_SZ
+#define F_SETPIPE_SZ (1024 + 7)
+#endif
+
+#define TOTAL_WRITE 3 /* total number of times that the write buffer is full. \
+ */
+#define BUF_SIZE 1024
+char read_buf[BUF_SIZE];
+char write_buf[BUF_SIZE];
+
+typedef struct {
+ int fd[2];
+ grpc_em em;
+ grpc_em_fd read_em_fd;
+ grpc_em_fd write_em_fd;
+ int num_write; /* number of times that the write buffer is full*/
+ ssize_t bytes_written_total; /* total number of bytes written to the pipe */
+ ssize_t bytes_read_total; /* total number of bytes read from the pipe */
+ pthread_mutex_t mu; /* protect cv and done */
+ pthread_cond_t cv; /* signaled when read finished */
+ int done; /* set to 1 when read finished */
+} async_pipe;
+
+void write_shutdown_cb(void *arg, /*async_pipe*/
+ enum grpc_em_cb_status status) {
+ async_pipe *ap = arg;
+ close(ap->fd[1]);
+ grpc_em_fd_destroy(&ap->write_em_fd);
+}
+
+void write_cb(void *arg, /*async_pipe*/ enum grpc_em_cb_status status) {
+ async_pipe *ap = arg;
+ ssize_t bytes_written = 0;
+
+ if (status == GRPC_CALLBACK_CANCELLED) {
+ write_shutdown_cb(arg, GRPC_CALLBACK_SUCCESS);
+ return;
+ }
+
+ do {
+ bytes_written = write(ap->fd[1], write_buf, BUF_SIZE);
+ if (bytes_written > 0) ap->bytes_written_total += bytes_written;
+ } while (bytes_written > 0);
+
+ if (errno == EAGAIN) {
+ if (ap->num_write < TOTAL_WRITE) {
+ ap->num_write++;
+ grpc_em_fd_notify_on_write(&ap->write_em_fd, write_cb, ap,
+ gpr_inf_future);
+ } else {
+ /* Note that this could just shut down directly; doing a trip through the
+ shutdown path serves only a demonstration of the API. */
+ grpc_em_fd_shutdown(&ap->write_em_fd);
+ grpc_em_fd_notify_on_write(&ap->write_em_fd, write_cb, ap,
+ gpr_inf_future);
+ }
+ } else {
+ GPR_ASSERT(0 && strcat("unknown errno: ", strerror(errno)));
+ }
+}
+
+void read_shutdown_cb(void *arg, /*async_pipe*/ enum grpc_em_cb_status status) {
+ async_pipe *ap = arg;
+ close(ap->fd[0]);
+ grpc_em_fd_destroy(&ap->read_em_fd);
+ pthread_mutex_lock(&ap->mu);
+ if (ap->done == 0) {
+ ap->done = 1;
+ pthread_cond_signal(&ap->cv);
+ }
+ pthread_mutex_unlock(&ap->mu);
+}
+
+void read_cb(void *arg, /*async_pipe*/ enum grpc_em_cb_status status) {
+ async_pipe *ap = arg;
+ ssize_t bytes_read = 0;
+
+ if (status == GRPC_CALLBACK_CANCELLED) {
+ read_shutdown_cb(arg, GRPC_CALLBACK_SUCCESS);
+ return;
+ }
+
+ do {
+ bytes_read = read(ap->fd[0], read_buf, BUF_SIZE);
+ if (bytes_read > 0) ap->bytes_read_total += bytes_read;
+ } while (bytes_read > 0);
+
+ if (bytes_read == 0) {
+ /* Note that this could just shut down directly; doing a trip through the
+ shutdown path serves only a demonstration of the API. */
+ grpc_em_fd_shutdown(&ap->read_em_fd);
+ grpc_em_fd_notify_on_read(&ap->read_em_fd, read_cb, ap, gpr_inf_future);
+ } else if (bytes_read == -1) {
+ if (errno == EAGAIN) {
+ grpc_em_fd_notify_on_read(&ap->read_em_fd, read_cb, ap, gpr_inf_future);
+ } else {
+ GPR_ASSERT(0 && strcat("unknown errno: ", strerror(errno)));
+ }
+ }
+}
+
+void dummy_cb(void *arg, /*async_pipe*/ enum grpc_em_cb_status status) {}
+
+void async_pipe_init(async_pipe *ap) {
+ int i;
+
+ ap->num_write = 0;
+ ap->bytes_written_total = 0;
+ ap->bytes_read_total = 0;
+
+ pthread_mutex_init(&ap->mu, NULL);
+ pthread_cond_init(&ap->cv, NULL);
+ ap->done = 0;
+
+ GPR_ASSERT(0 == pipe(ap->fd));
+ for (i = 0; i < 2; i++) {
+ int flags = fcntl(ap->fd[i], F_GETFL, 0);
+ GPR_ASSERT(fcntl(ap->fd[i], F_SETFL, flags | O_NONBLOCK) == 0);
+ GPR_ASSERT(fcntl(ap->fd[i], F_SETPIPE_SZ, 4096) == 4096);
+ }
+
+ grpc_em_init(&ap->em);
+ grpc_em_fd_init(&ap->read_em_fd, &ap->em, ap->fd[0]);
+ grpc_em_fd_init(&ap->write_em_fd, &ap->em, ap->fd[1]);
+}
+
+static void async_pipe_start(async_pipe *ap) {
+ grpc_em_fd_notify_on_read(&ap->read_em_fd, read_cb, ap, gpr_inf_future);
+ grpc_em_fd_notify_on_write(&ap->write_em_fd, write_cb, ap, gpr_inf_future);
+}
+
+static void async_pipe_wait_destroy(async_pipe *ap) {
+ pthread_mutex_lock(&ap->mu);
+ while (!ap->done) pthread_cond_wait(&ap->cv, &ap->mu);
+ pthread_mutex_unlock(&ap->mu);
+ pthread_mutex_destroy(&ap->mu);
+ pthread_cond_destroy(&ap->cv);
+
+ grpc_em_destroy(&ap->em);
+}
+
+int main(int argc, char **argv) {
+ async_pipe ap;
+ grpc_test_init(argc, argv);
+ async_pipe_init(&ap);
+ async_pipe_start(&ap);
+ async_pipe_wait_destroy(&ap);
+ GPR_ASSERT(ap.bytes_read_total == ap.bytes_written_total);
+ gpr_log(GPR_INFO, "read total bytes %d", ap.bytes_read_total);
+ return 0;
+}
diff --git a/test/core/eventmanager/em_test.c b/test/core/eventmanager/em_test.c
new file mode 100644
index 0000000000..2bcfe86c3b
--- /dev/null
+++ b/test/core/eventmanager/em_test.c
@@ -0,0 +1,725 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+/* Test gRPC event manager with a simple TCP upload server and client. */
+#include "src/core/eventmanager/em.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <unistd.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/time.h>
+#include "test/core/util/test_config.h"
+
+/* buffer size used to send and receive data.
+ 1024 is the minimal value to set TCP send and receive buffer. */
+#define BUF_SIZE 1024
+
+/* Create a test socket with the right properties for testing.
+ port is the TCP port to listen or connect to.
+ Return a socket FD and sockaddr_in. */
+static void create_test_socket(int port, int *socket_fd,
+ struct sockaddr_in *sin) {
+ int fd;
+ int one = 1;
+ int buf_size = BUF_SIZE;
+ int flags;
+
+ fd = socket(AF_INET, SOCK_STREAM, 0);
+ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
+ /* Reset the size of socket send buffer to the minimal value to facilitate
+ buffer filling up and triggering notify_on_write */
+ GPR_ASSERT(
+ setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &buf_size, sizeof(buf_size)) != -1);
+ GPR_ASSERT(
+ setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &buf_size, sizeof(buf_size)) != -1);
+ /* Make fd non-blocking */
+ flags = fcntl(fd, F_GETFL, 0);
+ GPR_ASSERT(fcntl(fd, F_SETFL, flags | O_NONBLOCK) == 0);
+ *socket_fd = fd;
+
+ /* Use local address for test */
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = 0;
+ sin->sin_port = htons(port);
+}
+
+/* Dummy gRPC callback */
+void no_op_cb(void *arg, enum grpc_em_cb_status status) {}
+
+/* =======An upload server to test notify_on_read===========
+ The server simply reads and counts a stream of bytes. */
+
+/* An upload server. */
+typedef struct {
+ grpc_em em; /* event manger used by the sever */
+ grpc_em_fd em_fd; /* listening fd */
+ ssize_t read_bytes_total; /* total number of received bytes */
+ gpr_mu mu; /* protect done and done_cv */
+ gpr_cv done_cv; /* signaled when a server finishes serving */
+ int done; /* set to 1 when a server finishes serving */
+} server;
+
+static void server_init(server *sv) {
+ GPR_ASSERT(grpc_em_init(&sv->em) == GRPC_EM_OK);
+ sv->read_bytes_total = 0;
+ gpr_mu_init(&sv->mu);
+ gpr_cv_init(&sv->done_cv);
+ sv->done = 0;
+}
+
+/* An upload session.
+ Created when a new upload request arrives in the server. */
+typedef struct {
+ server *sv; /* not owned by a single session */
+ grpc_em_fd em_fd; /* fd to read upload bytes */
+ char read_buf[BUF_SIZE]; /* buffer to store upload bytes */
+} session;
+
+/* Called when an upload session can be safely shutdown.
+ Close session FD and start to shutdown listen FD. */
+static void session_shutdown_cb(void *arg, /*session*/
+ enum grpc_em_cb_status status) {
+ session *se = arg;
+ server *sv = se->sv;
+ grpc_em_fd_destroy(&se->em_fd);
+ gpr_free(se);
+ /* Start to shutdown listen fd. */
+ grpc_em_fd_shutdown(&sv->em_fd);
+}
+
+/* Called when data become readable in a session. */
+static void session_read_cb(void *arg, /*session*/
+ enum grpc_em_cb_status status) {
+ session *se = arg;
+ int fd = grpc_em_fd_get(&se->em_fd);
+
+ ssize_t read_once = 0;
+ ssize_t read_total = 0;
+
+ if (status == GRPC_CALLBACK_CANCELLED) {
+ close(fd);
+ session_shutdown_cb(arg, GRPC_CALLBACK_SUCCESS);
+ return;
+ }
+
+ do {
+ read_once = read(fd, se->read_buf, BUF_SIZE);
+ if (read_once > 0) read_total += read_once;
+ } while (read_once > 0);
+ se->sv->read_bytes_total += read_total;
+
+ /* read() returns 0 to indicate the TCP connection was closed by the client.
+ read(fd, read_buf, 0) also returns 0 which should never be called as such.
+ It is possible to read nothing due to spurious edge event or data has
+ been drained, In such a case, read() returns -1 and set errno to EAGAIN. */
+ if (read_once == 0) {
+ grpc_em_fd_shutdown(&se->em_fd);
+ grpc_em_fd_notify_on_read(&se->em_fd, session_read_cb, se, gpr_inf_future);
+ } else if (read_once == -1) {
+ if (errno == EAGAIN) {
+ /* An edge triggered event is cached in the kernel until next poll.
+ In the current single thread implementation, session_read_cb is called
+ in the polling thread, such that polling only happens after this
+ callback, and will catch read edge event if data is available again
+ before notify_on_read.
+ TODO(chenw): in multi-threaded version, callback and polling can be
+ run in different threads. polling may catch a persist read edge event
+ before notify_on_read is called. */
+ GPR_ASSERT(grpc_em_fd_notify_on_read(&se->em_fd, session_read_cb, se,
+ gpr_inf_future) == GRPC_EM_OK);
+ } else {
+ gpr_log(GPR_ERROR, "Unhandled read error %s", strerror(errno));
+ GPR_ASSERT(0);
+ }
+ }
+}
+
+/* Called when the listen FD can be safely shutdown.
+ Close listen FD and signal that server can be shutdown. */
+static void listen_shutdown_cb(void *arg /*server*/,
+ enum grpc_em_cb_status status) {
+ server *sv = arg;
+
+ close(grpc_em_fd_get(&sv->em_fd));
+ grpc_em_fd_destroy(&sv->em_fd);
+
+ gpr_mu_lock(&sv->mu);
+ sv->done = 1;
+ gpr_cv_signal(&sv->done_cv);
+ gpr_mu_unlock(&sv->mu);
+}
+
+/* Called when a new TCP connection request arrives in the listening port. */
+static void listen_cb(void *arg, /*=sv_arg*/
+ enum grpc_em_cb_status status) {
+ server *sv = arg;
+ int fd;
+ int flags;
+ session *se;
+ struct sockaddr_storage ss;
+ socklen_t slen = sizeof(ss);
+ struct grpc_em_fd *listen_em_fd = &sv->em_fd;
+
+ if (status == GRPC_CALLBACK_CANCELLED) {
+ listen_shutdown_cb(arg, GRPC_CALLBACK_SUCCESS);
+ return;
+ }
+
+ fd = accept(grpc_em_fd_get(listen_em_fd), (struct sockaddr *)&ss, &slen);
+ GPR_ASSERT(fd >= 0);
+ GPR_ASSERT(fd < FD_SETSIZE);
+ flags = fcntl(fd, F_GETFL, 0);
+ fcntl(fd, F_SETFL, flags | O_NONBLOCK);
+ se = gpr_malloc(sizeof(*se));
+ se->sv = sv;
+ GPR_ASSERT(grpc_em_fd_init(&se->em_fd, &sv->em, fd) == GRPC_EM_OK);
+ GPR_ASSERT(grpc_em_fd_notify_on_read(&se->em_fd, session_read_cb, se,
+ gpr_inf_future) == GRPC_EM_OK);
+
+ GPR_ASSERT(grpc_em_fd_notify_on_read(listen_em_fd, listen_cb, sv,
+ gpr_inf_future) == GRPC_EM_OK);
+}
+
+/* Max number of connections pending to be accepted by listen(). */
+#define MAX_NUM_FD 1024
+
+/* Start a test server, return the TCP listening port bound to listen_fd.
+ listen_cb() is registered to be interested in reading from listen_fd.
+ When connection request arrives, listen_cb() is called to accept the
+ connection request. */
+static int server_start(server *sv) {
+ int port = 0;
+ int fd;
+ struct sockaddr_in sin;
+ socklen_t addr_len;
+
+ create_test_socket(port, &fd, &sin);
+ addr_len = sizeof(sin);
+ GPR_ASSERT(bind(fd, (struct sockaddr *)&sin, addr_len) == 0);
+ GPR_ASSERT(getsockname(fd, (struct sockaddr *)&sin, &addr_len) == GRPC_EM_OK);
+ port = ntohs(sin.sin_port);
+ GPR_ASSERT(listen(fd, MAX_NUM_FD) == 0);
+
+ GPR_ASSERT(grpc_em_fd_init(&sv->em_fd, &sv->em, fd) == GRPC_EM_OK);
+ /* Register to be interested in reading from listen_fd. */
+ GPR_ASSERT(grpc_em_fd_notify_on_read(&sv->em_fd, listen_cb, sv,
+ gpr_inf_future) == GRPC_EM_OK);
+
+ return port;
+}
+
+/* Wait and shutdown a sever. */
+static void server_wait_and_shutdown(server *sv) {
+ gpr_mu_lock(&sv->mu);
+ while (!sv->done) gpr_cv_wait(&sv->done_cv, &sv->mu, gpr_inf_future);
+ gpr_mu_unlock(&sv->mu);
+
+ gpr_mu_destroy(&sv->mu);
+ gpr_cv_destroy(&sv->done_cv);
+
+ GPR_ASSERT(grpc_em_destroy(&sv->em) == GRPC_EM_OK);
+}
+
+/* ===An upload client to test notify_on_write=== */
+
+/* Client write buffer size */
+#define CLIENT_WRITE_BUF_SIZE 10
+/* Total number of times that the client fills up the write buffer */
+#define CLIENT_TOTAL_WRITE_CNT 3
+
+/* An upload client. */
+typedef struct {
+ grpc_em em;
+ grpc_em_fd em_fd;
+ char write_buf[CLIENT_WRITE_BUF_SIZE];
+ ssize_t write_bytes_total;
+ /* Number of times that the client fills up the write buffer and calls
+ notify_on_write to schedule another write. */
+ int client_write_cnt;
+
+ gpr_mu mu; /* protect done and done_cv */
+ gpr_cv done_cv; /* signaled when a client finishes sending */
+ int done; /* set to 1 when a client finishes sending */
+} client;
+
+static void client_init(client *cl) {
+ GPR_ASSERT(grpc_em_init(&cl->em) == GRPC_EM_OK);
+ memset(cl->write_buf, 0, sizeof(cl->write_buf));
+ cl->write_bytes_total = 0;
+ cl->client_write_cnt = 0;
+ gpr_mu_init(&cl->mu);
+ gpr_cv_init(&cl->done_cv);
+ cl->done = 0;
+}
+
+/* Called when a client upload session is ready to shutdown. */
+static void client_session_shutdown_cb(void *arg /*client*/,
+ enum grpc_em_cb_status status) {
+ client *cl = arg;
+ grpc_em_fd_destroy(&cl->em_fd);
+ gpr_mu_lock(&cl->mu);
+ cl->done = 1;
+ gpr_cv_signal(&cl->done_cv);
+ gpr_mu_unlock(&cl->mu);
+}
+
+/* Write as much as possible, then register notify_on_write. */
+static void client_session_write(void *arg, /*client*/
+ enum grpc_em_cb_status status) {
+ client *cl = arg;
+ int fd = grpc_em_fd_get(&cl->em_fd);
+ ssize_t write_once = 0;
+
+ if (status == GRPC_CALLBACK_CANCELLED) {
+ client_session_shutdown_cb(arg, GRPC_CALLBACK_SUCCESS);
+ return;
+ }
+
+ do {
+ write_once = write(fd, cl->write_buf, CLIENT_WRITE_BUF_SIZE);
+ if (write_once > 0) cl->write_bytes_total += write_once;
+ } while (write_once > 0);
+
+ if (errno == EAGAIN) {
+ gpr_mu_lock(&cl->mu);
+ if (cl->client_write_cnt < CLIENT_TOTAL_WRITE_CNT) {
+ GPR_ASSERT(grpc_em_fd_notify_on_write(&cl->em_fd, client_session_write,
+ cl, gpr_inf_future) == GRPC_EM_OK);
+ cl->client_write_cnt++;
+ } else {
+ close(fd);
+ grpc_em_fd_shutdown(&cl->em_fd);
+ grpc_em_fd_notify_on_write(&cl->em_fd, client_session_write, cl,
+ gpr_inf_future);
+ }
+ gpr_mu_unlock(&cl->mu);
+ } else {
+ gpr_log(GPR_ERROR, "unknown errno %s", strerror(errno));
+ GPR_ASSERT(0);
+ }
+}
+
+/* Start a client to send a stream of bytes. */
+static void client_start(client *cl, int port) {
+ int fd;
+ struct sockaddr_in sin;
+ create_test_socket(port, &fd, &sin);
+ if (connect(fd, (struct sockaddr *)&sin, sizeof(sin)) == -1 &&
+ errno != EINPROGRESS) {
+ gpr_log(GPR_ERROR, "Failed to connect to the server");
+ GPR_ASSERT(0);
+ }
+
+ GPR_ASSERT(grpc_em_fd_init(&cl->em_fd, &cl->em, fd) == GRPC_EM_OK);
+
+ client_session_write(cl, GRPC_CALLBACK_SUCCESS);
+}
+
+/* Wait for the signal to shutdown a client. */
+static void client_wait_and_shutdown(client *cl) {
+ gpr_mu_lock(&cl->mu);
+ while (!cl->done) gpr_cv_wait(&cl->done_cv, &cl->mu, gpr_inf_future);
+ gpr_mu_unlock(&cl->mu);
+
+ gpr_mu_destroy(&cl->mu);
+ gpr_cv_destroy(&cl->done_cv);
+
+ GPR_ASSERT(grpc_em_destroy(&cl->em) == GRPC_EM_OK);
+}
+
+/* Test grpc_em_fd. Start an upload server and client, upload a stream of
+ bytes from the client to the server, and verify that the total number of
+ sent bytes is equal to the total number of received bytes. */
+static void test_grpc_em_fd() {
+ server sv;
+ client cl;
+ int port;
+
+ server_init(&sv);
+ port = server_start(&sv);
+ client_init(&cl);
+ client_start(&cl, port);
+ client_wait_and_shutdown(&cl);
+ server_wait_and_shutdown(&sv);
+ GPR_ASSERT(sv.read_bytes_total == cl.write_bytes_total);
+ gpr_log(GPR_INFO, "Total read bytes %d", sv.read_bytes_total);
+}
+
+typedef struct fd_change_data {
+ gpr_mu mu;
+ gpr_cv cv;
+ void (*cb_that_ran)(void *, enum grpc_em_cb_status);
+} fd_change_data;
+
+void init_change_data(fd_change_data *fdc) {
+ gpr_mu_init(&fdc->mu);
+ gpr_cv_init(&fdc->cv);
+ fdc->cb_that_ran = NULL;
+}
+
+void destroy_change_data(fd_change_data *fdc) {
+ gpr_mu_destroy(&fdc->mu);
+ gpr_cv_destroy(&fdc->cv);
+}
+
+static void first_read_callback(void *arg /* fd_change_data */,
+ enum grpc_em_cb_status status) {
+ fd_change_data *fdc = arg;
+
+ gpr_mu_lock(&fdc->mu);
+ fdc->cb_that_ran = first_read_callback;
+ gpr_cv_signal(&fdc->cv);
+ gpr_mu_unlock(&fdc->mu);
+}
+
+static void second_read_callback(void *arg /* fd_change_data */,
+ enum grpc_em_cb_status status) {
+ fd_change_data *fdc = arg;
+
+ gpr_mu_lock(&fdc->mu);
+ fdc->cb_that_ran = second_read_callback;
+ gpr_cv_signal(&fdc->cv);
+ gpr_mu_unlock(&fdc->mu);
+}
+
+/* Test that changing the callback we use for notify_on_read actually works.
+ Note that we have two different but almost identical callbacks above -- the
+ point is to have two different function pointers and two different data
+ pointers and make sure that changing both really works. */
+static void test_grpc_em_fd_change() {
+ grpc_em em;
+ grpc_em_fd em_fd;
+ fd_change_data a, b;
+ int flags;
+ int sv[2];
+ char data;
+ int result;
+
+ init_change_data(&a);
+ init_change_data(&b);
+
+ GPR_ASSERT(socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == 0);
+ flags = fcntl(sv[0], F_GETFL, 0);
+ GPR_ASSERT(fcntl(sv[0], F_SETFL, flags | O_NONBLOCK) == 0);
+ flags = fcntl(sv[1], F_GETFL, 0);
+ GPR_ASSERT(fcntl(sv[1], F_SETFL, flags | O_NONBLOCK) == 0);
+
+ grpc_em_init(&em);
+ grpc_em_fd_init(&em_fd, &em, sv[0]);
+
+ /* Register the first callback, then make its FD readable */
+ grpc_em_fd_notify_on_read(&em_fd, first_read_callback, &a, gpr_inf_future);
+ data = 0;
+ result = write(sv[1], &data, 1);
+ GPR_ASSERT(result == 1);
+
+ /* And now wait for it to run. */
+ gpr_mu_lock(&a.mu);
+ while (a.cb_that_ran == NULL) {
+ gpr_cv_wait(&a.cv, &a.mu, gpr_inf_future);
+ }
+ GPR_ASSERT(a.cb_that_ran == first_read_callback);
+ gpr_mu_unlock(&a.mu);
+
+ /* And drain the socket so we can generate a new read edge */
+ result = read(sv[0], &data, 1);
+ GPR_ASSERT(result == 1);
+
+ /* Now register a second callback with distinct change data, and do the same
+ thing again. */
+ grpc_em_fd_notify_on_read(&em_fd, second_read_callback, &b, gpr_inf_future);
+ data = 0;
+ result = write(sv[1], &data, 1);
+ GPR_ASSERT(result == 1);
+
+ gpr_mu_lock(&b.mu);
+ while (b.cb_that_ran == NULL) {
+ gpr_cv_wait(&b.cv, &b.mu, gpr_inf_future);
+ }
+ /* Except now we verify that second_read_callback ran instead */
+ GPR_ASSERT(b.cb_that_ran == second_read_callback);
+ gpr_mu_unlock(&b.mu);
+
+ grpc_em_fd_destroy(&em_fd);
+ grpc_em_destroy(&em);
+ destroy_change_data(&a);
+ destroy_change_data(&b);
+ close(sv[0]);
+ close(sv[1]);
+}
+
+void timeout_callback(void *arg, enum grpc_em_cb_status status) {
+ if (status == GRPC_CALLBACK_TIMED_OUT) {
+ gpr_event_set(arg, (void *)1);
+ } else {
+ gpr_event_set(arg, (void *)2);
+ }
+}
+
+void test_grpc_em_fd_notify_timeout() {
+ grpc_em em;
+ grpc_em_fd em_fd;
+ gpr_event ev;
+ int flags;
+ int sv[2];
+ gpr_timespec timeout;
+ gpr_timespec deadline;
+
+ gpr_event_init(&ev);
+
+ GPR_ASSERT(socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == 0);
+ flags = fcntl(sv[0], F_GETFL, 0);
+ GPR_ASSERT(fcntl(sv[0], F_SETFL, flags | O_NONBLOCK) == 0);
+ flags = fcntl(sv[1], F_GETFL, 0);
+ GPR_ASSERT(fcntl(sv[1], F_SETFL, flags | O_NONBLOCK) == 0);
+
+ grpc_em_init(&em);
+ grpc_em_fd_init(&em_fd, &em, sv[0]);
+
+ timeout = gpr_time_from_micros(1000000);
+ deadline = gpr_time_add(gpr_now(), timeout);
+
+ grpc_em_fd_notify_on_read(&em_fd, timeout_callback, &ev, deadline);
+
+ GPR_ASSERT(gpr_event_wait(&ev, gpr_time_add(deadline, timeout)));
+
+ GPR_ASSERT(gpr_event_get(&ev) == (void *)1);
+ grpc_em_fd_destroy(&em_fd);
+ grpc_em_destroy(&em);
+ close(sv[0]);
+ close(sv[1]);
+}
+
+typedef struct {
+ grpc_em *em;
+ gpr_cv cv;
+ gpr_mu mu;
+ int counter;
+ int done_success_ctr;
+ int done_cancel_ctr;
+ int done;
+ gpr_event fcb_arg;
+ grpc_em_cb_status status;
+} alarm_arg;
+
+static void followup_cb(void *arg, grpc_em_cb_status status) {
+ gpr_event_set((gpr_event *)arg, arg);
+}
+
+/* Called when an alarm expires. */
+static void alarm_cb(void *arg /* alarm_arg */, grpc_em_cb_status status) {
+ alarm_arg *a = arg;
+ gpr_mu_lock(&a->mu);
+ if (status == GRPC_CALLBACK_SUCCESS) {
+ a->counter++;
+ a->done_success_ctr++;
+ } else if (status == GRPC_CALLBACK_CANCELLED) {
+ a->done_cancel_ctr++;
+ } else {
+ GPR_ASSERT(0);
+ }
+ a->done = 1;
+ a->status = status;
+ gpr_cv_signal(&a->cv);
+ gpr_mu_unlock(&a->mu);
+ grpc_em_add_callback(a->em, followup_cb, &a->fcb_arg);
+}
+
+/* Test grpc_em_alarm add and cancel. */
+static void test_grpc_em_alarm() {
+ struct grpc_em em;
+ struct grpc_em_alarm alarm;
+ struct grpc_em_alarm alarm_to_cancel;
+ gpr_timespec tv0 = {0, 1};
+ /* Timeout on the alarm cond. var, so make big enough to absorb time
+ deviations. Otherwise, operations after wait will not be properly ordered
+ */
+ gpr_timespec tv1 = gpr_time_from_micros(200000);
+ gpr_timespec tv2 = {0, 1};
+ gpr_timespec alarm_deadline;
+ gpr_timespec followup_deadline;
+
+ alarm_arg *cancel_arg = NULL;
+ alarm_arg arg;
+ alarm_arg arg2;
+ void *fdone;
+
+ GPR_ASSERT(grpc_em_init(&em) == GRPC_EM_OK);
+
+ arg.em = &em;
+ arg.counter = 0;
+ arg.status = GRPC_CALLBACK_DO_NOT_USE;
+ arg.done_success_ctr = 0;
+ arg.done_cancel_ctr = 0;
+ arg.done = 0;
+ gpr_mu_init(&arg.mu);
+ gpr_cv_init(&arg.cv);
+ gpr_event_init(&arg.fcb_arg);
+
+ GPR_ASSERT(grpc_em_alarm_init(&alarm, &em, alarm_cb, &arg) == GRPC_EM_OK);
+ GPR_ASSERT(grpc_em_alarm_add(&alarm, gpr_time_add(tv0, gpr_now())) ==
+ GRPC_EM_OK);
+
+ alarm_deadline = gpr_time_add(gpr_now(), tv1);
+ gpr_mu_lock(&arg.mu);
+ while (arg.done == 0) {
+ gpr_cv_wait(&arg.cv, &arg.mu, alarm_deadline);
+ }
+ gpr_mu_unlock(&arg.mu);
+
+ followup_deadline = gpr_time_add(gpr_now(), tv1);
+ fdone = gpr_event_wait(&arg.fcb_arg, followup_deadline);
+
+ if (arg.counter != 1) {
+ gpr_log(GPR_ERROR, "Alarm callback not called");
+ GPR_ASSERT(0);
+ } else if (arg.done_success_ctr != 1) {
+ gpr_log(GPR_ERROR, "Alarm done callback not called with success");
+ GPR_ASSERT(0);
+ } else if (arg.done_cancel_ctr != 0) {
+ gpr_log(GPR_ERROR, "Alarm done callback called with cancel");
+ GPR_ASSERT(0);
+ } else if (arg.status == GRPC_CALLBACK_DO_NOT_USE) {
+ gpr_log(GPR_ERROR, "Alarm callback without status");
+ GPR_ASSERT(0);
+ } else {
+ gpr_log(GPR_INFO, "Alarm callback called successfully");
+ }
+
+ if (fdone != (void *)&arg.fcb_arg) {
+ gpr_log(GPR_ERROR, "Followup callback #1 not invoked properly %p %p", fdone,
+ &arg.fcb_arg);
+ GPR_ASSERT(0);
+ }
+ gpr_cv_destroy(&arg.cv);
+ gpr_mu_destroy(&arg.mu);
+
+ arg2.em = &em;
+ arg2.counter = 0;
+ arg2.status = GRPC_CALLBACK_DO_NOT_USE;
+ arg2.done_success_ctr = 0;
+ arg2.done_cancel_ctr = 0;
+ arg2.done = 0;
+ gpr_mu_init(&arg2.mu);
+ gpr_cv_init(&arg2.cv);
+ gpr_event_init(&arg2.fcb_arg);
+
+ GPR_ASSERT(grpc_em_alarm_init(&alarm_to_cancel, &em, alarm_cb, &arg2) ==
+ GRPC_EM_OK);
+ GPR_ASSERT(grpc_em_alarm_add(&alarm_to_cancel,
+ gpr_time_add(tv2, gpr_now())) == GRPC_EM_OK);
+ switch (grpc_em_alarm_cancel(&alarm_to_cancel, (void **)&cancel_arg)) {
+ case GRPC_EM_OK:
+ gpr_log(GPR_INFO, "Alarm cancel succeeded");
+ break;
+ case GRPC_EM_ERROR:
+ gpr_log(GPR_ERROR, "Alarm cancel failed");
+ GPR_ASSERT(0);
+ break;
+ case GRPC_EM_INVALID_ARGUMENTS:
+ gpr_log(GPR_ERROR, "Alarm cancel failed with bad response code");
+ gpr_log(GPR_ERROR, "Current value of triggered is %d\n",
+ (int)alarm_to_cancel.triggered);
+ GPR_ASSERT(0);
+ break;
+ }
+
+ alarm_deadline = gpr_time_add(gpr_now(), tv1);
+ gpr_mu_lock(&arg2.mu);
+ while (arg2.done == 0) {
+ gpr_cv_wait(&arg2.cv, &arg2.mu, alarm_deadline);
+ }
+ gpr_mu_unlock(&arg2.mu);
+
+ followup_deadline = gpr_time_add(gpr_now(), tv1);
+ fdone = gpr_event_wait(&arg2.fcb_arg, followup_deadline);
+
+ if (arg2.counter != arg2.done_success_ctr) {
+ gpr_log(GPR_ERROR, "Alarm callback called but didn't lead to done success");
+ GPR_ASSERT(0);
+ } else if (arg2.done_success_ctr && arg2.done_cancel_ctr) {
+ gpr_log(GPR_ERROR, "Alarm done callback called with success and cancel");
+ GPR_ASSERT(0);
+ } else if (arg2.done_cancel_ctr + arg2.done_success_ctr != 1) {
+ gpr_log(GPR_ERROR, "Alarm done callback called incorrect number of times");
+ GPR_ASSERT(0);
+ } else if (arg2.status == GRPC_CALLBACK_DO_NOT_USE) {
+ gpr_log(GPR_ERROR, "Alarm callback without status");
+ GPR_ASSERT(0);
+ } else if (arg2.done_success_ctr) {
+ gpr_log(GPR_INFO, "Alarm callback executed before cancel");
+ gpr_log(GPR_INFO, "Current value of triggered is %d\n",
+ (int)alarm_to_cancel.triggered);
+ } else if (arg2.done_cancel_ctr) {
+ gpr_log(GPR_INFO, "Alarm callback canceled");
+ gpr_log(GPR_INFO, "Current value of triggered is %d\n",
+ (int)alarm_to_cancel.triggered);
+ } else {
+ gpr_log(GPR_ERROR, "Alarm cancel test should not be here");
+ GPR_ASSERT(0);
+ }
+
+ if (cancel_arg != &arg2) {
+ gpr_log(GPR_ERROR, "Alarm cancel arg address wrong");
+ GPR_ASSERT(0);
+ }
+ if (fdone != (void *)&arg2.fcb_arg) {
+ gpr_log(GPR_ERROR, "Followup callback #2 not invoked properly %p %p", fdone,
+ &arg2.fcb_arg);
+ GPR_ASSERT(0);
+ }
+ gpr_cv_destroy(&arg2.cv);
+ gpr_mu_destroy(&arg2.mu);
+
+ GPR_ASSERT(grpc_em_destroy(&em) == GRPC_EM_OK);
+}
+
+int main(int argc, char **argv) {
+ grpc_test_init(argc, argv);
+ test_grpc_em_alarm();
+ test_grpc_em_fd();
+ test_grpc_em_fd_change();
+ test_grpc_em_fd_notify_timeout();
+ return 0;
+}
diff --git a/test/core/fling/client.c b/test/core/fling/client.c
new file mode 100644
index 0000000000..cc661c34c5
--- /dev/null
+++ b/test/core/fling/client.c
@@ -0,0 +1,196 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include <grpc/grpc.h>
+
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/support/cmdline.h>
+#include <grpc/support/histogram.h>
+#include <grpc/support/log.h>
+#include <grpc/support/time.h>
+#include <grpc/support/useful.h>
+#include "test/core/util/grpc_profiler.h"
+#include "test/core/util/test_config.h"
+
+static gpr_histogram *histogram;
+static grpc_byte_buffer *the_buffer;
+static grpc_channel *channel;
+static grpc_completion_queue *cq;
+static grpc_call *call;
+
+static void init_ping_pong_request() {}
+
+static void step_ping_pong_request() {
+ call = grpc_channel_create_call(channel, "/Reflector/reflectUnary",
+ "localhost", gpr_inf_future);
+ GPR_ASSERT(grpc_call_start_invoke(call, cq, (void *)1, (void *)1, (void *)1,
+ GRPC_WRITE_BUFFER_HINT) == GRPC_CALL_OK);
+ grpc_event_finish(grpc_completion_queue_next(cq, gpr_inf_future));
+ GPR_ASSERT(grpc_call_start_write(call, the_buffer, (void *)1,
+ GRPC_WRITE_BUFFER_HINT) == GRPC_CALL_OK);
+ grpc_event_finish(grpc_completion_queue_next(cq, gpr_inf_future));
+ GPR_ASSERT(grpc_call_start_read(call, (void *)1) == GRPC_CALL_OK);
+ GPR_ASSERT(grpc_call_writes_done(call, (void *)1) == GRPC_CALL_OK);
+ grpc_event_finish(grpc_completion_queue_next(cq, gpr_inf_future));
+ grpc_event_finish(grpc_completion_queue_next(cq, gpr_inf_future));
+ grpc_event_finish(grpc_completion_queue_next(cq, gpr_inf_future));
+ grpc_event_finish(grpc_completion_queue_next(cq, gpr_inf_future));
+ grpc_call_destroy(call);
+ call = NULL;
+}
+
+static void init_ping_pong_stream() {
+ call = grpc_channel_create_call(channel, "/Reflector/reflectStream",
+ "localhost", gpr_inf_future);
+ GPR_ASSERT(grpc_call_start_invoke(call, cq, (void *)1, (void *)1, (void *)1,
+ 0) == GRPC_CALL_OK);
+ grpc_event_finish(grpc_completion_queue_next(cq, gpr_inf_future));
+ grpc_event_finish(grpc_completion_queue_next(cq, gpr_inf_future));
+}
+
+static void step_ping_pong_stream() {
+ GPR_ASSERT(grpc_call_start_write(call, the_buffer, (void *)1, 0) ==
+ GRPC_CALL_OK);
+ GPR_ASSERT(grpc_call_start_read(call, (void *)1) == GRPC_CALL_OK);
+ grpc_event_finish(grpc_completion_queue_next(cq, gpr_inf_future));
+ grpc_event_finish(grpc_completion_queue_next(cq, gpr_inf_future));
+}
+
+static double now() {
+ gpr_timespec tv = gpr_now();
+ return 1e9 * tv.tv_sec + tv.tv_nsec;
+}
+
+typedef struct {
+ const char *name;
+ void (*init)();
+ void (*do_one_step)();
+} scenario;
+
+static const scenario scenarios[] = {
+ {"ping-pong-request", init_ping_pong_request, step_ping_pong_request},
+ {"ping-pong-stream", init_ping_pong_stream, step_ping_pong_stream},
+};
+
+int main(int argc, char **argv) {
+ gpr_slice slice = gpr_slice_from_copied_string("x");
+ double start, stop;
+ int i;
+
+ char *fake_argv[1];
+
+ int payload_size = 1;
+ int done;
+ int secure = 0;
+ char *target = "localhost:443";
+ gpr_cmdline *cl;
+ char *scenario_name = "ping-pong-request";
+ scenario sc = {NULL};
+
+ GPR_ASSERT(argc >= 1);
+ fake_argv[0] = argv[0];
+ grpc_test_init(1, fake_argv);
+
+ grpc_init();
+
+ cl = gpr_cmdline_create("fling client");
+ gpr_cmdline_add_int(cl, "payload_size", "Size of the payload to send",
+ &payload_size);
+ gpr_cmdline_add_string(cl, "target", "Target host:port", &target);
+ gpr_cmdline_add_flag(cl, "secure", "Run with security?", &secure);
+ gpr_cmdline_add_string(cl, "scenario", "Scenario", &scenario_name);
+ gpr_cmdline_parse(cl, argc, argv);
+ gpr_cmdline_destroy(cl);
+
+ for (i = 0; i < GPR_ARRAY_SIZE(scenarios); i++) {
+ if (0 == strcmp(scenarios[i].name, scenario_name)) {
+ sc = scenarios[i];
+ }
+ }
+ if (!sc.name) {
+ fprintf(stderr, "unsupported scenario '%s'. Valid are:", scenario_name);
+ for (i = 0; i < GPR_ARRAY_SIZE(scenarios); i++) {
+ fprintf(stderr, " %s", scenarios[i].name);
+ }
+ return 1;
+ }
+
+ channel = grpc_channel_create(target, NULL);
+ cq = grpc_completion_queue_create();
+ the_buffer = grpc_byte_buffer_create(&slice, payload_size);
+ histogram = gpr_histogram_create(0.01, 60e9);
+ sc.init();
+
+ for (i = 0; i < 1000; i++) {
+ sc.do_one_step();
+ }
+
+ gpr_log(GPR_INFO, "start profiling");
+ grpc_profiler_start("client.prof");
+ for (i = 0; i < 100000; i++) {
+ start = now();
+ sc.do_one_step();
+ stop = now();
+ gpr_histogram_add(histogram, stop - start);
+ }
+ grpc_profiler_stop();
+
+ if (call) {
+ grpc_call_destroy(call);
+ }
+
+ grpc_channel_destroy(channel);
+ grpc_completion_queue_shutdown(cq);
+ done = 0;
+ while (!done) {
+ grpc_event *ev = grpc_completion_queue_next(cq, gpr_inf_future);
+ done = (ev->type == GRPC_QUEUE_SHUTDOWN);
+ grpc_event_finish(ev);
+ }
+ grpc_completion_queue_destroy(cq);
+ grpc_byte_buffer_destroy(the_buffer);
+ gpr_slice_unref(slice);
+
+ gpr_log(GPR_INFO, "latency (50/95/99/99.9): %f/%f/%f/%f",
+ gpr_histogram_percentile(histogram, 50),
+ gpr_histogram_percentile(histogram, 95),
+ gpr_histogram_percentile(histogram, 99),
+ gpr_histogram_percentile(histogram, 99.9));
+ gpr_histogram_destroy(histogram);
+
+ grpc_shutdown();
+
+ return 0;
+}
diff --git a/test/core/fling/fling_stream_test.c b/test/core/fling/fling_stream_test.c
new file mode 100644
index 0000000000..14a31eb26e
--- /dev/null
+++ b/test/core/fling/fling_stream_test.c
@@ -0,0 +1,103 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#define _POSIX_SOURCE
+#include <unistd.h>
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/host_port.h>
+#include <grpc/support/string.h>
+#include "test/core/util/port.h"
+
+int main(int argc, char **argv) {
+ char *me = argv[0];
+ char *lslash = strrchr(me, '/');
+ char root[1024];
+ int port = grpc_pick_unused_port_or_die();
+ char *args[10];
+ int status;
+ pid_t svr, cli;
+ /* figure out where we are */
+ if (lslash) {
+ memcpy(root, me, lslash - me);
+ root[lslash - me] = 0;
+ } else {
+ strcpy(root, ".");
+ }
+ /* start the server */
+ svr = fork();
+ if (svr == 0) {
+ gpr_asprintf(&args[0], "%s/fling_server", root);
+ args[1] = "--bind";
+ gpr_join_host_port(&args[2], "::", port);
+ args[3] = "--no-secure";
+ args[4] = 0;
+ execv(args[0], args);
+
+ gpr_free(args[0]);
+ gpr_free(args[2]);
+ return 1;
+ }
+ /* wait a little */
+ sleep(2);
+ /* start the client */
+ cli = fork();
+ if (cli == 0) {
+ gpr_asprintf(&args[0], "%s/fling_client", root);
+ args[1] = "--target";
+ gpr_join_host_port(&args[2], "127.0.0.1", port);
+ args[3] = "--scenario=ping-pong-stream";
+ args[4] = "--no-secure";
+ args[5] = 0;
+ execv(args[0], args);
+
+ gpr_free(args[0]);
+ gpr_free(args[2]);
+ return 1;
+ }
+ /* wait for completion */
+ printf("waiting for client\n");
+ if (waitpid(cli, &status, 0) == -1) return 2;
+ if (!WIFEXITED(status)) return 4;
+ if (WEXITSTATUS(status)) return WEXITSTATUS(status);
+ printf("checking server\n");
+ if (waitpid(svr, &status, WNOHANG) != 0) return 2;
+ kill(svr, SIGKILL);
+ return 0;
+}
diff --git a/test/core/fling/fling_test.c b/test/core/fling/fling_test.c
new file mode 100644
index 0000000000..e5e63f9076
--- /dev/null
+++ b/test/core/fling/fling_test.c
@@ -0,0 +1,103 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#define _POSIX_SOURCE
+#include <unistd.h>
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/host_port.h>
+#include <grpc/support/string.h>
+#include "test/core/util/port.h"
+
+int main(int argc, char **argv) {
+ char *me = argv[0];
+ char *lslash = strrchr(me, '/');
+ char root[1024];
+ int port = grpc_pick_unused_port_or_die();
+ char *args[10];
+ int status;
+ pid_t svr, cli;
+ /* figure out where we are */
+ if (lslash) {
+ memcpy(root, me, lslash - me);
+ root[lslash - me] = 0;
+ } else {
+ strcpy(root, ".");
+ }
+ /* start the server */
+ svr = fork();
+ if (svr == 0) {
+ gpr_asprintf(&args[0], "%s/fling_server", root);
+ args[1] = "--bind";
+ gpr_join_host_port(&args[2], "::", port);
+ args[3] = "--no-secure";
+ args[4] = 0;
+ execv(args[0], args);
+
+ gpr_free(args[0]);
+ gpr_free(args[2]);
+ return 1;
+ }
+ /* wait a little */
+ sleep(2);
+ /* start the client */
+ cli = fork();
+ if (cli == 0) {
+ gpr_asprintf(&args[0], "%s/fling_client", root);
+ args[1] = "--target";
+ gpr_join_host_port(&args[2], "127.0.0.1", port);
+ args[3] = "--scenario=ping-pong-request";
+ args[4] = "--no-secure";
+ args[5] = 0;
+ execv(args[0], args);
+
+ gpr_free(args[0]);
+ gpr_free(args[2]);
+ return 1;
+ }
+ /* wait for completion */
+ printf("waiting for client\n");
+ if (waitpid(cli, &status, 0) == -1) return 2;
+ if (!WIFEXITED(status)) return 4;
+ if (WEXITSTATUS(status)) return WEXITSTATUS(status);
+ printf("checking server\n");
+ if (waitpid(svr, &status, WNOHANG) != 0) return 2;
+ kill(svr, SIGKILL);
+ return 0;
+}
diff --git a/test/core/fling/server.c b/test/core/fling/server.c
new file mode 100644
index 0000000000..7a5d89598d
--- /dev/null
+++ b/test/core/fling/server.c
@@ -0,0 +1,159 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include <grpc/grpc.h>
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "test/core/util/grpc_profiler.h"
+#include "test/core/util/test_config.h"
+#include <grpc/support/alloc.h>
+#include <grpc/support/cmdline.h>
+#include <grpc/support/host_port.h>
+#include <grpc/support/log.h>
+#include <grpc/support/time.h>
+#include "test/core/util/port.h"
+
+static grpc_completion_queue *cq;
+static grpc_server *server;
+static int done = 0;
+
+static const grpc_status status_ok = {GRPC_STATUS_OK, NULL};
+
+typedef struct {
+ gpr_refcount pending_ops;
+ gpr_uint32 flags;
+} call_state;
+
+static void request_call() {
+ call_state *s = gpr_malloc(sizeof(call_state));
+ gpr_ref_init(&s->pending_ops, 2);
+ grpc_server_request_call(server, s);
+}
+
+static void sigint_handler(int x) { done = 1; }
+
+int main(int argc, char **argv) {
+ grpc_event *ev;
+ call_state *s;
+ char *addr_buf = NULL;
+ gpr_cmdline *cl;
+
+ int secure = 0;
+ char *addr = NULL;
+
+ char *fake_argv[1];
+
+ GPR_ASSERT(argc >= 1);
+ fake_argv[0] = argv[0];
+ grpc_test_init(1, fake_argv);
+
+ grpc_init();
+ srand(clock());
+
+ cl = gpr_cmdline_create("fling server");
+ gpr_cmdline_add_string(cl, "bind", "Bind host:port", &addr);
+ gpr_cmdline_add_flag(cl, "secure", "Run with security?", &secure);
+ gpr_cmdline_parse(cl, argc, argv);
+ gpr_cmdline_destroy(cl);
+
+ if (addr == NULL) {
+ gpr_join_host_port(&addr_buf, "::", grpc_pick_unused_port_or_die());
+ addr = addr_buf;
+ }
+ gpr_log(GPR_INFO, "creating server on: %s", addr);
+
+ cq = grpc_completion_queue_create();
+ server = grpc_server_create(cq, NULL);
+ GPR_ASSERT(grpc_server_add_http2_port(server, addr));
+ grpc_server_start(server);
+
+ gpr_free(addr_buf);
+ addr = addr_buf = NULL;
+
+ request_call();
+
+ grpc_profiler_start("server.prof");
+ signal(SIGINT, sigint_handler);
+ while (!done) {
+ ev = grpc_completion_queue_next(
+ cq, gpr_time_add(gpr_now(), gpr_time_from_micros(1000000)));
+ if (!ev) continue;
+ s = ev->tag;
+ switch (ev->type) {
+ case GRPC_SERVER_RPC_NEW:
+ /* initial ops are already started in request_call */
+ if (0 == strcmp(ev->data.server_rpc_new.method,
+ "/Reflector/reflectStream")) {
+ s->flags = 0;
+ } else {
+ s->flags = GRPC_WRITE_BUFFER_HINT;
+ }
+ grpc_call_accept(ev->call, cq, s, s->flags);
+ GPR_ASSERT(grpc_call_start_read(ev->call, s) == GRPC_CALL_OK);
+ request_call();
+ break;
+ case GRPC_WRITE_ACCEPTED:
+ GPR_ASSERT(ev->data.write_accepted == GRPC_OP_OK);
+ GPR_ASSERT(grpc_call_start_read(ev->call, s) == GRPC_CALL_OK);
+ break;
+ case GRPC_READ:
+ if (ev->data.read) {
+ GPR_ASSERT(grpc_call_start_write(ev->call, ev->data.read, s,
+ s->flags) == GRPC_CALL_OK);
+ } else {
+ GPR_ASSERT(grpc_call_start_write_status(ev->call, status_ok, s) ==
+ GRPC_CALL_OK);
+ }
+ break;
+ case GRPC_FINISH_ACCEPTED:
+ case GRPC_FINISHED:
+ if (gpr_unref(&s->pending_ops)) {
+ grpc_call_destroy(ev->call);
+ gpr_free(s);
+ }
+ break;
+ default:
+ abort();
+ }
+ grpc_event_finish(ev);
+ }
+ grpc_profiler_stop();
+
+ grpc_shutdown();
+ return 0;
+}
diff --git a/test/core/httpcli/format_request_test.c b/test/core/httpcli/format_request_test.c
new file mode 100644
index 0000000000..12ea7fda60
--- /dev/null
+++ b/test/core/httpcli/format_request_test.c
@@ -0,0 +1,166 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include "src/core/httpcli/format_request.h"
+
+#include <string.h>
+
+#include <grpc/support/log.h>
+#include "test/core/util/test_config.h"
+
+static void test_format_get_request() {
+ grpc_httpcli_header hdr = {"x-yz", "abc"};
+ grpc_httpcli_request req;
+ gpr_slice slice;
+
+
+ memset(&req, 0, sizeof(req));
+ req.host = "example.com";
+ req.path = "/index.html";
+ req.hdr_count = 1;
+ req.hdrs = &hdr;
+
+ slice = grpc_httpcli_format_get_request(&req);
+
+ GPR_ASSERT(0 == gpr_slice_str_cmp(slice,
+ "GET /index.html HTTP/1.0\r\n"
+ "Host: example.com\r\n"
+ "Connection: close\r\n"
+ "User-Agent: " GRPC_HTTPCLI_USER_AGENT
+ "\r\n"
+ "x-yz: abc\r\n"
+ "\r\n"));
+
+ gpr_slice_unref(slice);
+}
+
+static void test_format_post_request() {
+ grpc_httpcli_header hdr = {"x-yz", "abc"};
+ grpc_httpcli_request req;
+ gpr_slice slice;
+ char body_bytes[] = "fake body";
+ size_t body_len = 9;
+
+ memset(&req, 0, sizeof(req));
+ req.host = "example.com";
+ req.path = "/index.html";
+ req.hdr_count = 1;
+ req.hdrs = &hdr;
+
+ slice = grpc_httpcli_format_post_request(&req, body_bytes, body_len);
+
+ GPR_ASSERT(0 == gpr_slice_str_cmp(slice,
+ "POST /index.html HTTP/1.0\r\n"
+ "Host: example.com\r\n"
+ "Connection: close\r\n"
+ "User-Agent: " GRPC_HTTPCLI_USER_AGENT
+ "\r\n"
+ "x-yz: abc\r\n"
+ "Content-Type: text/plain\r\n"
+ "Content-Length: 9\r\n"
+ "\r\n"
+ "fake body"));
+
+ gpr_slice_unref(slice);
+}
+
+static void test_format_post_request_no_body() {
+ grpc_httpcli_header hdr = {"x-yz", "abc"};
+ grpc_httpcli_request req;
+ gpr_slice slice;
+
+ memset(&req, 0, sizeof(req));
+ req.host = "example.com";
+ req.path = "/index.html";
+ req.hdr_count = 1;
+ req.hdrs = &hdr;
+
+ slice = grpc_httpcli_format_post_request(&req, NULL, 0);
+
+ GPR_ASSERT(0 == gpr_slice_str_cmp(slice,
+ "POST /index.html HTTP/1.0\r\n"
+ "Host: example.com\r\n"
+ "Connection: close\r\n"
+ "User-Agent: " GRPC_HTTPCLI_USER_AGENT
+ "\r\n"
+ "x-yz: abc\r\n"
+ "\r\n"));
+
+ gpr_slice_unref(slice);
+}
+
+static void test_format_post_request_content_type_override() {
+ grpc_httpcli_header hdrs[2];
+ grpc_httpcli_request req;
+ gpr_slice slice;
+ char body_bytes[] = "fake%20body";
+ size_t body_len = 11;
+
+ hdrs[0].key = "x-yz";
+ hdrs[0].value = "abc";
+ hdrs[1].key = "Content-Type";
+ hdrs[1].value = "application/x-www-form-urlencoded";
+ memset(&req, 0, sizeof(req));
+ req.host = "example.com";
+ req.path = "/index.html";
+ req.hdr_count = 2;
+ req.hdrs = hdrs;
+
+ slice = grpc_httpcli_format_post_request(&req, body_bytes, body_len);
+
+ GPR_ASSERT(0 == gpr_slice_str_cmp(
+ slice,
+ "POST /index.html HTTP/1.0\r\n"
+ "Host: example.com\r\n"
+ "Connection: close\r\n"
+ "User-Agent: " GRPC_HTTPCLI_USER_AGENT
+ "\r\n"
+ "x-yz: abc\r\n"
+ "Content-Type: application/x-www-form-urlencoded\r\n"
+ "Content-Length: 11\r\n"
+ "\r\n"
+ "fake%20body"));
+
+ gpr_slice_unref(slice);
+}
+
+int main(int argc, char **argv) {
+ grpc_test_init(argc, argv);
+
+ test_format_get_request();
+ test_format_post_request();
+ test_format_post_request_no_body();
+ test_format_post_request_content_type_override();
+
+ return 0;
+}
diff --git a/test/core/httpcli/httpcli_test.c b/test/core/httpcli/httpcli_test.c
new file mode 100644
index 0000000000..5c0d87c427
--- /dev/null
+++ b/test/core/httpcli/httpcli_test.c
@@ -0,0 +1,101 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include "src/core/httpcli/httpcli.h"
+
+#include <string.h>
+
+#include <grpc/support/log.h>
+#include "test/core/util/test_config.h"
+
+static gpr_event g_done;
+static grpc_em g_em;
+
+static gpr_timespec n_seconds_time(int seconds) {
+ return gpr_time_add(gpr_now(), gpr_time_from_micros(seconds * 1000000));
+}
+
+static void on_finish(void *arg, const grpc_httpcli_response *response) {
+ GPR_ASSERT(arg == (void *)42);
+ GPR_ASSERT(response);
+ GPR_ASSERT(response->status == 200);
+ gpr_event_set(&g_done, (void *)1);
+}
+
+static void test_get(int use_ssl) {
+ grpc_httpcli_request req;
+
+ gpr_log(GPR_INFO, "running %s with use_ssl=%d.", __FUNCTION__, (int)use_ssl);
+
+ gpr_event_init(&g_done);
+ memset(&req, 0, sizeof(req));
+ req.host = "www.google.com";
+ req.path = "/";
+ req.use_ssl = use_ssl;
+
+ grpc_httpcli_get(&req, n_seconds_time(15), &g_em, on_finish, (void *)42);
+ GPR_ASSERT(gpr_event_wait(&g_done, n_seconds_time(20)));
+}
+
+/*
+static void test_post(int use_ssl) {
+ grpc_httpcli_request req;
+
+ gpr_log(GPR_INFO, "running %s with use_ssl=%d.", __FUNCTION__, (int)use_ssl);
+
+ gpr_event_init(&g_done);
+ memset(&req, 0, sizeof(req));
+ req.host = "requestb.in";
+ req.path = "/1eamwr21";
+ req.use_ssl = use_ssl;
+
+ grpc_httpcli_post(&req, NULL, 0, n_seconds_time(15), &g_em, on_finish,
+ (void *)42);
+ GPR_ASSERT(gpr_event_wait(&g_done, n_seconds_time(20)));
+}
+*/
+
+int main(int argc, char **argv) {
+ grpc_test_init(argc, argv);
+ grpc_em_init(&g_em);
+
+ test_get(0);
+ test_get(1);
+
+ /* test_post(0); */
+ /* test_post(1); */
+
+ grpc_em_destroy(&g_em);
+
+ return 0;
+}
diff --git a/test/core/httpcli/parser_test.c b/test/core/httpcli/parser_test.c
new file mode 100644
index 0000000000..455f6a6393
--- /dev/null
+++ b/test/core/httpcli/parser_test.c
@@ -0,0 +1,155 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include "src/core/httpcli/parser.h"
+
+#include <stdarg.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/useful.h>
+#include "test/core/util/slice_splitter.h"
+#include "test/core/util/test_config.h"
+
+static void test_succeeds(grpc_slice_split_mode split_mode, char *response,
+ int expect_status, char *expect_body, ...) {
+ grpc_httpcli_parser parser;
+ gpr_slice input_slice = gpr_slice_from_copied_string(response);
+ size_t num_slices;
+ size_t i;
+ gpr_slice *slices;
+ va_list args;
+
+ grpc_split_slices(split_mode, &input_slice, 1, &slices, &num_slices);
+ gpr_slice_unref(input_slice);
+
+ grpc_httpcli_parser_init(&parser);
+
+ for (i = 0; i < num_slices; i++) {
+ GPR_ASSERT(grpc_httpcli_parser_parse(&parser, slices[i]));
+ gpr_slice_unref(slices[i]);
+ }
+ GPR_ASSERT(grpc_httpcli_parser_eof(&parser));
+
+ GPR_ASSERT(expect_status == parser.r.status);
+ if (expect_body != NULL) {
+ GPR_ASSERT(strlen(expect_body) == parser.r.body_length);
+ GPR_ASSERT(0 == memcmp(expect_body, parser.r.body, parser.r.body_length));
+ } else {
+ GPR_ASSERT(parser.r.body_length == 0);
+ }
+
+ va_start(args, expect_body);
+ i = 0;
+ for (;;) {
+ char *expect_key;
+ char *expect_value;
+ expect_key = va_arg(args, char *);
+ if (!expect_key) break;
+ GPR_ASSERT(i < parser.r.hdr_count);
+ expect_value = va_arg(args, char *);
+ GPR_ASSERT(expect_value);
+ GPR_ASSERT(0 == strcmp(expect_key, parser.r.hdrs[i].key));
+ GPR_ASSERT(0 == strcmp(expect_value, parser.r.hdrs[i].value));
+ i++;
+ }
+ va_end(args);
+ GPR_ASSERT(i == parser.r.hdr_count);
+
+ grpc_httpcli_parser_destroy(&parser);
+ gpr_free(slices);
+}
+
+static void test_fails(grpc_slice_split_mode split_mode, char *response) {
+ grpc_httpcli_parser parser;
+ gpr_slice input_slice = gpr_slice_from_copied_string(response);
+ size_t num_slices;
+ size_t i;
+ gpr_slice *slices;
+ int done = 0;
+
+ grpc_split_slices(split_mode, &input_slice, 1, &slices, &num_slices);
+ gpr_slice_unref(input_slice);
+
+ grpc_httpcli_parser_init(&parser);
+
+ for (i = 0; i < num_slices; i++) {
+ if (!done && !grpc_httpcli_parser_parse(&parser, slices[i])) {
+ done = 1;
+ }
+ gpr_slice_unref(slices[i]);
+ }
+ if (!done && !grpc_httpcli_parser_eof(&parser)) {
+ done = 1;
+ }
+ GPR_ASSERT(done);
+
+ grpc_httpcli_parser_destroy(&parser);
+ gpr_free(slices);
+}
+
+int main(int argc, char **argv) {
+ size_t i;
+ const grpc_slice_split_mode split_modes[] = {GRPC_SLICE_SPLIT_IDENTITY,
+ GRPC_SLICE_SPLIT_ONE_BYTE};
+
+ grpc_test_init(argc, argv);
+
+ for (i = 0; i < GPR_ARRAY_SIZE(split_modes); i++) {
+ test_succeeds(split_modes[i],
+ "HTTP/1.0 200 OK\r\n"
+ "xyz: abc\r\n"
+ "\r\n"
+ "hello world!",
+ 200, "hello world!", "xyz", "abc", NULL);
+ test_succeeds(split_modes[i],
+ "HTTP/1.0 404 Not Found\r\n"
+ "\r\n",
+ 404, NULL, NULL);
+ test_succeeds(split_modes[i],
+ "HTTP/1.1 200 OK\r\n"
+ "xyz: abc\r\n"
+ "\r\n"
+ "hello world!",
+ 200, "hello world!", "xyz", "abc", NULL);
+ test_fails(split_modes[i], "HTTP/1.0\r\n");
+ test_fails(split_modes[i], "HTTP/1.2\r\n");
+ test_fails(split_modes[i], "HTTP/1.0 000 XYX\r\n");
+ test_fails(split_modes[i], "HTTP/1.0 200 OK\n");
+ test_fails(split_modes[i], "HTTP/1.0 200 OK\r\n");
+ test_fails(split_modes[i], "HTTP/1.0 200 OK\r\nFoo x\r\n");
+ }
+
+ return 0;
+}
diff --git a/test/core/network_benchmarks/low_level_ping_pong.c b/test/core/network_benchmarks/low_level_ping_pong.c
new file mode 100644
index 0000000000..93c66a9ecb
--- /dev/null
+++ b/test/core/network_benchmarks/low_level_ping_pong.c
@@ -0,0 +1,626 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+/*
+ Basic I/O ping-pong benchmarks.
+
+ The goal here is to establish lower bounds on how fast the stack could get by
+ measuring the cost of using various I/O strategies to do a basic
+ request-response loop.
+ */
+
+#include <errno.h>
+#include <netinet/ip.h>
+#include <poll.h>
+#include <stdio.h>
+#include <string.h>
+#ifdef __linux__
+#include <sys/epoll.h>
+#endif
+#include <sys/socket.h>
+
+#include "src/core/endpoint/socket_utils.h"
+#include <grpc/support/cmdline.h>
+#include <grpc/support/histogram.h>
+#include <grpc/support/log.h>
+#include <grpc/support/thd.h>
+#include <grpc/support/time.h>
+
+typedef struct fd_pair {
+ int read_fd;
+ int write_fd;
+} fd_pair;
+
+typedef struct thread_args {
+ fd_pair fds;
+ size_t msg_size;
+ int (*read_bytes)(struct thread_args *args, char *buf);
+ int (*write_bytes)(struct thread_args *args, char *buf);
+ int (*setup)(struct thread_args *args);
+ int epoll_fd;
+} thread_args;
+
+/*
+ Read strategies
+
+ There are a number of read strategies, each of which has a blocking and
+ non-blocking version.
+ */
+
+/* Basic call to read() */
+static int read_bytes(int fd, char *buf, size_t read_size, int spin) {
+ int bytes_read = 0;
+ int err;
+ do {
+ err = read(fd, buf + bytes_read, read_size - bytes_read);
+ if (err < 0) {
+ if (errno == EINTR) {
+ continue;
+ } else {
+ if (errno == EAGAIN && spin == 1) {
+ continue;
+ }
+ gpr_log(GPR_ERROR, "Read failed: %s", strerror(errno));
+ return -1;
+ }
+ } else {
+ bytes_read += err;
+ }
+ } while (bytes_read < read_size);
+ return 0;
+}
+
+static int blocking_read_bytes(thread_args *args, char *buf) {
+ return read_bytes(args->fds.read_fd, buf, args->msg_size, 0);
+}
+
+static int spin_read_bytes(thread_args *args, char *buf) {
+ return read_bytes(args->fds.read_fd, buf, args->msg_size, 1);
+}
+
+/* Call poll() to monitor a non-blocking fd */
+static int poll_read_bytes(int fd, char *buf, size_t read_size, int spin) {
+ struct pollfd pfd;
+ size_t bytes_read = 0;
+ int err;
+
+ pfd.fd = fd;
+ pfd.events = POLLIN;
+ do {
+ err = poll(&pfd, 1, spin ? 0 : -1);
+ if (err < 0) {
+ if (errno == EINTR) {
+ continue;
+ } else {
+ gpr_log(GPR_ERROR, "Poll failed: %s", strerror(errno));
+ return -1;
+ }
+ }
+ if (err == 0 && spin) continue;
+ GPR_ASSERT(err == 1);
+ GPR_ASSERT(pfd.revents == POLLIN);
+ do {
+ err = read(fd, buf + bytes_read, read_size - bytes_read);
+ } while (err < 0 && errno == EINTR);
+ if (err < 0 && errno != EAGAIN) {
+ gpr_log(GPR_ERROR, "Read failed: %s", strerror(errno));
+ return -1;
+ }
+ bytes_read += err;
+ } while (bytes_read < read_size);
+ return 0;
+}
+
+static int poll_read_bytes_blocking(struct thread_args *args, char *buf) {
+ return poll_read_bytes(args->fds.read_fd, buf, args->msg_size, 0);
+}
+
+static int poll_read_bytes_spin(struct thread_args *args, char *buf) {
+ return poll_read_bytes(args->fds.read_fd, buf, args->msg_size, 1);
+}
+
+#ifdef __linux__
+/* Call epoll_wait() to monitor a non-blocking fd */
+static int epoll_read_bytes(struct thread_args *args, char *buf, int spin) {
+ struct epoll_event ev;
+ size_t bytes_read = 0;
+ int err;
+ size_t read_size = args->msg_size;
+
+ do {
+ err = epoll_wait(args->epoll_fd, &ev, 1, spin ? 0 : -1);
+ if (err < 0) {
+ if (errno == EINTR) continue;
+ gpr_log(GPR_ERROR, "epoll_wait failed: %s", strerror(errno));
+ return -1;
+ }
+ if (err == 0 && spin) continue;
+ GPR_ASSERT(err == 1);
+ GPR_ASSERT(ev.events & EPOLLIN);
+ GPR_ASSERT(ev.data.fd == args->fds.read_fd);
+ do {
+ do {
+ err = read(args->fds.read_fd, buf + bytes_read, read_size - bytes_read);
+ } while (err < 0 && errno == EINTR);
+ if (errno == EAGAIN) break;
+ bytes_read += err;
+ /* TODO(klempner): This should really be doing an extra call after we are
+ done to ensure we see an EAGAIN */
+ } while (bytes_read < read_size);
+ } while (bytes_read < read_size);
+ GPR_ASSERT(bytes_read == read_size);
+ return 0;
+}
+
+static int epoll_read_bytes_blocking(struct thread_args *args, char *buf) {
+ return epoll_read_bytes(args, buf, 0);
+}
+
+static int epoll_read_bytes_spin(struct thread_args *args, char *buf) {
+ return epoll_read_bytes(args, buf, 1);
+}
+#endif /* __linux__ */
+
+/* Write out bytes.
+ At this point we only have one strategy, since in the common case these
+ writes go directly out to the kernel.
+ */
+static int blocking_write_bytes(struct thread_args *args, char *buf) {
+ int bytes_written = 0;
+ int err;
+ size_t write_size = args->msg_size;
+ do {
+ err = write(args->fds.write_fd, buf + bytes_written,
+ write_size - bytes_written);
+ if (err < 0) {
+ if (errno == EINTR) {
+ continue;
+ } else {
+ gpr_log(GPR_ERROR, "Read failed: %s", strerror(errno));
+ return -1;
+ }
+ } else {
+ bytes_written += err;
+ }
+ } while (bytes_written < write_size);
+ return 0;
+}
+
+/*
+ Initialization code
+
+ These are called at the beginning of the client and server thread, depending
+ on the scenario we're using.
+ */
+static int set_socket_nonblocking(thread_args *args) {
+ if (!grpc_set_socket_nonblocking(args->fds.read_fd, 1)) {
+ gpr_log(GPR_ERROR, "Unable to set socket nonblocking: %s", strerror(errno));
+ return -1;
+ }
+ if (!grpc_set_socket_nonblocking(args->fds.write_fd, 1)) {
+ gpr_log(GPR_ERROR, "Unable to set socket nonblocking: %s", strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+static int do_nothing(thread_args *args) { return 0; }
+
+/* Special case for epoll, where we need to create the fd ahead of time. */
+static int epoll_setup(thread_args *args) {
+ int epoll_fd;
+ struct epoll_event ev;
+ set_socket_nonblocking(args);
+ epoll_fd = epoll_create(1);
+ if (epoll_fd < 0) {
+ gpr_log(GPR_ERROR, "epoll_create: %s", strerror(errno));
+ return -1;
+ }
+
+ args->epoll_fd = epoll_fd;
+
+ ev.events = EPOLLIN | EPOLLET;
+ ev.data.fd = args->fds.read_fd;
+ if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, args->fds.read_fd, &ev) < 0) {
+ gpr_log(GPR_ERROR, "epoll_ctl: %s", strerror(errno));
+ }
+ return 0;
+}
+
+static void server_thread(thread_args *args) {
+ char *buf = malloc(args->msg_size);
+ if (args->setup(args) < 0) {
+ gpr_log(GPR_ERROR, "Setup failed");
+ }
+ for (;;) {
+ if (args->read_bytes(args, buf) < 0) {
+ gpr_log(GPR_ERROR, "Server read failed");
+ free(buf);
+ return;
+ }
+ if (args->write_bytes(args, buf) < 0) {
+ gpr_log(GPR_ERROR, "Server write failed");
+ free(buf);
+ return;
+ }
+ }
+}
+
+static void server_thread_wrap(void *arg) {
+ thread_args *args = arg;
+ server_thread(args);
+}
+
+static void print_histogram(gpr_histogram *histogram) {
+ /* TODO(klempner): Print more detailed information, such as detailed histogram
+ buckets */
+ gpr_log(GPR_INFO, "latency (50/95/99/99.9): %f/%f/%f/%f",
+ gpr_histogram_percentile(histogram, 50),
+ gpr_histogram_percentile(histogram, 95),
+ gpr_histogram_percentile(histogram, 99),
+ gpr_histogram_percentile(histogram, 99.9));
+}
+
+static double now() {
+ gpr_timespec tv = gpr_now();
+ return 1e9 * tv.tv_sec + tv.tv_nsec;
+}
+
+static void client_thread(thread_args *args) {
+ char *buf = calloc(args->msg_size, sizeof(char));
+ gpr_histogram *histogram = gpr_histogram_create(0.01, 60e9);
+ double start_time;
+ double end_time;
+ double interval;
+ const int kNumIters = 100000;
+ int i;
+
+ if (args->setup(args) < 0) {
+ gpr_log(GPR_ERROR, "Setup failed");
+ }
+ for (i = 0; i < kNumIters; ++i) {
+ start_time = now();
+ if (args->write_bytes(args, buf) < 0) {
+ gpr_log(GPR_ERROR, "Client write failed");
+ goto error;
+ }
+ if (args->read_bytes(args, buf) < 0) {
+ gpr_log(GPR_ERROR, "Client read failed");
+ goto error;
+ }
+ end_time = now();
+ if (i > kNumIters / 2) {
+ interval = end_time - start_time;
+ gpr_histogram_add(histogram, interval);
+ }
+ }
+ print_histogram(histogram);
+error:
+ free(buf);
+ gpr_histogram_destroy(histogram);
+}
+
+/* This roughly matches tcp_server's create_listening_socket */
+static int create_listening_socket(struct sockaddr *port, socklen_t len) {
+ int fd = socket(port->sa_family, SOCK_STREAM, 0);
+ if (fd < 0) {
+ gpr_log(GPR_ERROR, "Unable to create socket: %s", strerror(errno));
+ goto error;
+ }
+
+ if (!grpc_set_socket_cloexec(fd, 1) || !grpc_set_socket_low_latency(fd, 1) ||
+ !grpc_set_socket_reuse_addr(fd, 1)) {
+ gpr_log(GPR_ERROR, "Unable to configure socket %d: %s", fd,
+ strerror(errno));
+ goto error;
+ }
+
+ if (bind(fd, port, len) < 0) {
+ gpr_log(GPR_ERROR, "bind: %s", strerror(errno));
+ goto error;
+ }
+
+ if (listen(fd, 1) < 0) {
+ gpr_log(GPR_ERROR, "listen: %s", strerror(errno));
+ goto error;
+ }
+
+ if (getsockname(fd, port, &len) < 0) {
+ gpr_log(GPR_ERROR, "getsockname: %s", strerror(errno));
+ goto error;
+ }
+
+ return fd;
+
+error:
+ if (fd >= 0) {
+ close(fd);
+ }
+ return -1;
+}
+
+static int connect_client(struct sockaddr *addr, int len) {
+ int fd = socket(addr->sa_family, SOCK_STREAM, 0);
+ int err;
+ if (fd < 0) {
+ gpr_log(GPR_ERROR, "Unable to create socket: %s", strerror(errno));
+ goto error;
+ }
+
+ if (!grpc_set_socket_cloexec(fd, 1) || !grpc_set_socket_low_latency(fd, 1)) {
+ gpr_log(GPR_ERROR, "Failed to configure socket");
+ goto error;
+ }
+
+ do {
+ err = connect(fd, addr, len);
+ } while (err < 0 && errno == EINTR);
+
+ if (err < 0) {
+ gpr_log(GPR_ERROR, "connect error: %s", strerror(errno));
+ goto error;
+ }
+ return fd;
+
+error:
+ if (fd >= 0) {
+ close(fd);
+ }
+ return -1;
+}
+
+static int accept_server(int listen_fd) {
+ int fd = accept(listen_fd, NULL, NULL);
+ if (fd < 0) {
+ gpr_log(GPR_ERROR, "Accept failed: %s", strerror(errno));
+ return -1;
+ }
+ return fd;
+}
+
+static int create_sockets_tcp(fd_pair *client_fds, fd_pair *server_fds) {
+ int listen_fd = -1;
+ int client_fd = -1;
+ int server_fd = -1;
+
+ struct sockaddr_in port;
+ struct sockaddr *sa_port = (struct sockaddr *)&port;
+
+ port.sin_family = AF_INET;
+ port.sin_port = 0;
+ port.sin_addr.s_addr = INADDR_ANY;
+
+ listen_fd = create_listening_socket(sa_port, sizeof(port));
+ if (listen_fd == -1) {
+ gpr_log(GPR_ERROR, "Listen failed");
+ goto error;
+ }
+
+ client_fd = connect_client(sa_port, sizeof(port));
+ if (client_fd == -1) {
+ gpr_log(GPR_ERROR, "Connect failed");
+ goto error;
+ }
+
+ server_fd = accept_server(listen_fd);
+ if (server_fd == -1) {
+ gpr_log(GPR_ERROR, "Accept failed");
+ goto error;
+ }
+
+ client_fds->read_fd = client_fd;
+ client_fds->write_fd = client_fd;
+ server_fds->read_fd = server_fd;
+ server_fds->write_fd = server_fd;
+ close(listen_fd);
+ return 0;
+
+error:
+ if (listen_fd != -1) {
+ close(listen_fd);
+ }
+ if (client_fd != -1) {
+ close(client_fd);
+ }
+ if (server_fd != -1) {
+ close(server_fd);
+ }
+ return -1;
+}
+
+static int create_sockets_socketpair(fd_pair *client_fds, fd_pair *server_fds) {
+ int fds[2];
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) {
+ gpr_log(GPR_ERROR, "socketpair: %s", strerror(errno));
+ return -1;
+ }
+
+ client_fds->read_fd = fds[0];
+ client_fds->write_fd = fds[0];
+ server_fds->read_fd = fds[1];
+ server_fds->write_fd = fds[1];
+ return 0;
+}
+
+static int create_sockets_pipe(fd_pair *client_fds, fd_pair *server_fds) {
+ int cfds[2];
+ int sfds[2];
+ if (pipe(cfds) < 0) {
+ gpr_log(GPR_ERROR, "pipe: %s", strerror(errno));
+ return -1;
+ }
+
+ if (pipe(sfds) < 0) {
+ gpr_log(GPR_ERROR, "pipe: %s", strerror(errno));
+ return -1;
+ }
+
+ client_fds->read_fd = cfds[0];
+ client_fds->write_fd = cfds[1];
+ server_fds->read_fd = sfds[0];
+ server_fds->write_fd = sfds[1];
+ return 0;
+}
+
+static const char *read_strategy_usage =
+ "Strategy for doing reads, which is one of:\n"
+ " blocking: blocking read calls\n"
+ " same_thread_poll: poll() call on same thread \n"
+#ifdef __linux__
+ " same_thread_epoll: epoll_wait() on same thread \n"
+#endif
+ " spin_read: spinning non-blocking read() calls \n"
+ " spin_poll: spinning 0 timeout poll() calls \n"
+#ifdef __linux__
+ " spin_epoll: spinning 0 timeout epoll_wait() calls \n"
+#endif
+ "";
+
+static const char *socket_type_usage =
+ "Type of socket used, one of:\n"
+ " tcp: fds are endpoints of a TCP connection\n"
+ " socketpair: fds come from socketpair()\n"
+ " pipe: fds come from pipe()\n";
+
+void print_usage(char *argv0) {
+ fprintf(stderr, "%s usage:\n\n", argv0);
+ fprintf(stderr, "%s read_strategy socket_type msg_size\n\n", argv0);
+ fprintf(stderr, "where read_strategy is one of:\n");
+ fprintf(stderr, " blocking: blocking read calls\n");
+ fprintf(stderr, " same_thread_poll: poll() call on same thread \n");
+#ifdef __linux__
+ fprintf(stderr, " same_thread_epoll: epoll_wait() on same thread \n");
+#endif
+ fprintf(stderr, " spin_read: spinning non-blocking read() calls \n");
+ fprintf(stderr, " spin_poll: spinning 0 timeout poll() calls \n");
+#ifdef __linux__
+ fprintf(stderr, " spin_epoll: spinning 0 timeout epoll_wait() calls \n");
+#endif
+ fprintf(stderr, "and socket_type is one of:\n");
+ fprintf(stderr, " tcp: fds are endpoints of a TCP connection\n");
+ fprintf(stderr, " socketpair: fds come from socketpair()\n");
+ fprintf(stderr, " pipe: fds come from pipe()\n");
+}
+
+typedef struct test_strategy {
+ char *name;
+ int (*read_strategy)(struct thread_args *args, char *buf);
+ int (*setup)(struct thread_args *args);
+} test_strategy;
+
+static test_strategy test_strategies[] = {
+ {"blocking", blocking_read_bytes, do_nothing},
+ {"same_thread_poll", poll_read_bytes_blocking, set_socket_nonblocking},
+#ifdef __linux__
+ {"same_thread_epoll", epoll_read_bytes_blocking, epoll_setup},
+ {"spin_epoll", epoll_read_bytes_spin, epoll_setup},
+#endif /* __linux__ */
+ {"spin_read", spin_read_bytes, set_socket_nonblocking},
+ {"spin_poll", poll_read_bytes_spin, set_socket_nonblocking}};
+
+int main(int argc, char **argv) {
+ gpr_thd_id tid;
+ thread_args *client_args = malloc(sizeof(thread_args));
+ thread_args *server_args = malloc(sizeof(thread_args));
+ int msg_size = -1;
+ char *read_strategy = NULL;
+ char *socket_type = NULL;
+ int i;
+ const test_strategy *test_strategy = NULL;
+
+ gpr_cmdline *cmdline =
+ gpr_cmdline_create("low_level_ping_pong network benchmarking tool");
+
+ gpr_cmdline_add_int(cmdline, "msg_size", "Size of sent messages", &msg_size);
+ gpr_cmdline_add_string(cmdline, "read_strategy", read_strategy_usage,
+ &read_strategy);
+ gpr_cmdline_add_string(cmdline, "socket_type", socket_type_usage,
+ &socket_type);
+
+ gpr_cmdline_parse(cmdline, argc, argv);
+
+ if (read_strategy == NULL) {
+ read_strategy = "blocking";
+ }
+ if (socket_type == NULL) {
+ socket_type = "tcp";
+ }
+ if (msg_size == -1) {
+ msg_size = 50;
+ }
+
+ for (i = 0; i < sizeof(test_strategies) / sizeof(struct test_strategy); ++i) {
+ if (!strcmp(test_strategies[i].name, read_strategy)) {
+ test_strategy = &test_strategies[i];
+ }
+ }
+ if (test_strategy == NULL) {
+ fprintf(stderr, "Invalid read strategy %s\n", read_strategy);
+ return -1;
+ }
+
+ client_args->read_bytes = test_strategy->read_strategy;
+ client_args->write_bytes = blocking_write_bytes;
+ client_args->setup = test_strategy->setup;
+ server_args->read_bytes = test_strategy->read_strategy;
+ server_args->write_bytes = blocking_write_bytes;
+ server_args->setup = test_strategy->setup;
+
+ if (strcmp(socket_type, "tcp") == 0) {
+ create_sockets_tcp(&client_args->fds, &server_args->fds);
+ } else if (strcmp(socket_type, "socketpair") == 0) {
+ create_sockets_socketpair(&client_args->fds, &server_args->fds);
+ } else if (strcmp(socket_type, "pipe") == 0) {
+ create_sockets_pipe(&client_args->fds, &server_args->fds);
+ } else {
+ fprintf(stderr, "Invalid socket type %s\n", socket_type);
+ return -1;
+ }
+
+ if (msg_size <= 0) {
+ fprintf(stderr, "msg_size must be > 0\n");
+ print_usage(argv[0]);
+ return -1;
+ }
+
+ server_args->msg_size = msg_size;
+ client_args->msg_size = msg_size;
+
+ gpr_log(GPR_INFO, "Starting test %s %s %d", read_strategy, socket_type,
+ msg_size);
+
+ gpr_thd_new(&tid, server_thread_wrap, server_args, NULL);
+ client_thread(client_args);
+ gpr_cmdline_destroy(cmdline);
+ return 0;
+}
diff --git a/test/core/security/credentials_test.c b/test/core/security/credentials_test.c
new file mode 100644
index 0000000000..260b39b4d8
--- /dev/null
+++ b/test/core/security/credentials_test.c
@@ -0,0 +1,167 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include "src/core/security/credentials.h"
+
+#include "src/core/httpcli/httpcli.h"
+#include <grpc/support/log.h>
+#include <grpc/support/time.h>
+#include "test/core/util/test_config.h"
+
+#include <string.h>
+
+static grpc_httpcli_response http_response(int status, char *body) {
+ grpc_httpcli_response response;
+ memset(&response, 0, sizeof(grpc_httpcli_response));
+ response.status = status;
+ response.body = body;
+ response.body_length = strlen(body);
+ return response;
+}
+
+static void test_compute_engine_creds_parsing_ok(void) {
+ grpc_mdctx *ctx = grpc_mdctx_create();
+ grpc_mdelem *token_elem = NULL;
+ gpr_timespec token_lifetime;
+ grpc_httpcli_response response =
+ http_response(200,
+ "{\"access_token\":\"ya29.AHES6ZRN3-HlhAPya30GnW_bHSb_\","
+ " \"expires_in\":3599, "
+ " \"token_type\":\"Bearer\"}");
+ GPR_ASSERT(grpc_compute_engine_credentials_parse_server_response(
+ &response, ctx, &token_elem, &token_lifetime) ==
+ GRPC_CREDENTIALS_OK);
+ GPR_ASSERT(token_lifetime.tv_sec == 3599);
+ GPR_ASSERT(token_lifetime.tv_nsec == 0);
+ GPR_ASSERT(!strcmp(grpc_mdstr_as_c_string(token_elem->key), "Authorization"));
+ GPR_ASSERT(!strcmp(grpc_mdstr_as_c_string(token_elem->value),
+ "Bearer ya29.AHES6ZRN3-HlhAPya30GnW_bHSb_"));
+ grpc_mdelem_unref(token_elem);
+ grpc_mdctx_orphan(ctx);
+}
+
+static void test_compute_engine_creds_parsing_bad_http_status(void) {
+ grpc_mdctx *ctx = grpc_mdctx_create();
+ grpc_mdelem *token_elem = NULL;
+ gpr_timespec token_lifetime;
+ grpc_httpcli_response response =
+ http_response(401,
+ "{\"access_token\":\"ya29.AHES6ZRN3-HlhAPya30GnW_bHSb_\","
+ " \"expires_in\":3599, "
+ " \"token_type\":\"Bearer\"}");
+ GPR_ASSERT(grpc_compute_engine_credentials_parse_server_response(
+ &response, ctx, &token_elem, &token_lifetime) ==
+ GRPC_CREDENTIALS_ERROR);
+ grpc_mdctx_orphan(ctx);
+}
+
+static void test_compute_engine_creds_parsing_empty_http_body(void) {
+ grpc_mdctx *ctx = grpc_mdctx_create();
+ grpc_mdelem *token_elem = NULL;
+ gpr_timespec token_lifetime;
+ grpc_httpcli_response response = http_response(200, "");
+ GPR_ASSERT(grpc_compute_engine_credentials_parse_server_response(
+ &response, ctx, &token_elem, &token_lifetime) ==
+ GRPC_CREDENTIALS_ERROR);
+ grpc_mdctx_orphan(ctx);
+}
+
+static void test_compute_engine_creds_parsing_invalid_json(void) {
+ grpc_mdctx *ctx = grpc_mdctx_create();
+ grpc_mdelem *token_elem = NULL;
+ gpr_timespec token_lifetime;
+ grpc_httpcli_response response =
+ http_response(200,
+ "{\"access_token\":\"ya29.AHES6ZRN3-HlhAPya30GnW_bHSb_\","
+ " \"expires_in\":3599, "
+ " \"token_type\":\"Bearer\"");
+ GPR_ASSERT(grpc_compute_engine_credentials_parse_server_response(
+ &response, ctx, &token_elem, &token_lifetime) ==
+ GRPC_CREDENTIALS_ERROR);
+ grpc_mdctx_orphan(ctx);
+}
+
+static void test_compute_engine_creds_parsing_missing_token(void) {
+ grpc_mdctx *ctx = grpc_mdctx_create();
+ grpc_mdelem *token_elem = NULL;
+ gpr_timespec token_lifetime;
+ grpc_httpcli_response response = http_response(200,
+ "{"
+ " \"expires_in\":3599, "
+ " \"token_type\":\"Bearer\"}");
+ GPR_ASSERT(grpc_compute_engine_credentials_parse_server_response(
+ &response, ctx, &token_elem, &token_lifetime) ==
+ GRPC_CREDENTIALS_ERROR);
+ grpc_mdctx_orphan(ctx);
+}
+
+static void test_compute_engine_creds_parsing_missing_token_type(void) {
+ grpc_mdctx *ctx = grpc_mdctx_create();
+ grpc_mdelem *token_elem = NULL;
+ gpr_timespec token_lifetime;
+ grpc_httpcli_response response =
+ http_response(200,
+ "{\"access_token\":\"ya29.AHES6ZRN3-HlhAPya30GnW_bHSb_\","
+ " \"expires_in\":3599, "
+ "}");
+ GPR_ASSERT(grpc_compute_engine_credentials_parse_server_response(
+ &response, ctx, &token_elem, &token_lifetime) ==
+ GRPC_CREDENTIALS_ERROR);
+ grpc_mdctx_orphan(ctx);
+}
+
+static void test_compute_engine_creds_parsing_missing_token_lifetime(void) {
+ grpc_mdctx *ctx = grpc_mdctx_create();
+ grpc_mdelem *token_elem = NULL;
+ gpr_timespec token_lifetime;
+ grpc_httpcli_response response =
+ http_response(200,
+ "{\"access_token\":\"ya29.AHES6ZRN3-HlhAPya30GnW_bHSb_\","
+ " \"token_type\":\"Bearer\"}");
+ GPR_ASSERT(grpc_compute_engine_credentials_parse_server_response(
+ &response, ctx, &token_elem, &token_lifetime) ==
+ GRPC_CREDENTIALS_ERROR);
+ grpc_mdctx_orphan(ctx);
+}
+
+int main(int argc, char **argv) {
+ grpc_test_init(argc, argv);
+ test_compute_engine_creds_parsing_ok();
+ test_compute_engine_creds_parsing_bad_http_status();
+ test_compute_engine_creds_parsing_empty_http_body();
+ test_compute_engine_creds_parsing_invalid_json();
+ test_compute_engine_creds_parsing_missing_token();
+ test_compute_engine_creds_parsing_missing_token_type();
+ test_compute_engine_creds_parsing_missing_token_lifetime();
+ return 0;
+}
diff --git a/test/core/statistics/census_stub_test.c b/test/core/statistics/census_stub_test.c
new file mode 100644
index 0000000000..7d85550f70
--- /dev/null
+++ b/test/core/statistics/census_stub_test.c
@@ -0,0 +1,75 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "src/core/statistics/census_interface.h"
+#include "src/core/statistics/census_rpc_stats.h"
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include "test/core/util/test_config.h"
+
+/* Tests census noop stubs in a simulated rpc flow */
+void test_census_stubs() {
+ census_op_id op_id;
+ census_rpc_stats* stats = census_rpc_stats_create_empty();
+ census_aggregated_rpc_stats data_map;
+ /* Initializes census library at server start up time. */
+ census_init();
+ /* Starts tracing at the beginning of a rpc. */
+ op_id = census_tracing_start_op();
+ /* Appends custom annotations on a trace object. */
+ census_tracing_print(op_id, "annotation foo");
+ census_tracing_print(op_id, "annotation bar");
+ /* Appends method tag on the trace object. */
+ census_add_method_tag(op_id, "service_foo/method.bar");
+ /* Either record client side stats or server side stats associated with the
+ op_id. Here for testing purpose, we record both. */
+ census_record_rpc_client_stats(op_id, stats);
+ census_record_rpc_server_stats(op_id, stats);
+ /* Ends a tracing. */
+ census_tracing_end_op(op_id);
+ /* In process stats queries. */
+ census_get_server_stats(&data_map);
+ census_get_client_stats(&data_map);
+ census_aggregated_rpc_stats_destroy(&data_map);
+ gpr_free(stats);
+ census_shutdown();
+}
+
+int main(int argc, char** argv) {
+ grpc_test_init(argc, argv);
+ test_census_stubs();
+ return 0;
+}
diff --git a/test/core/statistics/hash_table_test.c b/test/core/statistics/hash_table_test.c
new file mode 100644
index 0000000000..fb75de520e
--- /dev/null
+++ b/test/core/statistics/hash_table_test.c
@@ -0,0 +1,288 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "src/core/statistics/hash_table.h"
+
+#include "src/core/support/murmur_hash.h"
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/time.h>
+#include "test/core/util/test_config.h"
+
+static gpr_uint64 hash64(const void* k) {
+ size_t len = strlen(k);
+ gpr_uint64 higher = gpr_murmur_hash3((const char*)k, len / 2, 0);
+ return higher << 32 |
+ gpr_murmur_hash3((const char*)(k) + len / 2, len - len / 2, 0);
+}
+
+static int cmp_str_keys(const void* k1, const void* k2) {
+ return strcmp((const char*)k1, (const char*)k2);
+}
+
+static gpr_uint64 force_collision(const void* k) {
+ return (1997 + hash64(k) % 3);
+}
+
+static void free_data(void* data) { gpr_free(data); }
+
+/* Basic tests that empty hash table can be created and destroyed. */
+static void test_create_table() {
+ /* Create table with uint64 key type */
+ census_ht* ht = NULL;
+ census_ht_option ht_options = {CENSUS_HT_UINT64, 1999, NULL, NULL, NULL,
+ NULL};
+ ht = census_ht_create(&ht_options);
+ GPR_ASSERT(ht != NULL);
+ GPR_ASSERT(census_ht_get_size(ht) == 0);
+ census_ht_destroy(ht);
+ /* Create table with pointer key type */
+ ht = NULL;
+ ht_options.key_type = CENSUS_HT_POINTER;
+ ht_options.hash = &hash64;
+ ht_options.compare_keys = &cmp_str_keys;
+ ht = census_ht_create(&ht_options);
+ GPR_ASSERT(ht != NULL);
+ GPR_ASSERT(census_ht_get_size(ht) == 0);
+ census_ht_destroy(ht);
+}
+
+static void test_table_with_int_key() {
+ census_ht_option opt = {CENSUS_HT_UINT64, 7, NULL, NULL, NULL, NULL};
+ census_ht* ht = census_ht_create(&opt);
+ gpr_uint64 i = 0;
+ gpr_uint64 sum_of_keys = 0;
+ size_t num_elements;
+ census_ht_kv* elements = NULL;
+ GPR_ASSERT(ht != NULL);
+ GPR_ASSERT(census_ht_get_size(ht) == 0);
+ elements = census_ht_get_all_elements(ht, &num_elements);
+ GPR_ASSERT(num_elements == 0);
+ GPR_ASSERT(elements == NULL);
+ for (i = 0; i < 20; ++i) {
+ census_ht_key key;
+ key.val = i;
+ census_ht_insert(ht, key, (void*)i);
+ GPR_ASSERT(census_ht_get_size(ht) == i + 1);
+ }
+ for (i = 0; i < 20; i++) {
+ gpr_uint64* val = NULL;
+ census_ht_key key;
+ key.val = i;
+ val = census_ht_find(ht, key);
+ GPR_ASSERT(val == (void*)i);
+ }
+ elements = census_ht_get_all_elements(ht, &num_elements);
+ GPR_ASSERT(elements != NULL);
+ GPR_ASSERT(num_elements == 20);
+ for (i = 0; i < num_elements; i++) {
+ sum_of_keys += elements[i].k.val;
+ }
+ GPR_ASSERT(sum_of_keys == 190);
+ gpr_free(elements);
+ census_ht_destroy(ht);
+}
+
+/* Test that there is no memory leak when keys and values are owned by table. */
+static void test_value_and_key_deleter() {
+ census_ht_option opt = {CENSUS_HT_POINTER, 7, &hash64, &cmp_str_keys,
+ &free_data, &free_data};
+ census_ht* ht = census_ht_create(&opt);
+ census_ht_key key;
+ char* val;
+ key.ptr = gpr_malloc(100);
+ val = gpr_malloc(10);
+ strcpy(val, "value");
+ strcpy(key.ptr, "some string as a key");
+ GPR_ASSERT(ht != NULL);
+ GPR_ASSERT(census_ht_get_size(ht) == 0);
+ census_ht_insert(ht, key, val);
+ GPR_ASSERT(census_ht_get_size(ht) == 1);
+ census_ht_destroy(ht);
+}
+
+/* Test simple insert and erase operations. */
+static void test_simple_add_and_erase() {
+ census_ht_option opt = {CENSUS_HT_UINT64, 7, NULL, NULL, NULL, NULL};
+ census_ht* ht = census_ht_create(&opt);
+ GPR_ASSERT(ht != NULL);
+ GPR_ASSERT(census_ht_get_size(ht) == 0);
+ {
+ census_ht_key key;
+ int val = 3;
+ key.val = 2;
+ census_ht_insert(ht, key, (void*)&val);
+ GPR_ASSERT(census_ht_get_size(ht) == 1);
+ census_ht_erase(ht, key);
+ GPR_ASSERT(census_ht_get_size(ht) == 0);
+ /* Erasing a key from an empty table should be noop. */
+ census_ht_erase(ht, key);
+ GPR_ASSERT(census_ht_get_size(ht) == 0);
+ /* Erasing a non-existant key from a table should be noop. */
+ census_ht_insert(ht, key, (void*)&val);
+ key.val = 3;
+ census_ht_insert(ht, key, (void*)&val);
+ key.val = 9;
+ census_ht_insert(ht, key, (void*)&val);
+ GPR_ASSERT(census_ht_get_size(ht) == 3);
+ key.val = 1;
+ census_ht_erase(ht, key);
+ /* size unchanged after deleting non-existant key. */
+ GPR_ASSERT(census_ht_get_size(ht) == 3);
+ /* size decrease by 1 after deleting an existant key. */
+ key.val = 2;
+ census_ht_erase(ht, key);
+ GPR_ASSERT(census_ht_get_size(ht) == 2);
+ }
+ census_ht_destroy(ht);
+}
+
+static void test_insertion_and_deletion_with_high_collision_rate() {
+ census_ht_option opt = {CENSUS_HT_POINTER, 13, &force_collision,
+ &cmp_str_keys, NULL, NULL};
+ census_ht* ht = census_ht_create(&opt);
+ char key_str[1000][10];
+ gpr_uint64 val = 0;
+ int i = 0;
+ for (i = 0; i < 1000; i++) {
+ census_ht_key key;
+ key.ptr = key_str[i];
+ sprintf(key_str[i], "%d", i);
+ census_ht_insert(ht, key, (void*)(&val));
+ printf("%d\n", i);
+ GPR_ASSERT(census_ht_get_size(ht) == (i + 1));
+ }
+ for (i = 0; i < 1000; i++) {
+ census_ht_key key;
+ key.ptr = key_str[i];
+ census_ht_erase(ht, key);
+ GPR_ASSERT(census_ht_get_size(ht) == (999 - i));
+ }
+ census_ht_destroy(ht);
+}
+
+static void test_table_with_string_key() {
+ census_ht_option opt = {CENSUS_HT_POINTER, 7, &hash64, &cmp_str_keys, NULL,
+ NULL};
+ census_ht* ht = census_ht_create(&opt);
+ const char* keys[] = {"k1", "a", "000", "apple",
+ "banana_a_long_long_long_banana", "%$", "111", "foo",
+ "b"};
+ const int vals[] = {0, 1, 2, 3, 4, 5, 6, 7, 8};
+ int i = 0;
+ GPR_ASSERT(ht != NULL);
+ GPR_ASSERT(census_ht_get_size(ht) == 0);
+ for (i = 0; i < 9; i++) {
+ census_ht_key key;
+ key.ptr = (void*)(keys[i]);
+ census_ht_insert(ht, key, (void*)(vals + i));
+ }
+ GPR_ASSERT(census_ht_get_size(ht) == 9);
+ for (i = 0; i < 9; i++) {
+ census_ht_key key;
+ int* val_ptr;
+ key.ptr = (void*)(keys[i]);
+ val_ptr = census_ht_find(ht, key);
+ GPR_ASSERT(*val_ptr == vals[i]);
+ }
+ {
+ /* inserts duplicate keys */
+ census_ht_key key;
+ int* val_ptr = NULL;
+ key.ptr = (void*)(keys[2]);
+ census_ht_insert(ht, key, (void*)(vals + 8));
+ /* expect value to be over written by new insertion */
+ GPR_ASSERT(census_ht_get_size(ht) == 9);
+ val_ptr = census_ht_find(ht, key);
+ GPR_ASSERT(*val_ptr == vals[8]);
+ }
+ for (i = 0; i < 9; i++) {
+ census_ht_key key;
+ int* val_ptr;
+ gpr_uint32 expected_tbl_sz = 9 - i;
+ GPR_ASSERT(census_ht_get_size(ht) == expected_tbl_sz);
+ key.ptr = (void*)(keys[i]);
+ val_ptr = census_ht_find(ht, key);
+ GPR_ASSERT(val_ptr != NULL);
+ census_ht_erase(ht, key);
+ GPR_ASSERT(census_ht_get_size(ht) == expected_tbl_sz - 1);
+ val_ptr = census_ht_find(ht, key);
+ GPR_ASSERT(val_ptr == NULL);
+ }
+ census_ht_destroy(ht);
+}
+
+static void test_insertion_with_same_key() {
+ census_ht_option opt = {CENSUS_HT_UINT64, 11, NULL, NULL, NULL, NULL};
+ census_ht* ht = census_ht_create(&opt);
+ census_ht_key key;
+ const char vals[] = {'a', 'b', 'c'};
+ char* val_ptr;
+ key.val = 3;
+ census_ht_insert(ht, key, (void*)&(vals[0]));
+ GPR_ASSERT(census_ht_get_size(ht) == 1);
+ val_ptr = (char*)census_ht_find(ht, key);
+ GPR_ASSERT(val_ptr != NULL);
+ GPR_ASSERT(*val_ptr == 'a');
+ key.val = 4;
+ val_ptr = (char*)census_ht_find(ht, key);
+ GPR_ASSERT(val_ptr == NULL);
+ key.val = 3;
+ census_ht_insert(ht, key, (void*)&(vals[1]));
+ GPR_ASSERT(census_ht_get_size(ht) == 1);
+ val_ptr = (char*)census_ht_find(ht, key);
+ GPR_ASSERT(val_ptr != NULL);
+ GPR_ASSERT(*val_ptr == 'b');
+ census_ht_insert(ht, key, (void*)&(vals[2]));
+ GPR_ASSERT(census_ht_get_size(ht) == 1);
+ val_ptr = (char*)census_ht_find(ht, key);
+ GPR_ASSERT(val_ptr != NULL);
+ GPR_ASSERT(*val_ptr == 'c');
+ census_ht_destroy(ht);
+}
+
+int main(int argc, char** argv) {
+ grpc_test_init(argc, argv);
+ test_create_table();
+ test_simple_add_and_erase();
+ test_table_with_int_key();
+ test_table_with_string_key();
+ test_value_and_key_deleter();
+ test_insertion_with_same_key();
+ test_insertion_and_deletion_with_high_collision_rate();
+ return 0;
+}
diff --git a/test/core/statistics/log_tests.c b/test/core/statistics/log_tests.c
new file mode 100644
index 0000000000..f0cbdbdf70
--- /dev/null
+++ b/test/core/statistics/log_tests.c
@@ -0,0 +1,568 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include "src/core/statistics/log.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "src/core/support/cpu.h"
+#include <grpc/support/log.h>
+#include <grpc/support/port_platform.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/thd.h>
+#include <grpc/support/time.h>
+
+/* Fills in 'record' of size 'size'. Each byte in record is filled in with the
+ same value. The value is extracted from 'record' pointer. */
+static void write_record(char* record, size_t size) {
+ char data = (gpr_uintptr)record % 255;
+ memset(record, data, size);
+}
+
+/* Reads fixed size records. Returns the number of records read in
+ 'num_records'. */
+static void read_records(size_t record_size, const char* buffer,
+ size_t buffer_size, gpr_int32* num_records) {
+ gpr_int32 ix;
+ GPR_ASSERT(buffer_size >= record_size);
+ GPR_ASSERT(buffer_size % record_size == 0);
+ *num_records = buffer_size / record_size;
+ for (ix = 0; ix < *num_records; ++ix) {
+ gpr_int32 jx;
+ const char* record = buffer + (record_size * ix);
+ char data = (gpr_uintptr)record % 255;
+ for (jx = 0; jx < record_size; ++jx) {
+ GPR_ASSERT(data == record[jx]);
+ }
+ }
+}
+
+/* Tries to write the specified number of records. Stops when the log gets
+ full. Returns the number of records written. Spins for random
+ number of times, up to 'max_spin_count', between writes. */
+static size_t write_records_to_log(int writer_id, gpr_int32 record_size,
+ gpr_int32 num_records,
+ gpr_int32 max_spin_count) {
+ gpr_int32 ix;
+ int counter = 0;
+ for (ix = 0; ix < num_records; ++ix) {
+ gpr_int32 jx;
+ gpr_int32 spin_count = max_spin_count ? rand() % max_spin_count : 0;
+ char* record;
+ if (counter++ == num_records / 10) {
+ printf(" Writer %d: %d out of %d written\n", writer_id, ix,
+ num_records);
+ counter = 0;
+ }
+ record = (char*)(census_log_start_write(record_size));
+ if (record == NULL) {
+ return ix;
+ }
+ write_record(record, record_size);
+ census_log_end_write(record, record_size);
+ for (jx = 0; jx < spin_count; ++jx) {
+ GPR_ASSERT(jx >= 0);
+ }
+ }
+ return num_records;
+}
+
+/* Performs a single read iteration. Returns the number of records read. */
+static size_t perform_read_iteration(size_t record_size) {
+ const void* read_buffer = NULL;
+ size_t bytes_available;
+ size_t records_read = 0;
+ census_log_init_reader();
+ while ((read_buffer = census_log_read_next(&bytes_available))) {
+ gpr_int32 num_records = 0;
+ read_records(record_size, (const char*)read_buffer, bytes_available,
+ &num_records);
+ records_read += num_records;
+ }
+ return records_read;
+}
+
+/* Asserts that the log is empty. */
+static void assert_log_empty() {
+ size_t bytes_available;
+ census_log_init_reader();
+ GPR_ASSERT(census_log_read_next(&bytes_available) == NULL);
+}
+
+/* Given log size and record size, computes the minimum usable space. */
+static size_t min_usable_space(size_t log_size, size_t record_size) {
+ gpr_int32 num_blocks = log_size / CENSUS_LOG_MAX_RECORD_SIZE;
+ gpr_int32 waste_per_block = CENSUS_LOG_MAX_RECORD_SIZE % record_size;
+ /* In the worst case, all except one core-local block is empty. */
+ return (log_size - ((gpr_cpu_num_cores() - 1) * CENSUS_LOG_MAX_RECORD_SIZE) -
+ ((num_blocks - gpr_cpu_num_cores() - 1) * waste_per_block));
+}
+
+/* Fills the log and verifies data. If 'no fragmentation' is true, records
+ are sized such that CENSUS_LOG_2_MAX_RECORD_SIZE is a multiple of record
+ size. If not a circular log, verifies that the number of records written
+ match the number of records read. */
+static void fill_log(size_t log_size, int no_fragmentation, int circular_log) {
+ int size;
+ gpr_int32 records_written;
+ gpr_int32 usable_space;
+ gpr_int32 records_read;
+ if (no_fragmentation) {
+ int log2size = rand() % (CENSUS_LOG_2_MAX_RECORD_SIZE + 1);
+ size = (1 << log2size);
+ } else {
+ while (1) {
+ size = 1 + (rand() % CENSUS_LOG_MAX_RECORD_SIZE);
+ if (CENSUS_LOG_MAX_RECORD_SIZE % size) {
+ break;
+ }
+ }
+ }
+ printf(" Fill record size: %d\n", size);
+ records_written = write_records_to_log(
+ 0 /* writer id */, size, (log_size / size) * 2, 0 /* spin count */);
+ usable_space = min_usable_space(log_size, size);
+ GPR_ASSERT(usable_space > 0);
+ GPR_ASSERT(records_written * size >= usable_space);
+ records_read = perform_read_iteration(size);
+ if (!circular_log) {
+ GPR_ASSERT(records_written == records_read);
+ }
+ assert_log_empty();
+}
+
+/* Structure to pass args to writer_thread */
+typedef struct writer_thread_args {
+ /* Index of this thread in the writers vector. */
+ int index;
+ /* Record size. */
+ size_t record_size;
+ /* Number of records to write. */
+ gpr_int32 num_records;
+ /* Used to signal when writer is complete */
+ gpr_cv* done;
+ gpr_mu* mu;
+ int* count;
+} writer_thread_args;
+
+/* Writes the given number of records of random size (up to kMaxRecordSize) and
+ random data to the specified log. */
+static void writer_thread(void* arg) {
+ writer_thread_args* args = (writer_thread_args*)arg;
+ /* Maximum number of times to spin between writes. */
+ static const gpr_int32 MAX_SPIN_COUNT = 50;
+ int records_written = 0;
+ printf(" Writer: %d\n", args->index);
+ while (records_written < args->num_records) {
+ records_written += write_records_to_log(args->index, args->record_size,
+ args->num_records - records_written,
+ MAX_SPIN_COUNT);
+ if (records_written < args->num_records) {
+ /* Ran out of log space. Sleep for a bit and let the reader catch up.
+ This should never happen for circular logs. */
+ printf(" Writer stalled due to out-of-space: %d out of %d written\n",
+ records_written, args->num_records);
+ gpr_sleep_until(gpr_time_add(gpr_now(), gpr_time_from_micros(10000)));
+ }
+ }
+ /* Done. Decrement count and signal. */
+ gpr_mu_lock(args->mu);
+ (*args->count)--;
+ gpr_cv_broadcast(args->done);
+ printf(" Writer done: %d\n", args->index);
+ gpr_mu_unlock(args->mu);
+}
+
+/* struct to pass args to reader_thread */
+typedef struct reader_thread_args {
+ /* Record size. */
+ size_t record_size;
+ /* Interval between read iterations. */
+ gpr_int32 read_iteration_interval_in_msec;
+ /* Total number of records. */
+ gpr_int32 total_records;
+ /* Signalled when reader should stop. */
+ gpr_cv stop;
+ int stop_flag;
+ /* Used to signal when reader has finished */
+ gpr_cv* done;
+ gpr_mu* mu;
+ int running;
+} reader_thread_args;
+
+/* Reads and verifies the specified number of records. Reader can also be
+ stopped via gpr_cv_signal(&args->stop). Sleeps for 'read_interval_in_msec'
+ between read iterations. */
+static void reader_thread(void* arg) {
+ gpr_int32 records_read = 0;
+ reader_thread_args* args = (reader_thread_args*)arg;
+ gpr_int32 num_iterations = 0;
+ gpr_timespec interval;
+ int counter = 0;
+ printf(" Reader starting\n");
+ interval = gpr_time_from_micros(args->read_iteration_interval_in_msec * 1000);
+ gpr_mu_lock(args->mu);
+ while (!args->stop_flag && records_read < args->total_records) {
+ gpr_cv_wait(&args->stop, args->mu, interval);
+ if (!args->stop_flag) {
+ records_read += perform_read_iteration(args->record_size);
+ GPR_ASSERT(records_read <= args->total_records);
+ if (counter++ == 100000) {
+ printf(" Reader: %d out of %d read\n", records_read,
+ args->total_records);
+ counter = 0;
+ }
+ ++num_iterations;
+ }
+ }
+ /* Done */
+ args->running = 0;
+ gpr_cv_broadcast(args->done);
+ printf(" Reader: records: %d, iterations: %d\n", records_read,
+ num_iterations);
+ gpr_mu_unlock(args->mu);
+}
+
+/* Creates NUM_WRITERS writers where each writer writes NUM_RECORDS_PER_WRITER
+ records. Also, starts a reader that iterates over and reads blocks every
+ READ_ITERATION_INTERVAL_IN_MSEC. */
+/* Number of writers. */
+#define NUM_WRITERS 5
+static void multiple_writers_single_reader(int circular_log) {
+ /* Sleep interval between read iterations. */
+ static const gpr_int32 READ_ITERATION_INTERVAL_IN_MSEC = 10;
+ /* Number of records written by each writer. */
+ static const gpr_int32 NUM_RECORDS_PER_WRITER = 10 * 1024 * 1024;
+ /* Maximum record size. */
+ static const size_t MAX_RECORD_SIZE = 10;
+ int ix;
+ gpr_thd_id id;
+ gpr_cv writers_done;
+ int writers_count = NUM_WRITERS;
+ gpr_mu writers_mu; /* protects writers_done and writers_count */
+ writer_thread_args writers[NUM_WRITERS];
+ gpr_cv reader_done;
+ gpr_mu reader_mu; /* protects reader_done and reader.running */
+ reader_thread_args reader;
+ gpr_int32 record_size = 1 + rand() % MAX_RECORD_SIZE;
+ printf(" Record size: %d\n", record_size);
+ /* Create and start writers. */
+ gpr_cv_init(&writers_done);
+ gpr_mu_init(&writers_mu);
+ for (ix = 0; ix < NUM_WRITERS; ++ix) {
+ writers[ix].index = ix;
+ writers[ix].record_size = record_size;
+ writers[ix].num_records = NUM_RECORDS_PER_WRITER;
+ writers[ix].done = &writers_done;
+ writers[ix].count = &writers_count;
+ writers[ix].mu = &writers_mu;
+ gpr_thd_new(&id, &writer_thread, &writers[ix], NULL);
+ }
+ /* Start reader. */
+ reader.record_size = record_size;
+ reader.read_iteration_interval_in_msec = READ_ITERATION_INTERVAL_IN_MSEC;
+ reader.total_records = NUM_WRITERS * NUM_RECORDS_PER_WRITER;
+ reader.stop_flag = 0;
+ gpr_cv_init(&reader.stop);
+ gpr_cv_init(&reader_done);
+ reader.done = &reader_done;
+ gpr_mu_init(&reader_mu);
+ reader.mu = &reader_mu;
+ reader.running = 1;
+ gpr_thd_new(&id, &reader_thread, &reader, NULL);
+ /* Wait for writers to finish. */
+ gpr_mu_lock(&writers_mu);
+ while (writers_count != 0) {
+ gpr_cv_wait(&writers_done, &writers_mu, gpr_inf_future);
+ }
+ gpr_mu_unlock(&writers_mu);
+ gpr_mu_destroy(&writers_mu);
+ gpr_cv_destroy(&writers_done);
+ gpr_mu_lock(&reader_mu);
+ if (circular_log) {
+ /* Stop reader. */
+ reader.stop_flag = 1;
+ gpr_cv_signal(&reader.stop);
+ }
+ /* wait for reader to finish */
+ while (reader.running) {
+ gpr_cv_wait(&reader_done, &reader_mu, gpr_inf_future);
+ }
+ if (circular_log) {
+ /* Assert that there were no out-of-space errors. */
+ GPR_ASSERT(0 == census_log_out_of_space_count());
+ }
+ gpr_mu_unlock(&reader_mu);
+ gpr_mu_destroy(&reader_mu);
+ gpr_cv_destroy(&reader_done);
+ printf(" Reader: finished\n");
+}
+
+/* Log sizes to use for all tests. */
+#define LOG_SIZE_IN_MB 1
+#define LOG_SIZE_IN_BYTES (LOG_SIZE_IN_MB << 20)
+
+static void setup_test(int circular_log) {
+ census_log_initialize(LOG_SIZE_IN_MB, circular_log);
+ GPR_ASSERT(census_log_remaining_space() == LOG_SIZE_IN_BYTES);
+}
+
+/* Attempts to create a record of invalid size (size >
+ CENSUS_LOG_MAX_RECORD_SIZE). */
+void test_invalid_record_size() {
+ static const size_t INVALID_SIZE = CENSUS_LOG_MAX_RECORD_SIZE + 1;
+ static const size_t VALID_SIZE = 1;
+ void* record;
+ printf("Starting test: invalid record size\n");
+ setup_test(0);
+ record = census_log_start_write(INVALID_SIZE);
+ GPR_ASSERT(record == NULL);
+ /* Now try writing a valid record. */
+ record = census_log_start_write(VALID_SIZE);
+ GPR_ASSERT(record != NULL);
+ census_log_end_write(record, VALID_SIZE);
+ /* Verifies that available space went down by one block. In theory, this
+ check can fail if the thread is context switched to a new CPU during the
+ start_write execution (multiple blocks get allocated), but this has not
+ been observed in practice. */
+ GPR_ASSERT(LOG_SIZE_IN_BYTES - CENSUS_LOG_MAX_RECORD_SIZE ==
+ census_log_remaining_space());
+ census_log_shutdown();
+}
+
+/* Tests end_write() with a different size than what was specified in
+ start_write(). */
+void test_end_write_with_different_size() {
+ static const size_t START_WRITE_SIZE = 10;
+ static const size_t END_WRITE_SIZE = 7;
+ void* record_written;
+ const void* record_read;
+ size_t bytes_available;
+ printf("Starting test: end write with different size\n");
+ setup_test(0);
+ record_written = census_log_start_write(START_WRITE_SIZE);
+ GPR_ASSERT(record_written != NULL);
+ census_log_end_write(record_written, END_WRITE_SIZE);
+ census_log_init_reader();
+ record_read = census_log_read_next(&bytes_available);
+ GPR_ASSERT(record_written == record_read);
+ GPR_ASSERT(END_WRITE_SIZE == bytes_available);
+ assert_log_empty();
+ census_log_shutdown();
+}
+
+/* Verifies that pending records are not available via read_next(). */
+void test_read_pending_record() {
+ static const size_t PR_RECORD_SIZE = 1024;
+ size_t bytes_available;
+ const void* record_read;
+ void* record_written;
+ printf("Starting test: read pending record\n");
+ setup_test(0);
+ /* Start a write. */
+ record_written = census_log_start_write(PR_RECORD_SIZE);
+ GPR_ASSERT(record_written != NULL);
+ /* As write is pending, read should fail. */
+ census_log_init_reader();
+ record_read = census_log_read_next(&bytes_available);
+ GPR_ASSERT(record_read == NULL);
+ /* A read followed by end_write() should succeed. */
+ census_log_end_write(record_written, PR_RECORD_SIZE);
+ census_log_init_reader();
+ record_read = census_log_read_next(&bytes_available);
+ GPR_ASSERT(record_written == record_read);
+ GPR_ASSERT(PR_RECORD_SIZE == bytes_available);
+ assert_log_empty();
+ census_log_shutdown();
+}
+
+/* Tries reading beyond pending write. */
+void test_read_beyond_pending_record() {
+ /* Start a write. */
+ gpr_int32 incomplete_record_size = 10;
+ gpr_int32 complete_record_size = 20;
+ size_t bytes_available;
+ void* complete_record;
+ const void* record_read;
+ void* incomplete_record;
+ printf("Starting test: read beyond pending record\n");
+ setup_test(0);
+ incomplete_record = census_log_start_write(incomplete_record_size);
+ GPR_ASSERT(incomplete_record != NULL);
+ complete_record = census_log_start_write(complete_record_size);
+ GPR_ASSERT(complete_record != NULL);
+ GPR_ASSERT(complete_record != incomplete_record);
+ census_log_end_write(complete_record, complete_record_size);
+ /* Now iterate over blocks to read completed records. */
+ census_log_init_reader();
+ record_read = census_log_read_next(&bytes_available);
+ GPR_ASSERT(complete_record == record_read);
+ GPR_ASSERT(complete_record_size == bytes_available);
+ /* Complete first record. */
+ census_log_end_write(incomplete_record, incomplete_record_size);
+ /* Have read past the incomplete record, so read_next() should return NULL. */
+ /* NB: this test also assumes our thread did not get switched to a different
+ CPU between the two start_write calls */
+ record_read = census_log_read_next(&bytes_available);
+ GPR_ASSERT(record_read == NULL);
+ /* Reset reader to get the newly completed record. */
+ census_log_init_reader();
+ record_read = census_log_read_next(&bytes_available);
+ GPR_ASSERT(incomplete_record == record_read);
+ GPR_ASSERT(incomplete_record_size == bytes_available);
+ assert_log_empty();
+ census_log_shutdown();
+}
+
+/* Tests scenario where block being read is detached from a core and put on the
+ dirty list. */
+void test_detached_while_reading() {
+ static const size_t DWR_RECORD_SIZE = 10;
+ size_t bytes_available;
+ const void* record_read;
+ void* record_written;
+ gpr_int32 block_read = 0;
+ printf("Starting test: detached while reading\n");
+ setup_test(0);
+ /* Start a write. */
+ record_written = census_log_start_write(DWR_RECORD_SIZE);
+ GPR_ASSERT(record_written != NULL);
+ census_log_end_write(record_written, DWR_RECORD_SIZE);
+ /* Read this record. */
+ census_log_init_reader();
+ record_read = census_log_read_next(&bytes_available);
+ GPR_ASSERT(record_read != NULL);
+ GPR_ASSERT(DWR_RECORD_SIZE == bytes_available);
+ /* Now fill the log. This will move the block being read from core-local
+ array to the dirty list. */
+ while ((record_written = census_log_start_write(DWR_RECORD_SIZE))) {
+ census_log_end_write(record_written, DWR_RECORD_SIZE);
+ }
+
+ /* In this iteration, read_next() should only traverse blocks in the
+ core-local array. Therefore, we expect at most gpr_cpu_num_cores() more
+ blocks. As log is full, if read_next() is traversing the dirty list, we
+ will get more than gpr_cpu_num_cores() blocks. */
+ while ((record_read = census_log_read_next(&bytes_available))) {
+ ++block_read;
+ GPR_ASSERT(block_read <= gpr_cpu_num_cores());
+ }
+ census_log_shutdown();
+}
+
+/* Fills non-circular log with records sized such that size is a multiple of
+ CENSUS_LOG_MAX_RECORD_SIZE (no per-block fragmentation). */
+void test_fill_log_no_fragmentation() {
+ const int circular = 0;
+ printf("Starting test: fill log no fragmentation\n");
+ setup_test(circular);
+ fill_log(LOG_SIZE_IN_BYTES, 1 /* no fragmentation */, circular);
+ census_log_shutdown();
+}
+
+/* Fills circular log with records sized such that size is a multiple of
+ CENSUS_LOG_MAX_RECORD_SIZE (no per-block fragmentation). */
+void test_fill_circular_log_no_fragmentation() {
+ const int circular = 1;
+ printf("Starting test: fill circular log no fragmentation\n");
+ setup_test(circular);
+ fill_log(LOG_SIZE_IN_BYTES, 1 /* no fragmentation */, circular);
+ census_log_shutdown();
+}
+
+/* Fills non-circular log with records that may straddle end of a block. */
+void test_fill_log_with_straddling_records() {
+ const int circular = 0;
+ printf("Starting test: fill log with straddling records\n");
+ setup_test(circular);
+ fill_log(LOG_SIZE_IN_BYTES, 0 /* block straddling records */, circular);
+ census_log_shutdown();
+}
+
+/* Fills circular log with records that may straddle end of a block. */
+void test_fill_circular_log_with_straddling_records() {
+ const int circular = 1;
+ printf("Starting test: fill circular log with straddling records\n");
+ setup_test(circular);
+ fill_log(LOG_SIZE_IN_BYTES, 0 /* block straddling records */, circular);
+ census_log_shutdown();
+}
+
+/* Tests scenario where multiple writers and a single reader are using a log
+ that is configured to discard old records. */
+void test_multiple_writers_circular_log() {
+ const int circular = 1;
+ printf("Starting test: multiple writers circular log\n");
+ setup_test(circular);
+ multiple_writers_single_reader(circular);
+ census_log_shutdown();
+}
+
+/* Tests scenario where multiple writers and a single reader are using a log
+ that is configured to discard old records. */
+void test_multiple_writers() {
+ const int circular = 0;
+ printf("Starting test: multiple writers\n");
+ setup_test(circular);
+ multiple_writers_single_reader(circular);
+ census_log_shutdown();
+}
+
+void test_performance() {
+ int write_size = 1;
+ for (; write_size < CENSUS_LOG_MAX_RECORD_SIZE; write_size *= 2) {
+ gpr_timespec write_time;
+ gpr_timespec start_time;
+ double write_time_micro = 0.0;
+ int nrecords = 0;
+ setup_test(0);
+ start_time = gpr_now();
+ while (1) {
+ void* record = census_log_start_write(write_size);
+ if (record == NULL) {
+ break;
+ }
+ census_log_end_write(record, write_size);
+ nrecords++;
+ }
+ write_time = gpr_time_sub(gpr_now(), start_time);
+ write_time_micro = write_time.tv_sec * 1000000 + write_time.tv_nsec / 1000;
+ census_log_shutdown();
+ printf(
+ "Wrote %d %d byte records in %.3g microseconds: %g records/us "
+ "(%g ns/record), %g gigabytes/s\n",
+ nrecords, write_size, write_time_micro, nrecords / write_time_micro,
+ 1000 * write_time_micro / nrecords,
+ (write_size * nrecords) / write_time_micro / 1000);
+ }
+}
diff --git a/test/core/statistics/log_tests.h b/test/core/statistics/log_tests.h
new file mode 100644
index 0000000000..10993ac76f
--- /dev/null
+++ b/test/core/statistics/log_tests.h
@@ -0,0 +1,50 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#ifndef __GRPC_TEST_STATISTICS_LOG_TESTS_H__
+#define __GRPC_TEST_STATISTICS_LOG_TESTS_H__
+
+void test_invalid_record_size();
+void test_end_write_with_different_size();
+void test_read_pending_record();
+void test_read_beyond_pending_record();
+void test_detached_while_reading();
+void test_fill_log_no_fragmentation();
+void test_fill_circular_log_no_fragmentation();
+void test_fill_log_with_straddling_records();
+void test_fill_circular_log_with_straddling_records();
+void test_multiple_writers_circular_log();
+void test_multiple_writers();
+void test_performance();
+
+#endif /* __GRPC_TEST_STATISTICS_LOG_TESTS_H__ */
diff --git a/test/core/statistics/multiple_writers_circular_buffer_test.c b/test/core/statistics/multiple_writers_circular_buffer_test.c
new file mode 100644
index 0000000000..0cd0d78df2
--- /dev/null
+++ b/test/core/statistics/multiple_writers_circular_buffer_test.c
@@ -0,0 +1,46 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include "test/core/statistics/log_tests.h"
+
+#include <stdlib.h>
+
+#include <grpc/support/time.h>
+#include "test/core/util/test_config.h"
+
+int main(int argc, char **argv) {
+ grpc_test_init(argc, argv);
+ srand(gpr_now().tv_nsec);
+ test_multiple_writers_circular_log();
+ return 0;
+}
diff --git a/test/core/statistics/multiple_writers_test.c b/test/core/statistics/multiple_writers_test.c
new file mode 100644
index 0000000000..b1f3be4eba
--- /dev/null
+++ b/test/core/statistics/multiple_writers_test.c
@@ -0,0 +1,46 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include "test/core/statistics/log_tests.h"
+
+#include <stdlib.h>
+
+#include <grpc/support/time.h>
+#include "test/core/util/test_config.h"
+
+int main(int argc, char **argv) {
+ grpc_test_init(argc, argv);
+ srand(gpr_now().tv_nsec);
+ test_multiple_writers();
+ return 0;
+}
diff --git a/test/core/statistics/performance_test.c b/test/core/statistics/performance_test.c
new file mode 100644
index 0000000000..9197dd5c73
--- /dev/null
+++ b/test/core/statistics/performance_test.c
@@ -0,0 +1,46 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include "log_tests.h"
+
+#include <stdlib.h>
+
+#include <grpc/support/time.h>
+#include "test/core/util/test_config.h"
+
+int main(int argc, char **argv) {
+ grpc_test_init(argc, argv);
+ srand(gpr_now().tv_nsec);
+ test_performance();
+ return 0;
+}
diff --git a/test/core/statistics/quick_test.c b/test/core/statistics/quick_test.c
new file mode 100644
index 0000000000..fe2b89a9a4
--- /dev/null
+++ b/test/core/statistics/quick_test.c
@@ -0,0 +1,54 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include "log_tests.h"
+
+#include <stdlib.h>
+
+#include <grpc/support/time.h>
+#include "test/core/util/test_config.h"
+
+int main(int argc, char **argv) {
+ grpc_test_init(argc, argv);
+ srand(gpr_now().tv_nsec);
+ test_invalid_record_size();
+ test_end_write_with_different_size();
+ test_read_pending_record();
+ test_read_beyond_pending_record();
+ test_detached_while_reading();
+ test_fill_log_no_fragmentation();
+ test_fill_circular_log_no_fragmentation();
+ test_fill_log_with_straddling_records();
+ test_fill_circular_log_with_straddling_records();
+ return 0;
+}
diff --git a/test/core/statistics/window_stats_test.c b/test/core/statistics/window_stats_test.c
new file mode 100644
index 0000000000..2bf93d8c87
--- /dev/null
+++ b/test/core/statistics/window_stats_test.c
@@ -0,0 +1,317 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include "src/core/statistics/window_stats.h"
+#include <stdlib.h>
+#include <grpc/support/log.h>
+#include <grpc/support/time.h>
+#include "test/core/util/test_config.h"
+
+typedef struct test_stat {
+ double value1;
+ int value2;
+} test_stat;
+
+void add_test_stat(void* base, const void* addme) {
+ test_stat* b = (test_stat*)base;
+ const test_stat* a = (const test_stat*)addme;
+ b->value1 += a->value1;
+ b->value2 += a->value2;
+}
+
+void add_proportion_test_stat(double p, void* base, const void* addme) {
+ test_stat* b = (test_stat*)base;
+ const test_stat* a = (const test_stat*)addme;
+ b->value1 += p * a->value1;
+ b->value2 += p * a->value2 + 0.5; /* +0.5 is poor mans (no c99) round() */
+}
+
+const struct census_window_stats_stat_info kMyStatInfo = {
+ sizeof(test_stat), NULL, add_test_stat, add_proportion_test_stat};
+
+const gpr_timespec kMilliSecInterval = {0, 1000000};
+const gpr_timespec kSecInterval = {1, 0};
+const gpr_timespec kMinInterval = {60, 0};
+const gpr_timespec kHourInterval = {3600, 0};
+const gpr_timespec kPrimeInterval = {0, 101};
+
+static int compare_double(double a, double b, double epsilon) {
+ if (a >= b) {
+ return (a > b + epsilon) ? 1 : 0;
+ } else {
+ return (b > a + epsilon) ? -1 : 0;
+ }
+}
+
+void empty_test() {
+ census_window_stats_sums result;
+ const gpr_timespec zero = {0, 0};
+ test_stat sum;
+ struct census_window_stats* stats =
+ census_window_stats_create(1, &kMinInterval, 5, &kMyStatInfo);
+ GPR_ASSERT(stats != NULL);
+ result.statistic = &sum;
+ census_window_stats_get_sums(stats, zero, &result);
+ GPR_ASSERT(result.count == 0 && sum.value1 == 0 && sum.value2 == 0);
+ census_window_stats_get_sums(stats, gpr_now(), &result);
+ GPR_ASSERT(result.count == 0 && sum.value1 == 0 && sum.value2 == 0);
+ census_window_stats_destroy(stats);
+}
+
+void one_interval_test() {
+ const test_stat value = {0.1, 4};
+ const double epsilon = 1e10 - 11;
+ gpr_timespec when = {0, 0};
+ census_window_stats_sums result;
+ test_stat sum;
+ /* granularity == 5 so width of internal windows should be 12s */
+ struct census_window_stats* stats =
+ census_window_stats_create(1, &kMinInterval, 5, &kMyStatInfo);
+ GPR_ASSERT(stats != NULL);
+ /* phase 1: insert a single value at t=0s, and check that various measurement
+ times result in expected output values */
+ census_window_stats_add(stats, when, &value);
+ result.statistic = &sum;
+ /* when = 0s, values extracted should be everything */
+ census_window_stats_get_sums(stats, when, &result);
+ GPR_ASSERT(compare_double(result.count, 1, epsilon) == 0 &&
+ compare_double(sum.value1, value.value1, epsilon) == 0 &&
+ sum.value2 == value.value2);
+ /* when = 6,30,60s, should be all of the data */
+ when.tv_sec = 6;
+ census_window_stats_get_sums(stats, when, &result);
+ GPR_ASSERT(compare_double(result.count, 1.0, epsilon) == 0 &&
+ compare_double(sum.value1, value.value1, epsilon) == 0 &&
+ sum.value2 == value.value2);
+ /* when == 30s,60s, should be all of the data */
+ when.tv_sec = 30;
+ census_window_stats_get_sums(stats, when, &result);
+ GPR_ASSERT(compare_double(result.count, 1.0, epsilon) == 0 &&
+ compare_double(sum.value1, value.value1, epsilon) == 0 &&
+ sum.value2 == value.value2);
+ when.tv_sec = 60;
+ census_window_stats_get_sums(stats, when, &result);
+ GPR_ASSERT(compare_double(result.count, 1.0, epsilon) == 0 &&
+ compare_double(sum.value1, value.value1, epsilon) == 0 &&
+ sum.value2 == value.value2);
+ /* when = 66s, should be half (only take half of bottom bucket) */
+ when.tv_sec = 66;
+ census_window_stats_get_sums(stats, when, &result);
+ GPR_ASSERT(compare_double(result.count, 0.5, epsilon) == 0 &&
+ compare_double(sum.value1, value.value1 / 2, epsilon) == 0 &&
+ sum.value2 == value.value2 / 2);
+ /* when = 72s, should be completely out of window */
+ when.tv_sec = 72;
+ census_window_stats_get_sums(stats, when, &result);
+ GPR_ASSERT(compare_double(result.count, 0, epsilon) == 0 &&
+ compare_double(sum.value1, 0, epsilon) == 0 && sum.value2 == 0);
+
+ /* phase 2: tear down and do as before, but inserting two values */
+ census_window_stats_destroy(stats);
+ stats = census_window_stats_create(1, &kMinInterval, 5, &kMyStatInfo);
+ GPR_ASSERT(stats != NULL);
+ when.tv_sec = 0;
+ when.tv_nsec = 17;
+ census_window_stats_add(stats, when, &value);
+ when.tv_sec = 1;
+ census_window_stats_add(stats, when, &value);
+ when.tv_sec = 0;
+ census_window_stats_get_sums(stats, when, &result);
+ GPR_ASSERT(compare_double(result.count, 0, epsilon) == 0 &&
+ compare_double(sum.value1, 0, epsilon) == 0 && sum.value2 == 0);
+ /* time = 3s, 30s, should get all data */
+ when.tv_sec = 3;
+ census_window_stats_get_sums(stats, when, &result);
+ GPR_ASSERT(compare_double(result.count, 2, epsilon) == 0 &&
+ compare_double(sum.value1, 2 * value.value1, epsilon) == 0 &&
+ sum.value2 == 2 * value.value2);
+ when.tv_sec = 30;
+ census_window_stats_get_sums(stats, when, &result);
+ GPR_ASSERT(compare_double(result.count, 2, epsilon) == 0 &&
+ compare_double(sum.value1, 2 * value.value1, epsilon) == 0 &&
+ sum.value2 == 2 * value.value2);
+
+ /* phase 3: insert into "middle" bucket, and force a shift, pushing out
+ the two values in bottom bucket */
+ when.tv_sec = 30;
+ census_window_stats_add(stats, when, &value);
+ when.tv_sec = 76;
+ census_window_stats_add(stats, when, &value);
+ when.tv_sec = 0;
+ census_window_stats_get_sums(stats, when, &result);
+ GPR_ASSERT(result.count == 0 && sum.value1 == 0 && sum.value2 == 0);
+ when.tv_sec = 30;
+ census_window_stats_get_sums(stats, when, &result);
+ /* half of the single value in the 30 second bucket */
+ GPR_ASSERT(compare_double(result.count, 0.5, epsilon) == 0 &&
+ compare_double(sum.value1, value.value1 / 2, epsilon) == 0 &&
+ sum.value2 == value.value2 / 2);
+ when.tv_sec = 74;
+ census_window_stats_get_sums(stats, when, &result);
+ /* half of the 76 second bucket, all of the 30 second bucket */
+ GPR_ASSERT(compare_double(result.count, 1.5, epsilon) == 0 &&
+ compare_double(sum.value1, value.value1 * 1.5, epsilon) == 0 &&
+ sum.value2 == value.value2 / 2 * 3);
+ when.tv_sec = 76;
+ census_window_stats_get_sums(stats, when, &result);
+ /* >=76s, get all of the 76 second bucket, all of the 30 second bucket */
+ GPR_ASSERT(compare_double(result.count, 2, epsilon) == 0 &&
+ compare_double(sum.value1, value.value1 * 2, epsilon) == 0 &&
+ sum.value2 == value.value2 * 2);
+ when.tv_sec = 78;
+ census_window_stats_get_sums(stats, when, &result);
+ /* half of the 76 second bucket, all of the 30 second bucket */
+ GPR_ASSERT(compare_double(result.count, 2, epsilon) == 0 &&
+ compare_double(sum.value1, value.value1 * 2, epsilon) == 0 &&
+ sum.value2 == value.value2 * 2);
+ census_window_stats_destroy(stats);
+}
+
+void many_interval_test() {
+ gpr_timespec intervals[4];
+ const test_stat value = {123.45, 8};
+ const double epsilon = 1e10 - 11;
+ gpr_timespec when = {3600, 0}; /* one hour */
+ census_window_stats_sums result[4];
+ test_stat sums[4];
+ int i;
+ struct census_window_stats* stats;
+ intervals[0] = kMilliSecInterval;
+ intervals[1] = kSecInterval;
+ intervals[2] = kMinInterval;
+ intervals[3] = kHourInterval;
+ for (i = 0; i < 4; i++) {
+ result[i].statistic = &sums[i];
+ }
+ stats = census_window_stats_create(4, intervals, 100, &kMyStatInfo);
+ GPR_ASSERT(stats != NULL);
+ /* add 10 stats within half of each time range */
+ for (i = 0; i < 10; i++) {
+ when.tv_sec += 180; /* covers 30 min of one hour range */
+ census_window_stats_add(stats, when, &value);
+ }
+ when.tv_sec += 120;
+ for (i = 0; i < 10; i++) {
+ when.tv_sec += 3; /* covers 30 sec of one minute range */
+ census_window_stats_add(stats, when, &value);
+ }
+ when.tv_sec += 2;
+ for (i = 0; i < 10; i++) {
+ when.tv_nsec += 50000000; /* covers 0.5s of 1s range */
+ census_window_stats_add(stats, when, &value);
+ }
+ when.tv_nsec += 2000000;
+ for (i = 0; i < 10; i++) {
+ when.tv_nsec += 50000; /* covers 0.5 ms of 1 ms range */
+ census_window_stats_add(stats, when, &value);
+ }
+ when.tv_nsec += 20000;
+ census_window_stats_get_sums(stats, when, result);
+ GPR_ASSERT(compare_double(result[0].count, 10, epsilon) == 0 &&
+ compare_double(sums[0].value1, value.value1 * 10, epsilon) == 0 &&
+ sums[0].value2 == value.value2 * 10);
+ when.tv_nsec += 20000000;
+ census_window_stats_get_sums(stats, when, result);
+ GPR_ASSERT(compare_double(result[1].count, 20, epsilon) == 0 &&
+ compare_double(sums[1].value1, value.value1 * 20, epsilon) == 0 &&
+ sums[1].value2 == value.value2 * 20);
+ when.tv_sec += 2;
+ census_window_stats_get_sums(stats, when, result);
+ GPR_ASSERT(compare_double(result[2].count, 30, epsilon) == 0 &&
+ compare_double(sums[2].value1, value.value1 * 30, epsilon) == 0 &&
+ sums[2].value2 == value.value2 * 30);
+ when.tv_sec += 72;
+ census_window_stats_get_sums(stats, when, result);
+ GPR_ASSERT(compare_double(result[3].count, 40, epsilon) == 0 &&
+ compare_double(sums[3].value1, value.value1 * 40, epsilon) == 0 &&
+ sums[3].value2 == value.value2 * 40);
+ census_window_stats_destroy(stats);
+}
+
+void rolling_time_test() {
+ const test_stat value = {0.1, 4};
+ gpr_timespec when = {0, 0};
+ census_window_stats_sums result;
+ test_stat sum;
+ int i;
+ gpr_timespec increment = {0, 0};
+ struct census_window_stats* stats =
+ census_window_stats_create(1, &kMinInterval, 7, &kMyStatInfo);
+ GPR_ASSERT(stats != NULL);
+ srand(gpr_now().tv_nsec);
+ for (i = 0; i < 100000; i++) {
+ increment.tv_nsec = rand() % 100000000; /* up to 1/10th second */
+ when = gpr_time_add(when, increment);
+ census_window_stats_add(stats, when, &value);
+ }
+ result.statistic = &sum;
+ census_window_stats_get_sums(stats, when, &result);
+ /* With 1/20th second average between samples, we expect 20*60 = 1200
+ samples on average. Make sure we are within 100 of that. */
+ GPR_ASSERT(compare_double(result.count, 1200, 100) == 0);
+ census_window_stats_destroy(stats);
+}
+#include <stdio.h>
+void infinite_interval_test() {
+ const test_stat value = {0.1, 4};
+ gpr_timespec when = {0, 0};
+ census_window_stats_sums result;
+ test_stat sum;
+ int i;
+ const int count = 100000;
+ gpr_timespec increment = {0, 0};
+ struct census_window_stats* stats =
+ census_window_stats_create(1, &gpr_inf_future, 10, &kMyStatInfo);
+ srand(gpr_now().tv_nsec);
+ for (i = 0; i < count; i++) {
+ increment.tv_sec = rand() % 21600; /* 6 hours */
+ when = gpr_time_add(when, increment);
+ census_window_stats_add(stats, when, &value);
+ }
+ result.statistic = &sum;
+ census_window_stats_get_sums(stats, when, &result);
+ /* The only thing it makes sense to compare for "infinite" periods is the
+ total counts */
+ GPR_ASSERT(result.count == count);
+ census_window_stats_destroy(stats);
+}
+
+int main(int argc, char* argv[]) {
+ grpc_test_init(argc, argv);
+ empty_test();
+ one_interval_test();
+ many_interval_test();
+ rolling_time_test();
+ infinite_interval_test();
+ return 0;
+}
diff --git a/test/core/support/cancellable_test.c b/test/core/support/cancellable_test.c
new file mode 100644
index 0000000000..e90c999921
--- /dev/null
+++ b/test/core/support/cancellable_test.c
@@ -0,0 +1,160 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+/* Test of gpr_cancellable. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <grpc/support/log.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/thd.h>
+#include <grpc/support/time.h>
+#include "test/core/util/test_config.h"
+
+struct test {
+ gpr_mu mu;
+ gpr_cv cv;
+ gpr_event ev;
+ gpr_event done;
+ gpr_cancellable cancel;
+ int n;
+};
+
+/* A thread body. Wait until t->cancel is cancelledm then
+ decrement t->n. If t->n becomes 0, set t->done. */
+static void thd_body(void *v) {
+ struct test *t = v;
+ gpr_mu_lock(&t->mu);
+ while (!gpr_cv_cancellable_wait(&t->cv, &t->mu, gpr_inf_future, &t->cancel)) {
+ }
+ t->n--;
+ if (t->n == 0) {
+ gpr_event_set(&t->done, (void *)1);
+ }
+ gpr_mu_unlock(&t->mu);
+}
+
+static void test(void) {
+ int i;
+ gpr_thd_id thd;
+ struct test t;
+ int n = 1;
+ gpr_timespec interval;
+
+ gpr_mu_init(&t.mu);
+ gpr_cv_init(&t.cv);
+ gpr_event_init(&t.ev);
+ gpr_event_init(&t.done);
+ gpr_cancellable_init(&t.cancel);
+
+ /* A gpr_cancellable starts not cancelled. */
+ GPR_ASSERT(!gpr_cancellable_is_cancelled(&t.cancel));
+
+ /* Test timeout on event wait for uncancelled gpr_cancellable */
+ interval = gpr_now();
+ gpr_event_cancellable_wait(
+ &t.ev, gpr_time_add(gpr_now(), gpr_time_from_micros(1000000)), &t.cancel);
+ interval = gpr_time_sub(gpr_now(), interval);
+ GPR_ASSERT(gpr_time_cmp(interval, gpr_time_from_micros(500000)) >= 0);
+ GPR_ASSERT(gpr_time_cmp(gpr_time_from_micros(2000000), interval) >= 0);
+
+ /* Test timeout on cv wait for uncancelled gpr_cancellable */
+ gpr_mu_lock(&t.mu);
+ interval = gpr_now();
+ while (!gpr_cv_cancellable_wait(
+ &t.cv, &t.mu,
+ gpr_time_add(gpr_now(), gpr_time_from_micros(1000000)),
+ &t.cancel)) {
+ }
+ interval = gpr_time_sub(gpr_now(), interval);
+ GPR_ASSERT(gpr_time_cmp(interval, gpr_time_from_micros(500000)) >= 0);
+ GPR_ASSERT(gpr_time_cmp(gpr_time_from_micros(2000000), interval) >= 0);
+ gpr_mu_unlock(&t.mu);
+
+ /* Create some threads. They all wait until cancelled; the last to finish
+ sets t.done. */
+ t.n = n;
+ for (i = 0; i != n; i++) {
+ GPR_ASSERT(gpr_thd_new(&thd, &thd_body, &t, NULL));
+ }
+ /* Check that t.cancel still is not cancelled. */
+ GPR_ASSERT(!gpr_cancellable_is_cancelled(&t.cancel));
+
+ /* Wait a second, and check that no threads have finished waiting. */
+ gpr_mu_lock(&t.mu);
+ gpr_cv_wait(&t.cv, &t.mu,
+ gpr_time_add(gpr_now(), gpr_time_from_micros(1000000)));
+ GPR_ASSERT(t.n == n);
+ gpr_mu_unlock(&t.mu);
+
+ /* Check that t.cancel still is not cancelled, but when
+ cancelled it retports that it is cacncelled. */
+ GPR_ASSERT(!gpr_cancellable_is_cancelled(&t.cancel));
+ gpr_cancellable_cancel(&t.cancel);
+ GPR_ASSERT(gpr_cancellable_is_cancelled(&t.cancel));
+
+ /* Wait for threads to finish. */
+ gpr_event_wait(&t.done, gpr_inf_future);
+ GPR_ASSERT(t.n == 0);
+
+ /* Test timeout on cv wait for cancelled gpr_cancellable */
+ gpr_mu_lock(&t.mu);
+ interval = gpr_now();
+ while (!gpr_cv_cancellable_wait(
+ &t.cv, &t.mu,
+ gpr_time_add(gpr_now(), gpr_time_from_micros(1000000)),
+ &t.cancel)) {
+ }
+ interval = gpr_time_sub(gpr_now(), interval);
+ GPR_ASSERT(gpr_time_cmp(gpr_time_from_micros(100000), interval) >= 0);
+ gpr_mu_unlock(&t.mu);
+
+ /* Test timeout on event wait for cancelled gpr_cancellable */
+ interval = gpr_now();
+ gpr_event_cancellable_wait(
+ &t.ev, gpr_time_add(gpr_now(), gpr_time_from_micros(1000000)), &t.cancel);
+ interval = gpr_time_sub(gpr_now(), interval);
+ GPR_ASSERT(gpr_time_cmp(gpr_time_from_micros(100000), interval) >= 0);
+
+ gpr_mu_destroy(&t.mu);
+ gpr_cv_destroy(&t.cv);
+ gpr_cancellable_destroy(&t.cancel);
+}
+
+/* ------------------------------------------------- */
+
+int main(int argc, char *argv[]) {
+ grpc_test_init(argc, argv);
+ test();
+ return 0;
+}
diff --git a/test/core/support/cmdline_test.c b/test/core/support/cmdline_test.c
new file mode 100644
index 0000000000..91035a662b
--- /dev/null
+++ b/test/core/support/cmdline_test.c
@@ -0,0 +1,293 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include <grpc/support/cmdline.h>
+
+#include <string.h>
+
+#include <grpc/support/log.h>
+#include <grpc/support/useful.h>
+#include "test/core/util/test_config.h"
+
+#define LOG_TEST() gpr_log(GPR_INFO, "%s", __FUNCTION__)
+
+static void test_simple_int() {
+ int x = 1;
+ gpr_cmdline *cl;
+ char *args[] = {(char *)__FUNCTION__, "-foo", "3"};
+
+ LOG_TEST();
+
+ cl = gpr_cmdline_create(NULL);
+ gpr_cmdline_add_int(cl, "foo", NULL, &x);
+ GPR_ASSERT(x == 1);
+ gpr_cmdline_parse(cl, GPR_ARRAY_SIZE(args), args);
+ GPR_ASSERT(x == 3);
+ gpr_cmdline_destroy(cl);
+}
+
+static void test_eq_int() {
+ int x = 1;
+ gpr_cmdline *cl;
+ char *args[] = {(char *)__FUNCTION__, "-foo=3"};
+
+ LOG_TEST();
+
+ cl = gpr_cmdline_create(NULL);
+ gpr_cmdline_add_int(cl, "foo", NULL, &x);
+ GPR_ASSERT(x == 1);
+ gpr_cmdline_parse(cl, GPR_ARRAY_SIZE(args), args);
+ GPR_ASSERT(x == 3);
+ gpr_cmdline_destroy(cl);
+}
+
+static void test_2dash_int() {
+ int x = 1;
+ gpr_cmdline *cl;
+ char *args[] = {(char *)__FUNCTION__, "--foo", "3"};
+
+ LOG_TEST();
+
+ cl = gpr_cmdline_create(NULL);
+ gpr_cmdline_add_int(cl, "foo", NULL, &x);
+ GPR_ASSERT(x == 1);
+ gpr_cmdline_parse(cl, GPR_ARRAY_SIZE(args), args);
+ GPR_ASSERT(x == 3);
+ gpr_cmdline_destroy(cl);
+}
+
+static void test_2dash_eq_int() {
+ int x = 1;
+ gpr_cmdline *cl;
+ char *args[] = {(char *)__FUNCTION__, "--foo=3"};
+
+ LOG_TEST();
+
+ cl = gpr_cmdline_create(NULL);
+ gpr_cmdline_add_int(cl, "foo", NULL, &x);
+ GPR_ASSERT(x == 1);
+ gpr_cmdline_parse(cl, GPR_ARRAY_SIZE(args), args);
+ GPR_ASSERT(x == 3);
+ gpr_cmdline_destroy(cl);
+}
+
+static void test_simple_string() {
+ char *x = NULL;
+ gpr_cmdline *cl;
+ char *args[] = {(char *)__FUNCTION__, "-foo", "3"};
+
+ LOG_TEST();
+
+ cl = gpr_cmdline_create(NULL);
+ gpr_cmdline_add_string(cl, "foo", NULL, &x);
+ GPR_ASSERT(x == NULL);
+ gpr_cmdline_parse(cl, GPR_ARRAY_SIZE(args), args);
+ GPR_ASSERT(0 == strcmp(x, "3"));
+ gpr_cmdline_destroy(cl);
+}
+
+static void test_eq_string() {
+ char *x = NULL;
+ gpr_cmdline *cl;
+ char *args[] = {(char *)__FUNCTION__, "-foo=3"};
+
+ LOG_TEST();
+
+ cl = gpr_cmdline_create(NULL);
+ gpr_cmdline_add_string(cl, "foo", NULL, &x);
+ GPR_ASSERT(x == NULL);
+ gpr_cmdline_parse(cl, GPR_ARRAY_SIZE(args), args);
+ GPR_ASSERT(0 == strcmp(x, "3"));
+ gpr_cmdline_destroy(cl);
+}
+
+static void test_2dash_string() {
+ char *x = NULL;
+ gpr_cmdline *cl;
+ char *args[] = {(char *)__FUNCTION__, "--foo", "3"};
+
+ LOG_TEST();
+
+ cl = gpr_cmdline_create(NULL);
+ gpr_cmdline_add_string(cl, "foo", NULL, &x);
+ GPR_ASSERT(x == NULL);
+ gpr_cmdline_parse(cl, GPR_ARRAY_SIZE(args), args);
+ GPR_ASSERT(0 == strcmp(x, "3"));
+ gpr_cmdline_destroy(cl);
+}
+
+static void test_2dash_eq_string() {
+ char *x = NULL;
+ gpr_cmdline *cl;
+ char *args[] = {(char *)__FUNCTION__, "--foo=3"};
+
+ LOG_TEST();
+
+ cl = gpr_cmdline_create(NULL);
+ gpr_cmdline_add_string(cl, "foo", NULL, &x);
+ GPR_ASSERT(x == NULL);
+ gpr_cmdline_parse(cl, GPR_ARRAY_SIZE(args), args);
+ GPR_ASSERT(0 == strcmp(x, "3"));
+ gpr_cmdline_destroy(cl);
+}
+
+static void test_flag_on() {
+ int x = 2;
+ gpr_cmdline *cl;
+ char *args[] = {(char *)__FUNCTION__, "--foo"};
+
+ LOG_TEST();
+
+ cl = gpr_cmdline_create(NULL);
+ gpr_cmdline_add_flag(cl, "foo", NULL, &x);
+ GPR_ASSERT(x == 2);
+ gpr_cmdline_parse(cl, GPR_ARRAY_SIZE(args), args);
+ GPR_ASSERT(x == 1);
+ gpr_cmdline_destroy(cl);
+}
+
+static void test_flag_no() {
+ int x = 2;
+ gpr_cmdline *cl;
+ char *args[] = {(char *)__FUNCTION__, "--no-foo"};
+
+ LOG_TEST();
+
+ cl = gpr_cmdline_create(NULL);
+ gpr_cmdline_add_flag(cl, "foo", NULL, &x);
+ GPR_ASSERT(x == 2);
+ gpr_cmdline_parse(cl, GPR_ARRAY_SIZE(args), args);
+ GPR_ASSERT(x == 0);
+ gpr_cmdline_destroy(cl);
+}
+
+static void test_flag_val_1() {
+ int x = 2;
+ gpr_cmdline *cl;
+ char *args[] = {(char *)__FUNCTION__, "--foo=1"};
+
+ LOG_TEST();
+
+ cl = gpr_cmdline_create(NULL);
+ gpr_cmdline_add_flag(cl, "foo", NULL, &x);
+ GPR_ASSERT(x == 2);
+ gpr_cmdline_parse(cl, GPR_ARRAY_SIZE(args), args);
+ GPR_ASSERT(x == 1);
+ gpr_cmdline_destroy(cl);
+}
+
+static void test_flag_val_0() {
+ int x = 2;
+ gpr_cmdline *cl;
+ char *args[] = {(char *)__FUNCTION__, "--foo=0"};
+
+ LOG_TEST();
+
+ cl = gpr_cmdline_create(NULL);
+ gpr_cmdline_add_flag(cl, "foo", NULL, &x);
+ GPR_ASSERT(x == 2);
+ gpr_cmdline_parse(cl, GPR_ARRAY_SIZE(args), args);
+ GPR_ASSERT(x == 0);
+ gpr_cmdline_destroy(cl);
+}
+
+static void test_flag_val_true() {
+ int x = 2;
+ gpr_cmdline *cl;
+ char *args[] = {(char *)__FUNCTION__, "--foo=true"};
+
+ LOG_TEST();
+
+ cl = gpr_cmdline_create(NULL);
+ gpr_cmdline_add_flag(cl, "foo", NULL, &x);
+ GPR_ASSERT(x == 2);
+ gpr_cmdline_parse(cl, GPR_ARRAY_SIZE(args), args);
+ GPR_ASSERT(x == 1);
+ gpr_cmdline_destroy(cl);
+}
+
+static void test_flag_val_false() {
+ int x = 2;
+ gpr_cmdline *cl;
+ char *args[] = {(char *)__FUNCTION__, "--foo=false"};
+
+ LOG_TEST();
+
+ cl = gpr_cmdline_create(NULL);
+ gpr_cmdline_add_flag(cl, "foo", NULL, &x);
+ GPR_ASSERT(x == 2);
+ gpr_cmdline_parse(cl, GPR_ARRAY_SIZE(args), args);
+ GPR_ASSERT(x == 0);
+ gpr_cmdline_destroy(cl);
+}
+
+static void test_many() {
+ char *str = NULL;
+ int x = 0;
+ int flag = 2;
+ gpr_cmdline *cl;
+
+ char *args[] = {(char *)__FUNCTION__, "--str", "hello", "-x=4", "-no-flag"};
+
+ LOG_TEST();
+
+ cl = gpr_cmdline_create(NULL);
+ gpr_cmdline_add_string(cl, "str", NULL, &str);
+ gpr_cmdline_add_int(cl, "x", NULL, &x);
+ gpr_cmdline_add_flag(cl, "flag", NULL, &flag);
+ gpr_cmdline_parse(cl, GPR_ARRAY_SIZE(args), args);
+ GPR_ASSERT(x == 4);
+ GPR_ASSERT(0 == strcmp(str, "hello"));
+ GPR_ASSERT(flag == 0);
+ gpr_cmdline_destroy(cl);
+}
+
+int main(int argc, char **argv) {
+ grpc_test_init(argc, argv);
+ test_simple_int();
+ test_eq_int();
+ test_2dash_int();
+ test_2dash_eq_int();
+ test_simple_string();
+ test_eq_string();
+ test_2dash_string();
+ test_2dash_eq_string();
+ test_flag_on();
+ test_flag_no();
+ test_flag_val_1();
+ test_flag_val_0();
+ test_flag_val_true();
+ test_flag_val_false();
+ test_many();
+ return 0;
+}
diff --git a/test/core/support/histogram_test.c b/test/core/support/histogram_test.c
new file mode 100644
index 0000000000..3b5fd73047
--- /dev/null
+++ b/test/core/support/histogram_test.c
@@ -0,0 +1,178 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include <grpc/support/histogram.h>
+#include <grpc/support/log.h>
+
+#define LOG_TEST() gpr_log(GPR_INFO, "%s", __FUNCTION__);
+
+static void test_no_op() {
+ gpr_histogram_destroy(gpr_histogram_create(0.01, 60e9));
+}
+
+static void expect_percentile(gpr_histogram *h, double percentile,
+ double min_expect, double max_expect) {
+ double got = gpr_histogram_percentile(h, percentile);
+ gpr_log(GPR_INFO, "@%f%%, expect %f <= %f <= %f", percentile, min_expect, got,
+ max_expect);
+ GPR_ASSERT(min_expect <= got);
+ GPR_ASSERT(got <= max_expect);
+}
+
+static void test_simple() {
+ gpr_histogram *h;
+
+ LOG_TEST();
+
+ h = gpr_histogram_create(0.01, 60e9);
+ gpr_histogram_add(h, 10000);
+ gpr_histogram_add(h, 10000);
+ gpr_histogram_add(h, 11000);
+ gpr_histogram_add(h, 11000);
+
+ expect_percentile(h, 50, 10001, 10999);
+ GPR_ASSERT(gpr_histogram_mean(h) == 10500);
+
+ gpr_histogram_destroy(h);
+}
+
+static void test_percentile() {
+ gpr_histogram *h;
+ double last;
+ double i;
+ double cur;
+
+ LOG_TEST();
+
+ h = gpr_histogram_create(0.05, 1e9);
+ gpr_histogram_add(h, 2.5);
+ gpr_histogram_add(h, 2.5);
+ gpr_histogram_add(h, 8);
+ gpr_histogram_add(h, 4);
+
+ GPR_ASSERT(gpr_histogram_count(h) == 4);
+ GPR_ASSERT(gpr_histogram_minimum(h) == 2.5);
+ GPR_ASSERT(gpr_histogram_maximum(h) == 8);
+ GPR_ASSERT(gpr_histogram_sum(h) == 17);
+ GPR_ASSERT(gpr_histogram_sum_of_squares(h) == 92.5);
+ GPR_ASSERT(gpr_histogram_mean(h) == 4.25);
+ GPR_ASSERT(gpr_histogram_variance(h) == 5.0625);
+ GPR_ASSERT(gpr_histogram_stddev(h) == 2.25);
+
+ expect_percentile(h, -10, 2.5, 2.5);
+ expect_percentile(h, 0, 2.5, 2.5);
+ expect_percentile(h, 12.5, 2.5, 2.5);
+ expect_percentile(h, 25, 2.5, 2.5);
+ expect_percentile(h, 37.5, 2.5, 2.8);
+ expect_percentile(h, 50, 3.0, 3.5);
+ expect_percentile(h, 62.5, 3.5, 4.5);
+ expect_percentile(h, 75, 5, 7.9);
+ expect_percentile(h, 100, 8, 8);
+ expect_percentile(h, 110, 8, 8);
+
+ /* test monotonicity */
+ last = 0.0;
+ for (i = 0; i < 100.0; i += 0.01) {
+ cur = gpr_histogram_percentile(h, i);
+ GPR_ASSERT(cur >= last);
+ last = cur;
+ }
+
+ gpr_histogram_destroy(h);
+}
+
+static void test_merge() {
+ gpr_histogram *h1, *h2;
+ double last;
+ double i;
+ double cur;
+
+ LOG_TEST();
+
+ h1 = gpr_histogram_create(0.05, 1e9);
+ gpr_histogram_add(h1, 2.5);
+ gpr_histogram_add(h1, 2.5);
+ gpr_histogram_add(h1, 8);
+ gpr_histogram_add(h1, 4);
+
+ h2 = gpr_histogram_create(0.01, 1e9);
+ GPR_ASSERT(gpr_histogram_merge(h1, h2) == 0);
+ gpr_histogram_destroy(h2);
+
+ h2 = gpr_histogram_create(0.05, 1e10);
+ GPR_ASSERT(gpr_histogram_merge(h1, h2) == 0);
+ gpr_histogram_destroy(h2);
+
+ h2 = gpr_histogram_create(0.05, 1e9);
+ GPR_ASSERT(gpr_histogram_merge(h1, h2) == 1);
+ GPR_ASSERT(gpr_histogram_count(h1) == 4);
+ GPR_ASSERT(gpr_histogram_minimum(h1) == 2.5);
+ GPR_ASSERT(gpr_histogram_maximum(h1) == 8);
+ GPR_ASSERT(gpr_histogram_sum(h1) == 17);
+ GPR_ASSERT(gpr_histogram_sum_of_squares(h1) == 92.5);
+ GPR_ASSERT(gpr_histogram_mean(h1) == 4.25);
+ GPR_ASSERT(gpr_histogram_variance(h1) == 5.0625);
+ GPR_ASSERT(gpr_histogram_stddev(h1) == 2.25);
+ gpr_histogram_destroy(h2);
+
+ h2 = gpr_histogram_create(0.05, 1e9);
+ gpr_histogram_add(h2, 7.0);
+ gpr_histogram_add(h2, 17.0);
+ gpr_histogram_add(h2, 1.0);
+ GPR_ASSERT(gpr_histogram_merge(h1, h2) == 1);
+ GPR_ASSERT(gpr_histogram_count(h1) == 7);
+ GPR_ASSERT(gpr_histogram_minimum(h1) == 1.0);
+ GPR_ASSERT(gpr_histogram_maximum(h1) == 17.0);
+ GPR_ASSERT(gpr_histogram_sum(h1) == 42.0);
+ GPR_ASSERT(gpr_histogram_sum_of_squares(h1) == 431.5);
+ GPR_ASSERT(gpr_histogram_mean(h1) == 6.0);
+
+ /* test monotonicity */
+ last = 0.0;
+ for (i = 0; i < 100.0; i += 0.01) {
+ cur = gpr_histogram_percentile(h1, i);
+ GPR_ASSERT(cur >= last);
+ last = cur;
+ }
+
+ gpr_histogram_destroy(h1);
+ gpr_histogram_destroy(h2);
+}
+
+int main(void) {
+ test_no_op();
+ test_simple();
+ test_percentile();
+ test_merge();
+ return 0;
+}
diff --git a/test/core/support/host_port_test.c b/test/core/support/host_port_test.c
new file mode 100644
index 0000000000..d1553c514e
--- /dev/null
+++ b/test/core/support/host_port_test.c
@@ -0,0 +1,72 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/host_port.h>
+#include <grpc/support/log.h>
+#include "test/core/util/test_config.h"
+
+static void join_host_port_expect(const char *host, int port,
+ const char *expected) {
+ char *buf;
+ int len;
+ len = gpr_join_host_port(&buf, host, port);
+ GPR_ASSERT(strlen(expected) == len);
+ GPR_ASSERT(strcmp(expected, buf) == 0);
+ gpr_free(buf);
+}
+
+static void test_join_host_port() {
+ join_host_port_expect("foo", 101, "foo:101");
+ join_host_port_expect("", 102, ":102");
+ join_host_port_expect("1::2", 103, "[1::2]:103");
+ join_host_port_expect("[::1]", 104, "[::1]:104");
+}
+
+/* Garbage in, garbage out. */
+static void test_join_host_port_garbage() {
+ join_host_port_expect("[foo]", 105, "[foo]:105");
+ join_host_port_expect("[::", 106, "[:::106");
+ join_host_port_expect("::]", 107, "[::]]:107");
+}
+
+int main(int argc, char **argv) {
+ grpc_test_init(argc, argv);
+
+ test_join_host_port();
+ test_join_host_port_garbage();
+
+ return 0;
+}
diff --git a/test/core/support/log_test.c b/test/core/support/log_test.c
new file mode 100644
index 0000000000..fbb7c21ffc
--- /dev/null
+++ b/test/core/support/log_test.c
@@ -0,0 +1,47 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include <grpc/support/log.h>
+#include "test/core/util/test_config.h"
+
+int main(int argc, char **argv) {
+ grpc_test_init(argc, argv);
+ /* test logging at various verbosity levels */
+ gpr_log(GPR_DEBUG, "%s", "hello world");
+ gpr_log(GPR_INFO, "%s", "hello world");
+ gpr_log(GPR_ERROR, "%s", "hello world");
+ /* should succeed */
+ GPR_ASSERT(1);
+ /* TODO(ctiller): should we add a GPR_ASSERT failure test here */
+ return 0;
+}
diff --git a/test/core/support/murmur_hash_test.c b/test/core/support/murmur_hash_test.c
new file mode 100644
index 0000000000..366bcb20bf
--- /dev/null
+++ b/test/core/support/murmur_hash_test.c
@@ -0,0 +1,87 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include "src/core/support/murmur_hash.h"
+#include <grpc/support/log.h>
+#include "test/core/util/test_config.h"
+
+#include <string.h>
+
+typedef gpr_uint32 (*hash_func)(const void *key, size_t len, gpr_uint32 seed);
+
+/* From smhasher:
+ This should hopefully be a thorough and uambiguous test of whether a hash
+ is correctly implemented on a given platform */
+
+static void verification_test(hash_func hash, gpr_uint32 expected) {
+ gpr_uint8 key[256];
+ gpr_uint32 hashes[256];
+ gpr_uint32 final = 0;
+ int i;
+
+ memset(key, 0, sizeof(key));
+ memset(hashes, 0, sizeof(hashes));
+
+ /* Hash keys of the form {0}, {0,1}, {0,1,2}... up to N=255,using 256-N as
+ the seed */
+
+ for (i = 0; i < 256; i++) {
+ key[i] = (uint8_t)i;
+ hashes[i] = hash(key, i, 256 - i);
+ }
+
+ /* Then hash the result array */
+
+ final = hash(hashes, sizeof(hashes), 0);
+
+ /* The first four bytes of that hash, interpreted as a little-endian integer,
+ is our
+ verification value */
+
+ if (expected != final) {
+ gpr_log(GPR_INFO, "Verification value 0x%08X : Failed! (Expected 0x%08x)",
+ final, expected);
+ abort();
+ } else {
+ gpr_log(GPR_INFO, "Verification value 0x%08X : Passed!", final);
+ }
+}
+
+int main(int argc, char **argv) {
+ grpc_test_init(argc, argv);
+ /* basic tests to verify that things don't crash */
+ gpr_murmur_hash3("", 0, 0);
+ gpr_murmur_hash3("xyz", 3, 0);
+ verification_test(gpr_murmur_hash3, 0xB0F57EE3);
+ return 0;
+}
diff --git a/test/core/support/slice_buffer_test.c b/test/core/support/slice_buffer_test.c
new file mode 100644
index 0000000000..030d1d4249
--- /dev/null
+++ b/test/core/support/slice_buffer_test.c
@@ -0,0 +1,70 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include <grpc/support/log.h>
+#include <grpc/support/slice_buffer.h>
+#include "test/core/util/test_config.h"
+
+int main(int argc, char **argv) {
+ gpr_slice_buffer buf;
+ gpr_slice aaa = gpr_slice_from_copied_string("aaa");
+ gpr_slice bb = gpr_slice_from_copied_string("bb");
+ size_t i;
+
+ grpc_test_init(argc, argv);
+ gpr_slice_buffer_init(&buf);
+ for (i = 0; i < 10; i++) {
+ gpr_slice_ref(aaa);
+ gpr_slice_ref(bb);
+ gpr_slice_buffer_add(&buf, aaa);
+ gpr_slice_buffer_add(&buf, bb);
+ }
+ GPR_ASSERT(buf.count > 0);
+ GPR_ASSERT(buf.length == 50);
+ gpr_slice_buffer_reset_and_unref(&buf);
+ GPR_ASSERT(buf.count == 0);
+ GPR_ASSERT(buf.length == 0);
+ for (i = 0; i < 10; i++) {
+ gpr_slice_ref(aaa);
+ gpr_slice_ref(bb);
+ gpr_slice_buffer_add(&buf, aaa);
+ gpr_slice_buffer_add(&buf, bb);
+ }
+ GPR_ASSERT(buf.count > 0);
+ GPR_ASSERT(buf.length == 50);
+ gpr_slice_unref(aaa);
+ gpr_slice_unref(bb);
+ gpr_slice_buffer_destroy(&buf);
+
+ return 0;
+}
diff --git a/test/core/support/slice_test.c b/test/core/support/slice_test.c
new file mode 100644
index 0000000000..40440344c0
--- /dev/null
+++ b/test/core/support/slice_test.c
@@ -0,0 +1,227 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include <grpc/support/slice.h>
+
+#include <string.h>
+
+#include <grpc/support/log.h>
+#include "test/core/util/test_config.h"
+
+#define LOG_TEST_NAME() gpr_log(GPR_INFO, "%s", __FUNCTION__);
+
+static void test_slice_malloc_returns_something_sensible() {
+ /* Calls gpr_slice_create for various lengths and verifies the internals for
+ consistency. */
+ size_t length;
+ size_t i;
+ gpr_slice slice;
+
+ LOG_TEST_NAME();
+
+ for (length = 0; length <= 1024; length++) {
+ slice = gpr_slice_malloc(length);
+ /* If there is a length, slice.data must be non-NULL. If length is zero
+ we don't care. */
+ if (length) {
+ GPR_ASSERT(GPR_SLICE_START_PTR(slice));
+ }
+ /* Returned slice length must be what was requested. */
+ GPR_ASSERT(GPR_SLICE_LENGTH(slice) == length);
+ /* If the slice has a refcount, it must be destroyable. */
+ if (slice.refcount) {
+ GPR_ASSERT(slice.refcount->ref != NULL);
+ GPR_ASSERT(slice.refcount->unref != NULL);
+ }
+ /* We must be able to write to every byte of the data */
+ for (i = 0; i < length; i++) {
+ GPR_SLICE_START_PTR(slice)[i] = (char)i;
+ }
+ /* And finally we must succeed in destroying the slice */
+ gpr_slice_unref(slice);
+ }
+}
+
+static void do_nothing(void *ignored) {}
+
+static void test_slice_new_returns_something_sensible() {
+ gpr_uint8 x;
+
+ gpr_slice slice = gpr_slice_new(&x, 1, do_nothing);
+ GPR_ASSERT(slice.refcount);
+ GPR_ASSERT(slice.data.refcounted.bytes == &x);
+ GPR_ASSERT(slice.data.refcounted.length == 1);
+ gpr_slice_unref(slice);
+}
+
+static int do_nothing_with_len_1_calls = 0;
+
+static void do_nothing_with_len_1(void *ignored, size_t len) {
+ GPR_ASSERT(len == 1);
+ do_nothing_with_len_1_calls++;
+}
+
+static void test_slice_new_with_len_returns_something_sensible() {
+ gpr_uint8 x;
+
+ gpr_slice slice = gpr_slice_new_with_len(&x, 1, do_nothing_with_len_1);
+ GPR_ASSERT(slice.refcount);
+ GPR_ASSERT(slice.data.refcounted.bytes == &x);
+ GPR_ASSERT(slice.data.refcounted.length == 1);
+ GPR_ASSERT(do_nothing_with_len_1_calls == 0);
+ gpr_slice_unref(slice);
+ GPR_ASSERT(do_nothing_with_len_1_calls == 1);
+}
+
+static void test_slice_sub_works(int length) {
+ gpr_slice slice;
+ gpr_slice sub;
+ int i, j, k;
+
+ LOG_TEST_NAME();
+ gpr_log(GPR_INFO, "length=%d", length);
+
+ /* Create a slice in which each byte is equal to the distance from it to the
+ beginning of the slice. */
+ slice = gpr_slice_malloc(length);
+ for (i = 0; i < length; i++) {
+ GPR_SLICE_START_PTR(slice)[i] = i;
+ }
+
+ /* Ensure that for all subsets length is correct and that we start on the
+ correct byte. Additionally check that no copies were made. */
+ for (i = 0; i < length; i++) {
+ for (j = i; j < length; j++) {
+ sub = gpr_slice_sub(slice, i, j);
+ GPR_ASSERT(GPR_SLICE_LENGTH(sub) == j - i);
+ for (k = 0; k < j - i; k++) {
+ GPR_ASSERT(GPR_SLICE_START_PTR(sub)[k] == (gpr_uint8)(i + k));
+ }
+ gpr_slice_unref(sub);
+ }
+ }
+ gpr_slice_unref(slice);
+}
+
+static void check_head_tail(gpr_slice slice, gpr_slice head, gpr_slice tail) {
+ GPR_ASSERT(GPR_SLICE_LENGTH(slice) ==
+ GPR_SLICE_LENGTH(head) + GPR_SLICE_LENGTH(tail));
+ GPR_ASSERT(0 == memcmp(GPR_SLICE_START_PTR(slice), GPR_SLICE_START_PTR(head),
+ GPR_SLICE_LENGTH(head)));
+ GPR_ASSERT(0 == memcmp(GPR_SLICE_START_PTR(slice) + GPR_SLICE_LENGTH(head),
+ GPR_SLICE_START_PTR(tail), GPR_SLICE_LENGTH(tail)));
+}
+
+static void test_slice_split_head_works(int length) {
+ gpr_slice slice;
+ gpr_slice head, tail;
+ int i;
+
+ LOG_TEST_NAME();
+ gpr_log(GPR_INFO, "length=%d", length);
+
+ /* Create a slice in which each byte is equal to the distance from it to the
+ beginning of the slice. */
+ slice = gpr_slice_malloc(length);
+ for (i = 0; i < length; i++) {
+ GPR_SLICE_START_PTR(slice)[i] = i;
+ }
+
+ /* Ensure that for all subsets length is correct and that we start on the
+ correct byte. Additionally check that no copies were made. */
+ for (i = 0; i < length; i++) {
+ tail = gpr_slice_ref(slice);
+ head = gpr_slice_split_head(&tail, i);
+ check_head_tail(slice, head, tail);
+ gpr_slice_unref(tail);
+ gpr_slice_unref(head);
+ }
+
+ gpr_slice_unref(slice);
+}
+
+static void test_slice_split_tail_works(int length) {
+ gpr_slice slice;
+ gpr_slice head, tail;
+ int i;
+
+ LOG_TEST_NAME();
+ gpr_log(GPR_INFO, "length=%d", length);
+
+ /* Create a slice in which each byte is equal to the distance from it to the
+ beginning of the slice. */
+ slice = gpr_slice_malloc(length);
+ for (i = 0; i < length; i++) {
+ GPR_SLICE_START_PTR(slice)[i] = i;
+ }
+
+ /* Ensure that for all subsets length is correct and that we start on the
+ correct byte. Additionally check that no copies were made. */
+ for (i = 0; i < length; i++) {
+ head = gpr_slice_ref(slice);
+ tail = gpr_slice_split_tail(&head, i);
+ check_head_tail(slice, head, tail);
+ gpr_slice_unref(tail);
+ gpr_slice_unref(head);
+ }
+
+ gpr_slice_unref(slice);
+}
+
+static void test_slice_from_copied_string_works() {
+ static const char *text = "HELLO WORLD!";
+ gpr_slice slice;
+
+ LOG_TEST_NAME();
+
+ slice = gpr_slice_from_copied_string(text);
+ GPR_ASSERT(strlen(text) == GPR_SLICE_LENGTH(slice));
+ GPR_ASSERT(0 ==
+ memcmp(text, GPR_SLICE_START_PTR(slice), GPR_SLICE_LENGTH(slice)));
+ gpr_slice_unref(slice);
+}
+
+int main(int argc, char **argv) {
+ int length;
+ grpc_test_init(argc, argv);
+ test_slice_malloc_returns_something_sensible();
+ test_slice_new_returns_something_sensible();
+ test_slice_new_with_len_returns_something_sensible();
+ for (length = 0; length < 128; length++) {
+ test_slice_sub_works(length);
+ test_slice_split_head_works(length);
+ test_slice_split_tail_works(length);
+ }
+ test_slice_from_copied_string_works();
+ return 0;
+}
diff --git a/test/core/support/string_test.c b/test/core/support/string_test.c
new file mode 100644
index 0000000000..1e8039b1e8
--- /dev/null
+++ b/test/core/support/string_test.c
@@ -0,0 +1,158 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include <grpc/support/string.h>
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/useful.h>
+#include "test/core/util/test_config.h"
+
+#define LOG_TEST_NAME() gpr_log(GPR_INFO, "%s", __FUNCTION__)
+
+static void test_strdup() {
+ static const char *src1 = "hello world";
+ char *dst1;
+
+ LOG_TEST_NAME();
+
+ dst1 = gpr_strdup(src1);
+ GPR_ASSERT(0 == strcmp(src1, dst1));
+ gpr_free(dst1);
+
+ GPR_ASSERT(NULL == gpr_strdup(NULL));
+}
+
+static void expect_hexdump(const char *buf, size_t len, gpr_uint32 flags,
+ const char *result) {
+ char *got = gpr_hexdump(buf, len, flags);
+ GPR_ASSERT(0 == strcmp(got, result));
+ gpr_free(got);
+}
+
+static void test_hexdump() {
+ LOG_TEST_NAME();
+ expect_hexdump("\x01", 1, 0, "01");
+ expect_hexdump("\x01", 1, GPR_HEXDUMP_PLAINTEXT, "01 '.'");
+ expect_hexdump("\x01\x02", 2, 0, "01 02");
+ expect_hexdump("\x01\x23\x45\x67\x89\xab\xcd\xef", 8, 0,
+ "01 23 45 67 89 ab cd ef");
+ expect_hexdump("ab", 2, GPR_HEXDUMP_PLAINTEXT, "61 62 'ab'");
+}
+
+static void test_pu32_fail(const char *s) {
+ gpr_uint32 out;
+ GPR_ASSERT(!gpr_parse_bytes_to_uint32(s, strlen(s), &out));
+}
+
+static void test_pu32_succeed(const char *s, gpr_uint32 want) {
+ gpr_uint32 out;
+ GPR_ASSERT(gpr_parse_bytes_to_uint32(s, strlen(s), &out));
+ GPR_ASSERT(out == want);
+}
+
+static void test_parse_uint32() {
+ LOG_TEST_NAME();
+
+ test_pu32_fail("-1");
+ test_pu32_fail("a");
+ test_pu32_fail("");
+ test_pu32_succeed("0", 0);
+ test_pu32_succeed("1", 1);
+ test_pu32_succeed("2", 2);
+ test_pu32_succeed("3", 3);
+ test_pu32_succeed("4", 4);
+ test_pu32_succeed("5", 5);
+ test_pu32_succeed("6", 6);
+ test_pu32_succeed("7", 7);
+ test_pu32_succeed("8", 8);
+ test_pu32_succeed("9", 9);
+ test_pu32_succeed("10", 10);
+ test_pu32_succeed("11", 11);
+ test_pu32_succeed("12", 12);
+ test_pu32_succeed("13", 13);
+ test_pu32_succeed("14", 14);
+ test_pu32_succeed("15", 15);
+ test_pu32_succeed("16", 16);
+ test_pu32_succeed("17", 17);
+ test_pu32_succeed("18", 18);
+ test_pu32_succeed("19", 19);
+ test_pu32_succeed("1234567890", 1234567890);
+ test_pu32_succeed("4294967295", 4294967295u);
+ test_pu32_fail("4294967296");
+ test_pu32_fail("4294967297");
+ test_pu32_fail("4294967298");
+ test_pu32_fail("4294967299");
+}
+
+static void test_asprintf() {
+ char *buf;
+ int i, j;
+
+ LOG_TEST_NAME();
+
+ /* Print an empty string. */
+ GPR_ASSERT(gpr_asprintf(&buf, "") == 0);
+ GPR_ASSERT(buf[0] == '\0');
+ gpr_free(buf);
+
+ /* Print an invalid format. */
+ GPR_ASSERT(gpr_asprintf(&buf, "%") == -1);
+ GPR_ASSERT(buf == NULL);
+
+ /* Print strings of various lengths. */
+ for (i = 1; i < 100; i++) {
+ GPR_ASSERT(gpr_asprintf(&buf, "%0*d", i, 1) == i);
+
+ /* The buffer should resemble "000001\0". */
+ for (j = 0; j < i - 2; j++) {
+ GPR_ASSERT(buf[j] == '0');
+ }
+ GPR_ASSERT(buf[i - 1] == '1');
+ GPR_ASSERT(buf[i] == '\0');
+ gpr_free(buf);
+ }
+}
+
+int main(int argc, char **argv) {
+ grpc_test_init(argc, argv);
+ test_strdup();
+ test_hexdump();
+ test_parse_uint32();
+ test_asprintf();
+ return 0;
+}
diff --git a/test/core/support/sync_test.c b/test/core/support/sync_test.c
new file mode 100644
index 0000000000..93f9c4cc8d
--- /dev/null
+++ b/test/core/support/sync_test.c
@@ -0,0 +1,451 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+/* Test of gpr synchronization support. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/thd.h>
+#include <grpc/support/time.h>
+#include "test/core/util/test_config.h"
+
+/* ==================Example use of interface===================
+
+ A producer-consumer queue of up to N integers,
+ illustrating the use of the calls in this interface. */
+
+#define N 4
+
+typedef struct queue {
+ gpr_cv non_empty; /* Signalled when length becomes non-zero. */
+ gpr_cv non_full; /* Signalled when length becomes non-N. */
+ gpr_mu mu; /* Protects all fields below.
+ (That is, except during initialization or
+ destruction, the fields below should be accessed
+ only by a thread that holds mu.) */
+ int head; /* Index of head of queue 0..N-1. */
+ int length; /* Number of valid elements in queue 0..N. */
+ int elem[N]; /* elem[head .. head+length-1] are queue elements. */
+} queue;
+
+/* Initialize *q. */
+void queue_init(queue *q) {
+ gpr_mu_init(&q->mu);
+ gpr_cv_init(&q->non_empty);
+ gpr_cv_init(&q->non_full);
+ q->head = 0;
+ q->length = 0;
+}
+
+/* Free storage associated with *q. */
+void queue_destroy(queue *q) {
+ gpr_mu_destroy(&q->mu);
+ gpr_cv_destroy(&q->non_empty);
+ gpr_cv_destroy(&q->non_full);
+}
+
+/* Wait until there is room in *q, then append x to *q. */
+void queue_append(queue *q, int x) {
+ gpr_mu_lock(&q->mu);
+ /* To wait for a predicate without a deadline, loop on the negation of the
+ predicate, and use gpr_cv_wait(..., gpr_inf_future) inside the loop
+ to release the lock, wait, and reacquire on each iteration. Code that
+ makes the condition true should use gpr_cv_broadcast() on the
+ corresponding condition variable. The predicate must be on state
+ protected by the lock. */
+ while (q->length == N) {
+ gpr_cv_wait(&q->non_full, &q->mu, gpr_inf_future);
+ }
+ if (q->length == 0) { /* Wake threads blocked in queue_remove(). */
+ /* It's normal to use gpr_cv_broadcast() or gpr_signal() while
+ holding the lock. */
+ gpr_cv_broadcast(&q->non_empty);
+ }
+ q->elem[(q->head + q->length) % N] = x;
+ q->length++;
+ gpr_mu_unlock(&q->mu);
+}
+
+/* If it can be done without blocking, append x to *q and return non-zero.
+ Otherwise return 0. */
+int queue_try_append(queue *q, int x) {
+ int result = 0;
+ if (gpr_mu_trylock(&q->mu)) {
+ if (q->length != N) {
+ if (q->length == 0) { /* Wake threads blocked in queue_remove(). */
+ gpr_cv_broadcast(&q->non_empty);
+ }
+ q->elem[(q->head + q->length) % N] = x;
+ q->length++;
+ result = 1;
+ }
+ gpr_mu_unlock(&q->mu);
+ }
+ return result;
+}
+
+/* Wait until the *q is non-empty or deadline abs_deadline passes. If the
+ queue is non-empty, remove its head entry, place it in *head, and return
+ non-zero. Otherwise return 0. */
+int queue_remove(queue *q, int *head, gpr_timespec abs_deadline) {
+ int result = 0;
+ gpr_mu_lock(&q->mu);
+ /* To wait for a predicate with a deadline, loop on the negation of the
+ predicate or until gpr_cv_wait() returns true. Code that makes
+ the condition true should use gpr_cv_broadcast() on the corresponding
+ condition variable. The predicate must be on state protected by the
+ lock. */
+ while (q->length == 0 && !gpr_cv_wait(&q->non_empty, &q->mu, abs_deadline)) {
+ }
+ if (q->length != 0) { /* Queue is non-empty. */
+ result = 1;
+ if (q->length == N) { /* Wake threads blocked in queue_append(). */
+ gpr_cv_broadcast(&q->non_full);
+ }
+ *head = q->elem[q->head];
+ q->head = (q->head + 1) % N;
+ q->length--;
+ } /* else deadline exceeded */
+ gpr_mu_unlock(&q->mu);
+ return result;
+}
+
+/* ------------------------------------------------- */
+/* Tests for gpr_mu and gpr_cv, and the queue example. */
+struct test {
+ int threads; /* number of threads */
+
+ gpr_int64 iterations; /* number of iterations per thread */
+ gpr_int64 counter;
+ int thread_count; /* used to allocate thread ids */
+ int done; /* threads not yet completed */
+
+ gpr_mu mu; /* protects iterations, counter, thread_count, done */
+
+ gpr_cv cv; /* signalling depends on test */
+
+ gpr_cv done_cv; /* signalled when done == 0 */
+
+ queue q;
+
+ gpr_stats_counter stats_counter;
+
+ gpr_refcount refcount;
+ gpr_refcount thread_refcount;
+ gpr_event event;
+};
+
+/* Return pointer to a new struct test. */
+static struct test *test_new(int threads, gpr_int64 iterations) {
+ struct test *m = gpr_malloc(sizeof(*m));
+ m->threads = threads;
+ m->iterations = iterations;
+ m->counter = 0;
+ m->thread_count = 0;
+ m->done = threads;
+ gpr_mu_init(&m->mu);
+ gpr_cv_init(&m->cv);
+ gpr_cv_init(&m->done_cv);
+ queue_init(&m->q);
+ gpr_stats_init(&m->stats_counter, 0);
+ gpr_ref_init(&m->refcount, 0);
+ gpr_ref_init(&m->thread_refcount, threads);
+ gpr_event_init(&m->event);
+ return m;
+}
+
+/* Return pointer to a new struct test. */
+static void test_destroy(struct test *m) {
+ gpr_mu_destroy(&m->mu);
+ gpr_cv_destroy(&m->cv);
+ gpr_cv_destroy(&m->done_cv);
+ queue_destroy(&m->q);
+ gpr_free(m);
+}
+
+/* Create m->threads threads, each running (*body)(m) */
+static void test_create_threads(struct test *m, void (*body)(void *arg)) {
+ gpr_thd_id id;
+ int i;
+ for (i = 0; i != m->threads; i++) {
+ GPR_ASSERT(gpr_thd_new(&id, body, m, NULL));
+ }
+}
+
+/* Wait until all threads report done. */
+static void test_wait(struct test *m) {
+ gpr_mu_lock(&m->mu);
+ while (m->done != 0) {
+ gpr_cv_wait(&m->done_cv, &m->mu, gpr_inf_future);
+ }
+ gpr_mu_unlock(&m->mu);
+}
+
+/* Get an integer thread id in the raneg 0..threads-1 */
+static int thread_id(struct test *m) {
+ int id;
+ gpr_mu_lock(&m->mu);
+ id = m->thread_count++;
+ gpr_mu_unlock(&m->mu);
+ return id;
+}
+
+/* Indicate that a thread is done, by decrementing m->done
+ and signalling done_cv if m->done==0. */
+static void mark_thread_done(struct test *m) {
+ gpr_mu_lock(&m->mu);
+ GPR_ASSERT(m->done != 0);
+ m->done--;
+ if (m->done == 0) {
+ gpr_cv_signal(&m->done_cv);
+ }
+ gpr_mu_unlock(&m->mu);
+}
+
+/* Test several threads running (*body)(struct test *m) for increasing settings
+ of m->iterations, until about timeout_s to 2*timeout_s seconds have elapsed.
+ If extra!=NULL, run (*extra)(m) in an additional thread. */
+static void test(const char *name, void (*body)(void *m),
+ void (*extra)(void *m), int timeout_s) {
+ gpr_int64 iterations = 1024;
+ struct test *m;
+ gpr_timespec start = gpr_now();
+ gpr_timespec time_taken;
+ gpr_timespec deadline =
+ gpr_time_add(start, gpr_time_from_micros(timeout_s * 1000000));
+ fprintf(stderr, "%s:", name);
+ while (gpr_time_cmp(gpr_now(), deadline) < 0) {
+ iterations <<= 1;
+ fprintf(stderr, " %ld", (long)iterations);
+ m = test_new(10, iterations);
+ if (extra != NULL) {
+ gpr_thd_id id;
+ GPR_ASSERT(gpr_thd_new(&id, extra, m, NULL));
+ m->done++; /* one more thread to wait for */
+ }
+ test_create_threads(m, body);
+ test_wait(m);
+ if (m->counter != m->threads * m->iterations) {
+ fprintf(stderr, "counter %ld threads %d iterations %ld\n",
+ (long)m->counter, m->threads, (long)m->iterations);
+ GPR_ASSERT(0);
+ }
+ test_destroy(m);
+ }
+ time_taken = gpr_time_sub(gpr_now(), start);
+ fprintf(stderr, " done %ld.%09d s\n", (long)time_taken.tv_sec,
+ (int)time_taken.tv_nsec);
+}
+
+/* Increment m->counter on each iteration; then mark thread as done. */
+static void inc(void *v /*=m*/) {
+ struct test *m = v;
+ gpr_int64 i;
+ for (i = 0; i != m->iterations; i++) {
+ gpr_mu_lock(&m->mu);
+ m->counter++;
+ gpr_mu_unlock(&m->mu);
+ }
+ mark_thread_done(m);
+}
+
+/* Increment m->counter under lock acquired with trylock, m->iterations times;
+ then mark thread as done. */
+static void inctry(void *v /*=m*/) {
+ struct test *m = v;
+ gpr_int64 i;
+ for (i = 0; i != m->iterations;) {
+ if (gpr_mu_trylock(&m->mu)) {
+ m->counter++;
+ gpr_mu_unlock(&m->mu);
+ i++;
+ }
+ }
+ mark_thread_done(m);
+}
+
+/* Increment counter only when (m->counter%m->threads)==m->thread_id; then mark
+ thread as done. */
+static void inc_by_turns(void *v /*=m*/) {
+ struct test *m = v;
+ gpr_int64 i;
+ int id = thread_id(m);
+ for (i = 0; i != m->iterations; i++) {
+ gpr_mu_lock(&m->mu);
+ while ((m->counter % m->threads) != id) {
+ gpr_cv_wait(&m->cv, &m->mu, gpr_inf_future);
+ }
+ m->counter++;
+ gpr_cv_broadcast(&m->cv);
+ gpr_mu_unlock(&m->mu);
+ }
+ mark_thread_done(m);
+}
+
+/* Wait a millisecond and increment counter on each iteration;
+ then mark thread as done. */
+static void inc_with_1ms_delay(void *v /*=m*/) {
+ struct test *m = v;
+ gpr_int64 i;
+ for (i = 0; i != m->iterations; i++) {
+ gpr_timespec deadline;
+ gpr_mu_lock(&m->mu);
+ deadline = gpr_time_add(gpr_now(), gpr_time_from_micros(1000));
+ while (!gpr_cv_wait(&m->cv, &m->mu, deadline)) {
+ }
+ m->counter++;
+ gpr_mu_unlock(&m->mu);
+ }
+ mark_thread_done(m);
+}
+
+/* Wait a millisecond and increment counter on each iteration, using an event
+ for timing; then mark thread as done. */
+static void inc_with_1ms_delay_event(void *v /*=m*/) {
+ struct test *m = v;
+ gpr_int64 i;
+ for (i = 0; i != m->iterations; i++) {
+ gpr_timespec deadline;
+ deadline = gpr_time_add(gpr_now(), gpr_time_from_micros(1000));
+ GPR_ASSERT(gpr_event_wait(&m->event, deadline) == NULL);
+ gpr_mu_lock(&m->mu);
+ m->counter++;
+ gpr_mu_unlock(&m->mu);
+ }
+ mark_thread_done(m);
+}
+
+/* Produce m->iterations elements on queue m->q, then mark thread as done.
+ Even threads use queue_append(), and odd threads use queue_try_append()
+ until it succeeds. */
+static void many_producers(void *v /*=m*/) {
+ struct test *m = v;
+ gpr_int64 i;
+ int x = thread_id(m);
+ if ((x & 1) == 0) {
+ for (i = 0; i != m->iterations; i++) {
+ queue_append(&m->q, 1);
+ }
+ } else {
+ for (i = 0; i != m->iterations; i++) {
+ while (!queue_try_append(&m->q, 1)) {
+ }
+ }
+ }
+ mark_thread_done(m);
+}
+
+/* Consume elements from m->q until m->threads*m->iterations are seen,
+ wait an extra second to confirm that no more elements are arriving,
+ then mark thread as done. */
+static void consumer(void *v /*=m*/) {
+ struct test *m = v;
+ gpr_int64 n = m->iterations * m->threads;
+ gpr_int64 i;
+ int value;
+ for (i = 0; i != n; i++) {
+ queue_remove(&m->q, &value, gpr_inf_future);
+ }
+ gpr_mu_lock(&m->mu);
+ m->counter = n;
+ gpr_mu_unlock(&m->mu);
+ GPR_ASSERT(
+ !queue_remove(&m->q, &value,
+ gpr_time_add(gpr_now(), gpr_time_from_micros(1000000))));
+ mark_thread_done(m);
+}
+
+/* Increment m->stats_counter m->iterations times, transfer counter value to
+ m->counter, then mark thread as done. */
+static void statsinc(void *v /*=m*/) {
+ struct test *m = v;
+ gpr_int64 i;
+ for (i = 0; i != m->iterations; i++) {
+ gpr_stats_inc(&m->stats_counter, 1);
+ }
+ gpr_mu_lock(&m->mu);
+ m->counter = gpr_stats_read(&m->stats_counter);
+ gpr_mu_unlock(&m->mu);
+ mark_thread_done(m);
+}
+
+/* Increment m->refcount m->iterations times, decrement m->thread_refcount
+ once, and if it reaches zero, set m->event to (void*)1; then mark thread as
+ done. */
+static void refinc(void *v /*=m*/) {
+ struct test *m = v;
+ gpr_int64 i;
+ for (i = 0; i != m->iterations; i++) {
+ gpr_ref(&m->refcount);
+ }
+ if (gpr_unref(&m->thread_refcount)) {
+ gpr_event_set(&m->event, (void *)1);
+ }
+ mark_thread_done(m);
+}
+
+/* Wait until m->event is set to (void *)1, then decrement m->refcount
+ m->stats_counter m->iterations times, and ensure that the last decrement
+ caused the counter to reach zero, then mark thread as done. */
+static void refcheck(void *v /*=m*/) {
+ struct test *m = v;
+ gpr_int64 n = m->iterations * m->threads;
+ gpr_int64 i;
+ GPR_ASSERT(gpr_event_wait(&m->event, gpr_inf_future) == (void *)1);
+ GPR_ASSERT(gpr_event_get(&m->event) == (void *)1);
+ for (i = 1; i != n; i++) {
+ GPR_ASSERT(!gpr_unref(&m->refcount));
+ m->counter++;
+ }
+ GPR_ASSERT(gpr_unref(&m->refcount));
+ m->counter++;
+ mark_thread_done(m);
+}
+
+/* ------------------------------------------------- */
+
+int main(int argc, char *argv[]) {
+ grpc_test_init(argc, argv);
+ test("mutex", &inc, NULL, 1);
+ test("mutex try", &inctry, NULL, 1);
+ test("cv", &inc_by_turns, NULL, 1);
+ test("timedcv", &inc_with_1ms_delay, NULL, 1);
+ test("queue", &many_producers, &consumer, 10);
+ test("stats_counter", &statsinc, NULL, 1);
+ test("refcount", &refinc, &refcheck, 1);
+ test("timedevent", &inc_with_1ms_delay_event, NULL, 1);
+ return 0;
+}
diff --git a/test/core/support/thd_test.c b/test/core/support/thd_test.c
new file mode 100644
index 0000000000..c70e025326
--- /dev/null
+++ b/test/core/support/thd_test.c
@@ -0,0 +1,90 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+/* Test of gpr thread support. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <grpc/support/log.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/thd.h>
+#include <grpc/support/time.h>
+#include "test/core/util/test_config.h"
+
+struct test {
+ gpr_mu mu;
+ int n;
+ int is_done;
+ gpr_cv done_cv;
+};
+
+/* A Thread body. Decrement t->n, and if is becomes zero, set t->done. */
+static void thd_body(void *v) {
+ struct test *t = v;
+ gpr_mu_lock(&t->mu);
+ t->n--;
+ if (t->n == 0) {
+ t->is_done = 1;
+ gpr_cv_signal(&t->done_cv);
+ }
+ gpr_mu_unlock(&t->mu);
+}
+
+/* Test that we can create a number of threads and wait for them. */
+static void test(void) {
+ int i;
+ gpr_thd_id thd;
+ struct test t;
+ int n = 1000;
+ gpr_mu_init(&t.mu);
+ gpr_cv_init(&t.done_cv);
+ t.n = n;
+ t.is_done = 0;
+ for (i = 0; i != n; i++) {
+ GPR_ASSERT(gpr_thd_new(&thd, &thd_body, &t, NULL));
+ }
+ gpr_mu_lock(&t.mu);
+ while (!t.is_done) {
+ gpr_cv_wait(&t.done_cv, &t.mu, gpr_inf_future);
+ }
+ gpr_mu_unlock(&t.mu);
+ GPR_ASSERT(t.n == 0);
+}
+
+/* ------------------------------------------------- */
+
+int main(int argc, char *argv[]) {
+ grpc_test_init(argc, argv);
+ test();
+ return 0;
+}
diff --git a/test/core/support/time_test.c b/test/core/support/time_test.c
new file mode 100644
index 0000000000..d74d6a52d9
--- /dev/null
+++ b/test/core/support/time_test.c
@@ -0,0 +1,255 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+/* Test of gpr time support. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <grpc/support/log.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/thd.h>
+#include <grpc/support/time.h>
+#include "test/core/util/test_config.h"
+
+static void to_fp(void *arg, const char *buf, int len) {
+ fwrite(buf, 1, len, (FILE *)arg);
+}
+
+/* Convert gpr_uintmax x to ascii base b (2..16), and write with
+ (*writer)(arg, ...), zero padding to "chars" digits). */
+static void u_to_s(gpr_uintmax x, unsigned base, int chars,
+ void (*writer)(void *arg, const char *buf, int len),
+ void *arg) {
+ char buf[64];
+ char *p = buf + sizeof(buf);
+ do {
+ *--p = "0123456789abcdef"[x % base];
+ x /= base;
+ chars--;
+ } while (x != 0 || chars > 0);
+ (*writer)(arg, p, buf + sizeof(buf) - p);
+}
+
+/* Convert gpr_intmax x to ascii base b (2..16), and write with
+ (*writer)(arg, ...), zero padding to "chars" digits). */
+static void i_to_s(gpr_intmax x, unsigned base, int chars,
+ void (*writer)(void *arg, const char *buf, int len),
+ void *arg) {
+ if (x < 0) {
+ (*writer)(arg, "-", 1);
+ u_to_s(-x, base, chars - 1, writer, arg);
+ } else {
+ u_to_s(x, base, chars, writer, arg);
+ }
+}
+
+/* Convert ts to ascii, and write with (*writer)(arg, ...). */
+static void ts_to_s(gpr_timespec t,
+ void (*writer)(void *arg, const char *buf, int len),
+ void *arg) {
+ if (t.tv_sec < 0 && t.tv_nsec != 0) {
+ t.tv_sec++;
+ t.tv_nsec = 1000000000 - t.tv_nsec;
+ }
+ i_to_s(t.tv_sec, 10, 0, writer, arg);
+ (*writer)(arg, ".", 1);
+ i_to_s(t.tv_nsec, 10, 9, writer, arg);
+}
+
+static void test_values(void) {
+ int i;
+
+ gpr_timespec x = gpr_time_0;
+ GPR_ASSERT(x.tv_sec == 0 && x.tv_nsec == 0);
+
+ x = gpr_inf_future;
+ fprintf(stderr, "far future ");
+ u_to_s(x.tv_sec, 16, 16, &to_fp, stderr);
+ fprintf(stderr, "\n");
+ GPR_ASSERT(x.tv_sec >= INT_MAX);
+ fprintf(stderr, "far future ");
+ ts_to_s(x, &to_fp, stderr);
+ fprintf(stderr, "\n");
+
+ x = gpr_inf_past;
+ fprintf(stderr, "far past ");
+ u_to_s(x.tv_sec, 16, 16, &to_fp, stderr);
+ fprintf(stderr, "\n");
+ GPR_ASSERT(x.tv_sec <= INT_MIN);
+ fprintf(stderr, "far past ");
+ ts_to_s(x, &to_fp, stderr);
+ fprintf(stderr, "\n");
+
+ for (i = 1; i != 1000 * 1000 * 1000; i *= 10) {
+ x = gpr_time_from_micros(i);
+ GPR_ASSERT(x.tv_sec == i / GPR_US_PER_SEC &&
+ x.tv_nsec == (i % GPR_US_PER_SEC) * GPR_NS_PER_US);
+ x = gpr_time_from_nanos(i);
+ GPR_ASSERT(x.tv_sec == i / GPR_NS_PER_SEC &&
+ x.tv_nsec == (i % GPR_NS_PER_SEC));
+ x = gpr_time_from_millis(i);
+ GPR_ASSERT(x.tv_sec == i / GPR_MS_PER_SEC &&
+ x.tv_nsec == (i % GPR_MS_PER_SEC) * GPR_NS_PER_MS);
+ }
+
+ /* Test possible overflow in conversion of -ve values. */
+ x = gpr_time_from_micros(-(LONG_MAX - 999997));
+ GPR_ASSERT(x.tv_sec < 0);
+ GPR_ASSERT(x.tv_nsec >= 0 && x.tv_nsec < 1000000000);
+
+ x = gpr_time_from_nanos(-(LONG_MAX - 999999997));
+ GPR_ASSERT(x.tv_sec < 0);
+ GPR_ASSERT(x.tv_nsec >= 0 && x.tv_nsec < 1000000000);
+
+ x = gpr_time_from_millis(-(LONG_MAX - 997));
+ GPR_ASSERT(x.tv_sec < 0);
+ GPR_ASSERT(x.tv_nsec >= 0 && x.tv_nsec < 1000000000);
+
+ /* Test general -ve values. */
+ for (i = -1; i > -1000 * 1000 * 1000; i *= 7) {
+ x = gpr_time_from_micros(i);
+ GPR_ASSERT(x.tv_sec * GPR_US_PER_SEC + x.tv_nsec / GPR_NS_PER_US == i);
+ x = gpr_time_from_nanos(i);
+ GPR_ASSERT(x.tv_sec * GPR_NS_PER_SEC + x.tv_nsec == i);
+ x = gpr_time_from_millis(i);
+ GPR_ASSERT(x.tv_sec * GPR_MS_PER_SEC + x.tv_nsec / GPR_NS_PER_MS == i);
+ }
+}
+
+static void test_add_sub(void) {
+ int i;
+ int j;
+ int k;
+ /* Basic addition and subtraction. */
+ for (i = -100; i <= 100; i++) {
+ for (j = -100; j <= 100; j++) {
+ for (k = 1; k <= 10000000; k *= 10) {
+ int sum = i + j;
+ int diff = i - j;
+ gpr_timespec it = gpr_time_from_micros(i * k);
+ gpr_timespec jt = gpr_time_from_micros(j * k);
+ gpr_timespec sumt = gpr_time_add(it, jt);
+ gpr_timespec difft = gpr_time_sub(it, jt);
+ if (gpr_time_cmp(gpr_time_from_micros(sum * k), sumt) != 0) {
+ fprintf(stderr, "i %d j %d sum %d sumt ", i, j, sum);
+ ts_to_s(sumt, &to_fp, stderr);
+ fprintf(stderr, "\n");
+ GPR_ASSERT(0);
+ }
+ if (gpr_time_cmp(gpr_time_from_micros(diff * k), difft) != 0) {
+ fprintf(stderr, "i %d j %d diff %d diff ", i, j, diff);
+ ts_to_s(sumt, &to_fp, stderr);
+ fprintf(stderr, "\n");
+ GPR_ASSERT(0);
+ }
+ }
+ }
+ }
+}
+
+static void test_overflow(void) {
+ /* overflow */
+ gpr_timespec x = gpr_time_from_micros(1);
+ do {
+ x = gpr_time_add(x, x);
+ } while (gpr_time_cmp(x, gpr_inf_future) < 0);
+ GPR_ASSERT(gpr_time_cmp(x, gpr_inf_future) == 0);
+ x = gpr_time_from_micros(-1);
+ do {
+ x = gpr_time_add(x, x);
+ } while (gpr_time_cmp(x, gpr_inf_past) > 0);
+ GPR_ASSERT(gpr_time_cmp(x, gpr_inf_past) == 0);
+}
+
+static void test_sticky_infinities(void) {
+ int i;
+ int j;
+ int k;
+ static const gpr_timespec *infinity[] = {&gpr_inf_future, &gpr_inf_past};
+ static const gpr_timespec *addend[] = {&gpr_inf_future, &gpr_inf_past,
+ &gpr_time_0, NULL};
+
+ /* Infinities are sticky */
+ for (i = 0; i != sizeof(infinity) / sizeof(infinity[0]); i++) {
+ for (j = 0; j != sizeof(addend) / sizeof(addend[0]); j++) {
+ if (addend[j] == NULL) {
+ for (k = -200; k <= 200; k++) {
+ gpr_timespec y = gpr_time_from_micros(k * 100000);
+ gpr_timespec x = gpr_time_add(*infinity[i], y);
+ GPR_ASSERT(gpr_time_cmp(x, *infinity[i]) == 0);
+ x = gpr_time_sub(*infinity[i], y);
+ GPR_ASSERT(gpr_time_cmp(x, *infinity[i]) == 0);
+ }
+ } else {
+ gpr_timespec x = gpr_time_add(*infinity[i], *addend[j]);
+ GPR_ASSERT(gpr_time_cmp(x, *infinity[i]) == 0);
+ x = gpr_time_sub(*infinity[i], *addend[j]);
+ GPR_ASSERT(gpr_time_cmp(x, *infinity[i]) == 0);
+ }
+ }
+ }
+}
+
+static void test_similar() {
+ GPR_ASSERT(1 == gpr_time_similar(gpr_inf_future, gpr_inf_future, gpr_time_0));
+ GPR_ASSERT(1 == gpr_time_similar(gpr_inf_past, gpr_inf_past, gpr_time_0));
+ GPR_ASSERT(0 == gpr_time_similar(gpr_inf_past, gpr_inf_future, gpr_time_0));
+ GPR_ASSERT(0 == gpr_time_similar(gpr_inf_future, gpr_inf_past, gpr_time_0));
+ GPR_ASSERT(1 == gpr_time_similar(gpr_time_from_micros(10),
+ gpr_time_from_micros(10), gpr_time_0));
+ GPR_ASSERT(1 == gpr_time_similar(gpr_time_from_micros(10),
+ gpr_time_from_micros(15),
+ gpr_time_from_micros(10)));
+ GPR_ASSERT(1 == gpr_time_similar(gpr_time_from_micros(15),
+ gpr_time_from_micros(10),
+ gpr_time_from_micros(10)));
+ GPR_ASSERT(0 == gpr_time_similar(gpr_time_from_micros(10),
+ gpr_time_from_micros(25),
+ gpr_time_from_micros(10)));
+ GPR_ASSERT(0 == gpr_time_similar(gpr_time_from_micros(25),
+ gpr_time_from_micros(10),
+ gpr_time_from_micros(10)));
+}
+
+int main(int argc, char *argv[]) {
+ grpc_test_init(argc, argv);
+
+ test_values();
+ test_add_sub();
+ test_overflow();
+ test_sticky_infinities();
+ test_similar();
+ return 0;
+}
diff --git a/test/core/surface/byte_buffer_reader_test.c b/test/core/surface/byte_buffer_reader_test.c
new file mode 100644
index 0000000000..bc5a512a0b
--- /dev/null
+++ b/test/core/surface/byte_buffer_reader_test.c
@@ -0,0 +1,111 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include <grpc/byte_buffer_reader.h>
+#include <grpc/byte_buffer.h>
+#include <grpc/support/slice.h>
+#include <grpc/grpc.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/thd.h>
+#include <grpc/support/time.h>
+#include "test/core/util/test_config.h"
+
+#include <string.h>
+
+#define LOG_TEST() gpr_log(GPR_INFO, "%s", __FUNCTION__)
+
+static void test_create() {
+ grpc_byte_buffer *buffer;
+ grpc_byte_buffer_reader *reader;
+ gpr_slice empty = gpr_empty_slice();
+ LOG_TEST();
+ buffer = grpc_byte_buffer_create(&empty, 1);
+ reader = grpc_byte_buffer_reader_create(buffer);
+ grpc_byte_buffer_reader_destroy(reader);
+ grpc_byte_buffer_destroy(buffer);
+}
+
+static void test_read_one_slice() {
+ gpr_slice slice;
+ grpc_byte_buffer *buffer;
+ grpc_byte_buffer_reader *reader;
+ gpr_slice first_slice, second_slice;
+ int first_code, second_code;
+
+ LOG_TEST();
+ slice = gpr_slice_from_copied_string("test");
+ buffer = grpc_byte_buffer_create(&slice, 1);
+ gpr_slice_unref(slice);
+ reader = grpc_byte_buffer_reader_create(buffer);
+ first_code = grpc_byte_buffer_reader_next(reader, &first_slice);
+ GPR_ASSERT(first_code != 0);
+ GPR_ASSERT(memcmp(GPR_SLICE_START_PTR(first_slice), "test", 4) == 0);
+ gpr_slice_unref(first_slice);
+ second_code = grpc_byte_buffer_reader_next(reader, &second_slice);
+ GPR_ASSERT(second_code == 0);
+ grpc_byte_buffer_reader_destroy(reader);
+ grpc_byte_buffer_destroy(buffer);
+}
+
+static void test_read_one_slice_malloc() {
+ gpr_slice slice;
+ grpc_byte_buffer *buffer;
+ grpc_byte_buffer_reader *reader;
+ gpr_slice first_slice, second_slice;
+ int first_code, second_code;
+
+ LOG_TEST();
+ slice = gpr_slice_malloc(4);
+ memcpy(GPR_SLICE_START_PTR(slice), "test", 4);
+ buffer = grpc_byte_buffer_create(&slice, 1);
+ gpr_slice_unref(slice);
+ reader = grpc_byte_buffer_reader_create(buffer);
+ first_code = grpc_byte_buffer_reader_next(reader, &first_slice);
+ GPR_ASSERT(first_code != 0);
+ GPR_ASSERT(memcmp(GPR_SLICE_START_PTR(first_slice), "test", 4) == 0);
+ gpr_slice_unref(first_slice);
+ second_code = grpc_byte_buffer_reader_next(reader, &second_slice);
+ GPR_ASSERT(second_code == 0);
+ grpc_byte_buffer_reader_destroy(reader);
+ grpc_byte_buffer_destroy(buffer);
+}
+
+int main(int argc, char **argv) {
+ grpc_test_init(argc, argv);
+ test_create();
+ test_read_one_slice();
+ test_read_one_slice_malloc();
+ return 0;
+}
diff --git a/test/core/surface/completion_queue_benchmark.c b/test/core/surface/completion_queue_benchmark.c
new file mode 100644
index 0000000000..5360d7c6c3
--- /dev/null
+++ b/test/core/surface/completion_queue_benchmark.c
@@ -0,0 +1,168 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include "src/core/surface/completion_queue.h"
+
+#include <math.h>
+#include <stdio.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/thd.h>
+#include <grpc/support/time.h>
+
+typedef struct test_thread_options {
+ gpr_event on_started;
+ gpr_event *start;
+ gpr_event on_finished;
+ grpc_completion_queue *cc;
+ int iterations;
+} test_thread_options;
+
+static void producer_thread(void *arg) {
+ test_thread_options *opt = arg;
+ int i;
+
+ gpr_event_set(&opt->on_started, (void *)(gpr_intptr) 1);
+ GPR_ASSERT(gpr_event_wait(opt->start, gpr_inf_future));
+
+ for (i = 0; i < opt->iterations; i++) {
+ grpc_cq_begin_op(opt->cc, NULL, GRPC_WRITE_ACCEPTED);
+ grpc_cq_end_write_accepted(opt->cc, (void *)(gpr_intptr) 1, NULL, NULL,
+ NULL, GRPC_OP_OK);
+ }
+
+ gpr_event_set(&opt->on_finished, (void *)(gpr_intptr) 1);
+}
+
+static void consumer_thread(void *arg) {
+ test_thread_options *opt = arg;
+ grpc_event *ev;
+
+ gpr_event_set(&opt->on_started, (void *)(gpr_intptr) 1);
+ GPR_ASSERT(gpr_event_wait(opt->start, gpr_inf_future));
+
+ for (;;) {
+ ev = grpc_completion_queue_next(opt->cc, gpr_inf_future);
+ switch (ev->type) {
+ case GRPC_WRITE_ACCEPTED:
+ break;
+ case GRPC_QUEUE_SHUTDOWN:
+ gpr_event_set(&opt->on_finished, (void *)(gpr_intptr) 1);
+ return;
+ default:
+ gpr_log(GPR_ERROR, "Invalid event received: %d", ev->type);
+ abort();
+ }
+ grpc_event_finish(ev);
+ }
+}
+
+double ops_per_second(int consumers, int producers, int iterations) {
+ test_thread_options *options =
+ gpr_malloc((producers + consumers) * sizeof(test_thread_options));
+ gpr_event start = GPR_EVENT_INIT;
+ grpc_completion_queue *cc = grpc_completion_queue_create();
+ int i;
+ gpr_timespec t_start, t_end, t_delta;
+
+ /* start all threads: they will wait for phase1 */
+ for (i = 0; i < producers + consumers; i++) {
+ gpr_thd_id id;
+ gpr_event_init(&options[i].on_started);
+ gpr_event_init(&options[i].on_finished);
+ options[i].start = &start;
+ options[i].cc = cc;
+ options[i].iterations = iterations;
+ GPR_ASSERT(gpr_thd_new(&id,
+ i < producers ? producer_thread : consumer_thread,
+ options + i, NULL));
+ gpr_event_wait(&options[i].on_started, gpr_inf_future);
+ }
+
+ /* start the benchmark */
+ t_start = gpr_now();
+ gpr_event_set(&start, (void *)(gpr_intptr) 1);
+
+ /* wait for producers to finish */
+ for (i = 0; i < producers; i++) {
+ GPR_ASSERT(gpr_event_wait(&options[i].on_finished, gpr_inf_future));
+ }
+
+ /* in parallel, we shutdown the completion channel - all events should still
+ be consumed */
+ grpc_completion_queue_shutdown(cc);
+
+ /* join all threads */
+ for (i = producers; i < producers + consumers; i++) {
+ GPR_ASSERT(gpr_event_wait(&options[i].on_finished, gpr_inf_future));
+ }
+ t_end = gpr_now();
+
+ /* destroy the completion channel */
+ grpc_completion_queue_destroy(cc);
+
+ gpr_free(options);
+
+ t_delta = gpr_time_sub(t_end, t_start);
+ return (t_delta.tv_sec + 1e-9 * t_delta.tv_nsec) / (producers * iterations);
+}
+
+double ops_per_second_top(int consumers, int producers) {
+ return ops_per_second(consumers, producers, 1000000 / producers);
+}
+
+int main(void) {
+ const int counts[] = {1, 2, 3, 4, 5, 6, 7, 8, 12, 16, 20, 24, 32, 40, 64};
+ const int ncounts = sizeof(counts) / sizeof(*counts);
+ int i, j;
+
+ printf("\"\",");
+ for (i = 0; i < ncounts; i++) {
+ int producers = counts[i];
+ printf("%d%s", producers, i == ncounts - 1 ? "\n" : ",");
+ }
+
+ for (j = 0; j < ncounts; j++) {
+ int consumers = counts[j];
+ printf("%d,", consumers);
+ for (i = 0; i < ncounts; i++) {
+ int producers = counts[i];
+ printf("%f%s", ops_per_second_top(consumers, producers),
+ i == ncounts - 1 ? "\n" : ",");
+ fflush(stdout);
+ }
+ }
+
+ return 0;
+}
diff --git a/test/core/surface/completion_queue_test.c b/test/core/surface/completion_queue_test.c
new file mode 100644
index 0000000000..6df159f697
--- /dev/null
+++ b/test/core/surface/completion_queue_test.c
@@ -0,0 +1,435 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include "src/core/surface/completion_queue.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/thd.h>
+#include <grpc/support/time.h>
+#include <grpc/support/useful.h>
+#include "src/core/surface/surface_em.h"
+#include "test/core/util/test_config.h"
+
+#define LOG_TEST() gpr_log(GPR_INFO, "%s", __FUNCTION__)
+
+static void increment_int_on_finish(void *user_data, grpc_op_error error) {
+ ++*(int *)user_data;
+}
+
+static void *create_test_tag() {
+ static gpr_intptr i = 0;
+ return (void *)(++i);
+}
+
+/* helper for tests to shutdown correctly and tersely */
+static void shutdown_and_destroy(grpc_completion_queue *cc) {
+ grpc_event *ev;
+ grpc_completion_queue_shutdown(cc);
+ ev = grpc_completion_queue_next(cc, gpr_inf_past);
+ GPR_ASSERT(ev != NULL);
+ GPR_ASSERT(ev->type == GRPC_QUEUE_SHUTDOWN);
+ grpc_event_finish(ev);
+ grpc_completion_queue_destroy(cc);
+}
+
+/* ensure we can create and destroy a completion channel */
+static void test_no_op() {
+ LOG_TEST();
+ shutdown_and_destroy(grpc_completion_queue_create());
+}
+
+static void test_wait_empty() {
+ grpc_completion_queue *cc;
+
+ LOG_TEST();
+
+ cc = grpc_completion_queue_create();
+ GPR_ASSERT(grpc_completion_queue_next(cc, gpr_now()) == NULL);
+ shutdown_and_destroy(cc);
+}
+
+static void test_cq_end_read() {
+ grpc_event *ev;
+ grpc_completion_queue *cc;
+ int on_finish_called = 0;
+ void *tag = create_test_tag();
+
+ LOG_TEST();
+
+ cc = grpc_completion_queue_create();
+
+ grpc_cq_begin_op(cc, NULL, GRPC_READ);
+ grpc_cq_end_read(cc, tag, NULL, increment_int_on_finish, &on_finish_called,
+ NULL);
+
+ ev = grpc_completion_queue_next(cc, gpr_inf_past);
+ GPR_ASSERT(ev != NULL);
+ GPR_ASSERT(ev->type == GRPC_READ);
+ GPR_ASSERT(ev->tag == tag);
+ GPR_ASSERT(ev->data.read == NULL);
+ GPR_ASSERT(on_finish_called == 0);
+ grpc_event_finish(ev);
+ GPR_ASSERT(on_finish_called == 1);
+
+ shutdown_and_destroy(cc);
+}
+
+static void test_cq_end_invoke_accepted() {
+ grpc_event *ev;
+ grpc_completion_queue *cc;
+ int on_finish_called = 0;
+ void *tag = create_test_tag();
+
+ LOG_TEST();
+
+ cc = grpc_completion_queue_create();
+
+ grpc_cq_begin_op(cc, NULL, GRPC_INVOKE_ACCEPTED);
+ grpc_cq_end_invoke_accepted(cc, tag, NULL, increment_int_on_finish,
+ &on_finish_called, GRPC_OP_OK);
+
+ ev = grpc_completion_queue_next(cc, gpr_inf_past);
+ GPR_ASSERT(ev != NULL);
+ GPR_ASSERT(ev->type == GRPC_INVOKE_ACCEPTED);
+ GPR_ASSERT(ev->tag == tag);
+ GPR_ASSERT(ev->data.invoke_accepted == GRPC_OP_OK);
+ GPR_ASSERT(on_finish_called == 0);
+ grpc_event_finish(ev);
+ GPR_ASSERT(on_finish_called == 1);
+
+ shutdown_and_destroy(cc);
+}
+
+static void test_cq_end_write_accepted() {
+ grpc_event *ev;
+ grpc_completion_queue *cc;
+ int on_finish_called = 0;
+ void *tag = create_test_tag();
+
+ LOG_TEST();
+
+ cc = grpc_completion_queue_create();
+
+ grpc_cq_begin_op(cc, NULL, GRPC_WRITE_ACCEPTED);
+ grpc_cq_end_write_accepted(cc, tag, NULL, increment_int_on_finish,
+ &on_finish_called, GRPC_OP_OK);
+
+ ev = grpc_completion_queue_next(cc, gpr_inf_past);
+ GPR_ASSERT(ev != NULL);
+ GPR_ASSERT(ev->type == GRPC_WRITE_ACCEPTED);
+ GPR_ASSERT(ev->tag == tag);
+ GPR_ASSERT(ev->data.write_accepted == GRPC_OP_OK);
+ GPR_ASSERT(on_finish_called == 0);
+ grpc_event_finish(ev);
+ GPR_ASSERT(on_finish_called == 1);
+
+ shutdown_and_destroy(cc);
+}
+
+static void test_cq_end_finish_accepted() {
+ grpc_event *ev;
+ grpc_completion_queue *cc;
+ int on_finish_called = 0;
+ void *tag = create_test_tag();
+
+ LOG_TEST();
+
+ cc = grpc_completion_queue_create();
+
+ grpc_cq_begin_op(cc, NULL, GRPC_FINISH_ACCEPTED);
+ grpc_cq_end_finish_accepted(cc, tag, NULL, increment_int_on_finish,
+ &on_finish_called, GRPC_OP_OK);
+
+ ev = grpc_completion_queue_next(cc, gpr_inf_past);
+ GPR_ASSERT(ev != NULL);
+ GPR_ASSERT(ev->type == GRPC_FINISH_ACCEPTED);
+ GPR_ASSERT(ev->tag == tag);
+ GPR_ASSERT(ev->data.finish_accepted == GRPC_OP_OK);
+ GPR_ASSERT(on_finish_called == 0);
+ grpc_event_finish(ev);
+ GPR_ASSERT(on_finish_called == 1);
+
+ shutdown_and_destroy(cc);
+}
+
+static void test_cq_end_client_metadata_read() {
+ grpc_event *ev;
+ grpc_completion_queue *cc;
+ int on_finish_called = 0;
+ void *tag = create_test_tag();
+
+ LOG_TEST();
+
+ cc = grpc_completion_queue_create();
+
+ grpc_cq_begin_op(cc, NULL, GRPC_CLIENT_METADATA_READ);
+ grpc_cq_end_client_metadata_read(cc, tag, NULL, increment_int_on_finish,
+ &on_finish_called, 0, NULL);
+
+ ev = grpc_completion_queue_next(cc, gpr_inf_past);
+ GPR_ASSERT(ev != NULL);
+ GPR_ASSERT(ev->type == GRPC_CLIENT_METADATA_READ);
+ GPR_ASSERT(ev->tag == tag);
+ GPR_ASSERT(ev->data.client_metadata_read.count == 0);
+ GPR_ASSERT(ev->data.client_metadata_read.elements == NULL);
+ GPR_ASSERT(on_finish_called == 0);
+ grpc_event_finish(ev);
+ GPR_ASSERT(on_finish_called == 1);
+
+ shutdown_and_destroy(cc);
+}
+
+static void test_pluck() {
+ grpc_event *ev;
+ grpc_completion_queue *cc;
+ void *tags[128];
+ int i, j;
+ int on_finish_called = 0;
+
+ LOG_TEST();
+
+ for (i = 0; i < GPR_ARRAY_SIZE(tags); i++) {
+ tags[i] = create_test_tag();
+ for (j = 0; j < i; j++) {
+ GPR_ASSERT(tags[i] != tags[j]);
+ }
+ }
+
+ cc = grpc_completion_queue_create();
+
+ for (i = 0; i < GPR_ARRAY_SIZE(tags); i++) {
+ grpc_cq_begin_op(cc, NULL, GRPC_WRITE_ACCEPTED);
+ grpc_cq_end_write_accepted(cc, tags[i], NULL, increment_int_on_finish,
+ &on_finish_called, GRPC_OP_OK);
+ }
+
+ for (i = 0; i < GPR_ARRAY_SIZE(tags); i++) {
+ ev = grpc_completion_queue_pluck(cc, tags[i], gpr_inf_past);
+ GPR_ASSERT(ev->tag == tags[i]);
+ grpc_event_finish(ev);
+ }
+
+ GPR_ASSERT(on_finish_called == GPR_ARRAY_SIZE(tags));
+
+ for (i = 0; i < GPR_ARRAY_SIZE(tags); i++) {
+ grpc_cq_begin_op(cc, NULL, GRPC_WRITE_ACCEPTED);
+ grpc_cq_end_write_accepted(cc, tags[i], NULL, increment_int_on_finish,
+ &on_finish_called, GRPC_OP_OK);
+ }
+
+ for (i = 0; i < GPR_ARRAY_SIZE(tags); i++) {
+ ev = grpc_completion_queue_pluck(cc, tags[GPR_ARRAY_SIZE(tags) - i - 1],
+ gpr_inf_past);
+ GPR_ASSERT(ev->tag == tags[GPR_ARRAY_SIZE(tags) - i - 1]);
+ grpc_event_finish(ev);
+ }
+
+ GPR_ASSERT(on_finish_called == 2 * GPR_ARRAY_SIZE(tags));
+
+ shutdown_and_destroy(cc);
+}
+
+#define TEST_THREAD_EVENTS 10000
+
+typedef struct test_thread_options {
+ gpr_event on_started;
+ gpr_event *phase1;
+ gpr_event on_phase1_done;
+ gpr_event *phase2;
+ gpr_event on_finished;
+ int events_triggered;
+ int id;
+ grpc_completion_queue *cc;
+} test_thread_options;
+
+gpr_timespec ten_seconds_time() {
+ return gpr_time_add(gpr_now(), gpr_time_from_micros(10 * 1000000));
+}
+
+static void producer_thread(void *arg) {
+ test_thread_options *opt = arg;
+ int i;
+
+ gpr_log(GPR_INFO, "producer %d started", opt->id);
+ gpr_event_set(&opt->on_started, (void *)(gpr_intptr) 1);
+ GPR_ASSERT(gpr_event_wait(opt->phase1, ten_seconds_time()));
+
+ gpr_log(GPR_INFO, "producer %d phase 1", opt->id);
+ for (i = 0; i < TEST_THREAD_EVENTS; i++) {
+ grpc_cq_begin_op(opt->cc, NULL, GRPC_WRITE_ACCEPTED);
+ }
+
+ gpr_log(GPR_INFO, "producer %d phase 1 done", opt->id);
+ gpr_event_set(&opt->on_phase1_done, (void *)(gpr_intptr) 1);
+ GPR_ASSERT(gpr_event_wait(opt->phase2, ten_seconds_time()));
+
+ gpr_log(GPR_INFO, "producer %d phase 2", opt->id);
+ for (i = 0; i < TEST_THREAD_EVENTS; i++) {
+ grpc_cq_end_write_accepted(opt->cc, (void *)(gpr_intptr) 1, NULL, NULL,
+ NULL, GRPC_OP_OK);
+ opt->events_triggered++;
+ }
+
+ gpr_log(GPR_INFO, "producer %d phase 2 done", opt->id);
+ gpr_event_set(&opt->on_finished, (void *)(gpr_intptr) 1);
+}
+
+static void consumer_thread(void *arg) {
+ test_thread_options *opt = arg;
+ grpc_event *ev;
+
+ gpr_log(GPR_INFO, "consumer %d started", opt->id);
+ gpr_event_set(&opt->on_started, (void *)(gpr_intptr) 1);
+ GPR_ASSERT(gpr_event_wait(opt->phase1, ten_seconds_time()));
+
+ gpr_log(GPR_INFO, "consumer %d phase 1", opt->id);
+
+ gpr_log(GPR_INFO, "consumer %d phase 1 done", opt->id);
+ gpr_event_set(&opt->on_phase1_done, (void *)(gpr_intptr) 1);
+ GPR_ASSERT(gpr_event_wait(opt->phase2, ten_seconds_time()));
+
+ gpr_log(GPR_INFO, "consumer %d phase 2", opt->id);
+ for (;;) {
+ ev = grpc_completion_queue_next(opt->cc, ten_seconds_time());
+ GPR_ASSERT(ev);
+ switch (ev->type) {
+ case GRPC_WRITE_ACCEPTED:
+ GPR_ASSERT(ev->data.write_accepted == GRPC_OP_OK);
+ opt->events_triggered++;
+ grpc_event_finish(ev);
+ break;
+ case GRPC_QUEUE_SHUTDOWN:
+ gpr_log(GPR_INFO, "consumer %d phase 2 done", opt->id);
+ gpr_event_set(&opt->on_finished, (void *)(gpr_intptr) 1);
+ grpc_event_finish(ev);
+ return;
+ default:
+ gpr_log(GPR_ERROR, "Invalid event received: %d", ev->type);
+ abort();
+ }
+ }
+}
+
+static void test_threading(int producers, int consumers) {
+ test_thread_options *options =
+ gpr_malloc((producers + consumers) * sizeof(test_thread_options));
+ gpr_event phase1 = GPR_EVENT_INIT;
+ gpr_event phase2 = GPR_EVENT_INIT;
+ grpc_completion_queue *cc = grpc_completion_queue_create();
+ int i;
+ int total_consumed = 0;
+ static int optid = 101;
+
+ gpr_log(GPR_INFO, "%s: %d producers, %d consumers", __FUNCTION__, producers,
+ consumers);
+
+ grpc_completion_queue_dont_poll_test_only(cc);
+
+ /* start all threads: they will wait for phase1 */
+ for (i = 0; i < producers + consumers; i++) {
+ gpr_thd_id id;
+ gpr_event_init(&options[i].on_started);
+ gpr_event_init(&options[i].on_phase1_done);
+ gpr_event_init(&options[i].on_finished);
+ options[i].phase1 = &phase1;
+ options[i].phase2 = &phase2;
+ options[i].events_triggered = 0;
+ options[i].cc = cc;
+ options[i].id = optid++;
+ GPR_ASSERT(gpr_thd_new(&id,
+ i < producers ? producer_thread : consumer_thread,
+ options + i, NULL));
+ gpr_event_wait(&options[i].on_started, ten_seconds_time());
+ }
+
+ /* start phase1: producers will pre-declare all operations they will
+ complete */
+ gpr_log(GPR_INFO, "start phase 1");
+ gpr_event_set(&phase1, (void *)(gpr_intptr) 1);
+
+ gpr_log(GPR_INFO, "wait phase 1");
+ for (i = 0; i < producers + consumers; i++) {
+ GPR_ASSERT(gpr_event_wait(&options[i].on_phase1_done, ten_seconds_time()));
+ }
+ gpr_log(GPR_INFO, "done phase 1");
+
+ /* start phase2: operations will complete, and consumers will consume them */
+ gpr_log(GPR_INFO, "start phase 2");
+ gpr_event_set(&phase2, (void *)(gpr_intptr) 1);
+
+ /* in parallel, we shutdown the completion channel - all events should still
+ be consumed */
+ grpc_completion_queue_shutdown(cc);
+
+ /* join all threads */
+ gpr_log(GPR_INFO, "wait phase 2");
+ for (i = 0; i < producers + consumers; i++) {
+ GPR_ASSERT(gpr_event_wait(&options[i].on_finished, ten_seconds_time()));
+ }
+ gpr_log(GPR_INFO, "done phase 2");
+
+ /* destroy the completion channel */
+ grpc_completion_queue_destroy(cc);
+
+ /* verify that everything was produced and consumed */
+ for (i = 0; i < producers + consumers; i++) {
+ if (i < producers) {
+ GPR_ASSERT(options[i].events_triggered == TEST_THREAD_EVENTS);
+ } else {
+ total_consumed += options[i].events_triggered;
+ }
+ }
+ GPR_ASSERT(total_consumed == producers * TEST_THREAD_EVENTS);
+
+ gpr_free(options);
+}
+
+int main(int argc, char **argv) {
+ grpc_test_init(argc, argv);
+ grpc_surface_em_init();
+ test_no_op();
+ test_wait_empty();
+ test_cq_end_read();
+ test_cq_end_invoke_accepted();
+ test_cq_end_write_accepted();
+ test_cq_end_finish_accepted();
+ test_cq_end_client_metadata_read();
+ test_pluck();
+ test_threading(1, 1);
+ test_threading(1, 10);
+ test_threading(10, 1);
+ test_threading(10, 10);
+ grpc_surface_em_shutdown();
+ return 0;
+}
diff --git a/test/core/surface/lame_client_test.c b/test/core/surface/lame_client_test.c
new file mode 100644
index 0000000000..0520a39ea2
--- /dev/null
+++ b/test/core/surface/lame_client_test.c
@@ -0,0 +1,82 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include "src/core/surface/lame_client.h"
+
+#include "test/core/end2end/cq_verifier.h"
+#include "test/core/util/test_config.h"
+#include <grpc/support/log.h>
+
+static void *tag(gpr_intptr x) { return (void *)x; }
+
+int main(int argc, char **argv) {
+ grpc_channel *chan;
+ grpc_call *call;
+ grpc_metadata md = {"a", "b", 1};
+ grpc_completion_queue *cq;
+ cq_verifier *cqv;
+
+ grpc_test_init(argc, argv);
+ grpc_init();
+
+ chan = grpc_lame_client_channel_create();
+ GPR_ASSERT(chan);
+ call = grpc_channel_create_call(
+ chan, "/Foo", "anywhere",
+ gpr_time_add(gpr_now(), gpr_time_from_seconds(100)));
+ GPR_ASSERT(call);
+ cq = grpc_completion_queue_create();
+ cqv = cq_verifier_create(cq);
+
+ /* we should be able to add metadata */
+ GPR_ASSERT(GRPC_CALL_OK == grpc_call_add_metadata(call, &md, 0));
+
+ /* and invoke the call */
+ GPR_ASSERT(GRPC_CALL_OK ==
+ grpc_call_start_invoke(call, cq, tag(1), tag(2), tag(3), 0));
+
+ /* the call should immediately fail */
+ cq_expect_invoke_accepted(cqv, tag(1), GRPC_OP_ERROR);
+ cq_expect_client_metadata_read(cqv, tag(2), NULL);
+ cq_expect_finished(cqv, tag(3), NULL);
+ cq_verify(cqv);
+
+ grpc_call_destroy(call);
+ grpc_channel_destroy(chan);
+ cq_verifier_destroy(cqv);
+ grpc_completion_queue_destroy(cq);
+
+ grpc_shutdown();
+
+ return 0;
+}
diff --git a/test/core/transport/chttp2/hpack_parser_test.c b/test/core/transport/chttp2/hpack_parser_test.c
new file mode 100644
index 0000000000..12f8b35ada
--- /dev/null
+++ b/test/core/transport/chttp2/hpack_parser_test.c
@@ -0,0 +1,223 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include "src/core/transport/chttp2/hpack_parser.h"
+
+#include <stdarg.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/slice.h>
+#include "test/core/util/parse_hexstring.h"
+#include "test/core/util/slice_splitter.h"
+#include "test/core/util/test_config.h"
+
+typedef struct { va_list args; } test_checker;
+
+static void onhdr(void *ud, grpc_mdelem *md) {
+ const char *ekey, *evalue;
+ test_checker *chk = ud;
+ ekey = va_arg(chk->args, char *);
+ GPR_ASSERT(ekey);
+ evalue = va_arg(chk->args, char *);
+ GPR_ASSERT(evalue);
+ GPR_ASSERT(gpr_slice_str_cmp(md->key->slice, ekey) == 0);
+ GPR_ASSERT(gpr_slice_str_cmp(md->value->slice, evalue) == 0);
+ grpc_mdelem_unref(md);
+}
+
+static void test_vector(grpc_chttp2_hpack_parser *parser,
+ grpc_slice_split_mode mode, const char *hexstring,
+ ... /* char *key, char *value */) {
+ gpr_slice input = parse_hexstring(hexstring);
+ gpr_slice *slices;
+ size_t nslices;
+ size_t i;
+ test_checker chk;
+
+ va_start(chk.args, hexstring);
+
+ parser->on_header = onhdr;
+ parser->on_header_user_data = &chk;
+
+ grpc_split_slices(mode, &input, 1, &slices, &nslices);
+ gpr_slice_unref(input);
+
+ for (i = 0; i < nslices; i++) {
+ GPR_ASSERT(grpc_chttp2_hpack_parser_parse(
+ parser, GPR_SLICE_START_PTR(slices[i]), GPR_SLICE_END_PTR(slices[i])));
+ }
+
+ for (i = 0; i < nslices; i++) {
+ gpr_slice_unref(slices[i]);
+ }
+ gpr_free(slices);
+
+ GPR_ASSERT(NULL == va_arg(chk.args, char *));
+
+ va_end(chk.args);
+}
+
+static void test_vectors(grpc_slice_split_mode mode) {
+ grpc_chttp2_hpack_parser parser;
+ grpc_mdctx *mdctx = grpc_mdctx_create();
+
+ grpc_chttp2_hpack_parser_init(&parser, mdctx);
+ /* D.2.1 */
+ test_vector(&parser, mode,
+ "400a 6375 7374 6f6d 2d6b 6579 0d63 7573"
+ "746f 6d2d 6865 6164 6572",
+ "custom-key", "custom-header", NULL);
+ /* D.2.2 */
+ test_vector(&parser, mode, "040c 2f73 616d 706c 652f 7061 7468", ":path",
+ "/sample/path", NULL);
+ /* D.2.3 */
+ test_vector(&parser, mode,
+ "1008 7061 7373 776f 7264 0673 6563 7265"
+ "74",
+ "password", "secret", NULL);
+ /* D.2.4 */
+ test_vector(&parser, mode, "82", ":method", "GET", NULL);
+ grpc_chttp2_hpack_parser_destroy(&parser);
+
+ grpc_chttp2_hpack_parser_init(&parser, mdctx);
+ /* D.3.1 */
+ test_vector(&parser, mode,
+ "8286 8441 0f77 7777 2e65 7861 6d70 6c65"
+ "2e63 6f6d",
+ ":method", "GET", ":scheme", "http", ":path", "/", ":authority",
+ "www.example.com", NULL);
+ /* D.3.2 */
+ test_vector(&parser, mode, "8286 84be 5808 6e6f 2d63 6163 6865", ":method",
+ "GET", ":scheme", "http", ":path", "/", ":authority",
+ "www.example.com", "cache-control", "no-cache", NULL);
+ /* D.3.3 */
+ test_vector(&parser, mode,
+ "8287 85bf 400a 6375 7374 6f6d 2d6b 6579"
+ "0c63 7573 746f 6d2d 7661 6c75 65",
+ ":method", "GET", ":scheme", "https", ":path", "/index.html",
+ ":authority", "www.example.com", "custom-key", "custom-value",
+ NULL);
+ grpc_chttp2_hpack_parser_destroy(&parser);
+
+ grpc_chttp2_hpack_parser_init(&parser, mdctx);
+ /* D.4.1 */
+ test_vector(&parser, mode,
+ "8286 8441 8cf1 e3c2 e5f2 3a6b a0ab 90f4"
+ "ff",
+ ":method", "GET", ":scheme", "http", ":path", "/", ":authority",
+ "www.example.com", NULL);
+ /* D.4.2 */
+ test_vector(&parser, mode, "8286 84be 5886 a8eb 1064 9cbf", ":method", "GET",
+ ":scheme", "http", ":path", "/", ":authority", "www.example.com",
+ "cache-control", "no-cache", NULL);
+ /* D.4.3 */
+ test_vector(&parser, mode,
+ "8287 85bf 4088 25a8 49e9 5ba9 7d7f 8925"
+ "a849 e95b b8e8 b4bf",
+ ":method", "GET", ":scheme", "https", ":path", "/index.html",
+ ":authority", "www.example.com", "custom-key", "custom-value",
+ NULL);
+ grpc_chttp2_hpack_parser_destroy(&parser);
+
+ grpc_chttp2_hpack_parser_init(&parser, mdctx);
+ parser.table.max_bytes = 256;
+ /* D.5.1 */
+ test_vector(&parser, mode,
+ "4803 3330 3258 0770 7269 7661 7465 611d"
+ "4d6f 6e2c 2032 3120 4f63 7420 3230 3133"
+ "2032 303a 3133 3a32 3120 474d 546e 1768"
+ "7474 7073 3a2f 2f77 7777 2e65 7861 6d70"
+ "6c65 2e63 6f6d",
+ ":status", "302", "cache-control", "private", "date",
+ "Mon, 21 Oct 2013 20:13:21 GMT", "location",
+ "https://www.example.com", NULL);
+ /* D.5.2 */
+ test_vector(&parser, mode, "4803 3330 37c1 c0bf", ":status", "307",
+ "cache-control", "private", "date",
+ "Mon, 21 Oct 2013 20:13:21 GMT", "location",
+ "https://www.example.com", NULL);
+ /* D.5.3 */
+ test_vector(&parser, mode,
+ "88c1 611d 4d6f 6e2c 2032 3120 4f63 7420"
+ "3230 3133 2032 303a 3133 3a32 3220 474d"
+ "54c0 5a04 677a 6970 7738 666f 6f3d 4153"
+ "444a 4b48 514b 425a 584f 5157 454f 5049"
+ "5541 5851 5745 4f49 553b 206d 6178 2d61"
+ "6765 3d33 3630 303b 2076 6572 7369 6f6e"
+ "3d31",
+ ":status", "200", "cache-control", "private", "date",
+ "Mon, 21 Oct 2013 20:13:22 GMT", "location",
+ "https://www.example.com", "content-encoding", "gzip",
+ "set-cookie",
+ "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1", NULL);
+ grpc_chttp2_hpack_parser_destroy(&parser);
+
+ grpc_chttp2_hpack_parser_init(&parser, mdctx);
+ parser.table.max_bytes = 256;
+ /* D.6.1 */
+ test_vector(&parser, mode,
+ "4882 6402 5885 aec3 771a 4b61 96d0 7abe"
+ "9410 54d4 44a8 2005 9504 0b81 66e0 82a6"
+ "2d1b ff6e 919d 29ad 1718 63c7 8f0b 97c8"
+ "e9ae 82ae 43d3",
+ ":status", "302", "cache-control", "private", "date",
+ "Mon, 21 Oct 2013 20:13:21 GMT", "location",
+ "https://www.example.com", NULL);
+ /* D.6.2 */
+ test_vector(&parser, mode, "4883 640e ffc1 c0bf", ":status", "307",
+ "cache-control", "private", "date",
+ "Mon, 21 Oct 2013 20:13:21 GMT", "location",
+ "https://www.example.com", NULL);
+ /* D.6.3 */
+ test_vector(&parser, mode,
+ "88c1 6196 d07a be94 1054 d444 a820 0595"
+ "040b 8166 e084 a62d 1bff c05a 839b d9ab"
+ "77ad 94e7 821d d7f2 e6c7 b335 dfdf cd5b"
+ "3960 d5af 2708 7f36 72c1 ab27 0fb5 291f"
+ "9587 3160 65c0 03ed 4ee5 b106 3d50 07",
+ ":status", "200", "cache-control", "private", "date",
+ "Mon, 21 Oct 2013 20:13:22 GMT", "location",
+ "https://www.example.com", "content-encoding", "gzip",
+ "set-cookie",
+ "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1", NULL);
+ grpc_chttp2_hpack_parser_destroy(&parser);
+ grpc_mdctx_orphan(mdctx);
+}
+
+int main(int argc, char **argv) {
+ grpc_test_init(argc, argv);
+ test_vectors(GRPC_SLICE_SPLIT_MERGE_ALL);
+ test_vectors(GRPC_SLICE_SPLIT_ONE_BYTE);
+ return 0;
+}
diff --git a/test/core/transport/chttp2/hpack_table_test.c b/test/core/transport/chttp2/hpack_table_test.c
new file mode 100644
index 0000000000..8810925ba5
--- /dev/null
+++ b/test/core/transport/chttp2/hpack_table_test.c
@@ -0,0 +1,269 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include "src/core/transport/chttp2/hpack_table.h"
+
+#include <string.h>
+#include <stdio.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include "test/core/util/test_config.h"
+
+#define LOG_TEST() gpr_log(GPR_INFO, "%s", __FUNCTION__)
+
+static void assert_str(const grpc_chttp2_hptbl *tbl, grpc_mdstr *mdstr,
+ const char *str) {
+ GPR_ASSERT(gpr_slice_str_cmp(mdstr->slice, str) == 0);
+}
+
+static void assert_index(const grpc_chttp2_hptbl *tbl, int idx, const char *key,
+ const char *value) {
+ grpc_mdelem *md = grpc_chttp2_hptbl_lookup(tbl, idx);
+ assert_str(tbl, md->key, key);
+ assert_str(tbl, md->value, value);
+}
+
+static void test_static_lookup() {
+ grpc_chttp2_hptbl tbl;
+ grpc_mdctx *mdctx;
+
+ mdctx = grpc_mdctx_create();
+ grpc_chttp2_hptbl_init(&tbl, mdctx);
+
+ LOG_TEST();
+ assert_index(&tbl, 1, ":authority", "");
+ assert_index(&tbl, 2, ":method", "GET");
+ assert_index(&tbl, 3, ":method", "POST");
+ assert_index(&tbl, 4, ":path", "/");
+ assert_index(&tbl, 5, ":path", "/index.html");
+ assert_index(&tbl, 6, ":scheme", "http");
+ assert_index(&tbl, 7, ":scheme", "https");
+ assert_index(&tbl, 8, ":status", "200");
+ assert_index(&tbl, 9, ":status", "204");
+ assert_index(&tbl, 10, ":status", "206");
+ assert_index(&tbl, 11, ":status", "304");
+ assert_index(&tbl, 12, ":status", "400");
+ assert_index(&tbl, 13, ":status", "404");
+ assert_index(&tbl, 14, ":status", "500");
+ assert_index(&tbl, 15, "accept-charset", "");
+ assert_index(&tbl, 16, "accept-encoding", "gzip, deflate");
+ assert_index(&tbl, 17, "accept-language", "");
+ assert_index(&tbl, 18, "accept-ranges", "");
+ assert_index(&tbl, 19, "accept", "");
+ assert_index(&tbl, 20, "access-control-allow-origin", "");
+ assert_index(&tbl, 21, "age", "");
+ assert_index(&tbl, 22, "allow", "");
+ assert_index(&tbl, 23, "authorization", "");
+ assert_index(&tbl, 24, "cache-control", "");
+ assert_index(&tbl, 25, "content-disposition", "");
+ assert_index(&tbl, 26, "content-encoding", "");
+ assert_index(&tbl, 27, "content-language", "");
+ assert_index(&tbl, 28, "content-length", "");
+ assert_index(&tbl, 29, "content-location", "");
+ assert_index(&tbl, 30, "content-range", "");
+ assert_index(&tbl, 31, "content-type", "");
+ assert_index(&tbl, 32, "cookie", "");
+ assert_index(&tbl, 33, "date", "");
+ assert_index(&tbl, 34, "etag", "");
+ assert_index(&tbl, 35, "expect", "");
+ assert_index(&tbl, 36, "expires", "");
+ assert_index(&tbl, 37, "from", "");
+ assert_index(&tbl, 38, "host", "");
+ assert_index(&tbl, 39, "if-match", "");
+ assert_index(&tbl, 40, "if-modified-since", "");
+ assert_index(&tbl, 41, "if-none-match", "");
+ assert_index(&tbl, 42, "if-range", "");
+ assert_index(&tbl, 43, "if-unmodified-since", "");
+ assert_index(&tbl, 44, "last-modified", "");
+ assert_index(&tbl, 45, "link", "");
+ assert_index(&tbl, 46, "location", "");
+ assert_index(&tbl, 47, "max-forwards", "");
+ assert_index(&tbl, 48, "proxy-authenticate", "");
+ assert_index(&tbl, 49, "proxy-authorization", "");
+ assert_index(&tbl, 50, "range", "");
+ assert_index(&tbl, 51, "referer", "");
+ assert_index(&tbl, 52, "refresh", "");
+ assert_index(&tbl, 53, "retry-after", "");
+ assert_index(&tbl, 54, "server", "");
+ assert_index(&tbl, 55, "set-cookie", "");
+ assert_index(&tbl, 56, "strict-transport-security", "");
+ assert_index(&tbl, 57, "transfer-encoding", "");
+ assert_index(&tbl, 58, "user-agent", "");
+ assert_index(&tbl, 59, "vary", "");
+ assert_index(&tbl, 60, "via", "");
+ assert_index(&tbl, 61, "www-authenticate", "");
+
+ grpc_chttp2_hptbl_destroy(&tbl);
+ grpc_mdctx_orphan(mdctx);
+}
+
+static void test_many_additions() {
+ grpc_chttp2_hptbl tbl;
+ int i;
+ char key[32];
+ char value[32];
+ grpc_mdctx *mdctx;
+
+ LOG_TEST();
+
+ mdctx = grpc_mdctx_create();
+ grpc_chttp2_hptbl_init(&tbl, mdctx);
+
+ for (i = 0; i < 1000000; i++) {
+ sprintf(key, "K:%d", i);
+ sprintf(value, "VALUE:%d", i);
+ grpc_chttp2_hptbl_add(&tbl, grpc_mdelem_from_strings(mdctx, key, value));
+ assert_index(&tbl, 1 + GRPC_CHTTP2_LAST_STATIC_ENTRY, key, value);
+ if (i) {
+ sprintf(key, "K:%d", i - 1);
+ sprintf(value, "VALUE:%d", i - 1);
+ assert_index(&tbl, 2 + GRPC_CHTTP2_LAST_STATIC_ENTRY, key, value);
+ }
+ }
+
+ grpc_chttp2_hptbl_destroy(&tbl);
+ grpc_mdctx_orphan(mdctx);
+}
+
+static grpc_chttp2_hptbl_find_result find_simple(grpc_chttp2_hptbl *tbl,
+ const char *key,
+ const char *value) {
+ grpc_mdelem *md = grpc_mdelem_from_strings(tbl->mdctx, key, value);
+ grpc_chttp2_hptbl_find_result r = grpc_chttp2_hptbl_find(tbl, md);
+ grpc_mdelem_unref(md);
+ return r;
+}
+
+static void test_find() {
+ grpc_chttp2_hptbl tbl;
+ int i;
+ char buffer[32];
+ grpc_mdctx *mdctx;
+ grpc_chttp2_hptbl_find_result r;
+
+ LOG_TEST();
+
+ mdctx = grpc_mdctx_create();
+ grpc_chttp2_hptbl_init(&tbl, mdctx);
+ grpc_chttp2_hptbl_add(&tbl, grpc_mdelem_from_strings(mdctx, "abc", "xyz"));
+ grpc_chttp2_hptbl_add(&tbl, grpc_mdelem_from_strings(mdctx, "abc", "123"));
+ grpc_chttp2_hptbl_add(&tbl, grpc_mdelem_from_strings(mdctx, "x", "1"));
+
+ r = find_simple(&tbl, "abc", "123");
+ GPR_ASSERT(r.index == 2 + GRPC_CHTTP2_LAST_STATIC_ENTRY);
+ GPR_ASSERT(r.has_value == 1);
+
+ r = find_simple(&tbl, "abc", "xyz");
+ GPR_ASSERT(r.index == 3 + GRPC_CHTTP2_LAST_STATIC_ENTRY);
+ GPR_ASSERT(r.has_value == 1);
+
+ r = find_simple(&tbl, "x", "1");
+ GPR_ASSERT(r.index == 1 + GRPC_CHTTP2_LAST_STATIC_ENTRY);
+ GPR_ASSERT(r.has_value == 1);
+
+ r = find_simple(&tbl, "x", "2");
+ GPR_ASSERT(r.index == 1 + GRPC_CHTTP2_LAST_STATIC_ENTRY);
+ GPR_ASSERT(r.has_value == 0);
+
+ r = find_simple(&tbl, "vary", "some-vary-arg");
+ GPR_ASSERT(r.index == 59);
+ GPR_ASSERT(r.has_value == 0);
+
+ r = find_simple(&tbl, "accept-encoding", "gzip, deflate");
+ GPR_ASSERT(r.index == 16);
+ GPR_ASSERT(r.has_value == 1);
+
+ r = find_simple(&tbl, "accept-encoding", "gzip");
+ GPR_ASSERT(r.index == 16);
+ GPR_ASSERT(r.has_value == 0);
+
+ r = find_simple(&tbl, ":method", "GET");
+ GPR_ASSERT(r.index == 2);
+ GPR_ASSERT(r.has_value == 1);
+
+ r = find_simple(&tbl, ":method", "POST");
+ GPR_ASSERT(r.index == 3);
+ GPR_ASSERT(r.has_value == 1);
+
+ r = find_simple(&tbl, ":method", "PUT");
+ GPR_ASSERT(r.index == 2 || r.index == 3);
+ GPR_ASSERT(r.has_value == 0);
+
+ r = find_simple(&tbl, "this-does-not-exist", "");
+ GPR_ASSERT(r.index == 0);
+ GPR_ASSERT(r.has_value == 0);
+
+ /* overflow the string buffer, check find still works */
+ for (i = 0; i < 10000; i++) {
+ sprintf(buffer, "%d", i);
+ grpc_chttp2_hptbl_add(&tbl,
+ grpc_mdelem_from_strings(mdctx, "test", buffer));
+ }
+
+ r = find_simple(&tbl, "abc", "123");
+ GPR_ASSERT(r.index == 0);
+ GPR_ASSERT(r.has_value == 0);
+
+ r = find_simple(&tbl, "test", "9999");
+ GPR_ASSERT(r.index == 1 + GRPC_CHTTP2_LAST_STATIC_ENTRY);
+ GPR_ASSERT(r.has_value == 1);
+
+ r = find_simple(&tbl, "test", "9998");
+ GPR_ASSERT(r.index == 2 + GRPC_CHTTP2_LAST_STATIC_ENTRY);
+ GPR_ASSERT(r.has_value == 1);
+
+ for (i = 0; i < tbl.num_ents; i++) {
+ int expect = 9999 - i;
+ sprintf(buffer, "%d", expect);
+
+ r = find_simple(&tbl, "test", buffer);
+ GPR_ASSERT(r.index == i + 1 + GRPC_CHTTP2_LAST_STATIC_ENTRY);
+ GPR_ASSERT(r.has_value == 1);
+ }
+
+ r = find_simple(&tbl, "test", "10000");
+ GPR_ASSERT(r.index != 0);
+ GPR_ASSERT(r.has_value == 0);
+
+ grpc_chttp2_hptbl_destroy(&tbl);
+ grpc_mdctx_orphan(mdctx);
+}
+
+int main(int argc, char **argv) {
+ grpc_test_init(argc, argv);
+ test_static_lookup();
+ test_many_additions();
+ test_find();
+ return 0;
+}
diff --git a/test/core/transport/chttp2/status_conversion_test.c b/test/core/transport/chttp2/status_conversion_test.c
new file mode 100644
index 0000000000..bb5d7b8860
--- /dev/null
+++ b/test/core/transport/chttp2/status_conversion_test.c
@@ -0,0 +1,138 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include "src/core/transport/chttp2/status_conversion.h"
+#include <grpc/support/log.h>
+#include "test/core/util/test_config.h"
+
+#define GRPC_STATUS_TO_HTTP2_ERROR(a, b) \
+ GPR_ASSERT(grpc_chttp2_grpc_status_to_http2_error(a) == (b))
+#define HTTP2_ERROR_TO_GRPC_STATUS(a, b) \
+ GPR_ASSERT(grpc_chttp2_http2_error_to_grpc_status(a) == (b))
+#define GRPC_STATUS_TO_HTTP2_STATUS(a, b) \
+ GPR_ASSERT(grpc_chttp2_grpc_status_to_http2_status(a) == (b))
+#define HTTP2_STATUS_TO_GRPC_STATUS(a, b) \
+ GPR_ASSERT(grpc_chttp2_http2_status_to_grpc_status(a) == (b))
+
+int main(int argc, char **argv) {
+ int i;
+
+ grpc_test_init(argc, argv);
+
+ GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_OK, GRPC_CHTTP2_NO_ERROR);
+ GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_CANCELLED, GRPC_CHTTP2_CANCEL);
+ GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_UNKNOWN, GRPC_CHTTP2_INTERNAL_ERROR);
+ GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_INVALID_ARGUMENT,
+ GRPC_CHTTP2_INTERNAL_ERROR);
+ GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_DEADLINE_EXCEEDED,
+ GRPC_CHTTP2_INTERNAL_ERROR);
+ GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_NOT_FOUND, GRPC_CHTTP2_INTERNAL_ERROR);
+ GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_ALREADY_EXISTS,
+ GRPC_CHTTP2_INTERNAL_ERROR);
+ GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_PERMISSION_DENIED,
+ GRPC_CHTTP2_INADEQUATE_SECURITY);
+ GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_UNAUTHENTICATED,
+ GRPC_CHTTP2_INTERNAL_ERROR);
+ GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_RESOURCE_EXHAUSTED,
+ GRPC_CHTTP2_ENHANCE_YOUR_CALM);
+ GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_FAILED_PRECONDITION,
+ GRPC_CHTTP2_INTERNAL_ERROR);
+ GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_ABORTED, GRPC_CHTTP2_INTERNAL_ERROR);
+ GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_OUT_OF_RANGE,
+ GRPC_CHTTP2_INTERNAL_ERROR);
+ GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_UNIMPLEMENTED,
+ GRPC_CHTTP2_INTERNAL_ERROR);
+ GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_INTERNAL, GRPC_CHTTP2_INTERNAL_ERROR);
+ GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_UNAVAILABLE,
+ GRPC_CHTTP2_REFUSED_STREAM);
+ GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_DATA_LOSS, GRPC_CHTTP2_INTERNAL_ERROR);
+
+ GRPC_STATUS_TO_HTTP2_STATUS(GRPC_STATUS_OK, 200);
+ GRPC_STATUS_TO_HTTP2_STATUS(GRPC_STATUS_CANCELLED, 200);
+ GRPC_STATUS_TO_HTTP2_STATUS(GRPC_STATUS_UNKNOWN, 200);
+ GRPC_STATUS_TO_HTTP2_STATUS(GRPC_STATUS_INVALID_ARGUMENT, 200);
+ GRPC_STATUS_TO_HTTP2_STATUS(GRPC_STATUS_DEADLINE_EXCEEDED, 200);
+ GRPC_STATUS_TO_HTTP2_STATUS(GRPC_STATUS_NOT_FOUND, 200);
+ GRPC_STATUS_TO_HTTP2_STATUS(GRPC_STATUS_ALREADY_EXISTS, 200);
+ GRPC_STATUS_TO_HTTP2_STATUS(GRPC_STATUS_PERMISSION_DENIED, 200);
+ GRPC_STATUS_TO_HTTP2_STATUS(GRPC_STATUS_UNAUTHENTICATED, 200);
+ GRPC_STATUS_TO_HTTP2_STATUS(GRPC_STATUS_RESOURCE_EXHAUSTED, 200);
+ GRPC_STATUS_TO_HTTP2_STATUS(GRPC_STATUS_FAILED_PRECONDITION, 200);
+ GRPC_STATUS_TO_HTTP2_STATUS(GRPC_STATUS_ABORTED, 200);
+ GRPC_STATUS_TO_HTTP2_STATUS(GRPC_STATUS_OUT_OF_RANGE, 200);
+ GRPC_STATUS_TO_HTTP2_STATUS(GRPC_STATUS_UNIMPLEMENTED, 200);
+ GRPC_STATUS_TO_HTTP2_STATUS(GRPC_STATUS_INTERNAL, 200);
+ GRPC_STATUS_TO_HTTP2_STATUS(GRPC_STATUS_UNAVAILABLE, 200);
+ GRPC_STATUS_TO_HTTP2_STATUS(GRPC_STATUS_DATA_LOSS, 200);
+
+ HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_NO_ERROR, GRPC_STATUS_INTERNAL);
+ HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_PROTOCOL_ERROR, GRPC_STATUS_INTERNAL);
+ HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_INTERNAL_ERROR, GRPC_STATUS_INTERNAL);
+ HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_FLOW_CONTROL_ERROR,
+ GRPC_STATUS_INTERNAL);
+ HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_SETTINGS_TIMEOUT,
+ GRPC_STATUS_INTERNAL);
+ HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_STREAM_CLOSED, GRPC_STATUS_INTERNAL);
+ HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_FRAME_SIZE_ERROR,
+ GRPC_STATUS_INTERNAL);
+ HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_REFUSED_STREAM,
+ GRPC_STATUS_UNAVAILABLE);
+ HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_CANCEL, GRPC_STATUS_CANCELLED);
+ HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_COMPRESSION_ERROR,
+ GRPC_STATUS_INTERNAL);
+ HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_CONNECT_ERROR, GRPC_STATUS_INTERNAL);
+ HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_ENHANCE_YOUR_CALM,
+ GRPC_STATUS_RESOURCE_EXHAUSTED);
+ HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_INADEQUATE_SECURITY,
+ GRPC_STATUS_PERMISSION_DENIED);
+
+ HTTP2_STATUS_TO_GRPC_STATUS(200, GRPC_STATUS_OK);
+ HTTP2_STATUS_TO_GRPC_STATUS(400, GRPC_STATUS_INVALID_ARGUMENT);
+ HTTP2_STATUS_TO_GRPC_STATUS(401, GRPC_STATUS_UNAUTHENTICATED);
+ HTTP2_STATUS_TO_GRPC_STATUS(403, GRPC_STATUS_PERMISSION_DENIED);
+ HTTP2_STATUS_TO_GRPC_STATUS(404, GRPC_STATUS_NOT_FOUND);
+ HTTP2_STATUS_TO_GRPC_STATUS(409, GRPC_STATUS_ABORTED);
+ HTTP2_STATUS_TO_GRPC_STATUS(412, GRPC_STATUS_FAILED_PRECONDITION);
+ HTTP2_STATUS_TO_GRPC_STATUS(429, GRPC_STATUS_RESOURCE_EXHAUSTED);
+ HTTP2_STATUS_TO_GRPC_STATUS(499, GRPC_STATUS_CANCELLED);
+ HTTP2_STATUS_TO_GRPC_STATUS(500, GRPC_STATUS_UNKNOWN);
+ HTTP2_STATUS_TO_GRPC_STATUS(503, GRPC_STATUS_UNAVAILABLE);
+ HTTP2_STATUS_TO_GRPC_STATUS(504, GRPC_STATUS_DEADLINE_EXCEEDED);
+
+ /* check all status values can be converted */
+ for (i = 0; i <= 999; i++) {
+ grpc_chttp2_http2_status_to_grpc_status(i);
+ }
+
+ return 0;
+}
diff --git a/test/core/transport/chttp2/stream_encoder_test.c b/test/core/transport/chttp2/stream_encoder_test.c
new file mode 100644
index 0000000000..3ee11d94e8
--- /dev/null
+++ b/test/core/transport/chttp2/stream_encoder_test.c
@@ -0,0 +1,320 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include "src/core/transport/chttp2/stream_encoder.h"
+
+#include <stdio.h>
+
+#include "src/core/transport/chttp2/hpack_parser.h"
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string.h>
+#include "test/core/util/parse_hexstring.h"
+#include "test/core/util/slice_splitter.h"
+#include "test/core/util/test_config.h"
+
+#define TEST(x) run_test(x, #x)
+
+grpc_mdctx *g_mdctx;
+grpc_chttp2_hpack_compressor g_compressor;
+int g_failure = 0;
+grpc_stream_op_buffer g_sopb;
+
+static gpr_slice create_test_slice(size_t length) {
+ gpr_slice slice = gpr_slice_malloc(length);
+ size_t i;
+ for (i = 0; i < length; i++) {
+ GPR_SLICE_START_PTR(slice)[i] = i;
+ }
+ return slice;
+}
+
+/* verify that the output generated by encoding the stream matches the
+ hexstring passed in */
+static void verify_sopb(size_t window_available, int eof,
+ size_t expect_window_used, const char *expected) {
+ gpr_slice_buffer output;
+ gpr_slice merged;
+ gpr_slice expect = parse_hexstring(expected);
+ gpr_slice_buffer_init(&output);
+ GPR_ASSERT(expect_window_used ==
+ grpc_chttp2_encode_some(g_sopb.ops, &g_sopb.nops, eof, &output,
+ window_available, 0xdeadbeef,
+ &g_compressor));
+ merged = grpc_slice_merge(output.slices, output.count);
+ gpr_slice_buffer_destroy(&output);
+
+ if (0 != gpr_slice_cmp(merged, expect)) {
+ char *expect_str =
+ gpr_hexdump((char *)GPR_SLICE_START_PTR(expect),
+ GPR_SLICE_LENGTH(expect), GPR_HEXDUMP_PLAINTEXT);
+ char *got_str =
+ gpr_hexdump((char *)GPR_SLICE_START_PTR(merged),
+ GPR_SLICE_LENGTH(merged), GPR_HEXDUMP_PLAINTEXT);
+ gpr_log(GPR_ERROR, "mismatched output for %s", expected);
+ gpr_log(GPR_ERROR, "EXPECT: %s", expect_str);
+ gpr_log(GPR_ERROR, "GOT: %s", got_str);
+ gpr_free(expect_str);
+ gpr_free(got_str);
+ g_failure = 1;
+ }
+
+ gpr_slice_unref(merged);
+ gpr_slice_unref(expect);
+}
+
+static void assert_result_ok(void *user_data, grpc_op_error error) {
+ GPR_ASSERT(error == GRPC_OP_OK);
+}
+
+static void test_small_data_framing() {
+ grpc_sopb_add_no_op(&g_sopb);
+ verify_sopb(10, 0, 0, "");
+
+ grpc_sopb_add_flow_ctl_cb(&g_sopb, assert_result_ok, NULL);
+ grpc_sopb_add_slice(&g_sopb, create_test_slice(3));
+ verify_sopb(10, 0, 3, "000003 0000 deadbeef 000102");
+
+ grpc_sopb_add_slice(&g_sopb, create_test_slice(4));
+ verify_sopb(10, 0, 4, "000004 0000 deadbeef 00010203");
+
+ grpc_sopb_add_slice(&g_sopb, create_test_slice(3));
+ grpc_sopb_add_slice(&g_sopb, create_test_slice(4));
+ verify_sopb(10, 0, 7, "000007 0000 deadbeef 000102 00010203");
+
+ grpc_sopb_add_slice(&g_sopb, create_test_slice(0));
+ grpc_sopb_add_slice(&g_sopb, create_test_slice(0));
+ grpc_sopb_add_slice(&g_sopb, create_test_slice(0));
+ grpc_sopb_add_slice(&g_sopb, create_test_slice(0));
+ grpc_sopb_add_slice(&g_sopb, create_test_slice(3));
+ verify_sopb(10, 0, 3, "000003 0000 deadbeef 000102");
+
+ verify_sopb(10, 1, 0, "000000 0001 deadbeef");
+
+ grpc_sopb_add_begin_message(&g_sopb, 255, 0);
+ verify_sopb(10, 0, 5, "000005 0000 deadbeef 00000000ff");
+}
+
+static void add_sopb_header(const char *key, const char *value) {
+ grpc_sopb_add_metadata(&g_sopb,
+ grpc_mdelem_from_strings(g_mdctx, key, value));
+}
+
+static void test_basic_headers() {
+ int i;
+
+ add_sopb_header("a", "a");
+ verify_sopb(0, 0, 0, "000005 0104 deadbeef 40 0161 0161");
+
+ add_sopb_header("a", "a");
+ verify_sopb(0, 0, 0, "000001 0104 deadbeef be");
+
+ add_sopb_header("a", "a");
+ verify_sopb(0, 0, 0, "000001 0104 deadbeef be");
+
+ add_sopb_header("a", "a");
+ add_sopb_header("b", "c");
+ verify_sopb(0, 0, 0, "000006 0104 deadbeef be 40 0162 0163");
+
+ add_sopb_header("a", "a");
+ add_sopb_header("b", "c");
+ verify_sopb(0, 0, 0, "000002 0104 deadbeef bf be");
+
+ add_sopb_header("a", "d");
+ verify_sopb(0, 0, 0, "000004 0104 deadbeef 7f 00 0164");
+
+ /* flush out what's there to make a few values look very popular */
+ for (i = 0; i < 350; i++) {
+ add_sopb_header("a", "a");
+ add_sopb_header("b", "c");
+ add_sopb_header("a", "d");
+ verify_sopb(0, 0, 0, "000003 0104 deadbeef c0 bf be");
+ }
+
+ add_sopb_header("a", "a");
+ add_sopb_header("k", "v");
+ verify_sopb(0, 0, 0, "000006 0104 deadbeef c0 00 016b 0176");
+
+ add_sopb_header("a", "v");
+ /* this could be 000004 0104 deadbeef 0f 30 0176 also */
+ verify_sopb(0, 0, 0, "000004 0104 deadbeef 0f 2f 0176");
+}
+
+static void encode_int_to_str(int i, char *p) {
+ p[0] = 'a' + i % 26;
+ i /= 26;
+ GPR_ASSERT(i < 26);
+ p[1] = 'a' + i;
+ p[2] = 0;
+}
+
+static void test_decode_table_overflow() {
+ int i;
+ char key[3], value[3];
+ char expect[128];
+
+ for (i = 0; i < 114; i++) {
+ if (i > 0) {
+ add_sopb_header("aa", "ba");
+ }
+
+ encode_int_to_str(i, key);
+ encode_int_to_str(i + 1, value);
+
+ if (i + 61 >= 127) {
+ sprintf(expect, "000009 0104 deadbeef ff%02x 40 02%02x%02x 02%02x%02x",
+ i + 61 - 127, key[0], key[1], value[0], value[1]);
+ } else if (i > 0) {
+ sprintf(expect, "000008 0104 deadbeef %02x 40 02%02x%02x 02%02x%02x",
+ 0x80 + 61 + i, key[0], key[1], value[0], value[1]);
+ } else {
+ sprintf(expect, "000007 0104 deadbeef 40 02%02x%02x 02%02x%02x", key[0],
+ key[1], value[0], value[1]);
+ }
+
+ add_sopb_header(key, value);
+ verify_sopb(0, 0, 0, expect);
+ }
+
+ /* if the above passes, then we must have just knocked this pair out of the
+ decoder stack, and so we'll be forced to re-encode it */
+ add_sopb_header("aa", "ba");
+ verify_sopb(0, 0, 0, "000007 0104 deadbeef 40 026161 026261");
+}
+
+static void randstr(char *p, int bufsz) {
+ int i;
+ int len = 1 + rand() % bufsz;
+ for (i = 0; i < len; i++) {
+ p[i] = 'a' + rand() % 26;
+ }
+ p[len] = 0;
+}
+
+typedef struct {
+ char key[300];
+ char value[300];
+ int got_hdr;
+} test_decode_random_header_state;
+
+static void chk_hdr(void *p, grpc_mdelem *el) {
+ test_decode_random_header_state *st = p;
+ GPR_ASSERT(0 == gpr_slice_str_cmp(el->key->slice, st->key));
+ GPR_ASSERT(0 == gpr_slice_str_cmp(el->value->slice, st->value));
+ st->got_hdr = 1;
+ grpc_mdelem_unref(el);
+}
+
+static void test_decode_random_headers_inner(int max_len) {
+ int i;
+ test_decode_random_header_state st;
+ gpr_slice_buffer output;
+ gpr_slice merged;
+ grpc_chttp2_hpack_parser parser;
+
+ grpc_chttp2_hpack_parser_init(&parser, g_mdctx);
+
+ gpr_log(GPR_INFO, "max_len = %d", max_len);
+
+ for (i = 0; i < 100000; i++) {
+ randstr(st.key, max_len);
+ randstr(st.value, max_len);
+
+ add_sopb_header(st.key, st.value);
+ gpr_slice_buffer_init(&output);
+ GPR_ASSERT(0 == grpc_chttp2_encode_some(g_sopb.ops, &g_sopb.nops, 0,
+ &output, 0, 0xdeadbeef,
+ &g_compressor));
+ merged = grpc_slice_merge(output.slices, output.count);
+ gpr_slice_buffer_destroy(&output);
+
+ st.got_hdr = 0;
+ parser.on_header = chk_hdr;
+ parser.on_header_user_data = &st;
+ grpc_chttp2_hpack_parser_parse(&parser, GPR_SLICE_START_PTR(merged) + 9,
+ GPR_SLICE_END_PTR(merged));
+ GPR_ASSERT(st.got_hdr);
+
+ gpr_slice_unref(merged);
+ }
+
+ grpc_chttp2_hpack_parser_destroy(&parser);
+}
+
+#define DECL_TEST_DECODE_RANDOM_HEADERS(n) \
+ static void test_decode_random_headers_##n() { \
+ test_decode_random_headers_inner(n); \
+ } \
+ int keeps_formatting_correct_##n
+
+DECL_TEST_DECODE_RANDOM_HEADERS(1);
+DECL_TEST_DECODE_RANDOM_HEADERS(2);
+DECL_TEST_DECODE_RANDOM_HEADERS(3);
+DECL_TEST_DECODE_RANDOM_HEADERS(5);
+DECL_TEST_DECODE_RANDOM_HEADERS(8);
+DECL_TEST_DECODE_RANDOM_HEADERS(13);
+DECL_TEST_DECODE_RANDOM_HEADERS(21);
+DECL_TEST_DECODE_RANDOM_HEADERS(34);
+DECL_TEST_DECODE_RANDOM_HEADERS(55);
+DECL_TEST_DECODE_RANDOM_HEADERS(89);
+DECL_TEST_DECODE_RANDOM_HEADERS(144);
+
+static void run_test(void (*test)(), const char *name) {
+ gpr_log(GPR_INFO, "RUN TEST: %s", name);
+ g_mdctx = grpc_mdctx_create_with_seed(0);
+ grpc_chttp2_hpack_compressor_init(&g_compressor, g_mdctx);
+ grpc_sopb_init(&g_sopb);
+ test();
+ grpc_chttp2_hpack_compressor_destroy(&g_compressor);
+ grpc_mdctx_orphan(g_mdctx);
+ grpc_sopb_destroy(&g_sopb);
+}
+
+int main(int argc, char **argv) {
+ grpc_test_init(argc, argv);
+ TEST(test_small_data_framing);
+ TEST(test_basic_headers);
+ TEST(test_decode_table_overflow);
+ TEST(test_decode_random_headers_1);
+ TEST(test_decode_random_headers_2);
+ TEST(test_decode_random_headers_3);
+ TEST(test_decode_random_headers_5);
+ TEST(test_decode_random_headers_8);
+ TEST(test_decode_random_headers_13);
+ TEST(test_decode_random_headers_21);
+ TEST(test_decode_random_headers_34);
+ TEST(test_decode_random_headers_55);
+ TEST(test_decode_random_headers_89);
+ TEST(test_decode_random_headers_144);
+ return g_failure;
+}
diff --git a/test/core/transport/chttp2/stream_map_test.c b/test/core/transport/chttp2/stream_map_test.c
new file mode 100644
index 0000000000..459ef2aede
--- /dev/null
+++ b/test/core/transport/chttp2/stream_map_test.c
@@ -0,0 +1,228 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include "src/core/transport/chttp2/stream_map.h"
+#include <grpc/support/log.h>
+#include "test/core/util/test_config.h"
+
+#define LOG_TEST() gpr_log(GPR_INFO, "%s", __FUNCTION__)
+
+/* test creation & destruction */
+static void test_no_op() {
+ grpc_chttp2_stream_map map;
+
+ LOG_TEST();
+
+ grpc_chttp2_stream_map_init(&map, 8);
+ grpc_chttp2_stream_map_destroy(&map);
+}
+
+/* test lookup on an empty map */
+static void test_empty_find() {
+ grpc_chttp2_stream_map map;
+
+ LOG_TEST();
+
+ grpc_chttp2_stream_map_init(&map, 8);
+ GPR_ASSERT(NULL == grpc_chttp2_stream_map_find(&map, 39128));
+ grpc_chttp2_stream_map_destroy(&map);
+}
+
+/* test it's safe to delete twice */
+static void test_double_deletion() {
+ grpc_chttp2_stream_map map;
+
+ LOG_TEST();
+
+ grpc_chttp2_stream_map_init(&map, 8);
+ GPR_ASSERT(0 == grpc_chttp2_stream_map_size(&map));
+ grpc_chttp2_stream_map_add(&map, 1, (void *)1);
+ GPR_ASSERT((void *)1 == grpc_chttp2_stream_map_find(&map, 1));
+ GPR_ASSERT(1 == grpc_chttp2_stream_map_size(&map));
+ GPR_ASSERT((void *)1 == grpc_chttp2_stream_map_delete(&map, 1));
+ GPR_ASSERT(0 == grpc_chttp2_stream_map_size(&map));
+ GPR_ASSERT(NULL == grpc_chttp2_stream_map_find(&map, 1));
+ GPR_ASSERT(NULL == grpc_chttp2_stream_map_delete(&map, 1));
+ GPR_ASSERT(NULL == grpc_chttp2_stream_map_find(&map, 1));
+ GPR_ASSERT(NULL == grpc_chttp2_stream_map_delete(&map, 1));
+ GPR_ASSERT(NULL == grpc_chttp2_stream_map_find(&map, 1));
+ GPR_ASSERT(NULL == grpc_chttp2_stream_map_delete(&map, 1));
+ GPR_ASSERT(NULL == grpc_chttp2_stream_map_find(&map, 1));
+ grpc_chttp2_stream_map_destroy(&map);
+}
+
+/* test add & lookup */
+static void test_basic_add_find(size_t n) {
+ grpc_chttp2_stream_map map;
+ size_t i;
+ size_t got;
+
+ LOG_TEST();
+ gpr_log(GPR_INFO, "n = %d", n);
+
+ grpc_chttp2_stream_map_init(&map, 8);
+ GPR_ASSERT(0 == grpc_chttp2_stream_map_size(&map));
+ for (i = 1; i <= n; i++) {
+ grpc_chttp2_stream_map_add(&map, i, (void *)(gpr_uintptr)i);
+ }
+ GPR_ASSERT(n == grpc_chttp2_stream_map_size(&map));
+ GPR_ASSERT(NULL == grpc_chttp2_stream_map_find(&map, 0));
+ GPR_ASSERT(NULL == grpc_chttp2_stream_map_find(&map, n + 1));
+ for (i = 1; i <= n; i++) {
+ got = (gpr_uintptr)grpc_chttp2_stream_map_find(&map, i);
+ GPR_ASSERT(i == got);
+ }
+ grpc_chttp2_stream_map_destroy(&map);
+}
+
+/* verify that for_each gets the right values during test_delete_evens_XXX */
+static void verify_for_each(void *user_data, gpr_uint32 stream_id, void *ptr) {
+ size_t *for_each_check = user_data;
+ GPR_ASSERT(ptr);
+ GPR_ASSERT(*for_each_check == stream_id);
+ *for_each_check += 2;
+}
+
+static void check_delete_evens(grpc_chttp2_stream_map *map, size_t n) {
+ size_t for_each_check = 1;
+ size_t i;
+ size_t got;
+
+ GPR_ASSERT(NULL == grpc_chttp2_stream_map_find(map, 0));
+ GPR_ASSERT(NULL == grpc_chttp2_stream_map_find(map, n + 1));
+ for (i = 1; i <= n; i++) {
+ if (i & 1) {
+ got = (gpr_uintptr)grpc_chttp2_stream_map_find(map, i);
+ GPR_ASSERT(i == got);
+ } else {
+ GPR_ASSERT(NULL == grpc_chttp2_stream_map_find(map, i));
+ }
+ }
+
+ grpc_chttp2_stream_map_for_each(map, verify_for_each, &for_each_check);
+ if (n & 1) {
+ GPR_ASSERT(for_each_check == n + 2);
+ } else {
+ GPR_ASSERT(for_each_check == n + 1);
+ }
+}
+
+/* add a bunch of keys, delete the even ones, and make sure the map is
+ consistent */
+static void test_delete_evens_sweep(size_t n) {
+ grpc_chttp2_stream_map map;
+ size_t i;
+
+ LOG_TEST();
+ gpr_log(GPR_INFO, "n = %d", n);
+
+ grpc_chttp2_stream_map_init(&map, 8);
+ for (i = 1; i <= n; i++) {
+ grpc_chttp2_stream_map_add(&map, i, (void *)(gpr_uintptr)i);
+ }
+ for (i = 1; i <= n; i++) {
+ if ((i & 1) == 0) {
+ GPR_ASSERT((void *)i == grpc_chttp2_stream_map_delete(&map, i));
+ }
+ }
+ check_delete_evens(&map, n);
+ grpc_chttp2_stream_map_destroy(&map);
+}
+
+/* add a bunch of keys, delete the even ones immediately, and make sure the map
+ is consistent */
+static void test_delete_evens_incremental(size_t n) {
+ grpc_chttp2_stream_map map;
+ size_t i;
+
+ LOG_TEST();
+ gpr_log(GPR_INFO, "n = %d", n);
+
+ grpc_chttp2_stream_map_init(&map, 8);
+ for (i = 1; i <= n; i++) {
+ grpc_chttp2_stream_map_add(&map, i, (void *)(gpr_uintptr)i);
+ if ((i & 1) == 0) {
+ grpc_chttp2_stream_map_delete(&map, i);
+ }
+ }
+ check_delete_evens(&map, n);
+ grpc_chttp2_stream_map_destroy(&map);
+}
+
+/* add a bunch of keys, delete old ones after some time, ensure the
+ backing array does not grow */
+static void test_periodic_compaction(size_t n) {
+ grpc_chttp2_stream_map map;
+ size_t i;
+ size_t del;
+
+ LOG_TEST();
+ gpr_log(GPR_INFO, "n = %d", n);
+
+ grpc_chttp2_stream_map_init(&map, 16);
+ GPR_ASSERT(map.capacity == 16);
+ for (i = 1; i <= n; i++) {
+ grpc_chttp2_stream_map_add(&map, i, (void *)i);
+ if (i > 8) {
+ del = i - 8;
+ GPR_ASSERT((void *)del == grpc_chttp2_stream_map_delete(&map, del));
+ }
+ }
+ GPR_ASSERT(map.capacity == 16);
+ grpc_chttp2_stream_map_destroy(&map);
+}
+
+int main(int argc, char **argv) {
+ int n = 1;
+ int prev = 1;
+ int tmp;
+
+ grpc_test_init(argc, argv);
+
+ test_no_op();
+ test_empty_find();
+ test_double_deletion();
+
+ while (n < 10000000) {
+ test_basic_add_find(n);
+ test_delete_evens_sweep(n);
+ test_delete_evens_incremental(n);
+ test_periodic_compaction(n);
+
+ tmp = n;
+ n += prev;
+ prev = tmp;
+ }
+
+ return 0;
+}
diff --git a/test/core/transport/chttp2/timeout_encoding_test.c b/test/core/transport/chttp2/timeout_encoding_test.c
new file mode 100644
index 0000000000..793d2b945e
--- /dev/null
+++ b/test/core/transport/chttp2/timeout_encoding_test.c
@@ -0,0 +1,140 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include "src/core/transport/chttp2/timeout_encoding.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/support/log.h>
+#include <grpc/support/useful.h>
+#include "test/core/util/test_config.h"
+
+#define LOG_TEST() gpr_log(GPR_INFO, "%s", __FUNCTION__)
+
+static void assert_encodes_as(gpr_timespec ts, const char *s) {
+ char buffer[32];
+ grpc_chttp2_encode_timeout(ts, buffer);
+ gpr_log(GPR_INFO, "check '%s' == '%s'", buffer, s);
+ GPR_ASSERT(0 == strcmp(buffer, s));
+}
+
+void test_encoding() {
+ LOG_TEST();
+ assert_encodes_as(gpr_time_from_micros(-1), "1n");
+ assert_encodes_as(gpr_time_from_seconds(-10), "1n");
+ assert_encodes_as(gpr_time_from_nanos(10), "10n");
+ assert_encodes_as(gpr_time_from_nanos(999999999), "1S");
+ assert_encodes_as(gpr_time_from_micros(1), "1u");
+ assert_encodes_as(gpr_time_from_micros(10), "10u");
+ assert_encodes_as(gpr_time_from_micros(100), "100u");
+ assert_encodes_as(gpr_time_from_micros(890), "890u");
+ assert_encodes_as(gpr_time_from_micros(900), "900u");
+ assert_encodes_as(gpr_time_from_micros(901), "901u");
+ assert_encodes_as(gpr_time_from_millis(1), "1m");
+ assert_encodes_as(gpr_time_from_millis(2), "2m");
+ assert_encodes_as(gpr_time_from_micros(10001), "10100u");
+ assert_encodes_as(gpr_time_from_micros(999999), "1S");
+ assert_encodes_as(gpr_time_from_millis(1000), "1S");
+ assert_encodes_as(gpr_time_from_millis(2000), "2S");
+ assert_encodes_as(gpr_time_from_millis(2500), "2500m");
+ assert_encodes_as(gpr_time_from_millis(59900), "59900m");
+ assert_encodes_as(gpr_time_from_seconds(50), "50S");
+ assert_encodes_as(gpr_time_from_seconds(59), "59S");
+ assert_encodes_as(gpr_time_from_seconds(60), "1M");
+ assert_encodes_as(gpr_time_from_seconds(80), "80S");
+ assert_encodes_as(gpr_time_from_seconds(90), "90S");
+ assert_encodes_as(gpr_time_from_minutes(2), "2M");
+ assert_encodes_as(gpr_time_from_minutes(20), "20M");
+ assert_encodes_as(gpr_time_from_hours(1), "1H");
+ assert_encodes_as(gpr_time_from_hours(10), "10H");
+ assert_encodes_as(gpr_time_from_seconds(1000000000), "1000000000S");
+}
+
+static void assert_decodes_as(const char *buffer, gpr_timespec expected) {
+ gpr_timespec got;
+ gpr_log(GPR_INFO, "check decoding '%s'", buffer);
+ GPR_ASSERT(1 == grpc_chttp2_decode_timeout(buffer, &got));
+ GPR_ASSERT(0 == gpr_time_cmp(got, expected));
+}
+
+void decode_suite(char ext, gpr_timespec (*answer)(long x)) {
+ long test_vals[] = {1, 12, 123, 1234, 12345, 123456,
+ 1234567, 12345678, 123456789, 98765432, 9876543, 987654,
+ 98765, 9876, 987, 98, 9};
+ int i;
+ char input[32];
+ for (i = 0; i < GPR_ARRAY_SIZE(test_vals); i++) {
+ sprintf(input, "%ld%c", test_vals[i], ext);
+ assert_decodes_as(input, answer(test_vals[i]));
+ sprintf(input, " %ld%c", test_vals[i], ext);
+ assert_decodes_as(input, answer(test_vals[i]));
+ sprintf(input, "%ld %c", test_vals[i], ext);
+ assert_decodes_as(input, answer(test_vals[i]));
+ sprintf(input, "%ld %c ", test_vals[i], ext);
+ assert_decodes_as(input, answer(test_vals[i]));
+ }
+}
+
+void test_decoding() {
+ LOG_TEST();
+ decode_suite('n', gpr_time_from_nanos);
+ decode_suite('u', gpr_time_from_micros);
+ decode_suite('m', gpr_time_from_millis);
+ decode_suite('S', gpr_time_from_seconds);
+ decode_suite('M', gpr_time_from_minutes);
+ decode_suite('H', gpr_time_from_hours);
+ assert_decodes_as("1000000000000000000000u", gpr_inf_future);
+}
+
+void test_decoding_fails() {
+ gpr_timespec x;
+ LOG_TEST();
+ GPR_ASSERT(0 == grpc_chttp2_decode_timeout("", &x));
+ GPR_ASSERT(0 == grpc_chttp2_decode_timeout(" ", &x));
+ GPR_ASSERT(0 == grpc_chttp2_decode_timeout("x", &x));
+ GPR_ASSERT(0 == grpc_chttp2_decode_timeout("1", &x));
+ GPR_ASSERT(0 == grpc_chttp2_decode_timeout("1x", &x));
+ GPR_ASSERT(0 == grpc_chttp2_decode_timeout("1ux", &x));
+ GPR_ASSERT(0 == grpc_chttp2_decode_timeout("!", &x));
+ GPR_ASSERT(0 == grpc_chttp2_decode_timeout("n1", &x));
+ GPR_ASSERT(0 == grpc_chttp2_decode_timeout("-1u", &x));
+}
+
+int main(int argc, char **argv) {
+ grpc_test_init(argc, argv);
+ test_encoding();
+ test_decoding();
+ test_decoding_fails();
+ return 0;
+}
diff --git a/test/core/transport/chttp2_transport_end2end_test.c b/test/core/transport/chttp2_transport_end2end_test.c
new file mode 100644
index 0000000000..4a16789fbf
--- /dev/null
+++ b/test/core/transport/chttp2_transport_end2end_test.c
@@ -0,0 +1,139 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include "transport_end2end_tests.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include "test/core/util/test_config.h"
+#include "src/core/eventmanager/em.h"
+#include "src/core/transport/chttp2_transport.h"
+#include <grpc/support/log.h>
+
+static grpc_em em;
+
+static void create_sockets(int sv[2]) {
+ int flags;
+ GPR_ASSERT(socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == 0);
+ flags = fcntl(sv[0], F_GETFL, 0);
+ GPR_ASSERT(fcntl(sv[0], F_SETFL, flags | O_NONBLOCK) == 0);
+ flags = fcntl(sv[1], F_GETFL, 0);
+ GPR_ASSERT(fcntl(sv[1], F_SETFL, flags | O_NONBLOCK) == 0);
+}
+
+/* Wrapper to create an http2 transport pair */
+static int create_http2_transport_for_test(
+ grpc_transport_setup_callback client_setup_transport,
+ void *client_setup_arg,
+ grpc_transport_setup_callback server_setup_transport,
+ void *server_setup_arg, size_t slice_size, grpc_mdctx *mdctx) {
+ int sv[2];
+ grpc_endpoint *svr_ep, *cli_ep;
+
+ create_sockets(sv);
+ svr_ep = grpc_tcp_create_dbg(sv[1], &em, slice_size);
+ cli_ep = grpc_tcp_create_dbg(sv[0], &em, slice_size);
+
+ grpc_create_chttp2_transport(client_setup_transport, client_setup_arg, NULL,
+ cli_ep, NULL, 0, mdctx, 1);
+ grpc_create_chttp2_transport(server_setup_transport, server_setup_arg, NULL,
+ svr_ep, NULL, 0, mdctx, 0);
+
+ return 0;
+}
+
+static int create_http2_transport_for_test_small_slices(
+ grpc_transport_setup_callback client_setup_transport,
+ void *client_setup_arg,
+ grpc_transport_setup_callback server_setup_transport,
+ void *server_setup_arg, grpc_mdctx *mdctx) {
+ return create_http2_transport_for_test(
+ client_setup_transport, client_setup_arg, server_setup_transport,
+ server_setup_arg, 1, mdctx);
+}
+
+static int create_http2_transport_for_test_medium_slices(
+ grpc_transport_setup_callback client_setup_transport,
+ void *client_setup_arg,
+ grpc_transport_setup_callback server_setup_transport,
+ void *server_setup_arg, grpc_mdctx *mdctx) {
+ return create_http2_transport_for_test(
+ client_setup_transport, client_setup_arg, server_setup_transport,
+ server_setup_arg, 8192, mdctx);
+}
+
+static int create_http2_transport_for_test_large_slices(
+ grpc_transport_setup_callback client_setup_transport,
+ void *client_setup_arg,
+ grpc_transport_setup_callback server_setup_transport,
+ void *server_setup_arg, grpc_mdctx *mdctx) {
+ return create_http2_transport_for_test(
+ client_setup_transport, client_setup_arg, server_setup_transport,
+ server_setup_arg, 1024 * 1024, mdctx);
+}
+
+/* All configurations to be tested */
+grpc_transport_test_config fixture_configs[] = {
+ {"chttp2_on_socketpair/small",
+ create_http2_transport_for_test_small_slices},
+ {"chttp2_on_socketpair/medium",
+ create_http2_transport_for_test_medium_slices},
+ {"chttp2_on_socketpair/large",
+ create_http2_transport_for_test_large_slices},
+};
+
+/* Driver function: run the test suite for each test configuration */
+int main(int argc, char **argv) {
+ size_t i;
+
+ /* disable SIGPIPE */
+ signal(SIGPIPE, SIG_IGN);
+
+ grpc_test_init(argc, argv);
+ grpc_em_init(&em);
+
+ for (i = 0; i < sizeof(fixture_configs) / sizeof(*fixture_configs); i++) {
+ grpc_transport_end2end_tests(&fixture_configs[i]);
+ }
+
+ grpc_em_destroy(&em);
+
+ gpr_log(GPR_INFO, "exiting");
+ return 0;
+}
diff --git a/test/core/transport/metadata_test.c b/test/core/transport/metadata_test.c
new file mode 100644
index 0000000000..d71628a264
--- /dev/null
+++ b/test/core/transport/metadata_test.c
@@ -0,0 +1,258 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include "src/core/transport/metadata.h"
+
+#include <stdio.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include "test/core/util/test_config.h"
+
+#define LOG_TEST() gpr_log(GPR_INFO, "%s", __FUNCTION__)
+
+/* a large number */
+#define MANY 1000000
+
+static void test_no_op() {
+ grpc_mdctx *ctx;
+
+ LOG_TEST();
+
+ ctx = grpc_mdctx_create();
+ grpc_mdctx_orphan(ctx);
+}
+
+static void test_create_string() {
+ grpc_mdctx *ctx;
+ grpc_mdstr *s1, *s2, *s3;
+
+ LOG_TEST();
+
+ ctx = grpc_mdctx_create();
+ s1 = grpc_mdstr_from_string(ctx, "hello");
+ s2 = grpc_mdstr_from_string(ctx, "hello");
+ s3 = grpc_mdstr_from_string(ctx, "very much not hello");
+ GPR_ASSERT(s1 == s2);
+ GPR_ASSERT(s3 != s1);
+ GPR_ASSERT(gpr_slice_str_cmp(s1->slice, "hello") == 0);
+ GPR_ASSERT(gpr_slice_str_cmp(s3->slice, "very much not hello") == 0);
+ grpc_mdstr_unref(s1);
+ grpc_mdstr_unref(s2);
+ grpc_mdctx_orphan(ctx);
+ grpc_mdstr_unref(s3);
+}
+
+static void test_create_metadata() {
+ grpc_mdctx *ctx;
+ grpc_mdelem *m1, *m2, *m3;
+
+ LOG_TEST();
+
+ ctx = grpc_mdctx_create();
+ m1 = grpc_mdelem_from_strings(ctx, "a", "b");
+ m2 = grpc_mdelem_from_strings(ctx, "a", "b");
+ m3 = grpc_mdelem_from_strings(ctx, "a", "c");
+ GPR_ASSERT(m1 == m2);
+ GPR_ASSERT(m3 != m1);
+ GPR_ASSERT(m3->key == m1->key);
+ GPR_ASSERT(m3->value != m1->value);
+ GPR_ASSERT(gpr_slice_str_cmp(m1->key->slice, "a") == 0);
+ GPR_ASSERT(gpr_slice_str_cmp(m1->value->slice, "b") == 0);
+ GPR_ASSERT(gpr_slice_str_cmp(m3->value->slice, "c") == 0);
+ grpc_mdelem_unref(m1);
+ grpc_mdelem_unref(m2);
+ grpc_mdelem_unref(m3);
+ grpc_mdctx_orphan(ctx);
+}
+
+static void test_create_many_ephemeral_metadata() {
+ grpc_mdctx *ctx;
+ char buffer[256];
+ long i;
+ size_t mdtab_capacity_before;
+
+ LOG_TEST();
+
+ ctx = grpc_mdctx_create();
+ mdtab_capacity_before = grpc_mdctx_get_mdtab_capacity_test_only(ctx);
+ /* add, and immediately delete a bunch of different elements */
+ for (i = 0; i < MANY; i++) {
+ sprintf(buffer, "%ld", i);
+ grpc_mdelem_unref(grpc_mdelem_from_strings(ctx, "a", buffer));
+ }
+ /* capacity should not grow */
+ GPR_ASSERT(mdtab_capacity_before ==
+ grpc_mdctx_get_mdtab_capacity_test_only(ctx));
+ grpc_mdctx_orphan(ctx);
+}
+
+static void test_create_many_persistant_metadata() {
+ grpc_mdctx *ctx;
+ char buffer[256];
+ long i;
+ grpc_mdelem **created = gpr_malloc(sizeof(grpc_mdelem *) * MANY);
+ grpc_mdelem *md;
+
+ LOG_TEST();
+
+ ctx = grpc_mdctx_create();
+ /* add phase */
+ for (i = 0; i < MANY; i++) {
+ sprintf(buffer, "%ld", i);
+ created[i] = grpc_mdelem_from_strings(ctx, "a", buffer);
+ }
+ /* verify phase */
+ for (i = 0; i < MANY; i++) {
+ sprintf(buffer, "%ld", i);
+ md = grpc_mdelem_from_strings(ctx, "a", buffer);
+ GPR_ASSERT(md == created[i]);
+ grpc_mdelem_unref(md);
+ }
+ /* cleanup phase */
+ for (i = 0; i < MANY; i++) {
+ grpc_mdelem_unref(created[i]);
+ }
+ grpc_mdctx_orphan(ctx);
+
+ gpr_free(created);
+}
+
+static void test_spin_creating_the_same_thing() {
+ grpc_mdctx *ctx;
+
+ LOG_TEST();
+
+ ctx = grpc_mdctx_create();
+ GPR_ASSERT(grpc_mdctx_get_mdtab_count_test_only(ctx) == 0);
+ GPR_ASSERT(grpc_mdctx_get_mdtab_free_test_only(ctx) == 0);
+
+ grpc_mdelem_unref(grpc_mdelem_from_strings(ctx, "a", "b"));
+ GPR_ASSERT(grpc_mdctx_get_mdtab_count_test_only(ctx) == 1);
+ GPR_ASSERT(grpc_mdctx_get_mdtab_free_test_only(ctx) == 1);
+
+ grpc_mdelem_unref(grpc_mdelem_from_strings(ctx, "a", "b"));
+ GPR_ASSERT(grpc_mdctx_get_mdtab_count_test_only(ctx) == 1);
+ GPR_ASSERT(grpc_mdctx_get_mdtab_free_test_only(ctx) == 1);
+
+ grpc_mdelem_unref(grpc_mdelem_from_strings(ctx, "a", "b"));
+ GPR_ASSERT(grpc_mdctx_get_mdtab_count_test_only(ctx) == 1);
+ GPR_ASSERT(grpc_mdctx_get_mdtab_free_test_only(ctx) == 1);
+
+ grpc_mdctx_orphan(ctx);
+}
+
+static void test_things_stick_around() {
+ grpc_mdctx *ctx;
+ int i, j;
+ char buffer[64];
+ int nstrs = 10000;
+ grpc_mdstr **strs = gpr_malloc(sizeof(grpc_mdstr *) * nstrs);
+ int *shuf = gpr_malloc(sizeof(int) * nstrs);
+ grpc_mdstr *test;
+
+ LOG_TEST();
+
+ ctx = grpc_mdctx_create();
+
+ for (i = 0; i < nstrs; i++) {
+ sprintf(buffer, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%dx", i);
+ strs[i] = grpc_mdstr_from_string(ctx, buffer);
+ shuf[i] = i;
+ }
+
+ for (i = 0; i < nstrs; i++) {
+ grpc_mdstr_ref(strs[i]);
+ grpc_mdstr_unref(strs[i]);
+ }
+
+ for (i = 0; i < nstrs; i++) {
+ int p = rand() % nstrs;
+ int q = rand() % nstrs;
+ int temp = shuf[p];
+ shuf[p] = shuf[q];
+ shuf[q] = temp;
+ }
+
+ for (i = 0; i < nstrs; i++) {
+ grpc_mdstr_unref(strs[shuf[i]]);
+ for (j = i + 1; j < nstrs; j++) {
+ sprintf(buffer, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%dx", shuf[j]);
+ test = grpc_mdstr_from_string(ctx, buffer);
+ GPR_ASSERT(test == strs[shuf[j]]);
+ grpc_mdstr_unref(test);
+ }
+ }
+
+ grpc_mdctx_orphan(ctx);
+ gpr_free(strs);
+ gpr_free(shuf);
+}
+
+static void test_slices_work() {
+ /* ensure no memory leaks when switching representation from mdstr to slice */
+ grpc_mdctx *ctx;
+ grpc_mdstr *str;
+ gpr_slice slice;
+
+ LOG_TEST();
+
+ ctx = grpc_mdctx_create();
+
+ str = grpc_mdstr_from_string(
+ ctx, "123456789012345678901234567890123456789012345678901234567890");
+ slice = gpr_slice_ref(str->slice);
+ grpc_mdstr_unref(str);
+ gpr_slice_unref(slice);
+
+ str = grpc_mdstr_from_string(
+ ctx, "123456789012345678901234567890123456789012345678901234567890");
+ slice = gpr_slice_ref(str->slice);
+ gpr_slice_unref(slice);
+ grpc_mdstr_unref(str);
+
+ grpc_mdctx_orphan(ctx);
+}
+
+int main(int argc, char **argv) {
+ grpc_test_init(argc, argv);
+ test_no_op();
+ test_create_string();
+ test_create_metadata();
+ test_create_many_ephemeral_metadata();
+ test_create_many_persistant_metadata();
+ test_spin_creating_the_same_thing();
+ test_things_stick_around();
+ test_slices_work();
+ return 0;
+}
diff --git a/test/core/transport/stream_op_test.c b/test/core/transport/stream_op_test.c
new file mode 100644
index 0000000000..0d1122c0ad
--- /dev/null
+++ b/test/core/transport/stream_op_test.c
@@ -0,0 +1,125 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include "src/core/transport/stream_op.h"
+
+#include <string.h>
+
+#include <grpc/support/log.h>
+#include "test/core/util/test_config.h"
+
+static void flow_ctl_cb_fails(void *ignored, grpc_op_error error) {
+ GPR_ASSERT(error == GRPC_OP_ERROR);
+}
+
+static void assert_slices_equal(gpr_slice a, gpr_slice b) {
+ GPR_ASSERT(a.refcount == b.refcount);
+ if (a.refcount) {
+ GPR_ASSERT(a.data.refcounted.bytes == b.data.refcounted.bytes);
+ GPR_ASSERT(a.data.refcounted.length == b.data.refcounted.length);
+ } else {
+ GPR_ASSERT(a.data.inlined.length == b.data.inlined.length);
+ GPR_ASSERT(0 == memcmp(a.data.inlined.bytes, b.data.inlined.bytes,
+ a.data.inlined.length));
+ }
+}
+
+int main(int argc, char **argv) {
+ /* some basic test data */
+ gpr_slice test_slice_1 = gpr_slice_malloc(1);
+ gpr_slice test_slice_2 = gpr_slice_malloc(2);
+ gpr_slice test_slice_3 = gpr_slice_malloc(3);
+ gpr_slice test_slice_4 = gpr_slice_malloc(4);
+ char x;
+ int i;
+
+ grpc_stream_op_buffer buf;
+ grpc_stream_op_buffer buf2;
+
+ grpc_test_init(argc, argv);
+ /* initialize one of our buffers */
+ grpc_sopb_init(&buf);
+ /* it should start out empty */
+ GPR_ASSERT(buf.nops == 0);
+
+ /* add some data to the buffer */
+ grpc_sopb_add_begin_message(&buf, 1, 2);
+ grpc_sopb_add_slice(&buf, test_slice_1);
+ grpc_sopb_add_slice(&buf, test_slice_2);
+ grpc_sopb_add_slice(&buf, test_slice_3);
+ grpc_sopb_add_slice(&buf, test_slice_4);
+ grpc_sopb_add_flow_ctl_cb(&buf, flow_ctl_cb_fails, &x);
+ grpc_sopb_add_no_op(&buf);
+
+ /* verify that the data went in ok */
+ GPR_ASSERT(buf.nops == 7);
+ GPR_ASSERT(buf.ops[0].type == GRPC_OP_BEGIN_MESSAGE);
+ GPR_ASSERT(buf.ops[0].data.begin_message.length == 1);
+ GPR_ASSERT(buf.ops[0].data.begin_message.flags == 2);
+ GPR_ASSERT(buf.ops[1].type == GRPC_OP_SLICE);
+ assert_slices_equal(buf.ops[1].data.slice, test_slice_1);
+ GPR_ASSERT(buf.ops[2].type == GRPC_OP_SLICE);
+ assert_slices_equal(buf.ops[2].data.slice, test_slice_2);
+ GPR_ASSERT(buf.ops[3].type == GRPC_OP_SLICE);
+ assert_slices_equal(buf.ops[3].data.slice, test_slice_3);
+ GPR_ASSERT(buf.ops[4].type == GRPC_OP_SLICE);
+ assert_slices_equal(buf.ops[4].data.slice, test_slice_4);
+ GPR_ASSERT(buf.ops[5].type == GRPC_OP_FLOW_CTL_CB);
+ GPR_ASSERT(buf.ops[5].data.flow_ctl_cb.cb == flow_ctl_cb_fails);
+ GPR_ASSERT(buf.ops[5].data.flow_ctl_cb.arg == &x);
+ GPR_ASSERT(buf.ops[6].type == GRPC_NO_OP);
+
+ /* initialize the second buffer */
+ grpc_sopb_init(&buf2);
+ /* add a no-op, and then the original buffer */
+ grpc_sopb_add_no_op(&buf2);
+ grpc_sopb_append(&buf2, buf.ops, buf.nops);
+ /* should be one element bigger than the original */
+ GPR_ASSERT(buf2.nops == buf.nops + 1);
+ GPR_ASSERT(buf2.ops[0].type == GRPC_NO_OP);
+ /* and the tail should be the same */
+ for (i = 0; i < buf.nops; i++) {
+ GPR_ASSERT(buf2.ops[i + 1].type == buf.ops[i].type);
+ }
+
+ /* destroy the buffers */
+ grpc_sopb_destroy(&buf);
+ grpc_sopb_destroy(&buf2);
+
+ gpr_slice_unref(test_slice_1);
+ gpr_slice_unref(test_slice_2);
+ gpr_slice_unref(test_slice_3);
+ gpr_slice_unref(test_slice_4);
+
+ return 0;
+}
diff --git a/test/core/transport/transport_end2end_tests.c b/test/core/transport/transport_end2end_tests.c
new file mode 100644
index 0000000000..ce6fbcf91d
--- /dev/null
+++ b/test/core/transport/transport_end2end_tests.c
@@ -0,0 +1,926 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include "test/core/transport/transport_end2end_tests.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "src/core/transport/transport.h"
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string.h>
+#include <grpc/support/thd.h>
+#include <grpc/support/useful.h>
+
+enum { REQUEST_DEADLINE = 200000 }; /* valgrind need a large value */
+
+static grpc_mdctx *g_metadata_context;
+
+static gpr_once g_pending_ops_init = GPR_ONCE_INIT;
+static gpr_mu g_mu;
+static gpr_cv g_cv;
+static int g_pending_ops;
+
+/* Defines a suite of tests that all GRPC transports should be able to pass */
+
+/******************************************************************************
+ * Testing framework
+ */
+
+/* Forward declarations */
+typedef struct test_fixture test_fixture;
+
+/* User data passed to the transport and handed to each callback */
+typedef struct test_user_data { test_fixture *fixture; } test_user_data;
+
+/* A message we expect to receive (forms a singly linked list with next) */
+typedef struct expected_message {
+ /* The next message expected */
+ struct expected_message *next;
+ /* The (owned) data that we expect to receive */
+ gpr_uint8 *data;
+ /* The length of the expected message */
+ size_t length;
+ /* How many bytes of the expected message have we received? */
+ size_t read_pos;
+ /* Have we received the GRPC_OP_BEGIN for this message */
+ int begun;
+} expected_message;
+
+/* Metadata we expect to receive */
+typedef struct expected_metadata {
+ struct expected_metadata *next;
+ struct expected_metadata *prev;
+ grpc_mdelem *metadata;
+} expected_metadata;
+
+/* Tracks a stream for a test. Forms a doubly-linked list with (prev, next) */
+typedef struct test_stream {
+ /* The owning fixture */
+ test_fixture *fixture;
+ /* The transport client stream */
+ grpc_stream *client_stream;
+ /* The transport server stream */
+ grpc_stream *server_stream;
+ /* Linked lists of messages expected on client and server */
+ expected_message *client_expected_messages;
+ expected_message *server_expected_messages;
+ expected_metadata *client_expected_metadata;
+ expected_metadata *server_expected_metadata;
+
+ /* Test streams are linked in the fixture */
+ struct test_stream *next;
+ struct test_stream *prev;
+} test_stream;
+
+/* A test_fixture tracks all transport state and expectations for a test */
+struct test_fixture {
+ gpr_mu mu;
+ gpr_cv cv; /* broadcast when expectation state has changed */
+
+ /* The transport instances */
+ grpc_transport *client_transport;
+ grpc_transport *server_transport;
+ /* User data for the transport instances - pointers to these are passed
+ to the transport. */
+ test_user_data client_ud;
+ test_user_data server_ud;
+
+ /* A pointer to the head of the tracked streams list, or NULL if no streams
+ are open */
+ test_stream *streams;
+};
+
+static void expect_metadata(test_stream *s, int from_client, const char *key,
+ const char *value);
+
+/* Convert some number of seconds into a gpr_timespec that many seconds in the
+ future */
+static gpr_timespec deadline_from_seconds(double deadline_seconds) {
+ return gpr_time_add(gpr_now(), gpr_time_from_micros(deadline_seconds * 1e6));
+}
+
+/* Init a test_user_data instance */
+static void init_user_data(test_user_data *ud, test_fixture *f,
+ grpc_transport_test_config *config, int is_client) {
+ ud->fixture = f;
+}
+
+/* Implements the alloc_recv_buffer transport callback */
+static gpr_slice alloc_recv_buffer(void *user_data, grpc_transport *transport,
+ grpc_stream *stream, size_t size_hint) {
+ return gpr_slice_malloc(size_hint);
+}
+
+static void pending_ops_cleanup() {
+ gpr_mu_destroy(&g_mu);
+ gpr_cv_destroy(&g_cv);
+}
+
+static void pending_ops_init() {
+ gpr_mu_init(&g_mu);
+ gpr_cv_init(&g_cv);
+ atexit(pending_ops_cleanup);
+}
+
+static void use_pending_ops() {
+ gpr_once_init(&g_pending_ops_init, pending_ops_init);
+}
+
+static void add_pending_op() {
+ use_pending_ops();
+ gpr_mu_lock(&g_mu);
+ g_pending_ops++;
+ gpr_mu_unlock(&g_mu);
+}
+
+static void end_pending_op() {
+ gpr_mu_lock(&g_mu);
+ g_pending_ops--;
+ gpr_cv_broadcast(&g_cv);
+ gpr_mu_unlock(&g_mu);
+}
+
+static void wait_pending_ops() {
+ use_pending_ops();
+ gpr_mu_lock(&g_mu);
+ while (g_pending_ops > 0) {
+ gpr_cv_wait(&g_cv, &g_mu, gpr_inf_future);
+ }
+ gpr_mu_unlock(&g_mu);
+}
+
+/* Implements the create_stream transport callback */
+static void create_stream(void *user_data, grpc_transport *transport,
+ const void *server_data) {
+ test_user_data *ud = user_data;
+ test_fixture *f = ud->fixture;
+ test_stream *stream;
+
+ GPR_ASSERT(ud == &f->server_ud);
+ GPR_ASSERT(transport == f->server_transport);
+
+ gpr_mu_lock(&f->mu);
+
+ /* Search streams for the peer to this stream */
+ if (!f->streams) goto done;
+ /* found the expecting stream */
+ stream = f->streams;
+ stream->server_stream = gpr_malloc(grpc_transport_stream_size(transport));
+ grpc_transport_init_stream(transport, stream->server_stream, server_data);
+
+done:
+ /* wakeup begin_stream, and maybe wait_and_verify */
+ gpr_cv_broadcast(&f->cv);
+ gpr_mu_unlock(&f->mu);
+}
+
+/* Search fixture streams for the test_stream instance holding a given transport
+ stream */
+static test_stream *find_test_stream(test_fixture *f, grpc_stream *stream) {
+ test_stream *s;
+
+ GPR_ASSERT(f->streams);
+ s = f->streams;
+ do {
+ if (s->client_stream == stream || s->server_stream == stream) {
+ return s;
+ }
+ } while (s != f->streams);
+
+ GPR_ASSERT(0 && "found");
+ return NULL;
+}
+
+/* Stringify a grpc_stream_state for debugging */
+static const char *state_name(grpc_stream_state state) {
+ switch (state) {
+ case GRPC_STREAM_OPEN:
+ return "GRPC_STREAM_OPEN";
+ case GRPC_STREAM_RECV_CLOSED:
+ return "GRPC_STREAM_RECV_CLOSED";
+ case GRPC_STREAM_SEND_CLOSED:
+ return "GRPC_STREAM_SEND_CLOSED";
+ case GRPC_STREAM_CLOSED:
+ return "GRPC_STREAM_CLOSED";
+ }
+ GPR_ASSERT(0 && "reachable");
+ return NULL;
+}
+
+typedef struct {
+ grpc_transport *transport;
+ grpc_stream *stream;
+} destroy_stream_args;
+
+static void destroy_stream(void *p) {
+ destroy_stream_args *a = p;
+ grpc_transport_destroy_stream(a->transport, a->stream);
+ gpr_free(a->stream);
+ gpr_free(a);
+ end_pending_op();
+}
+
+static void recv_batch(void *user_data, grpc_transport *transport,
+ grpc_stream *stream, grpc_stream_op *ops,
+ size_t ops_count, grpc_stream_state final_state) {
+ test_user_data *ud = user_data;
+ test_fixture *f = ud->fixture;
+ test_stream *s;
+ /* Pointer to the root pointer of either client or server expected messages;
+ not a simple pointer as we may need to manipulate the list (on receipt
+ of messages */
+ expected_message **expect_root_message;
+ expected_metadata **expect_root_metadata;
+ expected_metadata *emd;
+ size_t i, j;
+ char *hexstr1, *hexstr2;
+ int repeats = 0;
+
+ gpr_mu_lock(&f->mu);
+
+ s = find_test_stream(f, stream);
+ expect_root_message = s->client_stream == stream
+ ? &s->client_expected_messages
+ : &s->server_expected_messages;
+ expect_root_metadata = s->client_stream == stream
+ ? &s->client_expected_metadata
+ : &s->server_expected_metadata;
+
+ /* Debug log */
+ gpr_log(GPR_DEBUG, "recv_batch: %d ops on %s final_state=%s", ops_count,
+ s->client_stream == stream ? "client" : "server",
+ state_name(final_state));
+#define CLEAR_REPEATS \
+ if (repeats) { \
+ gpr_log(GPR_DEBUG, " + %d more", repeats); \
+ repeats = 0; \
+ }
+ for (i = 0; i < ops_count; i++) {
+ switch (ops[i].type) {
+ case GRPC_NO_OP:
+ CLEAR_REPEATS;
+ gpr_log(GPR_DEBUG, " [%02d] GRPC_NO_OP", i);
+ break;
+ case GRPC_OP_METADATA_BOUNDARY:
+ CLEAR_REPEATS;
+ gpr_log(GPR_DEBUG, " [%02d] GRPC_OP_METADATA_BOUNDARY", i);
+ break;
+ case GRPC_OP_METADATA:
+ CLEAR_REPEATS;
+ hexstr1 =
+ gpr_hexdump(grpc_mdstr_as_c_string(ops[i].data.metadata->key),
+ GPR_SLICE_LENGTH(ops[i].data.metadata->key->slice),
+ GPR_HEXDUMP_PLAINTEXT);
+ hexstr2 =
+ gpr_hexdump(grpc_mdstr_as_c_string(ops[i].data.metadata->value),
+ GPR_SLICE_LENGTH(ops[i].data.metadata->value->slice),
+ GPR_HEXDUMP_PLAINTEXT);
+ gpr_log(GPR_DEBUG, " [%02d] GRPC_OP_METADATA key=%s value=%s", i,
+ hexstr1, hexstr2);
+ gpr_free(hexstr1);
+ gpr_free(hexstr2);
+ break;
+ case GRPC_OP_BEGIN_MESSAGE:
+ CLEAR_REPEATS;
+ gpr_log(GPR_DEBUG, " [%02d] GRPC_OP_BEGIN_MESSAGE len=%d", i,
+ ops[i].data.begin_message.length);
+ break;
+ case GRPC_OP_DEADLINE:
+ CLEAR_REPEATS;
+ gpr_log(GPR_DEBUG, " [%02d] GRPC_OP_DEADLINE value=%d.%09d", i,
+ ops[i].data.deadline.tv_sec, ops[i].data.deadline.tv_nsec);
+ break;
+ case GRPC_OP_SLICE:
+ if (i && ops[i - 1].type == GRPC_OP_SLICE &&
+ GPR_SLICE_LENGTH(ops[i - 1].data.slice) ==
+ GPR_SLICE_LENGTH(ops[i].data.slice)) {
+ repeats++;
+ } else {
+ CLEAR_REPEATS;
+ gpr_log(GPR_DEBUG, " [%02d] GRPC_OP_SLICE len=%d", i,
+ GPR_SLICE_LENGTH(ops[i].data.slice));
+ }
+ break;
+ case GRPC_OP_FLOW_CTL_CB:
+ CLEAR_REPEATS;
+ gpr_log(GPR_DEBUG, " [%02d] GRPC_OP_FLOW_CTL_CB", i);
+ break;
+ }
+ }
+ CLEAR_REPEATS;
+
+ /* Iterate over operations, and verify them against expectations */
+ for (i = 0; i < ops_count; i++) {
+ switch (ops[i].type) {
+ case GRPC_NO_OP:
+ break;
+ case GRPC_OP_METADATA_BOUNDARY:
+ break;
+ case GRPC_OP_METADATA:
+ GPR_ASSERT(*expect_root_metadata && "must be expecting metadata");
+ emd = *expect_root_metadata;
+ if (emd == NULL) {
+ gpr_log(GPR_ERROR, "metadata not found");
+ abort();
+ }
+ do {
+ if (emd->metadata == ops[i].data.metadata) {
+ if (emd == *expect_root_metadata) {
+ if (emd->next == emd) {
+ *expect_root_metadata = NULL;
+ } else {
+ *expect_root_metadata = emd->next;
+ }
+ }
+ emd->next->prev = emd->prev;
+ emd->prev->next = emd->next;
+ grpc_mdelem_unref(emd->metadata);
+ grpc_mdelem_unref(ops[i].data.metadata);
+ gpr_free(emd);
+ emd = NULL;
+ break;
+ }
+ emd = emd->next;
+ } while (emd != *expect_root_metadata);
+ if (emd) {
+ gpr_log(GPR_ERROR, "metadata not found");
+ abort();
+ }
+ break;
+ case GRPC_OP_BEGIN_MESSAGE:
+ GPR_ASSERT(*expect_root_message && "must be expecting a message");
+ GPR_ASSERT((*expect_root_message)->read_pos == 0 &&
+ "must be at the start of a message");
+ GPR_ASSERT((*expect_root_message)->begun == 0 &&
+ "can only BEGIN a message once");
+ GPR_ASSERT((*expect_root_message)->length ==
+ ops[i].data.begin_message.length &&
+ "message lengths must match");
+ (*expect_root_message)->begun = 1;
+ break;
+ case GRPC_OP_SLICE:
+ GPR_ASSERT(*expect_root_message && "must be expecting a message");
+ GPR_ASSERT((*expect_root_message)->begun == 1 &&
+ "must have begun a message");
+ GPR_ASSERT((*expect_root_message)->read_pos +
+ GPR_SLICE_LENGTH(ops[i].data.slice) <=
+ (*expect_root_message)->length &&
+ "must not send more data than expected");
+ for (j = 0; j < GPR_SLICE_LENGTH(ops[i].data.slice); j++) {
+ GPR_ASSERT((*expect_root_message)
+ ->data[(*expect_root_message)->read_pos + j] ==
+ GPR_SLICE_START_PTR(ops[i].data.slice)[j] &&
+ "must send the correct message");
+ }
+ (*expect_root_message)->read_pos += GPR_SLICE_LENGTH(ops[i].data.slice);
+ if ((*expect_root_message)->read_pos ==
+ (*expect_root_message)->length) {
+ expected_message *great_success = *expect_root_message;
+ *expect_root_message = great_success->next;
+ gpr_free(great_success->data);
+ gpr_free(great_success);
+ }
+ gpr_slice_unref(ops[i].data.slice);
+ break;
+ case GRPC_OP_FLOW_CTL_CB:
+ GPR_ASSERT(0 && "allowed");
+ break;
+ case GRPC_OP_DEADLINE:
+ GPR_ASSERT(0 && "implemented");
+ break;
+ }
+ }
+
+ /* If the stream has become fully closed then we must destroy the transport
+ part of the stream */
+ if (final_state == GRPC_STREAM_CLOSED) {
+ destroy_stream_args *dsa = gpr_malloc(sizeof(destroy_stream_args));
+ gpr_thd_id id;
+ dsa->transport = transport;
+ dsa->stream = stream;
+ /* start a thread after incrementing a pending op counter (so we can wait
+ at test completion */
+ add_pending_op();
+ gpr_thd_new(&id, destroy_stream, dsa, NULL);
+ if (stream == s->client_stream) {
+ GPR_ASSERT(s->client_expected_messages == NULL &&
+ "must receive all expected messages");
+ s->client_stream = NULL;
+ } else {
+ GPR_ASSERT(s->server_expected_messages == NULL &&
+ "must receive all expected messages");
+ s->server_stream = NULL;
+ }
+ /* And if both the client and the server report fully closed, we can
+ unlink the stream object entirely */
+ if (s->client_stream == NULL && s->server_stream == NULL) {
+ s->next->prev = s->prev;
+ s->prev->next = s->next;
+ if (s == f->streams) {
+ if (s->next == f->streams) {
+ f->streams = NULL;
+ } else {
+ f->streams = s->next;
+ }
+ }
+ }
+ }
+
+ /* wakeup wait_and_verify */
+ gpr_cv_broadcast(&f->cv);
+ gpr_mu_unlock(&f->mu);
+}
+
+static void close_transport(void *user_data, grpc_transport *transport) {}
+
+static grpc_transport_callbacks transport_callbacks = {
+ alloc_recv_buffer, create_stream, recv_batch, close_transport};
+
+/* Helper for tests to create a stream.
+ Arguments:
+ s - uninitialized test_stream struct to begin
+ f - test fixture to associate this stream with
+ method, host, deadline_seconds - header fields for the stream */
+static void begin_stream(test_stream *s, test_fixture *f, const char *method,
+ const char *host, double deadline_seconds) {
+ /* Deadline to initiate the stream (prevents the tests from hanging
+ forever) */
+ gpr_timespec deadline = deadline_from_seconds(10.0);
+ grpc_stream_op_buffer sopb;
+
+ grpc_sopb_init(&sopb);
+
+ gpr_mu_lock(&f->mu);
+
+ s->fixture = f;
+ s->client_stream =
+ gpr_malloc(grpc_transport_stream_size(f->client_transport));
+ /* server stream will be set once it's received by the peer transport */
+ s->server_stream = NULL;
+ s->client_expected_messages = NULL;
+ s->server_expected_messages = NULL;
+ s->client_expected_metadata = NULL;
+ s->server_expected_metadata = NULL;
+
+ if (f->streams) {
+ s->next = f->streams;
+ s->prev = s->next->prev;
+ s->next->prev = s->prev->next = s;
+ } else {
+ s->next = s->prev = s;
+ }
+ f->streams = s;
+
+ gpr_mu_unlock(&f->mu);
+
+ GPR_ASSERT(0 == grpc_transport_init_stream(f->client_transport,
+ s->client_stream, NULL));
+
+#define ADDMD(k, v) \
+ do { \
+ grpc_mdelem *md = grpc_mdelem_from_strings(g_metadata_context, (k), (v)); \
+ grpc_sopb_add_metadata(&sopb, md); \
+ expect_metadata(s, 1, (k), (v)); \
+ } while (0)
+
+ ADDMD(":path", method);
+ ADDMD(":authority", host);
+ ADDMD(":method", "POST");
+ grpc_transport_send_batch(f->client_transport, s->client_stream, sopb.ops,
+ sopb.nops, 0);
+ sopb.nops = 0;
+
+ grpc_sopb_destroy(&sopb);
+
+ /* wait for the server side stream to be created */
+ gpr_mu_lock(&f->mu);
+ while (s->server_stream == NULL) {
+ GPR_ASSERT(0 == gpr_cv_wait(&f->cv, &f->mu, deadline));
+ }
+ gpr_mu_unlock(&f->mu);
+}
+
+static grpc_transport_setup_result setup_transport(
+ test_fixture *f, grpc_transport **set_transport, void *user_data,
+ grpc_transport *transport) {
+ grpc_transport_setup_result result;
+
+ gpr_mu_lock(&f->mu);
+ *set_transport = transport;
+ gpr_cv_broadcast(&f->cv);
+ gpr_mu_unlock(&f->mu);
+
+ result.callbacks = &transport_callbacks;
+ result.user_data = user_data;
+ return result;
+}
+
+static grpc_transport_setup_result setup_server_transport(
+ void *arg, grpc_transport *transport, grpc_mdctx *mdctx) {
+ test_fixture *f = arg;
+ return setup_transport(f, &f->server_transport, &f->server_ud, transport);
+}
+
+static grpc_transport_setup_result setup_client_transport(
+ void *arg, grpc_transport *transport, grpc_mdctx *mdctx) {
+ test_fixture *f = arg;
+ return setup_transport(f, &f->client_transport, &f->client_ud, transport);
+}
+
+/* Begin a test
+
+ Arguments:
+ f - uninitialized test_fixture struct
+ config - test configuration for this test
+ name - the name of this test */
+static void begin_test(test_fixture *f, grpc_transport_test_config *config,
+ const char *name) {
+ gpr_timespec timeout = gpr_time_add(gpr_now(), gpr_time_from_micros(100e6));
+
+ gpr_log(GPR_INFO, "BEGIN: %s/%s", name, config->name);
+
+ gpr_mu_init(&f->mu);
+ gpr_cv_init(&f->cv);
+
+ f->streams = NULL;
+
+ init_user_data(&f->client_ud, f, config, 1);
+ init_user_data(&f->server_ud, f, config, 0);
+
+ f->client_transport = NULL;
+ f->server_transport = NULL;
+
+ GPR_ASSERT(0 ==
+ config->create_transport(setup_client_transport, f,
+ setup_server_transport, f,
+ g_metadata_context));
+
+ gpr_mu_lock(&f->mu);
+ while (!f->client_transport || !f->server_transport) {
+ GPR_ASSERT(gpr_cv_wait(&f->cv, &f->mu, timeout));
+ }
+ gpr_mu_unlock(&f->mu);
+}
+
+/* Enumerate expected messages on a stream */
+static void enumerate_expected_messages(
+ test_stream *s, expected_message *root, const char *stream_tag,
+ void (*cb)(void *user, const char *fmt, ...), void *user) {
+ expected_message *msg;
+
+ for (msg = root; msg; msg = msg->next) {
+ cb(user,
+ "Waiting for message to finish: "
+ "length=%zu read_pos=%zu begun=%d",
+ msg->length, msg->read_pos);
+ }
+}
+
+/* Walk through everything that is still waiting to happen, and call 'cb' with
+ userdata 'user' for that expectation. */
+static void enumerate_expectations(test_fixture *f,
+ void (*cb)(void *user, const char *fmt, ...),
+ void *user) {
+ test_stream *stream;
+
+ if (f->streams) {
+ stream = f->streams;
+ do {
+ cb(user,
+ "Waiting for request to close: "
+ "client=%p, server=%p",
+ stream->client_stream, stream->server_stream);
+ enumerate_expected_messages(stream, stream->client_expected_messages,
+ "client", cb, user);
+ enumerate_expected_messages(stream, stream->server_expected_messages,
+ "server", cb, user);
+ stream = stream->next;
+ } while (stream != f->streams);
+ }
+}
+
+/* Callback for enumerate_expectations, that increments an integer each time
+ an expectation is seen */
+static void increment_expectation_count(void *p, const char *fmt, ...) {
+ ++*(int *)p;
+}
+
+/* Returns the count of pending expectations in a fixture. Requires mu taken */
+static int count_expectations(test_fixture *f) {
+ int n = 0;
+ enumerate_expectations(f, increment_expectation_count, &n);
+ return n;
+}
+
+/* Callback for enumerate_expectations that adds an expectation to the log */
+static void dump_expectation(void *p, const char *fmt, ...) {
+ char buffer[256];
+ va_list args;
+ va_start(args, fmt);
+
+ vsprintf(buffer, fmt, args);
+ gpr_log(GPR_INFO, "EXPECTED: %s", buffer);
+
+ va_end(args);
+}
+
+/* Add all pending expectations to the log */
+static void dump_expectations(test_fixture *f) {
+ enumerate_expectations(f, dump_expectation, NULL);
+}
+
+/* Wait until all expectations are completed, or crash */
+static void wait_and_verify(test_fixture *f) {
+ gpr_timespec deadline = deadline_from_seconds(10.0);
+
+ gpr_mu_lock(&f->mu);
+ while (count_expectations(f) > 0) {
+ gpr_log(GPR_INFO, "waiting for expectations to complete");
+ if (gpr_cv_wait(&f->cv, &f->mu, deadline)) {
+ gpr_log(GPR_ERROR, "Timeout waiting for expectation completion");
+ dump_expectations(f);
+ gpr_mu_unlock(&f->mu);
+ abort();
+ }
+ }
+ gpr_mu_unlock(&f->mu);
+}
+
+/* Finish a test */
+static void end_test(test_fixture *f) {
+ wait_and_verify(f);
+
+ grpc_transport_close(f->client_transport);
+ grpc_transport_close(f->server_transport);
+ grpc_transport_destroy(f->client_transport);
+ grpc_transport_destroy(f->server_transport);
+
+ wait_pending_ops();
+}
+
+/* Generate a test slice filled with {0,1,2,3,...,255,0,1,2,3,4,...} */
+static gpr_slice generate_test_data(size_t length) {
+ gpr_slice slice = gpr_slice_malloc(length);
+ int i;
+ for (i = 0; i < length; i++) {
+ GPR_SLICE_START_PTR(slice)[i] = i;
+ }
+ return slice;
+}
+
+/* Add an expected message to the end of a list with root root */
+static void append_expected_message(expected_message **root,
+ expected_message *message) {
+ expected_message *end;
+
+ if (!*root) {
+ *root = message;
+ return;
+ }
+
+ for (end = *root; end->next; end = end->next)
+ ;
+ end->next = message;
+}
+
+/* Add an expected message on stream 's''.
+ If from_client==1, expect it on the server, otherwise expect it on the client
+ Variadic parameters are a NULL-terminated list of pointers to slices that
+ should be expected as payload */
+static void expect_message(test_stream *s, int from_client,
+ /* gpr_slice* */...) {
+ va_list args;
+ gpr_slice *slice;
+ size_t capacity = 32;
+ size_t length = 0;
+ gpr_uint8 *buffer = gpr_malloc(capacity);
+ expected_message *e;
+
+ va_start(args, from_client);
+ while ((slice = va_arg(args, gpr_slice *))) {
+ while (GPR_SLICE_LENGTH(*slice) + length > capacity) {
+ capacity *= 2;
+ buffer = gpr_realloc(buffer, capacity);
+ }
+ memcpy(buffer + length, GPR_SLICE_START_PTR(*slice),
+ GPR_SLICE_LENGTH(*slice));
+ length += GPR_SLICE_LENGTH(*slice);
+ }
+ va_end(args);
+
+ e = gpr_malloc(sizeof(expected_message));
+ e->data = buffer;
+ e->length = length;
+ e->read_pos = 0;
+ e->begun = 0;
+ e->next = NULL;
+
+ gpr_mu_lock(&s->fixture->mu);
+ append_expected_message(
+ from_client ? &s->server_expected_messages : &s->client_expected_messages,
+ e);
+ gpr_mu_unlock(&s->fixture->mu);
+}
+
+static void expect_metadata(test_stream *s, int from_client, const char *key,
+ const char *value) {
+ expected_metadata *e = gpr_malloc(sizeof(expected_metadata));
+ expected_metadata **root =
+ from_client ? &s->server_expected_metadata : &s->client_expected_metadata;
+ e->metadata = grpc_mdelem_from_strings(g_metadata_context, key, value);
+ gpr_mu_lock(&s->fixture->mu);
+ if (!*root) {
+ *root = e;
+ e->next = e->prev = e;
+ } else {
+ e->next = *root;
+ e->prev = e->next->prev;
+ e->next->prev = e->prev->next = e;
+ }
+ gpr_mu_unlock(&s->fixture->mu);
+}
+
+/******************************************************************************
+ * Actual unit tests
+ */
+
+/* Test that we can create, begin, and end a test */
+static void test_no_op(grpc_transport_test_config *config) {
+ test_fixture f;
+ begin_test(&f, config, __FUNCTION__);
+ end_test(&f);
+}
+
+/* Test that a request can be initiated and terminated normally */
+static void test_simple_request(grpc_transport_test_config *config) {
+ test_fixture f;
+ test_stream s;
+
+ begin_test(&f, config, __FUNCTION__);
+ begin_stream(&s, &f, "/Test", "foo.google.com", 10);
+ grpc_transport_send_batch(f.client_transport, s.client_stream, NULL, 0, 1);
+ grpc_transport_send_batch(f.server_transport, s.server_stream, NULL, 0, 1);
+ end_test(&f);
+}
+
+/* Test that a request can be aborted by the client */
+static void test_can_abort_client(grpc_transport_test_config *config) {
+ test_fixture f;
+ test_stream s;
+
+ begin_test(&f, config, __FUNCTION__);
+ begin_stream(&s, &f, "/Test", "foo.google.com", 10);
+ expect_metadata(&s, 0, "grpc-status", "1");
+ expect_metadata(&s, 1, "grpc-status", "1");
+ grpc_transport_abort_stream(f.client_transport, s.client_stream,
+ GRPC_STATUS_CANCELLED);
+ end_test(&f);
+}
+
+/* Test that a request can be aborted by the server */
+static void test_can_abort_server(grpc_transport_test_config *config) {
+ test_fixture f;
+ test_stream s;
+
+ begin_test(&f, config, __FUNCTION__);
+ begin_stream(&s, &f, "/Test", "foo.google.com", 10);
+ expect_metadata(&s, 0, "grpc-status", "1");
+ expect_metadata(&s, 1, "grpc-status", "1");
+ grpc_transport_abort_stream(f.server_transport, s.server_stream,
+ GRPC_STATUS_CANCELLED);
+ end_test(&f);
+}
+
+/* Test that a request can be sent with payload */
+static void test_request_with_data(grpc_transport_test_config *config,
+ size_t message_length) {
+ test_fixture f;
+ test_stream s;
+ gpr_slice data = generate_test_data(message_length);
+ grpc_stream_op_buffer sopb;
+
+ grpc_sopb_init(&sopb);
+ begin_test(&f, config, __FUNCTION__);
+ gpr_log(GPR_INFO, "message_length = %d", message_length);
+ begin_stream(&s, &f, "/Test", "foo.google.com", 10);
+ expect_message(&s, 1, &data, NULL);
+ grpc_sopb_add_begin_message(&sopb, message_length, 0);
+ grpc_sopb_add_slice(&sopb, data);
+ grpc_transport_set_allow_window_updates(f.server_transport, s.server_stream,
+ 1);
+ grpc_transport_send_batch(f.client_transport, s.client_stream, sopb.ops,
+ sopb.nops, 1);
+ sopb.nops = 0;
+ grpc_transport_send_batch(f.server_transport, s.server_stream, NULL, 0, 1);
+ end_test(&f);
+ grpc_sopb_destroy(&sopb);
+}
+
+/* Increment an integer pointed to by x - used for verifying flow control */
+static void increment_int(void *x, grpc_op_error error) { ++*(int *)x; }
+
+/* Test that flow control callbacks are made at appropriate times */
+static void test_request_with_flow_ctl_cb(grpc_transport_test_config *config,
+ size_t message_length) {
+ test_fixture f;
+ test_stream s;
+ int flow_ctl_called = 0;
+ gpr_slice data = generate_test_data(message_length);
+ grpc_stream_op_buffer sopb;
+
+ grpc_sopb_init(&sopb);
+ begin_test(&f, config, __FUNCTION__);
+ gpr_log(GPR_INFO, "length=%d", message_length);
+ begin_stream(&s, &f, "/Test", "foo.google.com", 10);
+ expect_message(&s, 1, &data, NULL);
+ grpc_sopb_add_begin_message(&sopb, message_length, 0);
+ grpc_sopb_add_slice(&sopb, data);
+ grpc_sopb_add_flow_ctl_cb(&sopb, increment_int, &flow_ctl_called);
+ grpc_transport_set_allow_window_updates(f.server_transport, s.server_stream,
+ 1);
+ grpc_transport_send_batch(f.client_transport, s.client_stream, sopb.ops,
+ sopb.nops, 1);
+ sopb.nops = 0;
+ grpc_transport_send_batch(f.server_transport, s.server_stream, NULL, 0, 1);
+ end_test(&f);
+ GPR_ASSERT(flow_ctl_called == 1);
+ grpc_sopb_destroy(&sopb);
+}
+
+/* Set an event on ping response */
+static void ping_cb(void *p) { gpr_event_set(p, (void *)1); }
+
+/* Test that pinging gets a response */
+static void test_ping(grpc_transport_test_config *config) {
+ test_fixture f;
+ gpr_event ev;
+
+ begin_test(&f, config, __FUNCTION__);
+ gpr_event_init(&ev);
+
+ grpc_transport_ping(f.client_transport, ping_cb, &ev);
+ GPR_ASSERT(gpr_event_wait(&ev, deadline_from_seconds(10)));
+
+ end_test(&f);
+}
+
+/******************************************************************************
+ * Test driver
+ */
+
+static const size_t interesting_message_lengths[] = {
+ 1, 100, 10000, 100000, 1000000,
+};
+
+void grpc_transport_end2end_tests(grpc_transport_test_config *config) {
+ int i;
+
+ g_metadata_context = grpc_mdctx_create();
+
+ test_no_op(config);
+ test_simple_request(config);
+ test_can_abort_client(config);
+ test_can_abort_server(config);
+ test_ping(config);
+ for (i = 0; i < GPR_ARRAY_SIZE(interesting_message_lengths); i++) {
+ test_request_with_data(config, interesting_message_lengths[i]);
+ test_request_with_flow_ctl_cb(config, interesting_message_lengths[i]);
+ }
+
+ grpc_mdctx_orphan(g_metadata_context);
+
+ gpr_log(GPR_INFO, "tests completed ok");
+}
diff --git a/test/core/transport/transport_end2end_tests.h b/test/core/transport/transport_end2end_tests.h
new file mode 100644
index 0000000000..322034f820
--- /dev/null
+++ b/test/core/transport/transport_end2end_tests.h
@@ -0,0 +1,68 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#ifndef __GRPC_TEST_TRANSPORT_TRANSPORT_END2END_TESTS_H__
+#define __GRPC_TEST_TRANSPORT_TRANSPORT_END2END_TESTS_H__
+
+#include "src/core/transport/transport.h"
+
+/* Defines a suite of tests that all GRPC transports should be able to pass */
+
+/* A test configuration has a name and a factory method */
+typedef struct grpc_transport_test_config {
+ /* The name of this configuration */
+ char *name;
+ /* Create a transport
+ Returns 0 on success
+
+ Arguments:
+ OUT: client - the created client half of the transport
+ IN: client_callbacks - callback structure to be used by the client
+ transport
+ IN: client_user_data - user data pointer to be passed into each client
+ callback
+ OUT: server - the created server half of the transport
+ IN: server_callbacks - callback structure to be used by the server
+ transport
+ IN: server_user_data - user data pointer to be passed into each
+ server */
+ int (*create_transport)(grpc_transport_setup_callback client_setup,
+ void *client_arg,
+ grpc_transport_setup_callback server_setup,
+ void *server_arg, grpc_mdctx *mdctx);
+} grpc_transport_test_config;
+
+/* Run the test suite on one configuration */
+void grpc_transport_end2end_tests(grpc_transport_test_config *config);
+
+#endif /* __GRPC_TEST_TRANSPORT_TRANSPORT_END2END_TESTS_H__ */
diff --git a/test/core/util/grpc_profiler.c b/test/core/util/grpc_profiler.c
new file mode 100644
index 0000000000..e135743d57
--- /dev/null
+++ b/test/core/util/grpc_profiler.c
@@ -0,0 +1,38 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include "test/core/util/grpc_profiler.h"
+
+void grpc_profiler_start(const char *filename) {}
+
+void grpc_profiler_stop() {}
diff --git a/test/core/util/grpc_profiler.h b/test/core/util/grpc_profiler.h
new file mode 100644
index 0000000000..7cc4733e65
--- /dev/null
+++ b/test/core/util/grpc_profiler.h
@@ -0,0 +1,48 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#ifndef __GRPC_TEST_UTIL_GRPC_PROFILER_H__
+#define __GRPC_TEST_UTIL_GRPC_PROFILER_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+void grpc_profiler_start(const char *filename);
+void grpc_profiler_stop();
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __GRPC_TEST_UTIL_GRPC_PROFILER_H__ */
diff --git a/test/core/util/parse_hexstring.c b/test/core/util/parse_hexstring.c
new file mode 100644
index 0000000000..888d03bc68
--- /dev/null
+++ b/test/core/util/parse_hexstring.c
@@ -0,0 +1,70 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include "test/core/util/parse_hexstring.h"
+#include <grpc/support/log.h>
+
+gpr_slice parse_hexstring(const char *hexstring) {
+ int nibbles = 0;
+ const char *p = 0;
+ gpr_uint8 *out;
+ gpr_uint8 temp;
+ gpr_slice slice;
+
+ for (p = hexstring; *p; p++) {
+ nibbles += (*p >= '0' && *p <= '9') || (*p >= 'a' && *p <= 'f');
+ }
+
+ GPR_ASSERT((nibbles & 1) == 0);
+
+ slice = gpr_slice_malloc(nibbles / 2);
+ out = GPR_SLICE_START_PTR(slice);
+
+ nibbles = 0;
+ temp = 0;
+ for (p = hexstring; *p; p++) {
+ if (*p >= '0' && *p <= '9') {
+ temp = (temp << 4) | (*p - '0');
+ nibbles++;
+ } else if (*p >= 'a' && *p <= 'f') {
+ temp = (temp << 4) | (*p - 'a' + 10);
+ nibbles++;
+ }
+ if (nibbles == 2) {
+ *out++ = temp;
+ nibbles = 0;
+ }
+ }
+
+ return slice;
+}
diff --git a/test/core/util/parse_hexstring.h b/test/core/util/parse_hexstring.h
new file mode 100644
index 0000000000..7477986d60
--- /dev/null
+++ b/test/core/util/parse_hexstring.h
@@ -0,0 +1,41 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#ifndef __GRPC_TEST_UTIL_PARSE_HEXSTRING_H_
+#define __GRPC_TEST_UTIL_PARSE_HEXSTRING_H_
+
+#include <grpc/support/slice.h>
+
+gpr_slice parse_hexstring(const char *hexstring);
+
+#endif /* __GRPC_TEST_UTIL_PARSE_HEXSTRING_H_ */
diff --git a/test/core/util/port.c b/test/core/util/port.c
new file mode 100644
index 0000000000..133b53fd2b
--- /dev/null
+++ b/test/core/util/port.c
@@ -0,0 +1,145 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include "test/core/util/port.h"
+
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <grpc/support/log.h>
+
+#define NUM_RANDOM_PORTS_TO_PICK 100
+
+static int is_port_available(int *port, int is_tcp) {
+ const int proto = is_tcp ? IPPROTO_TCP : 0;
+ const int fd = socket(AF_INET, is_tcp ? SOCK_STREAM : SOCK_DGRAM, proto);
+ int one = 1;
+ struct sockaddr_in addr;
+ socklen_t alen = sizeof(addr);
+ int actual_port;
+
+ GPR_ASSERT(*port >= 0);
+ GPR_ASSERT(*port <= 65535);
+ if (fd < 0) {
+ gpr_log(GPR_ERROR, "socket() failed: %s", strerror(errno));
+ return 0;
+ }
+
+ /* Reuseaddr lets us start up a server immediately after it exits */
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0) {
+ gpr_log(GPR_ERROR, "setsockopt() failed: %s", strerror(errno));
+ close(fd);
+ return 0;
+ }
+
+ /* Try binding to port */
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = INADDR_ANY;
+ addr.sin_port = htons(*port);
+ if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ gpr_log(GPR_DEBUG, "bind(port=%d) failed: %s", *port, strerror(errno));
+ close(fd);
+ return 0;
+ }
+
+ /* Get the bound port number */
+ if (getsockname(fd, (struct sockaddr *)&addr, &alen) < 0) {
+ gpr_log(GPR_ERROR, "getsockname() failed: %s", strerror(errno));
+ close(fd);
+ return 0;
+ }
+ GPR_ASSERT(alen <= sizeof(addr));
+ actual_port = ntohs(addr.sin_port);
+ GPR_ASSERT(actual_port > 0);
+ if (*port == 0) {
+ *port = actual_port;
+ } else {
+ GPR_ASSERT(*port == actual_port);
+ }
+
+ close(fd);
+ return 1;
+}
+
+int grpc_pick_unused_port() {
+ /* We repeatedly pick a port and then see whether or not it is
+ available for use both as a TCP socket and a UDP socket. First, we
+ pick a random large port number. For subsequent
+ iterations, we bind to an anonymous port and let the OS pick the
+ port number. The random port picking reduces the probability of
+ races with other processes on kernels that want to reuse the same
+ port numbers over and over. */
+
+ /* In alternating iterations we try UDP ports before TCP ports UDP
+ ports -- it could be the case that this machine has been using up
+ UDP ports and they are scarcer. */
+
+ /* Type of port to first pick in next iteration */
+ int is_tcp = 1;
+ int try
+ = 0;
+
+ for (;;) {
+ int port = try
+ < NUM_RANDOM_PORTS_TO_PICK ? rand() % (65536 - 30000) + 30000 : 0;
+ if (!is_port_available(&port, is_tcp)) {
+ continue;
+ }
+ GPR_ASSERT(port > 0);
+ /* Check that the port # is free for the other type of socket also */
+ if (!is_port_available(&port, !is_tcp)) {
+ /* In the next iteration try to bind to the other type first
+ because perhaps it is more rare. */
+ is_tcp = !is_tcp;
+ continue;
+ }
+
+ /* TODO(ctiller): consider caching this port in some structure, to avoid
+ handing it out again */
+
+ return port;
+ }
+
+ /* The port iterator reached the end without finding a suitable port. */
+ return 0;
+}
+
+int grpc_pick_unused_port_or_die() {
+ int port = grpc_pick_unused_port();
+ GPR_ASSERT(port > 0);
+ return port;
+}
diff --git a/test/core/util/port.h b/test/core/util/port.h
new file mode 100644
index 0000000000..d06f04731e
--- /dev/null
+++ b/test/core/util/port.h
@@ -0,0 +1,44 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#ifndef __GRPC_TEST_UTIL_PORT_H__
+#define __GRPC_TEST_UTIL_PORT_H__
+
+/* pick a port number that is currently unused by either tcp or udp. return
+ 0 on failure. */
+int grpc_pick_unused_port();
+/* pick a port number that is currently unused by either tcp or udp. abort
+ on failure. */
+int grpc_pick_unused_port_or_die();
+
+#endif /* __GRPC_TEST_UTIL_PORT_H__ */
diff --git a/test/core/util/slice_splitter.c b/test/core/util/slice_splitter.c
new file mode 100644
index 0000000000..1682ef4fcd
--- /dev/null
+++ b/test/core/util/slice_splitter.c
@@ -0,0 +1,138 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include "test/core/util/slice_splitter.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/useful.h>
+
+const char *grpc_slice_split_mode_name(grpc_slice_split_mode mode) {
+ switch (mode) {
+ case GRPC_SLICE_SPLIT_IDENTITY:
+ return "identity";
+ case GRPC_SLICE_SPLIT_MERGE_ALL:
+ return "merge_all";
+ case GRPC_SLICE_SPLIT_ONE_BYTE:
+ return "one_byte";
+ }
+ return "error";
+}
+
+void grpc_split_slices(grpc_slice_split_mode mode, gpr_slice *src_slices,
+ size_t src_slice_count, gpr_slice **dst_slices,
+ size_t *dst_slice_count) {
+ size_t i, j;
+ size_t length;
+
+ switch (mode) {
+ case GRPC_SLICE_SPLIT_IDENTITY:
+ *dst_slice_count = src_slice_count;
+ *dst_slices = gpr_malloc(sizeof(gpr_slice) * src_slice_count);
+ for (i = 0; i < src_slice_count; i++) {
+ (*dst_slices)[i] = src_slices[i];
+ gpr_slice_ref((*dst_slices)[i]);
+ }
+ break;
+ case GRPC_SLICE_SPLIT_MERGE_ALL:
+ *dst_slice_count = 1;
+ length = 0;
+ for (i = 0; i < src_slice_count; i++) {
+ length += GPR_SLICE_LENGTH(src_slices[i]);
+ }
+ *dst_slices = gpr_malloc(sizeof(gpr_slice));
+ **dst_slices = gpr_slice_malloc(length);
+ length = 0;
+ for (i = 0; i < src_slice_count; i++) {
+ memcpy(GPR_SLICE_START_PTR(**dst_slices) + length,
+ GPR_SLICE_START_PTR(src_slices[i]),
+ GPR_SLICE_LENGTH(src_slices[i]));
+ length += GPR_SLICE_LENGTH(src_slices[i]);
+ }
+ break;
+ case GRPC_SLICE_SPLIT_ONE_BYTE:
+ length = 0;
+ for (i = 0; i < src_slice_count; i++) {
+ length += GPR_SLICE_LENGTH(src_slices[i]);
+ }
+ *dst_slice_count = length;
+ *dst_slices = gpr_malloc(sizeof(gpr_slice) * length);
+ length = 0;
+ for (i = 0; i < src_slice_count; i++) {
+ for (j = 0; j < GPR_SLICE_LENGTH(src_slices[i]); j++) {
+ (*dst_slices)[length] = gpr_slice_sub(src_slices[i], j, j + 1);
+ length++;
+ }
+ }
+ break;
+ }
+}
+
+void grpc_split_slices_to_buffer(grpc_slice_split_mode mode,
+ gpr_slice *src_slices, size_t src_slice_count,
+ gpr_slice_buffer *dst) {
+ gpr_slice *slices;
+ size_t nslices;
+ size_t i;
+ grpc_split_slices(mode, src_slices, src_slice_count, &slices, &nslices);
+ for (i = 0; i < nslices; i++) {
+ /* add indexed to avoid re-merging split slices */
+ gpr_slice_buffer_add_indexed(dst, slices[i]);
+ }
+ gpr_free(slices);
+}
+
+void grpc_split_slice_buffer(grpc_slice_split_mode mode, gpr_slice_buffer *src,
+ gpr_slice_buffer *dst) {
+ grpc_split_slices_to_buffer(mode, src->slices, src->count, dst);
+}
+
+gpr_slice grpc_slice_merge(gpr_slice *slices, size_t nslices) {
+ gpr_uint8 *out = NULL;
+ size_t length = 0;
+ size_t capacity = 0;
+ size_t i;
+
+ for (i = 0; i < nslices; i++) {
+ if (GPR_SLICE_LENGTH(slices[i]) + length > capacity) {
+ capacity = GPR_MAX(capacity * 2, GPR_SLICE_LENGTH(slices[i]) + length);
+ out = gpr_realloc(out, capacity);
+ }
+ memcpy(out + length, GPR_SLICE_START_PTR(slices[i]),
+ GPR_SLICE_LENGTH(slices[i]));
+ length += GPR_SLICE_LENGTH(slices[i]);
+ }
+
+ return gpr_slice_new(out, length, gpr_free);
+}
diff --git a/test/core/util/slice_splitter.h b/test/core/util/slice_splitter.h
new file mode 100644
index 0000000000..bff4515c57
--- /dev/null
+++ b/test/core/util/slice_splitter.h
@@ -0,0 +1,68 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#ifndef __GRPC_TEST_UTIL_SLICE_SPLITTER_H__
+#define __GRPC_TEST_UTIL_SLICE_SPLITTER_H__
+
+/* utility function to split/merge slices together to help create test
+ cases */
+
+#include <grpc/support/slice.h>
+#include <grpc/support/slice_buffer.h>
+
+typedef enum {
+ /* merge all input slices into a single slice */
+ GRPC_SLICE_SPLIT_MERGE_ALL,
+ /* leave slices as is */
+ GRPC_SLICE_SPLIT_IDENTITY,
+ /* split slices into one byte chunks */
+ GRPC_SLICE_SPLIT_ONE_BYTE
+} grpc_slice_split_mode;
+
+/* allocates *dst_slices; caller must unref all slices in dst_slices then free
+ it */
+void grpc_split_slices(grpc_slice_split_mode mode, gpr_slice *src_slices,
+ size_t src_slice_count, gpr_slice **dst_slices,
+ size_t *dst_slice_count);
+
+void grpc_split_slices_to_buffer(grpc_slice_split_mode mode,
+ gpr_slice *src_slices, size_t src_slice_count,
+ gpr_slice_buffer *dst);
+void grpc_split_slice_buffer(grpc_slice_split_mode mode, gpr_slice_buffer *src,
+ gpr_slice_buffer *dst);
+
+gpr_slice grpc_slice_merge(gpr_slice *slices, size_t nslices);
+
+const char *grpc_slice_split_mode_name(grpc_slice_split_mode mode);
+
+#endif /* __GRPC_TEST_UTIL_SLICE_SPLITTER_H__ */
diff --git a/test/core/util/test_config.c b/test/core/util/test_config.c
new file mode 100644
index 0000000000..993014aa14
--- /dev/null
+++ b/test/core/util/test_config.c
@@ -0,0 +1,36 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#include "test/core/util/test_config.h"
+
+void grpc_test_init(int argc, char **argv) {}
diff --git a/test/core/util/test_config.h b/test/core/util/test_config.h
new file mode 100644
index 0000000000..8ada77bfc7
--- /dev/null
+++ b/test/core/util/test_config.h
@@ -0,0 +1,47 @@
+/*
+ *
+ * Copyright 2014, 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.
+ *
+ */
+
+#ifndef __GRPC_TEST_UTIL_TEST_CONFIG_H__
+#define __GRPC_TEST_UTIL_TEST_CONFIG_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+void grpc_test_init(int argc, char **argv);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __GRPC_TEST_UTIL_TEST_CONFIG_H__ */