aboutsummaryrefslogtreecommitdiffhomepage
path: root/test/core/transport
diff options
context:
space:
mode:
Diffstat (limited to 'test/core/transport')
-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
11 files changed, 2834 insertions, 0 deletions
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__ */