/*
 * libsyncml - A syncml protocol implementation
 * Copyright (C) 2009  Michael Bell <michael.bell@opensync.org>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
 *
 */

#include <config.h>

#include "tests/support.h"

#include <libsyncml/data_sync_api/sml_data_sync_defines.h>
#include <libsyncml/data_sync_api/sml_data_sync.h>

#include <string.h>
#include <unistd.h>

GMutex *runMutex = NULL;
int locks;

GList *clients;
typedef struct SmlDataSyncTestClient {
	GList *items;
	SmlDataSync *client;
	SmlDataSyncDataStore *data_store;
	gboolean is_binary;
} SmlDataSyncTestClient;

SmlDataSync *server;
SmlDataSyncDataStore *server_data_store;

const char *transport;


/* ************************************ */
/* *********** CALLBACKS ************** */
/* ************************************ */

SmlDataSyncTestClient*
getTestClientFromDataSyncSession(SmlDataSyncSession *session)
{
	/* This is a hack because of the behaviour of the OBEX server. */
	smlTrace(TRACE_INTERNAL, "%s: session(server->remote) ::= %p", __func__, sml_data_sync_session_get_remote(session));
	smlTrace(TRACE_INTERNAL, "%s: session(server->remote) ::= %s", __func__, sml_location_get_full_uri(sml_data_sync_session_get_remote(session)));
	smlTrace(TRACE_INTERNAL, "%s: session(server->local) ::= %p", __func__, sml_data_sync_session_get_local(session));
	smlTrace(TRACE_INTERNAL, "%s: session(server->local) ::= %s", __func__, sml_location_get_full_uri(sml_data_sync_session_get_local(session)));
	GList *list = clients;
	SmlDataSyncTestClient *client = NULL;
	for (;list; list = list->next) {
		client = list->data;
		smlTrace(TRACE_INTERNAL, "%s: client(local) ::= %p", __func__, sml_data_sync_get_local(client->client));
		smlTrace(TRACE_INTERNAL, "%s: client(local) ::= %s", __func__, sml_location_get_full_uri(sml_data_sync_get_local(client->client)));
		smlTrace(TRACE_INTERNAL, "%s: client(remote) ::= %p", __func__, sml_data_sync_get_remote(client->client));
		smlTrace(TRACE_INTERNAL, "%s: client(remote) ::= %s", __func__, sml_location_get_full_uri(sml_data_sync_get_remote(client->client)));
		if (sml_location_is_equal(sml_data_sync_get_local(client->client), sml_data_sync_session_get_remote(session)))
		{
			/* correct client */
			smlTrace(TRACE_INTERNAL, "%s - correct client", __func__);
			break;
		} else {
			/* wrong client */
			client = NULL;
		}
	}
	sml_fail_unless(client != NULL, "Cannot find correct client.");
	return client;
}

gboolean
sendAllChanges (SmlDataSyncSession *session,
                SmlDataSyncTestClient *client,
		GError **error)
{
	smlTrace(TRACE_ENTRY, "%s (%p, %p, %p)", __func__, session, client, error);

	gboolean is_server = client ? FALSE : TRUE;
	SmlDataSyncDataStoreSession *dss = NULL;
	if (!is_server)
		dss = sml_data_sync_session_get_data_store_session(session, client->data_store, error);
	else
		dss = sml_data_sync_session_get_data_store_session(session, server_data_store, error);
	sml_fail_unless(dss != NULL, "%s", GET_ERROR_MESSAGE((*error)));

	if (is_server)
	{
		/* determine the remote client */
		client = getTestClientFromDataSyncSession(session);
	}

	GList *list = client->items;
	size_t count = 0;
	for (;list;list = list->next)
	{
		count++;
		char *name = NULL;

		if (is_server)
			name = g_strdup_printf("%d from server", count);
		else
			name = g_strdup_printf("%d", count);

		SmlLocation *loc = sml_location_new();
		sml_location_set_uri(loc, name);
		smlSafeCFree(&name);

		SmlDataSyncChangeItem *item = sml_data_sync_change_item_new();
		sml_data_sync_change_item_set_action(item, SML_CHANGE_ADD);
		if (client->is_binary) {
			sml_fail_unless(sml_data_sync_change_item_set_binary_data(item, list->data, strlen(list->data), error), "%s", GET_ERROR_MESSAGE((*error)));
		} else {
			sml_fail_unless(sml_data_sync_change_item_set_data(item, list->data, 0, error), "%s", GET_ERROR_MESSAGE((*error)));
		}
		sml_fail_unless(sml_data_sync_change_item_set_location(item, loc, error), "%s", GET_ERROR_MESSAGE((*error)));
		g_object_unref(loc);
		loc = NULL;

		sml_fail_unless(sml_data_sync_data_store_session_add_change(dss, item, NULL, error), "%s", GET_ERROR_MESSAGE((*error)));
	}

	gboolean result = sml_data_sync_session_send_changes(session, error);
	smlTrace(TRACE_EXIT, "%s - %d", __func__);
	return result;
}

