mirror of https://github.com/grpc/grpc.git
parent
205aee1c82
commit
4e99edb642
10 changed files with 4 additions and 1203 deletions
@ -1,119 +0,0 @@ |
|||||||
/*
|
|
||||||
* |
|
||||||
* Copyright 2015, 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 "test/core/util/test_config.h" |
|
||||||
#include "src/core/iomgr/iomgr.h" |
|
||||||
#include "src/core/iomgr/endpoint_pair.h" |
|
||||||
#include "src/core/transport/chttp2_transport.h" |
|
||||||
#include <grpc/support/log.h> |
|
||||||
|
|
||||||
/* 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) { |
|
||||||
grpc_endpoint_pair p = grpc_iomgr_create_endpoint_pair(1); |
|
||||||
|
|
||||||
grpc_create_chttp2_transport(client_setup_transport, client_setup_arg, NULL, |
|
||||||
p.client, NULL, 0, mdctx, 1); |
|
||||||
grpc_create_chttp2_transport(server_setup_transport, server_setup_arg, NULL, |
|
||||||
p.server, 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; |
|
||||||
|
|
||||||
grpc_test_init(argc, argv); |
|
||||||
grpc_iomgr_init(); |
|
||||||
|
|
||||||
for (i = 0; i < sizeof(fixture_configs) / sizeof(*fixture_configs); i++) { |
|
||||||
grpc_transport_end2end_tests(&fixture_configs[i]); |
|
||||||
} |
|
||||||
|
|
||||||
grpc_iomgr_shutdown(); |
|
||||||
|
|
||||||
gpr_log(GPR_INFO, "exiting"); |
|
||||||
return 0; |
|
||||||
} |
|
@ -1,931 +0,0 @@ |
|||||||
/*
|
|
||||||
* |
|
||||||
* Copyright 2015, 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/support/string.h" |
|
||||||
#include "src/core/transport/transport.h" |
|
||||||
#include <grpc/support/alloc.h> |
|
||||||
#include <grpc/support/log.h> |
|
||||||
#include <grpc/support/thd.h> |
|
||||||
#include <grpc/support/useful.h> |
|
||||||
#include "test/core/util/test_config.h" |
|
||||||
|
|
||||||
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 GRPC_TIMEOUT_SECONDS_TO_DEADLINE(deadline_seconds); |
|
||||||
} |
|
||||||
|
|
||||||
/* 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(void) { |
|
||||||
gpr_mu_destroy(&g_mu); |
|
||||||
gpr_cv_destroy(&g_cv); |
|
||||||
} |
|
||||||
|
|
||||||
static void pending_ops_init(void) { |
|
||||||
gpr_mu_init(&g_mu); |
|
||||||
gpr_cv_init(&g_cv); |
|
||||||
atexit(pending_ops_cleanup); |
|
||||||
} |
|
||||||
|
|
||||||
static void use_pending_ops(void) { |
|
||||||
gpr_once_init(&g_pending_ops_init, pending_ops_init); |
|
||||||
} |
|
||||||
|
|
||||||
static void add_pending_op(void) { |
|
||||||
use_pending_ops(); |
|
||||||
gpr_mu_lock(&g_mu); |
|
||||||
g_pending_ops++; |
|
||||||
gpr_mu_unlock(&g_mu); |
|
||||||
} |
|
||||||
|
|
||||||
static void end_pending_op(void) { |
|
||||||
gpr_mu_lock(&g_mu); |
|
||||||
g_pending_ops--; |
|
||||||
gpr_cv_broadcast(&g_cv); |
|
||||||
gpr_mu_unlock(&g_mu); |
|
||||||
} |
|
||||||
|
|
||||||
static void wait_pending_ops(void) { |
|
||||||
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 void recv_goaway(void *user_data, grpc_transport *transport, |
|
||||||
grpc_status_code status, gpr_slice debug) { |
|
||||||
gpr_slice_unref(debug); |
|
||||||
} |
|
||||||
|
|
||||||
static grpc_transport_callbacks transport_callbacks = { |
|
||||||
alloc_recv_buffer, create_stream, recv_batch, recv_goaway, 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 = GRPC_TIMEOUT_SECONDS_TO_DEADLINE(100); |
|
||||||
|
|
||||||
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 *str; |
|
||||||
va_list args; |
|
||||||
va_start(args, fmt); |
|
||||||
|
|
||||||
gpr_asprintf(&str, fmt, args); |
|
||||||
gpr_log(GPR_INFO, "EXPECTED: %s", str); |
|
||||||
gpr_free(str); |
|
||||||
|
|
||||||
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); |
|
||||||
size_t 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) { |
|
||||||
unsigned 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_unref(g_metadata_context); |
|
||||||
|
|
||||||
gpr_log(GPR_INFO, "tests completed ok"); |
|
||||||
} |
|
@ -1,68 +0,0 @@ |
|||||||
/*
|
|
||||||
* |
|
||||||
* Copyright 2015, 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_CORE_TRANSPORT_TRANSPORT_END2END_TESTS_H |
|
||||||
#define GRPC_TEST_CORE_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_CORE_TRANSPORT_TRANSPORT_END2END_TESTS_H */ |
|
Loading…
Reference in new issue