mirror of
https://github.com/haiwen/libsearpc.git
synced 2025-04-27 18:25:06 +00:00
568 lines
16 KiB
C
568 lines
16 KiB
C
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include <glib.h>
|
|
#include <glib-object.h>
|
|
|
|
#define DFT_DOMAIN g_quark_from_string("TEST")
|
|
#include <searpc.h>
|
|
|
|
#include "searpc-server.h"
|
|
#include "searpc-client.h"
|
|
#include "searpc-named-pipe-transport.h"
|
|
#include "clar.h"
|
|
|
|
#if !defined(WIN32)
|
|
static const char *pipe_path = "/tmp/.searpc-test";
|
|
#else
|
|
static const char *pipe_path = "\\\\.\\pipe\\libsearpc-test";
|
|
#endif
|
|
|
|
/* sample class */
|
|
|
|
#define MAMAN_TYPE_BAR (maman_bar_get_type ())
|
|
#define MAMAN_BAR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MAMAN_TYPE_BAR, MamanBar))
|
|
#define MAMAN_IS_BAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MAMAN_TYPE_BAR))
|
|
#define MAMAN_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MAMAN_TYPE_BAR, MamanBarClass))
|
|
#define MAMAN_IS_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MAMAN_TYPE_BAR))
|
|
#define MAMAN_BAR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MAMAN_TYPE_BAR, MamanBarClass))
|
|
|
|
#define NAMED_PIPE_SERVER_THREAD_POOL_SIZE 50
|
|
|
|
typedef struct _MamanBar MamanBar;
|
|
typedef struct _MamanBarClass MamanBarClass;
|
|
|
|
struct _MamanBar
|
|
{
|
|
GObject parent_instance;
|
|
|
|
gchar *name;
|
|
int papa_number;
|
|
};
|
|
|
|
struct _MamanBarClass
|
|
{
|
|
GObjectClass parent_class;
|
|
};
|
|
|
|
G_DEFINE_TYPE (MamanBar, maman_bar, G_TYPE_OBJECT);
|
|
|
|
enum
|
|
{
|
|
PROP_0,
|
|
PROP_MAMAN_NAME,
|
|
PROP_PAPA_NUMBER
|
|
};
|
|
|
|
static void
|
|
maman_bar_set_property (GObject *object,
|
|
guint property_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
MamanBar *self = MAMAN_BAR (object);
|
|
|
|
switch (property_id) {
|
|
case PROP_MAMAN_NAME:
|
|
g_free (self->name);
|
|
self->name = g_value_dup_string (value);
|
|
break;
|
|
|
|
case PROP_PAPA_NUMBER:
|
|
self->papa_number = g_value_get_uchar (value);
|
|
break;
|
|
|
|
default:
|
|
/* We don't have any other property... */
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
maman_bar_get_property (GObject *object,
|
|
guint property_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
MamanBar *self = MAMAN_BAR (object);
|
|
|
|
switch (property_id) {
|
|
case PROP_MAMAN_NAME:
|
|
g_value_set_string (value, self->name);
|
|
break;
|
|
|
|
case PROP_PAPA_NUMBER:
|
|
g_value_set_uchar (value, self->papa_number);
|
|
break;
|
|
|
|
default:
|
|
/* We don't have any other property... */
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
maman_bar_finalize (GObject *gobject)
|
|
{
|
|
MamanBar *self = MAMAN_BAR (gobject);
|
|
|
|
g_free (self->name);
|
|
|
|
/* Chain up to the parent class */
|
|
G_OBJECT_CLASS (maman_bar_parent_class)->finalize (gobject);
|
|
}
|
|
|
|
static void
|
|
maman_bar_class_init (MamanBarClass *klass)
|
|
{
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
GParamSpec *pspec;
|
|
|
|
gobject_class->set_property = maman_bar_set_property;
|
|
gobject_class->get_property = maman_bar_get_property;
|
|
gobject_class->finalize = maman_bar_finalize;
|
|
|
|
pspec = g_param_spec_string ("name",
|
|
"Maman name",
|
|
"Set maman's name",
|
|
"no-name-set" /* default value */,
|
|
G_PARAM_READWRITE);
|
|
g_object_class_install_property (gobject_class,
|
|
PROP_MAMAN_NAME,
|
|
pspec);
|
|
|
|
pspec = g_param_spec_uchar ("papa-number",
|
|
"Number of current Papa",
|
|
"Set/Get papa's number",
|
|
0 /* minimum value */,
|
|
10 /* maximum value */,
|
|
2 /* default value */,
|
|
G_PARAM_READWRITE);
|
|
g_object_class_install_property (gobject_class,
|
|
PROP_PAPA_NUMBER,
|
|
pspec);
|
|
}
|
|
|
|
static void
|
|
maman_bar_init (MamanBar *self)
|
|
{
|
|
|
|
}
|
|
|
|
/* sample client */
|
|
static SearpcClient *client;
|
|
/* sample client with named pipe as transport */
|
|
static SearpcClient *client_with_pipe_transport;
|
|
|
|
char *
|
|
sample_send(void *arg, const gchar *fcall_str,
|
|
size_t fcall_len, size_t *ret_len)
|
|
{
|
|
cl_assert_ (strcmp(arg, "test") == 0, arg);
|
|
|
|
char *ret;
|
|
/* directly call in memory, instead of send via network */
|
|
gchar *temp = g_strdup(fcall_str);
|
|
ret = searpc_server_call_function ("test", temp, fcall_len, ret_len);
|
|
g_free (temp);
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
sample_async_send (void *arg, gchar *fcall_str,
|
|
size_t fcall_len, void *rpc_priv)
|
|
{
|
|
cl_assert (strcmp(arg, "test_async") == 0);
|
|
|
|
char *ret;
|
|
size_t ret_len;
|
|
gchar *temp = g_strdup(fcall_str);
|
|
|
|
ret = searpc_server_call_function ("test", temp, fcall_len, &ret_len);
|
|
g_free (temp);
|
|
|
|
searpc_client_generic_callback (ret, ret_len, rpc_priv, NULL);
|
|
|
|
g_free (ret);
|
|
return 0;
|
|
}
|
|
|
|
gchar *
|
|
get_substring (const gchar *orig_str, int sub_len, GError **error)
|
|
{
|
|
if (sub_len > strlen(orig_str)) {
|
|
g_set_error (error, DFT_DOMAIN, 100,
|
|
"Substring length larger than the length of origin string");
|
|
return NULL;
|
|
}
|
|
gchar *ret = g_malloc0(sub_len+1);
|
|
memcpy(ret, orig_str, sub_len);
|
|
ret[sub_len] = '\0';
|
|
return ret;
|
|
}
|
|
|
|
static SearpcClient *
|
|
do_create_client_with_pipe_transport(void)
|
|
{
|
|
SearpcNamedPipeClient *pipe_client = searpc_create_named_pipe_client(pipe_path);
|
|
cl_must_pass_(searpc_named_pipe_client_connect(pipe_client), "named pipe client failed to connect");
|
|
return searpc_client_with_named_pipe_transport(pipe_client, "test");
|
|
}
|
|
|
|
|
|
void
|
|
test_searpc__simple_call (void)
|
|
{
|
|
gchar* result;
|
|
GError *error = NULL;
|
|
|
|
result = searpc_client_call__string (client, "get_substring", &error,
|
|
2, "string", "hello", "int", 2);
|
|
cl_assert (error == NULL);
|
|
cl_assert (strcmp(result, "he") == 0);
|
|
g_free (result);
|
|
|
|
/* error should return */
|
|
result = NULL;
|
|
result = searpc_client_call__string (client, "get_substring", &error,
|
|
2, "string", "hello", "int", 10);
|
|
cl_assert (error->message);
|
|
g_free (result);
|
|
g_error_free(error);
|
|
}
|
|
|
|
void
|
|
test_searpc__invalid_call (void)
|
|
{
|
|
gchar* result;
|
|
GError *error = NULL;
|
|
|
|
result = searpc_client_call__string (client, "nonexist_func", &error,
|
|
2, "string", "hello", "int", 2);
|
|
cl_assert (error != NULL);
|
|
g_free (result);
|
|
g_error_free (error);
|
|
}
|
|
|
|
GObject *
|
|
get_maman_bar(const char *name, GError **error)
|
|
{
|
|
return g_object_new(MAMAN_TYPE_BAR, "name", name, NULL);
|
|
}
|
|
|
|
void
|
|
test_searpc__object_call (void)
|
|
{
|
|
GObject *result;
|
|
GError *error = NULL;
|
|
|
|
result = searpc_client_call__object (client, "get_maman_bar",
|
|
MAMAN_TYPE_BAR, &error,
|
|
1, "string", "kitty");
|
|
cl_assert (error == NULL);
|
|
g_object_unref (result);
|
|
}
|
|
|
|
GList *
|
|
get_maman_bar_list (const char *name, int num, GError **error)
|
|
{
|
|
char buf[256];
|
|
GList *ret = 0;
|
|
int i;
|
|
GObject *obj;
|
|
|
|
if (num < 0) {
|
|
g_set_error (error, DFT_DOMAIN, 100, "num must be positive.");
|
|
return NULL;
|
|
}
|
|
if (num > 1000) {
|
|
g_set_error (error, DFT_DOMAIN, 100, "num must no larger than 1000.");
|
|
return NULL;
|
|
}
|
|
|
|
for (i = 0; i < num; i++) {
|
|
sprintf (buf, "%s%d", name, i);
|
|
obj = g_object_new(MAMAN_TYPE_BAR, "name", buf, NULL);
|
|
ret = g_list_prepend (ret, obj);
|
|
}
|
|
ret = g_list_reverse (ret);
|
|
return ret;
|
|
}
|
|
|
|
|
|
void
|
|
test_searpc__objlist_call (void)
|
|
{
|
|
GList *result, *ptr;
|
|
GError *error = NULL;
|
|
|
|
result = searpc_client_call__objlist (client, "get_maman_bar_list",
|
|
MAMAN_TYPE_BAR, &error,
|
|
2, "string", "kitty", "int", 10);
|
|
cl_assert (error == NULL);
|
|
for (ptr = result; ptr; ptr = ptr->next)
|
|
g_object_unref (ptr->data);
|
|
g_list_free (result);
|
|
|
|
result = searpc_client_call__objlist (client, "get_maman_bar_list",
|
|
MAMAN_TYPE_BAR, &error,
|
|
2, "string", "kitty", "int", 0);
|
|
cl_assert (error == NULL);
|
|
for (ptr = result; ptr; ptr = ptr->next)
|
|
g_object_unref (ptr->data);
|
|
g_list_free (result);
|
|
}
|
|
|
|
json_t *
|
|
simple_json_rpc (const char *name, int num, GError **error)
|
|
{
|
|
json_t * ret = json_object();
|
|
json_object_set_new (ret, name, json_integer (num));
|
|
return ret;
|
|
}
|
|
|
|
void
|
|
test_searpc__json_return_type (void)
|
|
{
|
|
json_t *result;
|
|
GError *error = NULL;
|
|
|
|
result = searpc_client_call__json (client, "simple_json_rpc",
|
|
&error, 2,
|
|
"string", "year",
|
|
"int", 2016);
|
|
cl_assert (error == NULL);
|
|
cl_assert (json_integer_value(json_object_get(result, "year")) == 2016);
|
|
json_decref(result);
|
|
}
|
|
|
|
json_t *
|
|
count_json_kvs (const json_t *obj, GError **error)
|
|
{
|
|
int count = 0;
|
|
|
|
json_t *member;
|
|
member = json_object_iter ((json_t*)obj);
|
|
while (member != NULL) {
|
|
member = json_object_iter_next ((json_t*)obj, member);
|
|
count++;
|
|
}
|
|
|
|
json_t * ret = json_object();
|
|
json_object_set_new (ret, "number_of_kvs", json_integer (count));
|
|
return ret;
|
|
}
|
|
|
|
void
|
|
test_searpc__json_param_type (void)
|
|
{
|
|
json_t *result;
|
|
GError *error = NULL;
|
|
|
|
json_t *param = json_object();
|
|
json_object_set_new (param, "a", json_integer (1));
|
|
json_object_set_new (param, "b", json_integer (2));
|
|
|
|
result = searpc_client_call__json (client, "count_json_kvs",
|
|
&error, 1,
|
|
"json", param);
|
|
|
|
cl_assert_ (error == NULL, error ? error->message : "");
|
|
int count = json_integer_value(json_object_get((json_t*)result, "number_of_kvs"));
|
|
char *msg = json_dumps(result, JSON_INDENT(2));
|
|
cl_assert_(count == 2, msg);
|
|
free (msg);
|
|
json_decref(param);
|
|
json_decref(result);
|
|
}
|
|
|
|
|
|
void simple_callback (void *result, void *user_data, GError *error)
|
|
{
|
|
char *res = (char *)result;
|
|
|
|
cl_assert (strcmp(res, "he") == 0);
|
|
}
|
|
|
|
void simple_callback_error (void *result, void *user_data, GError *error)
|
|
{
|
|
cl_assert (result == NULL);
|
|
cl_assert (error != NULL);
|
|
g_error_free (error);
|
|
}
|
|
|
|
void
|
|
test_searpc__simple_call_async (void)
|
|
{
|
|
searpc_client_async_call__string (client, "get_substring",
|
|
simple_callback, NULL,
|
|
2, "string", "hello", "int", 2);
|
|
|
|
searpc_client_async_call__string (client, "get_substring",
|
|
simple_callback_error, NULL,
|
|
2, "string", "hello", "int", 10);
|
|
}
|
|
|
|
void async_callback_json (void *result, void *user_data, GError *error)
|
|
{
|
|
cl_assert(json_integer_value(json_object_get((json_t*)result, "hello")) == 10);
|
|
}
|
|
|
|
void
|
|
test_searpc__simple_call_async_json (void)
|
|
{
|
|
searpc_client_async_call__json (client, "simple_json_rpc",
|
|
async_callback_json, NULL,
|
|
2, "string", "hello", "int", 10);
|
|
}
|
|
|
|
void
|
|
test_searpc__pipe_simple_call (void)
|
|
{
|
|
gchar* result;
|
|
GError *error = NULL;
|
|
|
|
result = searpc_client_call__string (client_with_pipe_transport, "get_substring", &error,
|
|
2, "string", "hello", "int", 2);
|
|
cl_assert_ (error == NULL, error ? error->message : "");
|
|
cl_assert (strcmp(result, "he") == 0);
|
|
g_free (result);
|
|
|
|
/* error should return */
|
|
result = searpc_client_call__string (client_with_pipe_transport, "get_substring", &error,
|
|
2, "string", "hello", "int", 10);
|
|
cl_assert (error->message);
|
|
g_free (result);
|
|
}
|
|
|
|
void
|
|
test_searpc__pipe_large_request (void)
|
|
{
|
|
gchar* result;
|
|
GError *error = NULL;
|
|
|
|
// 10MB
|
|
int size = 10 * 1024 * 1024;
|
|
GString *large_string = g_string_sized_new(size);
|
|
while (large_string->len < size) {
|
|
g_string_append(large_string, "aaaa");
|
|
}
|
|
|
|
// Large request
|
|
result = searpc_client_call__string (client_with_pipe_transport, "get_substring", &error,
|
|
2, "string", large_string->str, "int", 2);
|
|
cl_assert_ (error == NULL, error ? error->message : "");
|
|
cl_assert_ (strcmp(result, "aa") == 0, result);
|
|
g_free (result);
|
|
|
|
// Large request & Large response
|
|
result = searpc_client_call__string (client_with_pipe_transport, "get_substring", &error,
|
|
2, "string", large_string->str, "int", size - 2);
|
|
cl_assert_ (error == NULL, error ? error->message : "");
|
|
// cl_assert (strcmp(result, "aa") == 0);
|
|
g_free (result);
|
|
|
|
g_string_free (large_string, TRUE);
|
|
}
|
|
|
|
static void * do_pipe_connect_and_request(void *arg)
|
|
{
|
|
SearpcClient *client = do_create_client_with_pipe_transport();
|
|
|
|
// 100KB
|
|
int size = 100 * 1024;
|
|
GString *large_string = g_string_sized_new(size);
|
|
while (large_string->len < size) {
|
|
g_string_append(large_string, "aaaa");
|
|
}
|
|
|
|
gchar* result;
|
|
GError *error = NULL;
|
|
result = searpc_client_call__string (client, "get_substring", &error,
|
|
2, "string", large_string->str, "int", 2);
|
|
cl_assert_ (error == NULL, error ? error->message : "");
|
|
cl_assert_ (strcmp(result, "aa") == 0, result);
|
|
g_free (result);
|
|
|
|
g_string_free (large_string, TRUE);
|
|
searpc_free_client_with_pipe_transport(client);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
// Simulate the situation that the server can handle multiple clients connecting
|
|
// at the same time.
|
|
void
|
|
test_searpc__pipe_concurrent_clients (void)
|
|
{
|
|
// M concurrent clients, and run the test for N times.
|
|
int m_clients = 5;
|
|
int n_times = 20;
|
|
|
|
int i;
|
|
for (i = 0; i < n_times; i++) {
|
|
g_usleep(100000);
|
|
pthread_t *threads = g_new0(pthread_t, m_clients);
|
|
|
|
int j;
|
|
for (j = 0; j < m_clients; j++) {
|
|
pthread_create(&threads[j], NULL, do_pipe_connect_and_request, NULL);
|
|
}
|
|
|
|
void *ret;
|
|
for (j = 0; j < m_clients; j++) {
|
|
pthread_join(threads[j], &ret);
|
|
}
|
|
g_free (threads);
|
|
}
|
|
}
|
|
|
|
|
|
#include "searpc-signature.h"
|
|
#include "searpc-marshal.h"
|
|
|
|
void
|
|
test_searpc__initialize (void)
|
|
{
|
|
searpc_server_init (register_marshals);
|
|
searpc_create_service ("test");
|
|
searpc_server_register_function ("test", get_substring, "get_substring",
|
|
searpc_signature_string__string_int());
|
|
searpc_server_register_function ("test", get_maman_bar, "get_maman_bar",
|
|
searpc_signature_object__string());
|
|
searpc_server_register_function ("test", get_maman_bar_list, "get_maman_bar_list",
|
|
searpc_signature_objlist__string_int());
|
|
searpc_server_register_function ("test", simple_json_rpc, "simple_json_rpc",
|
|
searpc_signature_json__string_int());
|
|
searpc_server_register_function ("test", count_json_kvs, "count_json_kvs",
|
|
searpc_signature_json__json());
|
|
|
|
/* sample client */
|
|
client = searpc_client_new();
|
|
client->send = sample_send;
|
|
client->arg = "test";
|
|
|
|
client->async_send = sample_async_send;
|
|
client->async_arg = "test_async";
|
|
|
|
SearpcNamedPipeServer *pipe_server = searpc_create_named_pipe_server_with_threadpool(pipe_path, NAMED_PIPE_SERVER_THREAD_POOL_SIZE);
|
|
cl_must_pass_(searpc_named_pipe_server_start(pipe_server), "named pipe server failed to start");
|
|
#if defined(WIN32)
|
|
// Wait for the server thread to start
|
|
Sleep(1000);
|
|
#endif
|
|
|
|
client_with_pipe_transport = do_create_client_with_pipe_transport();
|
|
}
|
|
|
|
void
|
|
test_searpc__cleanup (void)
|
|
{
|
|
searpc_free_client_with_pipe_transport(client_with_pipe_transport);
|
|
|
|
/* free memory for memory debug with valgrind */
|
|
searpc_server_final();
|
|
}
|