static void
recvEventCallback (SmlDataSyncSession *session,
                   SmlDataSync *dsObject,
                   SmlDataSyncEventType type,
                   void *userdata,
                   const GError *error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %i, %p, %p)", __func__, session, dsObject, type, userdata, error);
	GError *locerror = NULL;

	SmlDataSyncTestClient *client = NULL;
	if (userdata)
		client = userdata;
	
	switch (type) {
		case SML_DATA_SYNC_SESSION_EVENT_ERROR:
			if (client) {
				sml_fail_unless(FALSE, "OMA DS client failed: %s", error->message);
			} else {
				sml_fail_unless(FALSE, "OMA DS server failed: %s", error->message);
			}
			break;
		case SML_DATA_SYNC_SESSION_EVENT_CONNECT:
			/* g_message("Remote device was successfully connected."); */
			break;
		case SML_DATA_SYNC_SESSION_EVENT_DISCONNECT:
			/* g_message("Remote device was successfully disconnected."); */
			break;
		case SML_DATA_SYNC_SESSION_EVENT_FINISHED:
			/* g_message("SyncML session finished successfully."); */
			if (g_atomic_int_dec_and_test(&locks))
				g_mutex_unlock(runMutex);
			break;
		case SML_DATA_SYNC_SESSION_EVENT_GOT_ALL_ALERTS:
			/* g_message("All alerts of the remote device were received."); */
			if (client)
			{
				if (!sendAllChanges(session, client, &locerror))
					goto error;
			}
			break;
		case SML_DATA_SYNC_SESSION_EVENT_GOT_ALL_CHANGES:
			/* g_message("All changes of the remote device were received."); */
			if (!client)
			{
				if (!sendAllChanges(session, NULL, &locerror))
					goto error;
			}
			/* the map of the client is send automatically */
			break;
		case SML_DATA_SYNC_SESSION_EVENT_GOT_ALL_MAPPINGS:
			if (dsObject == server)
			{
				/* g_message("All mappings of the remote device were received."); */
			} else {
				g_error("Received a map but I'm a client!");
			}
			break;
		default:
			g_error("Unknown event(%d).\n", type);
			break;
	}
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return;

error:
	fprintf(stderr, "An error occured while handling events: %s\n", locerror->message);
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, locerror->message);
	g_error_free(locerror);
	exit(3);
}

static gboolean
serverRecvChangeCallback (SmlDataSyncDataStoreSession *session,
                    SmlDataSyncChangeItem *item,
                    void *userdata,
                    GError **error)
{
	smlTrace(TRACE_ENTRY, "%s: (%p, %p, %p, %p)", __func__, session, item, userdata, error);

	/* determine the relevant data sync objects */

	SmlDataSyncDataStore *datastore = sml_data_sync_data_store_session_get_data_store(session);
	SmlDataSyncSession *dss = sml_data_sync_data_store_session_get_data_sync_session(session);
	SmlDataSync *dsObject = sml_data_sync_session_get_data_sync(dss);

	/* check the source */

	if (dsObject != server || datastore != server_data_store) {
		sml_fail_unless(FALSE, "This is the server callback.");
	}

	/* determine client */

	SmlDataSyncTestClient *client = getTestClientFromDataSyncSession(dss);

	/* handle the item */
	GList *list = client->items;
	size_t count = 0;
	for (; list; list = list->next) {
		count++;

		/* test the uid */
		const gchar *uid = sml_location_get_full_uri(sml_data_sync_change_item_get_location(item));
		char *name = g_strdup_printf("%d", count);
		if (strcmp(name, uid)) {
			smlSafeCFree(&name);
			continue;
		}
		smlSafeCFree(&name);

		/* item with correct uid */
		const gchar *data = sml_data_sync_change_item_get_data(item);
		if (client->is_binary) {
			/* this is a base64 encoded item */
			if (!strcmp(list->data, data)) {
				sml_fail_unless(FALSE, "The binary item was not base64 encoded.", uid, data, list->data);
			}
			gchar *binary = NULL;
			gsize size = 0;
			sml_fail_unless(sml_data_sync_change_item_get_binary_data(item, &binary, &size, error), "%s", GET_ERROR_MESSAGE((*error)));
			sml_fail_unless(strlen(list->data) == size, "The length of the decoded item is wrong.");
			sml_fail_unless(strcmp(list->data, binary) == 0, "The data of the decoded item is wrong.");
			smlSafeCFree(&binary);
		} else {
			/* this is a plain text item */
			if (strcmp(list->data, data)) {
				sml_fail_unless(FALSE, "The data of the item %s was wrong(%s != %s).", uid, data, list->data);
			}
		}
	}

	smlTrace(TRACE_EXIT, "%s - TRUE", __func__);
	return TRUE;
}

