Ilya Lipnitskiy 59688a92b3 protobuf-c-rpc/protobuf-c-rpc.c: free the timer created by autoreconnect
in case the client has failed to connect and got destroyed (fixes #70)

protobuf-c-rpc/t/test-rpc.c: add a test case for #70
2013-11-18 22:22:46 -08:00

306 lines
9.4 KiB
C

#include <assert.h>
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
#include <signal.h>
#include <protobuf-c-rpc/protobuf-c-rpc.h>
#include "t/test.pb-c.h"
static void
message (const char *format, ...)
{
va_list args;
va_start (args, format);
vfprintf (stderr, format, args);
va_end (args);
fputc ('\n', stderr);
}
/* --- A local service --- */
static void
test__by_name (Foo__DirLookup_Service *service,
const Foo__Name *name,
Foo__LookupResult_Closure closure,
void *closure_data)
{
Foo__LookupResult result = FOO__LOOKUP_RESULT__INIT;
Foo__Person__PhoneNumber pn = FOO__PERSON__PHONE_NUMBER__INIT;
Foo__Person__PhoneNumber *pns[] = { &pn };
Foo__Person person = FOO__PERSON__INIT;
char *number = NULL;
Foo__Person__PhoneType type = FOO__PERSON__PHONE_TYPE__MOBILE;
char *email = NULL;
(void) service;
if (name->name == NULL)
{
closure (NULL, closure_data);
return;
}
else if (strcmp (name->name, "dave") == 0)
{
number = "555-1212";
type = FOO__PERSON__PHONE_TYPE__MOBILE;
}
else if (strcmp (name->name, "joe the plumber") == 0)
{
number = "976-PIPE";
type = FOO__PERSON__PHONE_TYPE__WORK;
email = "joe@the-plumber.com";
}
if (number != NULL)
{
pn.number = number;
pn.has_type = 1;
pn.type = type;
person.n_phone = 1;
person.phone = pns;
person.email = email;
person.name = name->name;
result.person = &person;
}
closure (&result, closure_data);
}
static Foo__DirLookup_Service the_dir_lookup_service =
FOO__DIR_LOOKUP__INIT(test__);
static void
test_dave_closure (const Foo__LookupResult *result,
void *closure_data)
{
assert (result);
assert (result->person != NULL);
assert (result->person->name != NULL);
assert (strcmp (result->person->name, "dave") == 0);
assert (result->person->n_phone == 1);
assert (strcmp (result->person->phone[0]->number, "555-1212") == 0);
assert (result->person->phone[0]->type == FOO__PERSON__PHONE_TYPE__MOBILE);
* (protobuf_c_boolean *) closure_data = 1;
}
static void
test_joe_the_plumber_closure (const Foo__LookupResult *result,
void *closure_data)
{
assert (result);
assert (result->person != NULL);
assert (result->person->name != NULL);
assert (strcmp (result->person->name, "joe the plumber") == 0);
assert (result->person->n_phone == 1);
assert (strcmp (result->person->phone[0]->number, "976-PIPE") == 0);
assert (result->person->phone[0]->type == FOO__PERSON__PHONE_TYPE__WORK);
* (protobuf_c_boolean *) closure_data = 1;
}
static void
test_not_found_closure (const Foo__LookupResult *result,
void *closure_data)
{
assert (result);
assert (result->person == NULL);
* (protobuf_c_boolean *) closure_data = 1;
}
static void
test_defunct_closure (const Foo__LookupResult *result,
void *closure_data)
{
assert (result == NULL);
* (protobuf_c_boolean *) closure_data = 1;
}
static void
test_service (ProtobufCService *service)
{
Foo__Name name = FOO__NAME__INIT;
protobuf_c_boolean is_done;
name.name = "dave";
is_done = 0;
foo__dir_lookup__by_name (service, &name, test_dave_closure, &is_done);
while (!is_done)
protobuf_c_dispatch_run (protobuf_c_dispatch_default ());
name.name = "joe the plumber";
is_done = 0;
foo__dir_lookup__by_name (service, &name, test_joe_the_plumber_closure, &is_done);
while (!is_done)
protobuf_c_dispatch_run (protobuf_c_dispatch_default ());
name.name = "asdfvcvzxsa";
is_done = 0;
foo__dir_lookup__by_name (service, &name, test_not_found_closure, &is_done);
while (!is_done)
protobuf_c_dispatch_run (protobuf_c_dispatch_default ());
name.name = NULL;
is_done = 0;
foo__dir_lookup__by_name (service, &name, test_defunct_closure, &is_done);
while (!is_done)
protobuf_c_dispatch_run (protobuf_c_dispatch_default ());
}
static void
test_defunct_client (ProtobufCService *service)
{
Foo__Name name = FOO__NAME__INIT;
protobuf_c_boolean is_done;
name.name = "dave";
is_done = 0;
foo__dir_lookup__by_name (service, &name, test_defunct_closure, &is_done);
while (!is_done)
protobuf_c_dispatch_run (protobuf_c_dispatch_default ());
name.name = "joe the plumber";
is_done = 0;
foo__dir_lookup__by_name (service, &name, test_defunct_closure, &is_done);
while (!is_done)
protobuf_c_dispatch_run (protobuf_c_dispatch_default ());
name.name = "asdfvcvzxsa";
is_done = 0;
foo__dir_lookup__by_name (service, &name, test_defunct_closure, &is_done);
while (!is_done)
protobuf_c_dispatch_run (protobuf_c_dispatch_default ());
}
/* --- main() --- */
static void
set_boolean_true (ProtobufCDispatch *dispatch,
void *func_data)
{
(void) dispatch;
* (protobuf_c_boolean *) func_data = 1;
}
static protobuf_c_boolean
pretend_we_are_in_another_thread (ProtobufC_RPC_Server *server,
ProtobufCDispatch *dispatch,
void *data)
{
return 0; /* indicate we are NOT in RPC thread */
}
static void test_client_create_destroy (void)
{
ProtobufCService *rpc_service =
protobuf_c_rpc_client_new(PROTOBUF_C_RPC_ADDRESS_LOCAL, "test.socket", NULL, NULL);
protobuf_c_dispatch_run(protobuf_c_dispatch_default());
rpc_service->destroy(rpc_service);
protobuf_c_dispatch_destroy_default();
}
int main()
{
protobuf_c_boolean is_done;
ProtobufCService *local_service = (ProtobufCService *) &the_dir_lookup_service;
ProtobufCService *remote_service;
ProtobufC_RPC_Client *client;
ProtobufC_RPC_Server *server;
signal (SIGPIPE, SIG_IGN);
message ("testing local service");
test_service (local_service);
message ("creating client");
/* Create a client with no server. Verify that
the client returns a failure immediately */
remote_service = protobuf_c_rpc_client_new (PROTOBUF_C_RPC_ADDRESS_LOCAL,
"test.socket",
&foo__dir_lookup__descriptor,
NULL);
assert (remote_service != NULL);
client = (ProtobufC_RPC_Client *) remote_service;
protobuf_c_rpc_client_set_autoreconnect_period (client, 10);
is_done = 0;
protobuf_c_dispatch_add_timer_millis (protobuf_c_dispatch_default (),
250, set_boolean_true, &is_done);
message ("verify client cannot connect");
while (!is_done)
{
protobuf_c_dispatch_run (protobuf_c_dispatch_default ());
assert (!protobuf_c_rpc_client_is_connected (client));
}
message ("testing unconnected client");
test_defunct_client (remote_service);
message ("creating server");
/* Create a server and wait for the client to connect. */
server = protobuf_c_rpc_server_new (PROTOBUF_C_RPC_ADDRESS_LOCAL,
"test.socket",
local_service,
NULL);
assert (server != NULL);
message ("waiting to connect");
is_done = 0;
/* technically, there's no way to know how long it'll take to connect
if the machine is heavily loaded. We give 250 millis,
which should be ample on an unloaded system. */
protobuf_c_dispatch_add_timer_millis (protobuf_c_dispatch_default (),
250, set_boolean_true, &is_done);
while (!is_done && !protobuf_c_rpc_client_is_connected (client))
protobuf_c_dispatch_run (protobuf_c_dispatch_default ());
if (!protobuf_c_rpc_client_is_connected (client))
assert(0); /* operation timed-out; could be high machine load */
/* wait for the timer to elapse, since that's laziest way to handle it. */
while (!is_done)
protobuf_c_dispatch_run (protobuf_c_dispatch_default ());
/* Test the client */
message ("testing client");
test_service (remote_service);
/* Destroy the server and ensure that a request is failed in
a timely fashion. */
message ("destroying server");
protobuf_c_rpc_server_destroy (server, 0);
server = NULL;
message ("test client has no data");
test_defunct_client (remote_service);
/* Create a server again and wait for the client to reconnect. */
message ("creating server again");
server = protobuf_c_rpc_server_new (PROTOBUF_C_RPC_ADDRESS_LOCAL,
"test.socket",
local_service,
NULL);
assert (server != NULL);
is_done = 0;
protobuf_c_dispatch_add_timer_millis (protobuf_c_dispatch_default (),
250, set_boolean_true, &is_done);
while (!is_done)
protobuf_c_dispatch_run (protobuf_c_dispatch_default ());
assert (protobuf_c_rpc_client_is_connected (client));
/* Test the client again, for kicks. */
message ("testing client again");
test_service (remote_service);
message ("testing client again (simulating threaded environment)");
protobuf_c_rpc_server_configure_threading (server, pretend_we_are_in_another_thread, NULL);
test_service (remote_service);
/* Destroy the client */
message ("destroying client");
protobuf_c_service_destroy (remote_service);
/* Destroy the server */
message ("destroying server");
protobuf_c_rpc_server_destroy (server, 0);
protobuf_c_dispatch_destroy_default ();
message ("creating and destroying a client");
test_client_create_destroy();
return 0;
}