/*
* Test interoperability of tcp_client/tcp_server
*/
#include "../tcp.h"
#include "test.h"
struct test_tcp_ctx {
service_t *service;
bool on_service_error;
bool on_client_connect;
bool on_service_client_error;
transport_t *service_client, *client;
char data;
bool eof;
};
static void on_service_error (service_t *service, const error_t *err, void *arg)
{
struct test_tcp_ctx *ctx = arg;
log_debug("%s", error_msg(err));
assert(service == ctx->service);
assert(!ctx->on_service_error);
ctx->on_service_error = true;
}
static void on_service_client_connect (transport_t *transport, void *arg)
{
struct test_tcp_ctx *ctx = arg;
log_debug("%p", transport);
assert(!ctx->service_client);
ctx->service_client = transport;
}
static void on_service_client_read (transport_t *transport, void *arg)
{
struct test_tcp_ctx *ctx = arg;
char byte;
int ret;
error_t err;
log_debug("%p", transport);
assert(ctx->service_client == transport);
if ((ret = transport_read(transport, &byte, sizeof(byte), &err)) == 1) {
assert(byte == ctx->data);
ctx->data = 0;
} else if (ERROR_CODE(&err) == ERR_EOF) {
assert(ctx->eof);
ctx->eof = false;
} else {
FATAL_ERROR(&err, "xxx");
}
}
static void on_service_client_error (transport_t *transport, const error_t *err, void *arg)
{
struct test_tcp_ctx *ctx = arg;
log_debug("%p: %s", transport, error_msg(err));
assert(ctx->service_client = transport);
ctx->on_service_client_error = true;
}
static void on_client_connect (transport_t *transport, void *arg)
{
struct test_tcp_ctx *ctx = arg;
log_debug("%p", transport);
assert(transport == ctx->client);
ctx->on_client_connect = true;
}
static void on_client_read (transport_t *transport, void *arg)
{
struct test_tcp_ctx *ctx = arg;
char byte;
int ret;
error_t err;
log_debug("%p", transport);
assert(transport == ctx->client);
if ((ret = transport_read(transport, &byte, sizeof(byte), &err)) == 1) {
assert(byte == ctx->data);
ctx->data = 0;
} else if (ERROR_CODE(&err) == ERR_EOF) {
assert(ctx->eof);
ctx->eof = false;
} else {
FATAL_ERROR(&err, "xxx");
}
}
static void on_client_error (transport_t *transport, const error_t *err, void *arg)
{
struct test_tcp_ctx *ctx = arg;
log_debug("%p", transport);
assert(transport == ctx->client);
// XXX
}
static struct service_callbacks test_tcp_service_callbacks = {
.on_error = on_service_error,
};
static struct transport_callbacks test_tcp_service_transport_callbacks = {
.on_connect = on_service_client_connect,
.on_read = on_service_client_read,
.on_write = NULL,
.on_error = on_service_client_error,
};
static struct transport_callbacks test_tcp_client_callbacks = {
.on_connect = on_client_connect,
.on_read = on_client_read,
.on_error = on_client_error,
};
void test_tcp (void)
{
struct test_tcp_ctx ctx; memset(&ctx, 0, sizeof(ctx));
struct service_info serv_info = {
&test_tcp_service_callbacks, &ctx,
{ &test_tcp_service_transport_callbacks, &ctx, TRANSPORT_READ }
};
struct transport_info client_info = {
&test_tcp_client_callbacks, &ctx, TRANSPORT_READ
};
struct event_base *ev_base = _test_ctx.ev_base;
error_t err;
// create the service
assert_success(tcp_listen(&serv_info, &ctx.service, "localhost", "3232", &err));
// connect client -> server
assert_success(tcp_connect(&client_info, &ctx.client, "localhost", "3232", &err));
event_base_loop(ev_base, EVLOOP_ONCE);
assert(ctx.service_client);
assert(ctx.on_client_connect);
// send client -> server
ctx.data = 'A';
assert(transport_write(ctx.client, &ctx.data, sizeof(ctx.data), &err) == 1);
event_base_loop(ev_base, EVLOOP_ONCE);
assert(!ctx.data);
// send server -> client
ctx.data = 'B';
assert(transport_write(ctx.service_client, &ctx.data, sizeof(ctx.data), &err) == 1);
event_base_loop(ev_base, EVLOOP_ONCE);
assert(!ctx.data);
// close client-side connection
ctx.eof = true;
transport_destroy(ctx.client);
event_base_loop(ev_base, EVLOOP_ONCE);
assert(ctx.on_service_client_error || !ctx.eof);
// cleanup
transport_destroy(ctx.service_client);
service_destroy(ctx.service);
}