static gboolean
clientRecvChangeCallback (SmlDataSyncDataStoreSession *session,
                          SmlDataSyncChangeItem *item,
                          void *userdata,
                          GError **error)
{
	smlTrace(TRACE_ENTRY, "%s: (%p, %p, %p, %p)", __func__, session, item, userdata, error);

	/* determine the relevant data sync objects */

	SmlDataSyncDataStore *datastore = sml_data_sync_data_store_session_get_data_store(session);
	SmlDataSyncSession *dss = sml_data_sync_data_store_session_get_data_sync_session(session);
	SmlDataSync *dsObject = sml_data_sync_session_get_data_sync(dss);

	/* check the source */

	if (dsObject == server || datastore == server_data_store) {
		sml_fail_unless(FALSE, "This is the client callback.");
	}

	/* document remote and local locations */

	smlTrace(TRACE_INTERNAL, "%s: server->remote ::= %p", __func__, sml_data_sync_get_remote(server));
	if (sml_data_sync_get_remote(server))
		smlTrace(TRACE_INTERNAL, "%s: server->remote ::= %s", __func__, sml_location_get_full_uri(sml_data_sync_get_remote(server)));
	smlTrace(TRACE_INTERNAL, "%s: server->local ::= %p", __func__, sml_data_sync_get_local(server));
	smlTrace(TRACE_INTERNAL, "%s: server->local ::= %s", __func__, sml_location_get_full_uri(sml_data_sync_get_local(server)));
	smlTrace(TRACE_INTERNAL, "%s: client session(local) ::= %p", __func__, sml_data_sync_session_get_local(dss));
	smlTrace(TRACE_INTERNAL, "%s: client session(local) ::= %s", __func__, sml_location_get_full_uri(sml_data_sync_session_get_local(dss)));
	smlTrace(TRACE_INTERNAL, "%s: client session(remote) ::= %p", __func__, sml_data_sync_session_get_remote(dss));
	smlTrace(TRACE_INTERNAL, "%s: client session(remote) ::= %s", __func__, sml_location_get_full_uri(sml_data_sync_session_get_remote(dss)));

	/* create a mapping per add item */

	SmlLocation* remote = sml_data_sync_change_item_get_location(item);
	SmlLocation* local = sml_location_clone(remote);
	gchar *local_uri = g_strdup_printf("%s => mapped ID", sml_location_get_uri(remote));
	sml_location_set_uri(local, local_uri);
	smlSafeCFree(&local_uri);
	SmlMapItem *map = sml_map_item_new();
	sml_fail_unless(sml_map_item_set_remote(map, remote, error), "%s", GET_ERROR_MESSAGE((*error)));
	sml_fail_unless(sml_map_item_set_local(map, local, error), "%s", GET_ERROR_MESSAGE((*error)));
	sml_fail_unless(sml_map_item_is_compliant(map, error), "%s", GET_ERROR_MESSAGE((*error)));
	g_object_unref(local);
	local = NULL;

	/* add the mapping to the session */

	sml_fail_unless(sml_data_sync_data_store_session_add_mapping(session, map, NULL, error), "%s", GET_ERROR_MESSAGE((*error)));

	smlTrace(TRACE_EXIT, "%s - TRUE", __func__);
	return TRUE;
}

