| /* |
| * |
| * Copyright 2014, Google Inc. |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following disclaimer |
| * in the documentation and/or other materials provided with the |
| * distribution. |
| * * Neither the name of Google Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| * |
| */ |
| |
| #include "src/core/channel/metadata_buffer.h" |
| #include <grpc/support/alloc.h> |
| #include <grpc/support/log.h> |
| #include "test/core/util/test_config.h" |
| |
| #include <string.h> |
| #include <stdio.h> |
| |
| /* construct a buffer with some prefix followed by an integer converted to |
| a string */ |
| static gpr_slice construct_buffer(size_t prefix_length, size_t index) { |
| gpr_slice buffer = gpr_slice_malloc(prefix_length + 32); |
| memset(GPR_SLICE_START_PTR(buffer), 'a', prefix_length); |
| GPR_SLICE_SET_LENGTH( |
| buffer, prefix_length + |
| sprintf((char *)GPR_SLICE_START_PTR(buffer) + prefix_length, |
| "%d", (int)index)); |
| return buffer; |
| } |
| |
| static void do_nothing(void *ignored, grpc_op_error also_ignored) {} |
| |
| /* we need a fake channel & call stack, which is defined here */ |
| |
| /* a fake channel needs to track some information about the test */ |
| typedef struct { |
| size_t key_prefix_len; |
| size_t value_prefix_len; |
| } channel_data; |
| |
| static void fail_call_op(grpc_call_element *elem, grpc_call_element *from_elem, |
| grpc_call_op *op) { |
| abort(); |
| } |
| |
| /* verify that the metadata passed on during flush is the same as we expect */ |
| static void expect_call_op(grpc_call_element *elem, |
| grpc_call_element *from_elem, grpc_call_op *op) { |
| size_t *n = elem->call_data; |
| channel_data *cd = elem->channel_data; |
| gpr_slice key = construct_buffer(cd->key_prefix_len, *n); |
| gpr_slice value = construct_buffer(cd->value_prefix_len, *n); |
| |
| GPR_ASSERT(op->type == GRPC_SEND_METADATA); |
| GPR_ASSERT(op->dir == GRPC_CALL_DOWN); |
| GPR_ASSERT(op->flags == *n); |
| GPR_ASSERT(op->done_cb == do_nothing); |
| GPR_ASSERT(op->user_data == (void *)(gpr_uintptr)*n); |
| GPR_ASSERT(0 == gpr_slice_cmp(op->data.metadata->key->slice, key)); |
| GPR_ASSERT(0 == gpr_slice_cmp(op->data.metadata->value->slice, value)); |
| |
| ++*n; |
| |
| gpr_slice_unref(key); |
| gpr_slice_unref(value); |
| grpc_mdelem_unref(op->data.metadata); |
| } |
| |
| static void fail_channel_op(grpc_channel_element *elem, |
| grpc_channel_element *from_elem, |
| grpc_channel_op *op) { |
| abort(); |
| } |
| |
| static void init_call_elem(grpc_call_element *elem, |
| const void *transport_server_data) { |
| *(size_t *)elem->call_data = 0; |
| } |
| |
| static void destroy_call_elem(grpc_call_element *elem) {} |
| |
| static void init_channel_elem(grpc_channel_element *elem, |
| const grpc_channel_args *args, grpc_mdctx *mdctx, |
| int is_first, int is_last) {} |
| |
| static void destroy_channel_elem(grpc_channel_element *elem) {} |
| |
| static const grpc_channel_filter top_filter = { |
| fail_call_op, fail_channel_op, sizeof(size_t), |
| init_call_elem, destroy_call_elem, sizeof(channel_data), |
| init_channel_elem, destroy_channel_elem}; |
| |
| static const grpc_channel_filter bottom_filter = { |
| expect_call_op, fail_channel_op, sizeof(size_t), |
| init_call_elem, destroy_call_elem, sizeof(channel_data), |
| init_channel_elem, destroy_channel_elem}; |
| |
| static const grpc_channel_filter *filters[2] = {&top_filter, &bottom_filter}; |
| |
| /* run a test with differently sized keys, and values, some number of times. */ |
| static void test_case(size_t key_prefix_len, size_t value_prefix_len, |
| size_t num_calls) { |
| size_t i; |
| size_t got_calls; |
| grpc_metadata_buffer buffer; |
| grpc_channel_stack *stk; |
| grpc_call_stack *call; |
| grpc_mdctx *mdctx; |
| |
| gpr_log(GPR_INFO, "Test %d calls, {key,value}_prefix_len = {%d, %d}", |
| (int)num_calls, (int)key_prefix_len, (int)value_prefix_len); |
| |
| mdctx = grpc_mdctx_create(); |
| |
| grpc_metadata_buffer_init(&buffer); |
| |
| /* queue metadata elements */ |
| for (i = 0; i < num_calls; i++) { |
| grpc_call_op op; |
| gpr_slice key = construct_buffer(key_prefix_len, i); |
| gpr_slice value = construct_buffer(value_prefix_len, i); |
| |
| op.type = GRPC_SEND_METADATA; |
| op.dir = GRPC_CALL_DOWN; |
| op.flags = i; |
| op.data.metadata = grpc_mdelem_from_slices(mdctx, key, value); |
| op.done_cb = do_nothing; |
| op.user_data = (void *)(gpr_uintptr)i; |
| |
| grpc_metadata_buffer_queue(&buffer, &op); |
| } |
| |
| /* construct a test channel, call stack */ |
| stk = gpr_malloc(grpc_channel_stack_size(filters, 2)); |
| grpc_channel_stack_init(filters, 2, NULL, mdctx, stk); |
| |
| for (i = 0; i < 2; i++) { |
| channel_data *cd = |
| (channel_data *)grpc_channel_stack_element(stk, i)->channel_data; |
| cd->key_prefix_len = key_prefix_len; |
| cd->value_prefix_len = value_prefix_len; |
| } |
| |
| call = gpr_malloc(stk->call_stack_size); |
| grpc_call_stack_init(stk, NULL, call); |
| |
| /* flush out metadata, verifying each element (see expect_call_op) */ |
| grpc_metadata_buffer_flush(&buffer, grpc_call_stack_element(call, 0)); |
| |
| /* verify expect_call_op was called an appropriate number of times */ |
| got_calls = *(size_t *)grpc_call_stack_element(call, 1)->call_data; |
| GPR_ASSERT(num_calls == got_calls); |
| |
| /* clean up the things */ |
| grpc_call_stack_destroy(call); |
| gpr_free(call); |
| grpc_channel_stack_destroy(stk); |
| gpr_free(stk); |
| |
| grpc_metadata_buffer_destroy(&buffer, GRPC_OP_OK); |
| grpc_mdctx_orphan(mdctx); |
| } |
| |
| int main(int argc, char **argv) { |
| grpc_test_init(argc, argv); |
| test_case(0, 0, 0); |
| test_case(0, 0, 1); |
| test_case(0, 0, 2); |
| test_case(0, 0, 10000); |
| test_case(10, 10, 1); |
| test_case(10, 10, 2); |
| test_case(10, 10, 10000); |
| test_case(100, 100, 1); |
| test_case(100, 100, 2); |
| test_case(100, 100, 10000); |
| return 0; |
| } |