diff options
Diffstat (limited to 'test/core/transport')
-rw-r--r-- | test/core/transport/chttp2/hpack_parser_test.c | 223 | ||||
-rw-r--r-- | test/core/transport/chttp2/hpack_table_test.c | 269 | ||||
-rw-r--r-- | test/core/transport/chttp2/status_conversion_test.c | 138 | ||||
-rw-r--r-- | test/core/transport/chttp2/stream_encoder_test.c | 320 | ||||
-rw-r--r-- | test/core/transport/chttp2/stream_map_test.c | 228 | ||||
-rw-r--r-- | test/core/transport/chttp2/timeout_encoding_test.c | 140 | ||||
-rw-r--r-- | test/core/transport/chttp2_transport_end2end_test.c | 139 | ||||
-rw-r--r-- | test/core/transport/metadata_test.c | 258 | ||||
-rw-r--r-- | test/core/transport/stream_op_test.c | 125 | ||||
-rw-r--r-- | test/core/transport/transport_end2end_tests.c | 926 | ||||
-rw-r--r-- | test/core/transport/transport_end2end_tests.h | 68 |
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__ */ |