static gboolean
serverRecvMappingCallback (SmlDataSyncDataStoreSession *session,
                           SmlMapItem *item,
                           void *userdata,
                           GError **error)
{
	smlTrace(TRACE_ENTRY, "%s: (%p, %p, %p, %p)", __func__, session, item, userdata, error);

	/* determine the relevant data sync objects */

	SmlDataSyncDataStore *datastore = sml_data_sync_data_store_session_get_data_store(session);
	SmlDataSyncSession *dss = sml_data_sync_data_store_session_get_data_sync_session(session);
	SmlDataSync *dsObject = sml_data_sync_session_get_data_sync(dss);

	/* check the source */

	if (dsObject != server || datastore != server_data_store) {
		sml_fail_unless(FALSE, "This is the server callback.");
	}

	/* determine client */

	SmlDataSyncTestClient *client = getTestClientFromDataSyncSession(dss);

	/* handle the item */
	GList *list = client->items;
	size_t count = 0;
	for (; list; list = list->next) {
		count++;

		/* test the uid */
		const gchar *uid = sml_map_item_get_remote_uri(item);
		char *name = g_strdup_printf("%d from server", count);
		if (strcmp(name, uid)) {
			smlSafeCFree(&name);
			continue;
		}
		smlSafeCFree(&name);

		/* item with correct uid */
		const gchar *mapped = sml_map_item_get_local_uri(item);
		name = g_strdup_printf("%d from server => mapped ID", count);
		if (strcmp(name, mapped)) {
			sml_fail_unless(FALSE, "The mapped ID of the item %s was wrong(%s != %s).", uid, name, mapped);
		}
		smlSafeCFree(&name);
	}

	smlTrace(TRACE_EXIT, "%s - TRUE", __func__);
	return TRUE;
}

//static SmlAlertType recvAlertTypeCallback(
//                        SmlDataSyncObject *dsObject,
//                        const char *source,
//                        SmlAlertType type,
//                        void *userdata,
//                        GError **error)
//{
//	smlTrace(TRACE_ENTRY, "%s - %s: %d", __func__, VA_STRING(source), type);
//	/* find the appropriate datasoure */
//	SmlDsToolLocationType *datastore = NULL;
//	GList *o = datastores;
//	while (o) {
//		datastore = o->data;
//		if (!strcmp(datastore->source, source)) {
//			/* abort the scan */
//			o = NULL;
//		} else {
//			datastore = NULL;
//		}
//		if (o) o = o->next;
//	}
//	if (!datastore) {
//		SML_SET_ERROR(error, SML_ERROR_GENERIC,
//			"Cannot found datastore %s.",
//			source);
//		goto error;
//	}
//	smlTrace(TRACE_INTERNAL, "%s: datastores scanned", __func__);
//
//	/* synchronize the alert type */
//	if (type == SML_ALERT_SLOW_SYNC)
//		datastore->slow = TRUE;
//	if (datastore->slow)
//		type = SML_ALERT_SLOW_SYNC;
//
//	smlTrace(TRACE_EXIT, "%s - slow == %d", __func__, datastore->slow);
//	return type;
//error:
//	smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, (*error)->message);
//	return SML_ALERT_UNKNOWN;
//}

void init_testbed(unsigned int peers, const char *transport_type, const char *port)
{
	/* general init */
	setup_testbed(NULL);
	GError *error = NULL;
	clients = NULL;
	server = NULL;
	server_data_store = NULL;

	/* initialize clients */
	unsigned int i = 0;
	for (;i < peers; i++)
	{
		SmlDataSyncTestClient *client = smlTryMalloc0(sizeof(SmlDataSyncTestClient), &error);
		if (!client)
			goto error;
		clients = g_list_append(clients, client);
	}

#ifdef ENABLE_OPENOBEX_TCP
	transport = "OBEX";
#endif
#ifdef ENABLE_HTTP
	transport = "HTTP";
#endif
#ifdef ENABLE_OPENOBEX_TCP
	if (!strcasecmp(transport_type, "OBEX")) {
		transport = "OBEX";
	}
#endif
#ifdef ENABLE_HTTP
	if (!strcasecmp(transport_type, "HTTP")) {
		transport = "HTTP";
	}
#endif
	if (!strcmp(transport, "HTTP"))
	{
		GList *list = clients;
		for (; list; list = list->next)
		{
			SmlDataSyncTestClient *client = list->data;
			client->client = sml_data_sync_new();
			if (!sml_data_sync_set_session_type(client->client, SML_SESSION_TYPE_CLIENT, &error))
				goto error;
			if (!sml_data_sync_set_transport_type(client->client, SML_TRANSPORT_HTTP_CLIENT, &error))
				goto error;
		}
		server = sml_data_sync_new();
		if (!sml_data_sync_set_session_type(server, SML_SESSION_TYPE_SERVER, &error))
			goto error;
		if (!sml_data_sync_set_transport_type(server, SML_TRANSPORT_HTTP_SERVER, &error))
			goto error;
	} else { /* OBEX */
		GList *list = clients;
		for (; list; list = list->next)
		{
			SmlDataSyncTestClient *client = list->data;
			client->client = sml_data_sync_new();
			if (!sml_data_sync_set_session_type(client->client, SML_SESSION_TYPE_CLIENT, &error))
				goto error;
			if (!sml_data_sync_set_transport_type(client->client, SML_TRANSPORT_OBEX_SERVER, &error))
				goto error;
		}
		server = sml_data_sync_new();
		if (!sml_data_sync_set_session_type(server, SML_SESSION_TYPE_SERVER, &error))
			goto error;
		if (!sml_data_sync_set_transport_type(server, SML_TRANSPORT_OBEX_CLIENT, &error))
			goto error;
	}

	/* client configuration */

	GList *list = clients;
	unsigned int count = 0;
	for (; list; list = list->next)
	{
		count++;
		SmlDataSyncTestClient *client = list->data;

		/* set identifier for test verification */
		gchar *name = g_strdup_printf("client %d", count);
		if (!sml_data_sync_set_option (client->client,
		                               SML_DATA_SYNC_CONFIG_IDENTIFIER,
		                               name,
		                               &error))
			goto error;
		if (!sml_data_sync_set_option (client->client,
		                               SML_DATA_SYNC_CONFIG_TARGET,
		                               "account",
		                               &error))
			goto error;
		smlSafeCFree(&name);
		

		/* default configuration of callbacks */
		sml_data_sync_register_event_callback(client->client, recvEventCallback, client);

		/* configure transport */
		if (!strcmp(transport, "HTTP")) {
			/* HTTP */
			char *url = g_strdup_printf("http://127.0.0.1:%s", port);
			if (!sml_data_sync_set_option(
					client->client,
					SML_TRANSPORT_CONFIG_URL,
					url, &error))
				goto error;
			smlSafeCFree(&url);
		} else {
			/* OBEX */
			if (!sml_data_sync_set_option(
					client->client,
					SML_DATA_SYNC_CONFIG_CONNECTION_TYPE, 
					SML_DATA_SYNC_CONFIG_CONNECTION_NET,
					&error))
				goto error;
			if (!sml_data_sync_set_option(
					client->client,
					SML_TRANSPORT_CONFIG_PORT, 
					port, &error))
				goto error;
		}
	}

	/* server configuration */
	
	/* default configuration of callbacks */
	sml_data_sync_register_event_callback(server, recvEventCallback, NULL);

	/* configure transport */
	if (strcmp(transport, "HTTP")) {
		/* OBEX */
		if (!sml_data_sync_set_option(
				server,
				SML_DATA_SYNC_CONFIG_CONNECTION_TYPE, 
				SML_DATA_SYNC_CONFIG_CONNECTION_NET,
				&error))
			goto error;
		if (!sml_data_sync_set_option(
				server,
				SML_TRANSPORT_CONFIG_URL, 
				"127.0.0.1", &error))
			goto error;
	}
	if (!sml_data_sync_set_option(
			server,
			SML_TRANSPORT_CONFIG_PORT, 
			port, &error))
		goto error;

	return;
error:
	sml_fail_unless(FALSE, "%s", error->message);
}

void test_ds_api_cleanup_server ()
{
	g_object_unref(server);
	server = NULL;
	g_object_unref(server_data_store);
	server_data_store = NULL;
}

void test_ds_api_cleanup_client ()
{
	while (clients)
	{
		SmlDataSyncTestClient *client = clients->data;
		clients = g_list_remove(clients, client);
		g_object_unref(client->client);
		g_object_unref(client->data_store);
		while(client->items)
		{
			gchar* item = client->items->data;
			client->items = g_list_remove(client->items, item);
			smlSafeCFree(&item);
		}
		smlSafeFree((gpointer *)&client);
	}
}

void run_testbed()
{
	GError *error = NULL;
	locks = 2*g_list_length(clients);

	runMutex = g_mutex_new();
	g_mutex_lock(runMutex);

	/* init the sync */
	if (!strcmp(transport, "HTTP"))
	{
		if (!sml_data_sync_initialize(server, &error))
			goto error;
		GList *list = clients;
		for (; list; list = list->next)
		{
			SmlDataSyncTestClient *client = list->data;
			if (!sml_data_sync_initialize(client->client, &error))
				goto error;
		}
		if (!sml_data_sync_run(server, &error))
			goto error;
		list = clients;
		for (; list; list = list->next)
		{
			SmlDataSyncTestClient *client = list->data;
			if (!sml_data_sync_run(client->client, &error))
				goto error;
			/* It is necessary to perform the first request alone
			 * because otherwise the test can fail on Solaris
			 * because Solaris uses an outdated libsoup 2.2.
			 */
			if (list == clients) {
				/* first client => wait for Soup Session init */
				usleep(5000);
			}
		}
	} else {
		/* OBEX */
		GList *list = clients;
		for (; list; list = list->next)
		{
			SmlDataSyncTestClient *client = list->data;
			if (!sml_data_sync_initialize(client->client, &error))
				goto error;
		}
		if (!sml_data_sync_initialize(server, &error))
			goto error;
		list = clients;
		for (; list; list = list->next)
		{
			SmlDataSyncTestClient *client = list->data;
			if (!sml_data_sync_run(client->client, &error))
				goto error;
		}
		/* The OBEX server needs some time to start. */
		if (g_getenv("SYNCML_TRACE"))
			usleep(2000);
		else
			usleep(5000);
		if (!sml_data_sync_run(server, &error))
			goto error;
	}

	g_mutex_lock(runMutex);
	g_mutex_unlock(runMutex);
	g_mutex_free(runMutex);
	runMutex = NULL;

	/* close the object */
	smlTrace(TRACE_INTERNAL, "%s: shutting down all SmlDataSync objects", __func__);
	if (sml_data_sync_get_transport_type(server) == SML_TRANSPORT_OBEX_CLIENT) {
		test_ds_api_cleanup_server();
		test_ds_api_cleanup_client();
	} else {
		test_ds_api_cleanup_client();
		test_ds_api_cleanup_server();
	}

	return;
error:
	if (runMutex) {
		g_mutex_trylock(runMutex);
		g_mutex_unlock(runMutex);
		g_mutex_free(runMutex);
		runMutex = NULL;
	}
	sml_fail_unless(FALSE, "%s", error->message);
}

START_TEST (ds_api_single_client_single_text_vcard_21)
{
	GError *error = NULL;
	init_testbed(1, "HTTP", "17001");

	/* register datastore
	 * the source must be identical because this is http
	 */

	SmlDataSyncTestClient *client = clients->data;
	client->data_store = sml_data_sync_data_store_new();
	sml_data_sync_data_store_set_content_type (client->data_store, "text/x-vcard");
	sml_data_sync_data_store_set_local_uri (client->data_store, "contacts");
	sml_data_sync_data_store_register_change_callback(client->data_store, clientRecvChangeCallback, client);
	if (!sml_data_sync_add_data_store(client->client, client->data_store, &error))
		goto error;

	server_data_store = sml_data_sync_data_store_new();
	sml_data_sync_data_store_set_content_type (server_data_store, "text/x-vcard");
	sml_data_sync_data_store_set_local_uri (server_data_store, "contacts");
	sml_data_sync_data_store_register_change_callback(server_data_store, serverRecvChangeCallback, NULL);
	sml_data_sync_data_store_register_mapping_callback(server_data_store, serverRecvMappingCallback, NULL);
	if (!sml_data_sync_add_data_store(server, server_data_store, &error))
		goto error;

	/* configure test data */
	client->items = g_list_append(client->items, g_strdup("blabla"));

	run_testbed();

	return;
error:
	sml_fail_unless(FALSE, "%s", error->message);
}
END_TEST

START_TEST (ds_api_single_client_single_image_jpeg)
{
	GError *error = NULL;
	init_testbed(1, "OBEX", "17002");

	/* register datastore
	 * the source must be identical if this is http
	 */

	SmlDataSyncTestClient *client = clients->data;
	client->data_store = sml_data_sync_data_store_new();
	sml_data_sync_data_store_set_content_type (client->data_store, "image/jpeg");
	sml_data_sync_data_store_set_local_uri (client->data_store, "dcim");
	client->is_binary = TRUE;
	sml_data_sync_data_store_register_change_callback(client->data_store, clientRecvChangeCallback, NULL);
	if (!sml_data_sync_add_data_store(client->client, client->data_store, &error))
		goto error;

	server_data_store = sml_data_sync_data_store_new();
	sml_data_sync_data_store_set_content_type (server_data_store, "image/jpeg");
	if (!strcmp(transport, "OBEX"))
		sml_data_sync_data_store_set_local_uri (server_data_store, "photos");
	else
		sml_data_sync_data_store_set_local_uri (server_data_store, "dcim");
	sml_data_sync_data_store_register_change_callback(server_data_store, serverRecvChangeCallback, NULL);
	sml_data_sync_data_store_register_mapping_callback(server_data_store, serverRecvMappingCallback, NULL);
	if (!sml_data_sync_add_data_store(server, server_data_store, &error))
		goto error;

	/* configure test data */
	client->items = g_list_append(client->items, g_strdup("this is an image"));

	run_testbed();

	return;
error:
	sml_fail_unless(FALSE, "%s", error->message);
}
END_TEST

START_TEST (ds_api_single_client_single_unknown_ct)
{
	GError *error = NULL;
	init_testbed(1, "HTTP", "17003");

	/* register datastore
	 * the source must be identical because this is http
	 */

	SmlDataSyncTestClient *client = clients->data;
	client->data_store = sml_data_sync_data_store_new();
	sml_data_sync_data_store_set_content_type (client->data_store, "unknown/content-type");
	sml_data_sync_data_store_set_local_uri (client->data_store, "data");
	sml_data_sync_data_store_register_change_callback(client->data_store, clientRecvChangeCallback, NULL);
	if (!sml_data_sync_add_data_store(client->client, client->data_store, &error))
		goto error;

	server_data_store = sml_data_sync_data_store_new();
	sml_data_sync_data_store_set_content_type (server_data_store, "unknown/content-type");
	sml_data_sync_data_store_set_local_uri (server_data_store, "data");
	sml_data_sync_data_store_register_change_callback(server_data_store, serverRecvChangeCallback, NULL);
	sml_data_sync_data_store_register_mapping_callback(server_data_store, serverRecvMappingCallback, NULL);
	if (!sml_data_sync_add_data_store(server, server_data_store, &error))
		goto error;

	/* configure test data */
	client->items = g_list_append(client->items, g_strdup("this is some data"));

	run_testbed();

	return;
error:
	sml_fail_unless(FALSE, "%s", error->message);
}
END_TEST

START_TEST (ds_api_single_client_multi_text_vcard_21)
{
	GError *error = NULL;
	init_testbed(1, "HTTP", "17004");

	/* register datastore
	 * the source must be identical because this is http
	 */

	SmlDataSyncTestClient *client = clients->data;
	client->data_store = sml_data_sync_data_store_new();
	sml_data_sync_data_store_set_content_type (client->data_store, "text/x-vcard");
	sml_data_sync_data_store_set_local_uri (client->data_store, "contacts");
	sml_data_sync_data_store_register_change_callback(client->data_store, clientRecvChangeCallback, NULL);
	if (!sml_data_sync_add_data_store(client->client, client->data_store, &error))
		goto error;

	server_data_store = sml_data_sync_data_store_new();
	sml_data_sync_data_store_set_content_type (server_data_store, "text/x-vcard");
	sml_data_sync_data_store_set_local_uri (server_data_store, "contacts");
	sml_data_sync_data_store_register_change_callback(server_data_store, serverRecvChangeCallback, NULL);
	sml_data_sync_data_store_register_mapping_callback(server_data_store, serverRecvMappingCallback, NULL);
	if (!sml_data_sync_add_data_store(server, server_data_store, &error))
		goto error;

	/* configure test data */
	int max_items = 1000;
	int i;
	for(i = 0; i < max_items; i++) {
		client->items = g_list_append(client->items, g_strdup_printf("client 1 data %d", (i+1)));
	}

	run_testbed();

	return;
error:
	sml_fail_unless(FALSE, "%s", error->message);
}
END_TEST

START_TEST (ds_api_multi_client_single_text_vcard_21)
{
	GError *error = NULL;
	init_testbed(5, "HTTP", "17005");

	/* register datastore
	 * the source must be identical because this is http
	 */

	SmlDataSyncTestClient *client = NULL;
	GList *list = clients;
	unsigned int count = 0;
	for (;list; list = list->next)
	{
		count ++;
		client = list->data;
		client->data_store = sml_data_sync_data_store_new();
		sml_data_sync_data_store_set_content_type (client->data_store, "text/x-vcard");
		sml_data_sync_data_store_set_local_uri (client->data_store, "contacts");
		sml_data_sync_data_store_register_change_callback(client->data_store, clientRecvChangeCallback, client);
		if (!sml_data_sync_add_data_store(client->client, client->data_store, &error))
			goto error;

		/* configure test data */
		client->items = g_list_append(client->items, g_strdup_printf("client %d data 1", count));
	}

	server_data_store = sml_data_sync_data_store_new();
	sml_data_sync_data_store_set_content_type (server_data_store, "text/x-vcard");
	sml_data_sync_data_store_set_local_uri (server_data_store, "contacts");
	sml_data_sync_data_store_register_change_callback(server_data_store, serverRecvChangeCallback, NULL);
	sml_data_sync_data_store_register_mapping_callback(server_data_store, serverRecvMappingCallback, NULL);
	if (!sml_data_sync_add_data_store(server, server_data_store, &error))
		goto error;

	run_testbed();

	return;
error:
	sml_fail_unless(FALSE, "%s", error->message);
}
END_TEST

START_TEST (ds_api_multi_client_multi_text_vcard_21)
{
	GError *error = NULL;
	init_testbed(10, "HTTP", "17006");

	/* register datastore
	 * the source must be identical because this is http
	 */

	SmlDataSyncTestClient *client = NULL;
	GList *list = clients;
	unsigned int count = 0;
	for (;list; list = list->next)
	{
		count ++;
		client = list->data;
		client->data_store = sml_data_sync_data_store_new();
		sml_data_sync_data_store_set_content_type (client->data_store, "text/x-vcard");
		sml_data_sync_data_store_set_local_uri (client->data_store, "contacts");
		sml_data_sync_data_store_register_change_callback(client->data_store, clientRecvChangeCallback, client);
		if (!sml_data_sync_add_data_store(client->client, client->data_store, &error))
			goto error;

		/* configure test data */
		int max_items = 100;
		int i;
		for(i = 0; i < max_items; i++) {
			client->items = g_list_append(client->items, g_strdup_printf("client %d data %d", count, (i+1)));
		}
	}

	server_data_store = sml_data_sync_data_store_new();
	sml_data_sync_data_store_set_content_type (server_data_store, "text/x-vcard");
	sml_data_sync_data_store_set_local_uri (server_data_store, "contacts");
	sml_data_sync_data_store_register_change_callback(server_data_store, serverRecvChangeCallback, NULL);
	sml_data_sync_data_store_register_mapping_callback(server_data_store, serverRecvMappingCallback, NULL);
	if (!sml_data_sync_add_data_store(server, server_data_store, &error))
		goto error;

	run_testbed();

	return;
error:
	sml_fail_unless(FALSE, "%s", error->message);
}
END_TEST

void
test_ds_api_max_msg_changes (const char *port,
                             const char *max,
                             gsize max_items)
{
	GError *error = NULL;
	init_testbed(1, "HTTP", port);
	if (!sml_data_sync_set_option(server, SML_DATA_SYNC_CONFIG_MAX_MSG_CHANGES, max, &error))
		goto error;

	/* register datastore
	 * the source must be identical because this is http
	 */

	SmlDataSyncTestClient *client = clients->data;
	client->data_store = sml_data_sync_data_store_new();
	sml_data_sync_data_store_set_content_type (client->data_store, "text/x-vcard");
	sml_data_sync_data_store_set_local_uri (client->data_store, "contacts");
	sml_data_sync_data_store_register_change_callback(client->data_store, clientRecvChangeCallback, NULL);
	if (!sml_data_sync_add_data_store(client->client, client->data_store, &error))
		goto error;

	server_data_store = sml_data_sync_data_store_new();
	sml_data_sync_data_store_set_content_type (server_data_store, "text/x-vcard");
	sml_data_sync_data_store_set_local_uri (server_data_store, "contacts");
	sml_data_sync_data_store_register_change_callback(server_data_store, serverRecvChangeCallback, NULL);
	sml_data_sync_data_store_register_mapping_callback(server_data_store, serverRecvMappingCallback, NULL);
	if (!sml_data_sync_add_data_store(server, server_data_store, &error))
		goto error;

	/* configure test data */
	int i;
	for(i = 0; i < max_items; i++) {
		client->items = g_list_append(client->items, g_strdup_printf("client 1 data %d", (i+1)));
	}

	run_testbed();

	return;
error:
	sml_fail_unless(FALSE, "%s", error->message);
}

START_TEST (ds_api_max_msg_changes_empty)
{
	test_ds_api_max_msg_changes("17007", "0", 1000);
}
END_TEST

START_TEST (ds_api_max_msg_changes_single)
{
	test_ds_api_max_msg_changes("17008", "1", 100);
}
END_TEST

START_TEST (ds_api_max_msg_changes_multi)
{
	test_ds_api_max_msg_changes("17009", "5", 500);
}
END_TEST

START_TEST (ds_api_max_msg_changes_multi_plus_one)
{
	test_ds_api_max_msg_changes("17010", "5", 501);
}
END_TEST

@SML_TESTCASE_CODE@

