/*
 * libsyncml - A syncml protocol implementation
 * Copyright (C) 2005  Armin Bauer <armin.bauer@opensync.org>
 * Copyright (C) 2008  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 "tests/support.h"

#include <libsyncml/objects/sml_ds_server.h>
#include <libsyncml/objects/sml_auth.h>

#include <libsyncml/objects/sml_devinf_obj.h>

#define NUM_SESSIONS 30

typedef struct managerTracker {
	SmlManager *manager;
	SmlSession *session;
	SmlDsSession *dssessions[NUM_SESSIONS];
	SmlDsServer *ds[3];
} managerTracker;

unsigned int init_alerts_received;
unsigned int alerts_received;
unsigned int syncs_received;
unsigned int sans_received;
unsigned int sync_ends_received;
unsigned int changes_received;
unsigned int replies_received;
unsigned int adds_received;
unsigned int add_replies_received;
unsigned int deletes_received;
unsigned int delete_replies_received;
unsigned int modifies_received;
unsigned int modify_replies_received;
unsigned int session_ends;

unsigned int transport_errors;
unsigned int num_sessions;
unsigned int num_finals;
unsigned int num_end;
int num_disconnects;
unsigned int session_errors;

/* This is a sync test only and not a test of large object support.
 * Therefore all commands must be put into one message.
 * This means one message per package.
 */
#define TEST_DEFAULT_MAX_MSG_SIZE    1024000
#define TEST_DEFAULT_MAX_OBJ_SIZE    1024000
unsigned int num_changes;
SmlError *eventError;

void reset_testbed()
{
	init_alerts_received = 0;
	alerts_received = 0;
	syncs_received = 0;
	sans_received = 0;
	sync_ends_received = 0;
	changes_received = 0;
	replies_received = 0;
	adds_received = 0;
	add_replies_received = 0;
	deletes_received = 0;
	delete_replies_received = 0;
	modifies_received = 0;
	modify_replies_received = 0;
	session_ends = 0;

	transport_errors = 0;
	num_sessions = 0;
	num_finals = 0;
	num_end = 0;
	num_disconnects = 0;
	session_errors = 0;

	num_changes = 0;
	eventError = NULL;

	setup_testbed(NULL);
}

static void _manager_event(SmlManager *manager, SmlManagerEventType type, SmlSession *session, SmlError *error, void *userdata)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %i, %p, %p, %p)", __func__, manager, type, session, error, userdata);
	managerTracker *tracker = userdata;
	smlAssert(manager);
	smlAssert(userdata);
	
	switch (type) {
		case SML_MANAGER_SESSION_ESTABLISHED:
		case SML_MANAGER_SESSION_FLUSH:
		case SML_MANAGER_CONNECT_DONE:
			break;
		case SML_MANAGER_DISCONNECT_DONE:
			g_atomic_int_inc(&num_disconnects);
			break;
		case SML_MANAGER_TRANSPORT_ERROR:
			smlTrace(TRACE_INTERNAL, "%s - error: %s", __func__, smlErrorPrint(&error));
			eventError = error;
			smlErrorRef(&eventError);
			transport_errors++;
			break;
		case SML_MANAGER_SESSION_NEW:
			smlAssert(session);
			tracker->session = session;
			num_sessions++;
			smlSessionRef(session);
			break;
		case SML_MANAGER_SESSION_FINAL:
			num_finals++;
			break;
		case SML_MANAGER_SESSION_END:
			num_end++;
			break;
		case SML_MANAGER_SESSION_ERROR:
		case SML_MANAGER_SESSION_WARNING:
			smlTrace(TRACE_INTERNAL, "%s - error: %s", __func__, smlErrorPrint(&error));
			if (eventError)
				smlErrorDeref(&eventError);
			eventError = error;
			smlErrorRef(&eventError);
			session_errors++;
			break;
		default:
			sml_fail_unless(FALSE, "An unexpected manager event %d was received.", type);
			break;
	}
	
	smlTrace(TRACE_EXIT, "%s", __func__);
}

static void _recv_init_alert(SmlDsSession *dsession, void *userdata)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, dsession, userdata);
	managerTracker *tracker = userdata;
	
	if (tracker->ds[0] == smlDsSessionGetServer(dsession))
		tracker->dssessions[0] = dsession;
	else if (tracker->ds[1] == smlDsSessionGetServer(dsession))
		tracker->dssessions[1] = dsession;
	else if (tracker->ds[2] == smlDsSessionGetServer(dsession))
		tracker->dssessions[2] = dsession;
	
	smlDsSessionRef(dsession);
	
	init_alerts_received++;
	
	smlTrace(TRACE_EXIT, "%s", __func__);
}

static SmlBool _recv_alert(SmlDsSession *dsession, SmlAlertType type, const char *last, const char *next, void *userdata)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %i, %s, %s, %p)", __func__, dsession, type, VA_STRING(last), VA_STRING(next), userdata);
	
	sml_fail_unless(last != NULL, NULL);
	sml_fail_unless(next != NULL, NULL);
	
	alerts_received++;
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;
}

static void _alert_reply(SmlSession *dsession, SmlStatus *status, void *userdata)
{
	smlTrace(TRACE_ENTRY, "%s(%p)", __func__, dsession);
	
	sml_fail_unless(status != NULL, NULL);
	
	if (GPOINTER_TO_INT(userdata) == 1) {
		sml_fail_unless(smlStatusGetClass(status) == SML_ERRORCLASS_SUCCESS, NULL);
	} else {
		sml_fail_unless(smlStatusGetClass(status) == SML_ERRORCLASS_FATAL, NULL);
	}
	
	replies_received++;
	
	smlTrace(TRACE_EXIT, "%s", __func__);
}

static void _sync_reply(SmlSession *dsession, SmlStatus *status, void *userdata)
{
	smlTrace(TRACE_ENTRY, "%s(%p)", __func__, dsession);
	
	sml_fail_unless(status != NULL, NULL);
	
	if (GPOINTER_TO_INT(userdata) == 1) {
		sml_fail_unless(smlStatusGetClass(status) == SML_ERRORCLASS_SUCCESS, NULL);
	} else {
		sml_fail_unless(smlStatusGetClass(status) == SML_ERRORCLASS_FATAL, NULL);
	}
	
	replies_received++;
	
	smlTrace(TRACE_EXIT, "%s", __func__);
}

static void _add_reply(SmlDsSession *session, SmlStatus *status, const char *newuid, void *userdata)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %s, %p)", __func__, session, status, VA_STRING(newuid), userdata);
	
	sml_fail_unless(status != NULL, NULL);
	sml_fail_unless(smlStatusGetClass(status) == SML_ERRORCLASS_SUCCESS, NULL);
	sml_fail_unless(GPOINTER_TO_INT(userdata) == 1 || GPOINTER_TO_INT(userdata) == 2, NULL);

	if (GPOINTER_TO_INT(userdata) == 1) {
		sml_fail_unless(!strcmp(newuid, "newuid"), NULL);
	} else {
		sml_fail_unless(newuid == NULL, NULL);
	}
	
	add_replies_received++;

	smlTrace(TRACE_EXIT, "%s", __func__);
}

static void _modify_reply(SmlDsSession *session, SmlStatus *status, const char *newuid, void *userdata)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %s, %p)", __func__, session, status, VA_STRING(newuid), userdata);
	
	sml_fail_unless(status != NULL, NULL);
	sml_fail_unless(smlStatusGetClass(status) == SML_ERRORCLASS_SUCCESS, NULL);
	sml_fail_unless(GPOINTER_TO_INT(userdata) == 1, NULL);

	sml_fail_unless(newuid == NULL, NULL);
	
	modify_replies_received++;

	smlTrace(TRACE_EXIT, "%s", __func__);
}

static void _delete_reply(SmlDsSession *session, SmlStatus *status, const char *newuid, void *userdata)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %s, %p)", __func__, session, status, VA_STRING(newuid), userdata);
	
	sml_fail_unless(status != NULL, NULL);
	sml_fail_unless(smlStatusGetClass(status) == SML_ERRORCLASS_SUCCESS, NULL);
	sml_fail_unless(GPOINTER_TO_INT(userdata) == 1, NULL);

	sml_fail_unless(newuid == NULL, NULL);
	
	delete_replies_received++;

	smlTrace(TRACE_EXIT, "%s", __func__);
}

static void _map_reply(SmlSession *dsession, SmlStatus *status, void *userdata)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, dsession, userdata);
	
	sml_fail_unless(status != NULL, NULL);
	sml_fail_unless(smlStatusGetClass(status) == SML_ERRORCLASS_SUCCESS, NULL);
	
	replies_received++;
	
	smlTrace(TRACE_EXIT, "%s", __func__);
}

static SmlErrorType _recv_san(SmlDsServer *server, SmlSession *session, SmlAlertType type, void *userdata)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p, %p)", __func__, server, session, type, userdata);
	sml_fail_unless(GPOINTER_TO_INT(userdata) == 1, NULL);
	
	sml_fail_unless(type == SML_ALERT_TWO_WAY_BY_SERVER, NULL);
	sans_received++;
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return SML_NO_ERROR;
}

static void _recv_sync(SmlDsSession *dsession, unsigned int numChanges, void *userdata)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %d, %p)", __func__, dsession, numChanges, userdata);
	
	sml_fail_unless(numChanges == num_changes, NULL);
	syncs_received++;
		
	smlTrace(TRACE_EXIT, "%s", __func__);
}

static SmlBool _recv_changes(SmlDsSession *dsession, SmlChangeType type, const char *uid, char *data, unsigned int size, const char *contenttype, void *userdata, SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %i, %s, %s, %i, %s, %p, %p)", __func__, dsession, type, VA_STRING(uid), VA_STRING(data), size, VA_STRING(contenttype), userdata, error);

	if (type == SML_CHANGE_ADD) {
		adds_received++;
		sml_fail_unless(!strcmp(uid, "uid"), NULL);
		sml_fail_unless(!strncmp(data, "data", size), NULL);
		sml_fail_unless(size == 4, NULL);
	} else if (type == SML_CHANGE_REPLACE) {
		modifies_received++;
		sml_fail_unless(!strcmp(uid, "uid"), NULL);
		sml_fail_unless(!strncmp(data, "newdata", size), NULL);
		sml_fail_unless(size == 7, NULL);
	} else if (type == SML_CHANGE_DELETE) {
		deletes_received++;
		sml_fail_unless(!strcmp(uid, "uid"), NULL);
		sml_fail_unless(!data, NULL);
		sml_fail_unless(size == 0, NULL);
	} else {
		sml_fail_unless(FALSE, "The received SyncML change type %d is unsupported.", type);
	}

	if (data)
		smlSafeCFree(&data);
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;
}

START_TEST (sync_multi_start)
{
	reset_testbed();
	
	SmlError *error = NULL;
	SmlTransport *server = smlTransportNew(SML_TRANSPORT_HTTP_SERVER, &error);
	SmlTransport *client = smlTransportNew(SML_TRANSPORT_HTTP_CLIENT, &error);
	
	sml_fail_unless(smlTransportSetConfigOption(client, "URL", "http://127.0.0.1:12012", &error), NULL);

	sml_fail_unless(smlTransportSetConfigOption(server, "PORT", "12012", &error), NULL);
	
	managerTracker *clienttracker = g_malloc0(sizeof(managerTracker));
	SmlManager *clientmanager = clienttracker->manager = smlManagerNew(client, &error);
	smlManagerSetEventCallback(clienttracker->manager, _manager_event, clienttracker);
	managerTracker *servertracker = g_malloc0(sizeof(managerTracker));
	SmlManager *servermanager = servertracker->manager = smlManagerNew(server, &error);
	smlManagerSetEventCallback(servertracker->manager, _manager_event, servertracker);
	smlManagerSetLocalMaxMsgSize(servertracker->manager, TEST_DEFAULT_MAX_MSG_SIZE);
	smlManagerSetLocalMaxObjSize(servertracker->manager, TEST_DEFAULT_MAX_OBJ_SIZE);
	
	sml_fail_unless(error == NULL, "%s", smlErrorPrint(&error));
	sml_fail_unless(smlTransportInitialize(client, &error), "%s", smlErrorPrint(&error));
	sml_fail_unless(smlTransportInitialize(server, &error), "%s", smlErrorPrint(&error));

	sml_fail_unless(smlManagerStart(clientmanager, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	sml_fail_unless(smlManagerStart(servermanager, &error), NULL);
	sml_fail_unless(error == NULL, NULL);

	SmlLocation *loc = smlLocationNew("test", NULL, &error);
	sml_fail_unless(loc != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);
	
	SmlLocation *loc1 = smlLocationNew("test1", NULL, &error);
	sml_fail_unless(loc1 != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);
	
	SmlLocation *loc2 = smlLocationNew("test2", NULL, &error);
	sml_fail_unless(loc2 != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);
	
	SmlLocation *loc3 = smlLocationNew("test3", NULL, &error);
	sml_fail_unless(loc3 != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);
	
	clienttracker->ds[0] = smlDsClientNew(SML_ELEMENT_TEXT_VCARD, loc1, loc1, &error);
	clienttracker->ds[1] = smlDsClientNew(SML_ELEMENT_TEXT_VCAL, loc2, loc2, &error);
	clienttracker->ds[2] = smlDsClientNew(SML_ELEMENT_TEXT_PLAIN, loc3, loc3, &error);
	smlDsServerRegister(clienttracker->ds[0], clientmanager, &error);
	smlDsServerRegister(clienttracker->ds[1], clientmanager, &error);
	smlDsServerRegister(clienttracker->ds[2], clientmanager, &error);
				
	servertracker->ds[0] = smlDsServerNew(SML_ELEMENT_TEXT_VCARD, loc1, &error);
	servertracker->ds[1] = smlDsServerNew(SML_ELEMENT_TEXT_VCAL, loc2, &error);
	servertracker->ds[2] = smlDsServerNew(SML_ELEMENT_TEXT_PLAIN, loc3, &error);
	smlDsServerSetConnectCallback(servertracker->ds[0], _recv_init_alert, servertracker);
	smlDsServerSetConnectCallback(servertracker->ds[1], _recv_init_alert, servertracker);
	smlDsServerSetConnectCallback(servertracker->ds[2], _recv_init_alert, servertracker);
	smlDsServerRegister(servertracker->ds[0], servermanager, &error);
	smlDsServerRegister(servertracker->ds[1], servermanager, &error);
	smlDsServerRegister(servertracker->ds[2], servermanager, &error);
	
	smlLocationUnref(loc1);
	smlLocationUnref(loc2);
	smlLocationUnref(loc3);
	
	/* The authenticator */
	/* The authenticator */
	SmlAuthenticator *auth = smlAuthNew(&error);
	smlAuthSetEnable(auth, FALSE);
	smlAuthRegister(auth, clientmanager, &error);
	
	/* The authenticator */
	SmlAuthenticator *auth2 = smlAuthNew(&error);
	smlAuthSetEnable(auth2, FALSE);
	smlAuthRegister(auth2, servermanager, &error);
	
	SmlSession *session = smlSessionNew(SML_SESSION_TYPE_CLIENT, SML_MIMETYPE_XML, SML_VERSION_12, SML_PROTOCOL_SYNCML, loc, loc, NULL, 0, &error);
	
	sml_fail_unless(smlManagerSessionAdd(clientmanager, session, NULL, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	smlLocationUnref(loc);
	
	clienttracker->dssessions[0] = smlDsServerSendAlert(clienttracker->ds[0], session, SML_ALERT_TWO_WAY, "last", "next", _alert_reply, GINT_TO_POINTER(1), &error);
	clienttracker->dssessions[1] = smlDsServerSendAlert(clienttracker->ds[1], session, SML_ALERT_TWO_WAY, "last", "next", _alert_reply, GINT_TO_POINTER(1), &error);
	clienttracker->dssessions[2] = smlDsServerSendAlert(clienttracker->ds[2], session, SML_ALERT_TWO_WAY, "last", "next", _alert_reply, GINT_TO_POINTER(1), &error);
	
	sml_fail_unless(smlSessionFlush(session, TRUE, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	while (init_alerts_received < 3 || num_sessions < 2) {
		smlManagerDispatch(servermanager);
		smlManagerDispatch(clientmanager);
		usleep(100);
		sml_fail_unless(eventError == NULL, "%s", smlErrorPrint(&eventError));
	}
	
	smlDsSessionGetAlert(servertracker->dssessions[0], _recv_alert, GINT_TO_POINTER(1));
	smlDsSessionGetAlert(servertracker->dssessions[1], _recv_alert, GINT_TO_POINTER(1));
	smlDsSessionGetAlert(servertracker->dssessions[2], _recv_alert, GINT_TO_POINTER(1));
	
	sml_fail_unless(servertracker->session != NULL, NULL);
	sml_fail_unless(smlSessionEnd(servertracker->session, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	smlSessionUnref(servertracker->session); /* unref for ref from callback */
	smlSessionUnref(clienttracker->session); /* unref for ref from callback */
	smlSessionUnref(clienttracker->session); /* unref for smlSessionNew */
	
	while (num_end < 2 || replies_received < 3 || num_finals < 2) {
		smlManagerDispatch(servermanager);
		smlManagerDispatch(clientmanager);
		usleep(100);
		sml_fail_unless(eventError == NULL, "%s", smlErrorPrint(&eventError));
	}
	
	sml_fail_unless(init_alerts_received == 3, NULL);
	sml_fail_unless(alerts_received == 3, NULL);
	sml_fail_unless(syncs_received == 0, NULL);
	sml_fail_unless(sync_ends_received == 0, NULL);
	sml_fail_unless(changes_received == 0, NULL);
	sml_fail_unless(replies_received == 3, NULL);
	sml_fail_unless(adds_received == 0, NULL);
	sml_fail_unless(add_replies_received == 0, NULL);
	sml_fail_unless(deletes_received == 0, NULL);
	sml_fail_unless(delete_replies_received == 0, NULL);
	sml_fail_unless(modifies_received == 0, NULL);
	sml_fail_unless(modify_replies_received == 0, NULL);
	sml_fail_unless(transport_errors == 0, NULL);
	sml_fail_unless(num_sessions == 2, NULL);
	sml_fail_unless(num_finals == 2, NULL);
	sml_fail_unless(num_end == 2, NULL);
	sml_fail_unless(session_errors == 0, NULL);
	
	smlDsSessionUnref(clienttracker->dssessions[0]);
	smlDsSessionUnref(clienttracker->dssessions[1]);
	smlDsSessionUnref(clienttracker->dssessions[2]);
	
	smlDsSessionUnref(servertracker->dssessions[0]);
	smlDsSessionUnref(servertracker->dssessions[1]);
	smlDsSessionUnref(servertracker->dssessions[2]);
	
	smlDsServerFree(clienttracker->ds[0]);
	smlDsServerFree(clienttracker->ds[1]);
	smlDsServerFree(clienttracker->ds[2]);
	
	smlDsServerFree(servertracker->ds[0]);
	smlDsServerFree(servertracker->ds[1]);
	smlDsServerFree(servertracker->ds[2]);
	
	g_free(clienttracker);
	g_free(servertracker);
	
	smlManagerStop(clientmanager);
	smlManagerStop(servermanager);

	while(num_disconnects < 2) {
		smlManagerDispatch(clientmanager);
		smlManagerDispatch(servermanager);
	}
	
	smlManagerFree(clientmanager);
	smlManagerFree(servermanager);

	smlAuthFree(auth);
	smlAuthFree(auth2);
	
	sml_fail_unless(smlTransportFinalize(server, &error), NULL);
	sml_fail_unless(smlTransportFinalize(client, &error), "%s", smlErrorPrint(&error));
	
	smlTransportFree(server);
	smlTransportFree(client);
}
END_TEST

/* client -- 3 * alert ---> server
 * server -- statuses, 3 * alert ---> client
 * client -- statuses, num * add, num * replace, num * delete ---> server
 * server -- statuses ---> client
 */
START_TEST (sync_multi_stress)
{
	reset_testbed();

	unsigned int num_items = 10;
	num_changes = 3 * num_items;
	
	SmlError *error = NULL;
	SmlTransport *server = smlTransportNew(SML_TRANSPORT_HTTP_SERVER, &error);
	SmlTransport *client = smlTransportNew(SML_TRANSPORT_HTTP_CLIENT, &error);
	
	sml_fail_unless(smlTransportSetConfigOption(client, "URL", "http://127.0.0.1:12013", &error), NULL);

	sml_fail_unless(smlTransportSetConfigOption(server, "PORT", "12013", &error), NULL);
	
	managerTracker *clienttracker = g_malloc0(sizeof(managerTracker));
	SmlManager *clientmanager = clienttracker->manager = smlManagerNew(client, &error);
	smlManagerSetEventCallback(clienttracker->manager, _manager_event, clienttracker);
	managerTracker *servertracker = g_malloc0(sizeof(managerTracker));
	SmlManager *servermanager = servertracker->manager = smlManagerNew(server, &error);
	smlManagerSetEventCallback(servertracker->manager, _manager_event, servertracker);
	smlManagerSetLocalMaxMsgSize(servertracker->manager, TEST_DEFAULT_MAX_MSG_SIZE);
	smlManagerSetLocalMaxObjSize(servertracker->manager, TEST_DEFAULT_MAX_OBJ_SIZE);
	
	sml_fail_unless(error == NULL, "%s", smlErrorPrint(&error));
	sml_fail_unless(smlTransportInitialize(client, &error), "%s", smlErrorPrint(&error));
	sml_fail_unless(smlTransportInitialize(server, &error), "%s", smlErrorPrint(&error));

	sml_fail_unless(smlManagerStart(clientmanager, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	sml_fail_unless(smlManagerStart(servermanager, &error), NULL);
	sml_fail_unless(error == NULL, NULL);

	SmlLocation *loc = smlLocationNew("test", NULL, &error);
	sml_fail_unless(loc != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);
	
	SmlLocation *loc1 = smlLocationNew("test1", NULL, &error);
	sml_fail_unless(loc1 != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);
	
	SmlLocation *loc2 = smlLocationNew("test2", NULL, &error);
	sml_fail_unless(loc2 != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);
	
	SmlLocation *loc3 = smlLocationNew("test3", NULL, &error);
	sml_fail_unless(loc3 != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);
	
	clienttracker->ds[0] = smlDsClientNew(SML_ELEMENT_TEXT_VCARD, loc1, loc1, &error);
	clienttracker->ds[1] = smlDsClientNew(SML_ELEMENT_TEXT_VCAL, loc2, loc2, &error);
	clienttracker->ds[2] = smlDsClientNew(SML_ELEMENT_TEXT_PLAIN, loc3, loc3, &error);
	smlDsServerRegister(clienttracker->ds[0], clientmanager, &error);
	smlDsServerRegister(clienttracker->ds[1], clientmanager, &error);
	smlDsServerRegister(clienttracker->ds[2], clientmanager, &error);
				
	servertracker->ds[0] = smlDsServerNew(SML_ELEMENT_TEXT_VCARD, loc1, &error);
	servertracker->ds[1] = smlDsServerNew(SML_ELEMENT_TEXT_VCAL, loc2, &error);
	servertracker->ds[2] = smlDsServerNew(SML_ELEMENT_TEXT_PLAIN, loc3, &error);
	smlDsServerSetConnectCallback(servertracker->ds[0], _recv_init_alert, servertracker);
	smlDsServerSetConnectCallback(servertracker->ds[1], _recv_init_alert, servertracker);
	smlDsServerSetConnectCallback(servertracker->ds[2], _recv_init_alert, servertracker);
	smlDsServerRegister(servertracker->ds[0], servermanager, &error);
	smlDsServerRegister(servertracker->ds[1], servermanager, &error);
	smlDsServerRegister(servertracker->ds[2], servermanager, &error);
	
	smlLocationUnref(loc1);
	smlLocationUnref(loc2);
	smlLocationUnref(loc3);
	
	/* The authenticator */
	SmlAuthenticator *auth = smlAuthNew(&error);
	smlAuthSetEnable(auth, FALSE);
	smlAuthRegister(auth, clientmanager, &error);
	
	/* The authenticator */
	SmlAuthenticator *auth2 = smlAuthNew(&error);
	smlAuthSetEnable(auth2, FALSE);
	smlAuthRegister(auth2, servermanager, &error);
	
	clienttracker->session = smlSessionNew(SML_SESSION_TYPE_CLIENT, SML_MIMETYPE_XML, SML_VERSION_12, SML_PROTOCOL_SYNCML, loc, loc, NULL, 0, &error);
	
	sml_fail_unless(smlManagerSessionAdd(clientmanager, clienttracker->session, NULL, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	smlLocationUnref(loc);
	
	clienttracker->dssessions[0] = smlDsServerSendAlert(clienttracker->ds[0], clienttracker->session, SML_ALERT_TWO_WAY, "last", "next", _alert_reply, GINT_TO_POINTER(1), &error);
	clienttracker->dssessions[1] = smlDsServerSendAlert(clienttracker->ds[1], clienttracker->session, SML_ALERT_TWO_WAY, "last", "next", _alert_reply, GINT_TO_POINTER(1), &error);
	clienttracker->dssessions[2] = smlDsServerSendAlert(clienttracker->ds[2], clienttracker->session, SML_ALERT_TWO_WAY, "last", "next", _alert_reply, GINT_TO_POINTER(1), &error);
	
	sml_fail_unless(smlSessionFlush(clienttracker->session, TRUE, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	while (init_alerts_received < 3 || num_sessions < 2) {
		smlManagerDispatch(servermanager);
		smlManagerDispatch(clientmanager);
		usleep(100);
		sml_fail_unless(eventError == NULL, "%s", smlErrorPrint(&eventError));
	}
	
	smlDsSessionGetAlert(servertracker->dssessions[0], _recv_alert, GINT_TO_POINTER(1));
	smlDsSessionGetAlert(servertracker->dssessions[1], _recv_alert, GINT_TO_POINTER(1));
	smlDsSessionGetAlert(servertracker->dssessions[2], _recv_alert, GINT_TO_POINTER(1));
	
	smlDsSessionSendAlert(servertracker->dssessions[0], SML_ALERT_TWO_WAY, "last", "next", _alert_reply, GINT_TO_POINTER(1), &error);
	smlDsSessionSendAlert(servertracker->dssessions[1], SML_ALERT_TWO_WAY, "last", "next", _alert_reply, GINT_TO_POINTER(1), &error);
	smlDsSessionSendAlert(servertracker->dssessions[2], SML_ALERT_TWO_WAY, "last", "next", _alert_reply, GINT_TO_POINTER(1), &error);
	
	sml_fail_unless(servertracker->session != NULL, NULL);
	sml_fail_unless(smlSessionFlush(servertracker->session, TRUE, &error), NULL);
	
	smlDsSessionGetAlert(clienttracker->dssessions[0], _recv_alert, GINT_TO_POINTER(1));
	smlDsSessionGetAlert(clienttracker->dssessions[1], _recv_alert, GINT_TO_POINTER(1));
	smlDsSessionGetAlert(clienttracker->dssessions[2], _recv_alert, GINT_TO_POINTER(1));
	
	while (alerts_received < 6) {
		smlDsSessionDispatch(servertracker->dssessions[0]);
		smlDsSessionDispatch(servertracker->dssessions[1]);
		smlDsSessionDispatch(servertracker->dssessions[2]);
		
		smlDsSessionDispatch(clienttracker->dssessions[0]);
		smlDsSessionDispatch(clienttracker->dssessions[1]);
		smlDsSessionDispatch(clienttracker->dssessions[2]);
		
		smlManagerDispatch(servermanager);
		smlManagerDispatch(clientmanager);
		usleep(100);
		sml_fail_unless(eventError == NULL, "%s", smlErrorPrint(&eventError));
	}

	const char *content_types[] = {SML_ELEMENT_TEXT_VCARD, SML_ELEMENT_TEXT_VCAL, SML_ELEMENT_TEXT_PLAIN};
	int k = 0;
	for (k = 0; k < 3; k++)
	{
		smlDsSessionSendSync(clienttracker->dssessions[k], num_changes, _sync_reply, GINT_TO_POINTER(1), &error);
	
		unsigned int i = 0;
		for (i = 0; i < num_items; i++) {
			sml_fail_unless(smlDsSessionQueueChange(clienttracker->dssessions[k], SML_CHANGE_ADD, "uid", "data", 4, content_types[k], _add_reply, GINT_TO_POINTER(2), &error), NULL);
			sml_fail_unless(error == NULL, NULL);
			sml_fail_unless(smlDsSessionQueueChange(clienttracker->dssessions[k], SML_CHANGE_REPLACE, "uid", "newdata", 7, content_types[k], _modify_reply, GINT_TO_POINTER(1), &error), NULL);
			sml_fail_unless(error == NULL, NULL);
			sml_fail_unless(smlDsSessionQueueChange(clienttracker->dssessions[k], SML_CHANGE_DELETE, "uid", NULL, 0, content_types[k], _delete_reply, GINT_TO_POINTER(1), &error), NULL);
			sml_fail_unless(error == NULL, NULL);
		}

		smlDsSessionCloseSync(clienttracker->dssessions[k], &error);
	}

	sml_fail_unless(smlSessionFlush(clienttracker->session, TRUE, &error), NULL);
	
	smlDsSessionGetSync(servertracker->dssessions[0], _recv_sync, GINT_TO_POINTER(1));
	smlDsSessionGetSync(servertracker->dssessions[1], _recv_sync, GINT_TO_POINTER(1));
	smlDsSessionGetSync(servertracker->dssessions[2], _recv_sync, GINT_TO_POINTER(1));
	smlDsSessionGetChanges(servertracker->dssessions[0], _recv_changes, GINT_TO_POINTER(1));
	smlDsSessionGetChanges(servertracker->dssessions[1], _recv_changes, GINT_TO_POINTER(1));
	smlDsSessionGetChanges(servertracker->dssessions[2], _recv_changes, GINT_TO_POINTER(1));
	
	while (syncs_received < 3 || adds_received < num_items * 3 || deletes_received < num_items * 3 || modifies_received < num_items * 3) {
		smlDsSessionDispatch(servertracker->dssessions[0]);
		smlDsSessionDispatch(servertracker->dssessions[1]);
		smlDsSessionDispatch(servertracker->dssessions[2]);
		
		smlDsSessionDispatch(clienttracker->dssessions[0]);
		smlDsSessionDispatch(clienttracker->dssessions[1]);
		smlDsSessionDispatch(clienttracker->dssessions[2]);
		
		smlManagerDispatch(servermanager);
		smlManagerDispatch(clientmanager);
		usleep(100);
		sml_fail_unless(eventError == NULL, "%s", smlErrorPrint(&eventError));
	}
	
	sml_fail_unless(servertracker->session != NULL, NULL);
	sml_fail_unless(smlSessionEnd(servertracker->session, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	smlSessionUnref(servertracker->session); /* unref for ref from callback */
	smlSessionUnref(clienttracker->session); /* unref for ref from callback */
	smlSessionUnref(clienttracker->session); /* unref for smlSessionNew */
	
	while (num_end < 2 || replies_received < 9 || num_finals < 4) {
		smlManagerDispatch(servermanager);
		smlManagerDispatch(clientmanager);
		usleep(100);
		sml_fail_unless(eventError == NULL, "%s", smlErrorPrint(&eventError));
	}
	
	sml_fail_unless(init_alerts_received == 3, NULL);
	sml_fail_unless(alerts_received == 6, NULL);
	sml_fail_unless(syncs_received == 3, NULL);
	sml_fail_unless(sync_ends_received == 0, NULL);
	sml_fail_unless(changes_received == 0, NULL);
	sml_fail_unless(replies_received == 9, NULL);
	sml_fail_unless(adds_received == num_items * 3, NULL);
	sml_fail_unless(add_replies_received == num_items * 3, NULL);
	sml_fail_unless(deletes_received == num_items * 3, NULL);
	sml_fail_unless(delete_replies_received == num_items * 3, NULL);
	sml_fail_unless(modifies_received == num_items * 3, NULL);
	sml_fail_unless(modify_replies_received == num_items * 3, NULL);
	sml_fail_unless(transport_errors == 0, NULL);
	sml_fail_unless(num_sessions == 2, NULL);
	sml_fail_unless(num_finals == 4, NULL);
	sml_fail_unless(num_end == 2, NULL);
	sml_fail_unless(session_errors == 0, NULL);
	
	smlDsSessionUnref(clienttracker->dssessions[0]);
	smlDsSessionUnref(clienttracker->dssessions[1]);
	smlDsSessionUnref(clienttracker->dssessions[2]);
	
	smlDsSessionUnref(servertracker->dssessions[0]);
	smlDsSessionUnref(servertracker->dssessions[1]);
	smlDsSessionUnref(servertracker->dssessions[2]);
	
	smlDsServerFree(clienttracker->ds[0]);
	smlDsServerFree(clienttracker->ds[1]);
	smlDsServerFree(clienttracker->ds[2]);
	
	smlDsServerFree(servertracker->ds[0]);
	smlDsServerFree(servertracker->ds[1]);
	smlDsServerFree(servertracker->ds[2]);
	
	smlAuthFree(auth);
	smlAuthFree(auth2);
	
	g_free(clienttracker);
	g_free(servertracker);
	
	smlManagerStop(clientmanager);
	smlManagerStop(servermanager);
	
	while(num_disconnects < 2) {
		smlManagerDispatch(clientmanager);
		smlManagerDispatch(servermanager);
	}
	
	smlManagerFree(clientmanager);
	smlManagerFree(servermanager);
	
	sml_fail_unless(smlTransportFinalize(server, &error), NULL);
	sml_fail_unless(smlTransportFinalize(client, &error), "%s", smlErrorPrint(&error));
	
	smlTransportFree(server);
	smlTransportFree(client);
}
END_TEST

/* client -- 3 * alert ---> server
 * server -- statuses, 3 * alert ---> client
 * client -- statuses, num * add, num * replace, num * delete ---> server
 * server -- statuses, num * add, num * replace, num * delete  ---> client
 * client -- statuses, map ---> server
 * server -- statuses ---> client
 */
START_TEST (sync_multi_stress2)
{
	reset_testbed();

	unsigned int num_items = 100;
	num_changes = 3 * num_items;
	
	SmlError *error = NULL;
	SmlTransport *server = smlTransportNew(SML_TRANSPORT_HTTP_SERVER, &error);
	SmlTransport *client = smlTransportNew(SML_TRANSPORT_HTTP_CLIENT, &error);
		
	sml_fail_unless(smlTransportSetConfigOption(client, "URL", "http://127.0.0.1:12014", &error), NULL);

	sml_fail_unless(smlTransportSetConfigOption(server, "PORT", "12014", &error), NULL);
	
	managerTracker *clienttracker = g_malloc0(sizeof(managerTracker));
	SmlManager *clientmanager = clienttracker->manager = smlManagerNew(client, &error);
	smlManagerSetEventCallback(clienttracker->manager, _manager_event, clienttracker);
	managerTracker *servertracker = g_malloc0(sizeof(managerTracker));
	SmlManager *servermanager = servertracker->manager = smlManagerNew(server, &error);
	smlManagerSetEventCallback(servertracker->manager, _manager_event, servertracker);
	smlManagerSetLocalMaxMsgSize(servertracker->manager, TEST_DEFAULT_MAX_MSG_SIZE);
	smlManagerSetLocalMaxObjSize(servertracker->manager, TEST_DEFAULT_MAX_OBJ_SIZE);
	
	sml_fail_unless(error == NULL, "%s", smlErrorPrint(&error));
	sml_fail_unless(smlTransportInitialize(client, &error), "%s", smlErrorPrint(&error));
	sml_fail_unless(smlTransportInitialize(server, &error), "%s", smlErrorPrint(&error));

	sml_fail_unless(smlManagerStart(clientmanager, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	sml_fail_unless(smlManagerStart(servermanager, &error), NULL);
	sml_fail_unless(error == NULL, NULL);

	SmlLocation *loc = smlLocationNew("test", NULL, &error);
	sml_fail_unless(loc != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);
	
	SmlLocation *loc1 = smlLocationNew("test1", NULL, &error);
	sml_fail_unless(loc1 != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);
	
	SmlLocation *loc2 = smlLocationNew("test2", NULL, &error);
	sml_fail_unless(loc2 != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);
	
	SmlLocation *loc3 = smlLocationNew("test3", NULL, &error);
	sml_fail_unless(loc3 != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);
	
	clienttracker->ds[0] = smlDsClientNew(SML_ELEMENT_TEXT_VCARD, loc1, loc1, &error);
	clienttracker->ds[1] = smlDsClientNew(SML_ELEMENT_TEXT_VCAL, loc2, loc2, &error);
	clienttracker->ds[2] = smlDsClientNew(SML_ELEMENT_TEXT_PLAIN, loc3, loc3, &error);
	smlDsServerRegister(clienttracker->ds[0], clientmanager, &error);
	smlDsServerRegister(clienttracker->ds[1], clientmanager, &error);
	smlDsServerRegister(clienttracker->ds[2], clientmanager, &error);
				
	servertracker->ds[0] = smlDsServerNew(SML_ELEMENT_TEXT_VCARD, loc1, &error);
	servertracker->ds[1] = smlDsServerNew(SML_ELEMENT_TEXT_VCAL, loc2, &error);
	servertracker->ds[2] = smlDsServerNew(SML_ELEMENT_TEXT_PLAIN, loc3, &error);
	smlDsServerSetConnectCallback(servertracker->ds[0], _recv_init_alert, servertracker);
	smlDsServerSetConnectCallback(servertracker->ds[1], _recv_init_alert, servertracker);
	smlDsServerSetConnectCallback(servertracker->ds[2], _recv_init_alert, servertracker);
	smlDsServerRegister(servertracker->ds[0], servermanager, &error);
	smlDsServerRegister(servertracker->ds[1], servermanager, &error);
	smlDsServerRegister(servertracker->ds[2], servermanager, &error);
	
	smlLocationUnref(loc1);
	smlLocationUnref(loc2);
	smlLocationUnref(loc3);
	
	/* The authenticator */
	SmlAuthenticator *auth = smlAuthNew(&error);
	smlAuthSetEnable(auth, FALSE);
	smlAuthRegister(auth, clientmanager, &error);
	
	/* The authenticator */
	SmlAuthenticator *auth2 = smlAuthNew(&error);
	smlAuthSetEnable(auth2, FALSE);
	smlAuthRegister(auth2, servermanager, &error);
	
	clienttracker->session = smlSessionNew(SML_SESSION_TYPE_CLIENT, SML_MIMETYPE_XML, SML_VERSION_12, SML_PROTOCOL_SYNCML, loc, loc, NULL, 0, &error);
	
	sml_fail_unless(smlManagerSessionAdd(clientmanager, clienttracker->session, NULL, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	smlLocationUnref(loc);
	
	clienttracker->dssessions[0] = smlDsServerSendAlert(clienttracker->ds[0], clienttracker->session, SML_ALERT_TWO_WAY, "last", "next", _alert_reply, GINT_TO_POINTER(1), &error);
	clienttracker->dssessions[1] = smlDsServerSendAlert(clienttracker->ds[1], clienttracker->session, SML_ALERT_TWO_WAY, "last", "next", _alert_reply, GINT_TO_POINTER(1), &error);
	clienttracker->dssessions[2] = smlDsServerSendAlert(clienttracker->ds[2], clienttracker->session, SML_ALERT_TWO_WAY, "last", "next", _alert_reply, GINT_TO_POINTER(1), &error);
	
	sml_fail_unless(smlSessionFlush(clienttracker->session, TRUE, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	while (init_alerts_received < 3 || num_sessions < 2) {
		smlManagerDispatch(servermanager);
		smlManagerDispatch(clientmanager);
		usleep(100);
		sml_fail_unless(eventError == NULL, "%s", smlErrorPrint(&eventError));
	}
	
	smlDsSessionGetAlert(servertracker->dssessions[0], _recv_alert, GINT_TO_POINTER(1));
	smlDsSessionGetAlert(servertracker->dssessions[1], _recv_alert, GINT_TO_POINTER(1));
	smlDsSessionGetAlert(servertracker->dssessions[2], _recv_alert, GINT_TO_POINTER(1));
	
	smlDsSessionSendAlert(servertracker->dssessions[0], SML_ALERT_TWO_WAY, "last", "next", _alert_reply, GINT_TO_POINTER(1), &error);
	smlDsSessionSendAlert(servertracker->dssessions[1], SML_ALERT_TWO_WAY, "last", "next", _alert_reply, GINT_TO_POINTER(1), &error);
	smlDsSessionSendAlert(servertracker->dssessions[2], SML_ALERT_TWO_WAY, "last", "next", _alert_reply, GINT_TO_POINTER(1), &error);
	
	sml_fail_unless(servertracker->session != NULL, NULL);
	sml_fail_unless(smlSessionFlush(servertracker->session, TRUE, &error), NULL);
	
	smlDsSessionGetAlert(clienttracker->dssessions[0], _recv_alert, GINT_TO_POINTER(1));
	smlDsSessionGetAlert(clienttracker->dssessions[1], _recv_alert, GINT_TO_POINTER(1));
	smlDsSessionGetAlert(clienttracker->dssessions[2], _recv_alert, GINT_TO_POINTER(1));
	
	while (alerts_received < 6) {
		smlDsSessionDispatch(servertracker->dssessions[0]);
		smlDsSessionDispatch(servertracker->dssessions[1]);
		smlDsSessionDispatch(servertracker->dssessions[2]);
		
		smlDsSessionDispatch(clienttracker->dssessions[0]);
		smlDsSessionDispatch(clienttracker->dssessions[1]);
		smlDsSessionDispatch(clienttracker->dssessions[2]);
		
		smlManagerDispatch(servermanager);
		smlManagerDispatch(clientmanager);
		usleep(100);
		sml_fail_unless(eventError == NULL, "%s", smlErrorPrint(&eventError));
	}
	
	const char *content_types[] = {SML_ELEMENT_TEXT_VCARD, SML_ELEMENT_TEXT_VCAL, SML_ELEMENT_TEXT_PLAIN};
	int k = 0;
	for (k = 0; k < 3; k++)
	{
		smlDsSessionSendSync(clienttracker->dssessions[k], num_changes, _sync_reply, GINT_TO_POINTER(1), &error);
	
		unsigned int i = 0;
		for (i = 0; i < num_items; i++) {
			sml_fail_unless(smlDsSessionQueueChange(clienttracker->dssessions[k], SML_CHANGE_ADD, "uid", "data", 4, content_types[k], _add_reply, GINT_TO_POINTER(2), &error), NULL);
			sml_fail_unless(error == NULL, NULL);
			sml_fail_unless(smlDsSessionQueueChange(clienttracker->dssessions[k], SML_CHANGE_REPLACE, "uid", "newdata", 7, content_types[k], _modify_reply, GINT_TO_POINTER(1), &error), NULL);
			sml_fail_unless(error == NULL, NULL);
			sml_fail_unless(smlDsSessionQueueChange(clienttracker->dssessions[k], SML_CHANGE_DELETE, "uid", NULL, 0, content_types[k], _delete_reply, GINT_TO_POINTER(1), &error), NULL);
			sml_fail_unless(error == NULL, NULL);
		}

		smlDsSessionCloseSync(clienttracker->dssessions[k], &error);
	}
	
	sml_fail_unless(smlSessionFlush(clienttracker->session, TRUE, &error), NULL);
	
	smlDsSessionGetSync(servertracker->dssessions[0], _recv_sync, GINT_TO_POINTER(1));
	smlDsSessionGetSync(servertracker->dssessions[1], _recv_sync, GINT_TO_POINTER(1));
	smlDsSessionGetSync(servertracker->dssessions[2], _recv_sync, GINT_TO_POINTER(1));
	smlDsSessionGetChanges(servertracker->dssessions[0], _recv_changes, GINT_TO_POINTER(1));
	smlDsSessionGetChanges(servertracker->dssessions[1], _recv_changes, GINT_TO_POINTER(1));
	smlDsSessionGetChanges(servertracker->dssessions[2], _recv_changes, GINT_TO_POINTER(1));
	
	while (syncs_received < 3 || adds_received < num_items * 3 || deletes_received < num_items * 3 || modifies_received < num_items * 3) {
		smlDsSessionDispatch(servertracker->dssessions[0]);
		smlDsSessionDispatch(servertracker->dssessions[1]);
		smlDsSessionDispatch(servertracker->dssessions[2]);
		
		smlDsSessionDispatch(clienttracker->dssessions[0]);
		smlDsSessionDispatch(clienttracker->dssessions[1]);
		smlDsSessionDispatch(clienttracker->dssessions[2]);
		
		smlManagerDispatch(servermanager);
		smlManagerDispatch(clientmanager);
		usleep(100);
		sml_fail_unless(eventError == NULL, "%s", smlErrorPrint(&eventError));
	}
	
	for (k = 0; k < 3; k++)
	{
		smlDsSessionSendSync(servertracker->dssessions[k], num_changes, _sync_reply, GINT_TO_POINTER(1), &error);

		unsigned int i = 0;	
		for (i = 0; i < num_items; i++) {
			sml_fail_unless(smlDsSessionQueueChange(servertracker->dssessions[k], SML_CHANGE_ADD, "uid", "data", 4, content_types[k], _add_reply, GINT_TO_POINTER(1), &error), NULL);
			sml_fail_unless(error == NULL, NULL);
			sml_fail_unless(smlDsSessionQueueChange(servertracker->dssessions[k], SML_CHANGE_REPLACE, "uid", "newdata", 7, content_types[k], _modify_reply, GINT_TO_POINTER(1), &error), NULL);
			sml_fail_unless(error == NULL, NULL);
			sml_fail_unless(smlDsSessionQueueChange(servertracker->dssessions[k], SML_CHANGE_DELETE, "uid", NULL, 0, content_types[k], _delete_reply, GINT_TO_POINTER(1), &error), NULL);
			sml_fail_unless(error == NULL, NULL);
		}

		smlDsSessionCloseSync(servertracker->dssessions[k], &error);
	}
	
	sml_fail_unless(smlSessionFlush(servertracker->session, TRUE, &error), NULL);
	
	smlDsSessionGetSync(clienttracker->dssessions[0], _recv_sync, GINT_TO_POINTER(1));
	smlDsSessionGetSync(clienttracker->dssessions[1], _recv_sync, GINT_TO_POINTER(1));
	smlDsSessionGetSync(clienttracker->dssessions[2], _recv_sync, GINT_TO_POINTER(1));
	smlDsSessionGetChanges(clienttracker->dssessions[0], _recv_changes, GINT_TO_POINTER(1));
	smlDsSessionGetChanges(clienttracker->dssessions[1], _recv_changes, GINT_TO_POINTER(1));
	smlDsSessionGetChanges(clienttracker->dssessions[2], _recv_changes, GINT_TO_POINTER(1));
	
	while (syncs_received < 6 || adds_received < num_items * 6 || deletes_received < num_items * 6 || modifies_received < num_items * 6) {
		smlDsSessionDispatch(servertracker->dssessions[0]);
		smlDsSessionDispatch(servertracker->dssessions[1]);
		smlDsSessionDispatch(servertracker->dssessions[2]);
		
		smlDsSessionDispatch(clienttracker->dssessions[0]);
		smlDsSessionDispatch(clienttracker->dssessions[1]);
		smlDsSessionDispatch(clienttracker->dssessions[2]);
		
		smlManagerDispatch(servermanager);
		smlManagerDispatch(clientmanager);
		usleep(100);
		sml_fail_unless(eventError == NULL, "%s", smlErrorPrint(&eventError));
	}

	unsigned int i = 0;	
	for (i = 0; i < num_items; i++) {
		sml_fail_unless(smlDsSessionQueueMap(clienttracker->dssessions[0], "uid", "newuid", &error), NULL);
		sml_fail_unless(smlDsSessionQueueMap(clienttracker->dssessions[1], "uid", "newuid", &error), NULL);
		sml_fail_unless(smlDsSessionQueueMap(clienttracker->dssessions[2], "uid", "newuid", &error), NULL);
	}
	
	smlDsSessionCloseMap(clienttracker->dssessions[0], _map_reply, GINT_TO_POINTER(1), &error);
	smlDsSessionCloseMap(clienttracker->dssessions[1], _map_reply, GINT_TO_POINTER(1), &error);
	smlDsSessionCloseMap(clienttracker->dssessions[2], _map_reply, GINT_TO_POINTER(1), &error);
	
	sml_fail_unless(smlSessionFlush(clienttracker->session, TRUE, &error), NULL);
	
	while (delete_replies_received < num_items * 6) {
		smlDsSessionDispatch(servertracker->dssessions[0]);
		smlDsSessionDispatch(servertracker->dssessions[1]);
		smlDsSessionDispatch(servertracker->dssessions[2]);
		
		smlDsSessionDispatch(clienttracker->dssessions[0]);
		smlDsSessionDispatch(clienttracker->dssessions[1]);
		smlDsSessionDispatch(clienttracker->dssessions[2]);
		
		smlManagerDispatch(servermanager);
		smlManagerDispatch(clientmanager);
		usleep(100);
		sml_fail_unless(eventError == NULL, "%s", smlErrorPrint(&eventError));
	}
	
	sml_fail_unless(servertracker->session != NULL, NULL);
	sml_fail_unless(smlSessionEnd(servertracker->session, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	smlSessionUnref(servertracker->session); /* unref for event SESSION_NEW */
	smlSessionUnref(clienttracker->session); /* unref for event SESSION_NEW */
	smlSessionUnref(clienttracker->session); /* unref for smlSessionNew */
	
	while (num_end < 2 || replies_received < 15 || num_finals < 6) {
		smlManagerDispatch(servermanager);
		smlManagerDispatch(clientmanager);
		usleep(100);
		sml_fail_unless(eventError == NULL, "%s", smlErrorPrint(&eventError));
	}
	
	sml_fail_unless(init_alerts_received == 3, NULL);
	sml_fail_unless(alerts_received == 6, NULL);
	sml_fail_unless(syncs_received == 6, NULL);
	sml_fail_unless(sync_ends_received == 0, NULL);
	sml_fail_unless(changes_received == 0, NULL);
	sml_fail_unless(replies_received == 15, NULL);
	sml_fail_unless(adds_received == num_items * 6, NULL);
	sml_fail_unless(add_replies_received == num_items * 6, NULL);
	sml_fail_unless(deletes_received == num_items * 6, NULL);
	sml_fail_unless(delete_replies_received == num_items * 6, NULL);
	sml_fail_unless(modifies_received == num_items * 6, NULL);
	sml_fail_unless(modify_replies_received == num_items * 6, NULL);
	sml_fail_unless(transport_errors == 0, NULL);
	sml_fail_unless(num_sessions == 2, NULL);
	sml_fail_unless(num_finals == 6, NULL);
	sml_fail_unless(num_end == 2, NULL);
	sml_fail_unless(session_errors == 0, NULL);
	
	smlDsSessionUnref(clienttracker->dssessions[0]);
	smlDsSessionUnref(clienttracker->dssessions[1]);
	smlDsSessionUnref(clienttracker->dssessions[2]);
	
	smlDsSessionUnref(servertracker->dssessions[0]);
	smlDsSessionUnref(servertracker->dssessions[1]);
	smlDsSessionUnref(servertracker->dssessions[2]);
	
	smlDsServerFree(clienttracker->ds[0]);
	smlDsServerFree(clienttracker->ds[1]);
	smlDsServerFree(clienttracker->ds[2]);
	
	smlDsServerFree(servertracker->ds[0]);
	smlDsServerFree(servertracker->ds[1]);
	smlDsServerFree(servertracker->ds[2]);
	
	smlAuthFree(auth);
	smlAuthFree(auth2);
	
	g_free(clienttracker);
	g_free(servertracker);
	
	smlManagerStop(clientmanager);
	smlManagerStop(servermanager);
	
	while(num_disconnects < 2) {
		smlManagerDispatch(clientmanager);
		smlManagerDispatch(servermanager);
	}
	
	smlManagerFree(clientmanager);
	smlManagerFree(servermanager);
	
	sml_fail_unless(smlTransportFinalize(server, &error), NULL);
	sml_fail_unless(smlTransportFinalize(client, &error), "%s", smlErrorPrint(&error));
	
	smlTransportFree(server);
	smlTransportFree(client);
}
END_TEST

/* client -- 3 * alert ---> server
 * server -- statuses, 3 * alert ---> client
 * client -- statuses, num * add, num * replace, num * delete ---> server
 * server -- statuses ---> client
 */
START_TEST (sync_multi_conf_from_devinf)
{
	reset_testbed();

	unsigned int num_items = 100;
	num_changes = 3 * num_items;

	SmlError *error = NULL;
	SmlTransport *server = smlTransportNew(SML_TRANSPORT_HTTP_SERVER, &error);
	SmlTransport *client = smlTransportNew(SML_TRANSPORT_HTTP_CLIENT, &error);
	
	sml_fail_unless(smlTransportSetConfigOption(client, "URL", "http://127.0.0.1:12015", &error), NULL);

	sml_fail_unless(smlTransportSetConfigOption(server, "PORT", "12015", &error), NULL);
	
	managerTracker *clienttracker = g_malloc0(sizeof(managerTracker));
	SmlManager *clientmanager = clienttracker->manager = smlManagerNew(client, &error);
	smlManagerSetEventCallback(clienttracker->manager, _manager_event, clienttracker);
	managerTracker *servertracker = g_malloc0(sizeof(managerTracker));
	SmlManager *servermanager = servertracker->manager = smlManagerNew(server, &error);
	smlManagerSetEventCallback(servertracker->manager, _manager_event, servertracker);
	smlManagerSetLocalMaxMsgSize(servertracker->manager, TEST_DEFAULT_MAX_MSG_SIZE);
	smlManagerSetLocalMaxObjSize(servertracker->manager, TEST_DEFAULT_MAX_OBJ_SIZE);
	
	sml_fail_unless(error == NULL, "%s", smlErrorPrint(&error));
	sml_fail_unless(smlTransportInitialize(client, &error), "%s", smlErrorPrint(&error));
	sml_fail_unless(smlTransportInitialize(server, &error), "%s", smlErrorPrint(&error));

	sml_fail_unless(smlManagerStart(clientmanager, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	sml_fail_unless(smlManagerStart(servermanager, &error), NULL);
	sml_fail_unless(error == NULL, NULL);

	SmlLocation *loc = smlLocationNew("test", NULL, &error);
	sml_fail_unless(loc != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);
	
	SmlLocation *loc1 = smlLocationNew("test1", NULL, &error);
	sml_fail_unless(loc1 != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);
	
	SmlLocation *loc2 = smlLocationNew("test2", NULL, &error);
	sml_fail_unless(loc2 != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);
	
	SmlLocation *loc3 = smlLocationNew("test3", NULL, &error);
	sml_fail_unless(loc3 != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);
	
	clienttracker->ds[0] = smlDsClientNew(SML_ELEMENT_TEXT_VCARD, loc1, loc1, &error);
	clienttracker->ds[1] = smlDsClientNew(SML_ELEMENT_TEXT_VCAL, loc2, loc2, &error);
	clienttracker->ds[2] = smlDsClientNew(SML_ELEMENT_TEXT_PLAIN, loc3, loc3, &error);
	smlDsServerRegister(clienttracker->ds[0], clientmanager, &error);
	smlDsServerRegister(clienttracker->ds[1], clientmanager, &error);
	smlDsServerRegister(clienttracker->ds[2], clientmanager, &error);
				
	servertracker->ds[0] = smlDsServerNew(SML_ELEMENT_TEXT_VCARD, loc1, &error);
	servertracker->ds[1] = smlDsServerNew(SML_ELEMENT_TEXT_VCAL, loc2, &error);
	servertracker->ds[2] = smlDsServerNew(SML_ELEMENT_TEXT_PLAIN, loc3, &error);
	smlDsServerSetConnectCallback(servertracker->ds[0], _recv_init_alert, servertracker);
	smlDsServerSetConnectCallback(servertracker->ds[1], _recv_init_alert, servertracker);
	smlDsServerSetConnectCallback(servertracker->ds[2], _recv_init_alert, servertracker);
	smlDsServerRegister(servertracker->ds[0], servermanager, &error);
	smlDsServerRegister(servertracker->ds[1], servermanager, &error);
	smlDsServerRegister(servertracker->ds[2], servermanager, &error);
	
	/* The devinf obj */
	SmlDevInf *devinf = smlDevInfNew("LibSyncmML", SML_DEVINF_DEVTYPE_WORKSTATION, &error);
	smlDevInfSetSupportsNumberOfChanges(devinf, TRUE);
	smlDevInfSetSupportsLargeObjs(devinf, FALSE);
	smlDevInfSetSupportsUTC(devinf, FALSE);
	SmlDevInfAgent *clientagent = smlDevInfAgentNew(devinf, &error);
	smlDevInfAgentRegister(clientagent, clientmanager, &error);

	/* And we also add the devinfo to the devinf agent */
	SmlDevInfDataStore *datastore = smlDevInfDataStoreNew(smlLocationGetURI(loc1), &error);
	smlDevInfDataStoreSetRxPref(datastore, SML_ELEMENT_TEXT_VCARD, "2.1");
	smlDevInfDataStoreSetTxPref(datastore, SML_ELEMENT_TEXT_VCARD, "2.1");
	smlDevInfDataStoreSetSyncCap(datastore, SML_DEVINF_SYNCTYPE_TWO_WAY, TRUE);
	smlDevInfDataStoreSetSyncCap(datastore, SML_DEVINF_SYNCTYPE_SLOW_SYNC, TRUE);
	smlDevInfDataStoreSetSyncCap(datastore, SML_DEVINF_SYNCTYPE_SERVER_ALERTED_SYNC, TRUE);
	smlDevInfAddDataStore(devinf, datastore);
	
	datastore = smlDevInfDataStoreNew(smlLocationGetURI(loc2), &error);
	smlDevInfDataStoreSetRxPref(datastore, SML_ELEMENT_TEXT_VCAL, "2.0");
	smlDevInfDataStoreSetTxPref(datastore, SML_ELEMENT_TEXT_VCAL, "2.0");
	smlDevInfDataStoreSetSyncCap(datastore, SML_DEVINF_SYNCTYPE_TWO_WAY, TRUE);
	smlDevInfDataStoreSetSyncCap(datastore, SML_DEVINF_SYNCTYPE_SLOW_SYNC, TRUE);
	smlDevInfDataStoreSetSyncCap(datastore, SML_DEVINF_SYNCTYPE_SERVER_ALERTED_SYNC, TRUE);
	smlDevInfAddDataStore(devinf, datastore);
	
	datastore = smlDevInfDataStoreNew(smlLocationGetURI(loc3), &error);
	smlDevInfDataStoreSetRxPref(datastore, SML_ELEMENT_TEXT_PLAIN, "1.0");
	smlDevInfDataStoreSetTxPref(datastore, SML_ELEMENT_TEXT_PLAIN, "1.0");
	smlDevInfDataStoreSetSyncCap(datastore, SML_DEVINF_SYNCTYPE_TWO_WAY, TRUE);
	smlDevInfDataStoreSetSyncCap(datastore, SML_DEVINF_SYNCTYPE_SLOW_SYNC, TRUE);
	smlDevInfDataStoreSetSyncCap(datastore, SML_DEVINF_SYNCTYPE_SERVER_ALERTED_SYNC, TRUE);
	smlDevInfAddDataStore(devinf, datastore);
	
	devinf = smlDevInfNew("LibSyncmML", SML_DEVINF_DEVTYPE_SERVER, &error);
	smlDevInfSetSupportsNumberOfChanges(devinf, TRUE);
	smlDevInfSetSupportsLargeObjs(devinf, TRUE);
	smlDevInfSetSupportsUTC(devinf, FALSE);
	SmlDevInfAgent *serveragent = smlDevInfAgentNew(devinf, &error);
	smlDevInfAgentRegister(serveragent, servermanager, &error);
	
	datastore = smlDevInfDataStoreNew(smlLocationGetURI(loc1), &error);
	smlDevInfDataStoreSetRxPref(datastore, SML_ELEMENT_TEXT_VCARD, "2.1");
	smlDevInfDataStoreSetTxPref(datastore, SML_ELEMENT_TEXT_VCARD, "2.1");
	smlDevInfDataStoreSetSyncCap(datastore, SML_DEVINF_SYNCTYPE_TWO_WAY, TRUE);
	smlDevInfDataStoreSetSyncCap(datastore, SML_DEVINF_SYNCTYPE_SLOW_SYNC, TRUE);
	smlDevInfDataStoreSetSyncCap(datastore, SML_DEVINF_SYNCTYPE_SERVER_ALERTED_SYNC, TRUE);
	smlDevInfAddDataStore(devinf, datastore);
	
	datastore = smlDevInfDataStoreNew(smlLocationGetURI(loc2), &error);
	smlDevInfDataStoreSetRxPref(datastore, SML_ELEMENT_TEXT_VCAL, "2.0");
	smlDevInfDataStoreSetTxPref(datastore, SML_ELEMENT_TEXT_VCAL, "2.0");
	smlDevInfDataStoreSetSyncCap(datastore, SML_DEVINF_SYNCTYPE_TWO_WAY, TRUE);
	smlDevInfDataStoreSetSyncCap(datastore, SML_DEVINF_SYNCTYPE_SLOW_SYNC, TRUE);
	smlDevInfDataStoreSetSyncCap(datastore, SML_DEVINF_SYNCTYPE_SERVER_ALERTED_SYNC, TRUE);
	smlDevInfAddDataStore(devinf, datastore);
	
	datastore = smlDevInfDataStoreNew(smlLocationGetURI(loc3), &error);
	smlDevInfDataStoreSetRxPref(datastore, SML_ELEMENT_TEXT_PLAIN, "1.0");
	smlDevInfDataStoreSetTxPref(datastore, SML_ELEMENT_TEXT_PLAIN, "1.0");
	smlDevInfDataStoreSetSyncCap(datastore, SML_DEVINF_SYNCTYPE_TWO_WAY, TRUE);
	smlDevInfDataStoreSetSyncCap(datastore, SML_DEVINF_SYNCTYPE_SLOW_SYNC, TRUE);
	smlDevInfDataStoreSetSyncCap(datastore, SML_DEVINF_SYNCTYPE_SERVER_ALERTED_SYNC, TRUE);
	smlDevInfAddDataStore(devinf, datastore);
	
	smlLocationUnref(loc1);
	smlLocationUnref(loc2);
	smlLocationUnref(loc3);
	
	/* The authenticator */
	SmlAuthenticator *auth = smlAuthNew(&error);
	smlAuthSetEnable(auth, FALSE);
	smlAuthRegister(auth, clientmanager, &error);
	
	/* The authenticator */
	SmlAuthenticator *auth2 = smlAuthNew(&error);
	smlAuthSetEnable(auth2, FALSE);
	smlAuthRegister(auth2, servermanager, &error);
	
	clienttracker->session = smlSessionNew(SML_SESSION_TYPE_CLIENT, SML_MIMETYPE_XML, SML_VERSION_12, SML_PROTOCOL_SYNCML, loc, loc, NULL, 0, &error);
	smlSessionSetLocalMaxMsgSize(clienttracker->session, TEST_DEFAULT_MAX_MSG_SIZE);
	sml_fail_unless(smlSessionGetRemoteMaxObjSize(clienttracker->session) == 0, NULL);
	smlSessionSetLocalMaxObjSize(clienttracker->session, TEST_DEFAULT_MAX_OBJ_SIZE);
	sml_fail_unless(smlSessionGetLocalMaxObjSize(clienttracker->session) == TEST_DEFAULT_MAX_OBJ_SIZE, NULL);
	
	smlDevInfConfigureSession(devinf, clienttracker->session);
	
	sml_fail_unless(smlManagerSessionAdd(clientmanager, clienttracker->session, NULL, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	smlLocationUnref(loc);
	
	clienttracker->dssessions[0] = smlDsServerSendAlert(clienttracker->ds[0], clienttracker->session, SML_ALERT_TWO_WAY, "last", "next", _alert_reply, GINT_TO_POINTER(1), &error);
	clienttracker->dssessions[1] = smlDsServerSendAlert(clienttracker->ds[1], clienttracker->session, SML_ALERT_TWO_WAY, "last", "next", _alert_reply, GINT_TO_POINTER(1), &error);
	clienttracker->dssessions[2] = smlDsServerSendAlert(clienttracker->ds[2], clienttracker->session, SML_ALERT_TWO_WAY, "last", "next", _alert_reply, GINT_TO_POINTER(1), &error);
	
	sml_fail_unless(smlDevInfAgentSendDevInf(clientagent, clienttracker->session, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	sml_fail_unless(smlSessionFlush(clienttracker->session, TRUE, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	while (init_alerts_received < 3 || num_sessions < 2) {
		smlManagerDispatch(servermanager);
		smlManagerDispatch(clientmanager);
		usleep(100);
		sml_fail_unless(eventError == NULL, "%s", smlErrorPrint(&eventError));
	}
	
	sml_fail_unless(smlSessionGetRemoteMaxMsgSize(servertracker->session) == TEST_DEFAULT_MAX_MSG_SIZE, NULL);
	sml_fail_unless(smlSessionGetRemoteMaxObjSize(servertracker->session) == TEST_DEFAULT_MAX_OBJ_SIZE, "%u", smlSessionGetRemoteMaxObjSize(servertracker->session));
	smlSessionSetLocalMaxMsgSize(servertracker->session, TEST_DEFAULT_MAX_MSG_SIZE);
	smlSessionSetLocalMaxObjSize(servertracker->session, TEST_DEFAULT_MAX_OBJ_SIZE);
	
	smlDsSessionGetAlert(servertracker->dssessions[0], _recv_alert, GINT_TO_POINTER(1));
	smlDsSessionGetAlert(servertracker->dssessions[1], _recv_alert, GINT_TO_POINTER(1));
	smlDsSessionGetAlert(servertracker->dssessions[2], _recv_alert, GINT_TO_POINTER(1));
	
	while (alerts_received < 3 || !smlDevInfAgentGetSessionDevInf(serveragent, servertracker->session)) {
		smlDsSessionDispatch(servertracker->dssessions[0]);
		smlDsSessionDispatch(servertracker->dssessions[1]);
		smlDsSessionDispatch(servertracker->dssessions[2]);
		usleep(100);
		sml_fail_unless(eventError == NULL, "%s", smlErrorPrint(&eventError));
	}
	
	SmlDevInf *recvClientDevInf = smlDevInfAgentGetSessionDevInf(serveragent, servertracker->session);
	sml_fail_unless(recvClientDevInf != NULL, NULL);
	sml_fail_unless(smlDevInfNumDataStores(recvClientDevInf) == 3, NULL);
	sml_fail_unless(smlDevInfSupportsNumberOfChanges(recvClientDevInf) == TRUE, NULL);
	sml_fail_unless(smlDevInfSupportsLargeObjs(recvClientDevInf) == FALSE, NULL);
	sml_fail_unless(smlDevInfSupportsUTC(recvClientDevInf) == FALSE, NULL);
	
	sml_fail_unless(smlDevInfAgentSendDevInf(serveragent, servertracker->session, &error), "%s", smlErrorPrint(&error));
	sml_fail_unless(error == NULL, "%s", smlErrorPrint(&error));
	
	smlDsSessionSendAlert(servertracker->dssessions[0], SML_ALERT_TWO_WAY, "last", "next", _alert_reply, GINT_TO_POINTER(1), &error);
	smlDsSessionSendAlert(servertracker->dssessions[1], SML_ALERT_TWO_WAY, "last", "next", _alert_reply, GINT_TO_POINTER(1), &error);
	smlDsSessionSendAlert(servertracker->dssessions[2], SML_ALERT_TWO_WAY, "last", "next", _alert_reply, GINT_TO_POINTER(1), &error);
	
	sml_fail_unless(servertracker->session != NULL, NULL);
	sml_fail_unless(smlSessionFlush(servertracker->session, TRUE, &error), NULL);
	
	smlDsSessionGetAlert(clienttracker->dssessions[0], _recv_alert, GINT_TO_POINTER(1));
	smlDsSessionGetAlert(clienttracker->dssessions[1], _recv_alert, GINT_TO_POINTER(1));
	smlDsSessionGetAlert(clienttracker->dssessions[2], _recv_alert, GINT_TO_POINTER(1));
	
	while (alerts_received < 6) {
		smlDsSessionDispatch(servertracker->dssessions[0]);
		smlDsSessionDispatch(servertracker->dssessions[1]);
		smlDsSessionDispatch(servertracker->dssessions[2]);
		
		smlDsSessionDispatch(clienttracker->dssessions[0]);
		smlDsSessionDispatch(clienttracker->dssessions[1]);
		smlDsSessionDispatch(clienttracker->dssessions[2]);
		
		smlManagerDispatch(servermanager);
		smlManagerDispatch(clientmanager);
		usleep(100);
		sml_fail_unless(eventError == NULL, "%s", smlErrorPrint(&eventError));
	}
	
	sml_fail_unless(smlSessionGetRemoteMaxMsgSize(clienttracker->session) == TEST_DEFAULT_MAX_MSG_SIZE, NULL);
	sml_fail_unless(smlSessionGetRemoteMaxObjSize(servertracker->session) == TEST_DEFAULT_MAX_OBJ_SIZE, NULL);
	
	SmlDevInf *recvServerDevInf = smlDevInfAgentGetSessionDevInf(clientagent, clienttracker->session);
	sml_fail_unless(recvServerDevInf != NULL, NULL);
	sml_fail_unless(smlDevInfNumDataStores(recvServerDevInf) == 3, NULL);
	sml_fail_unless(smlDevInfSupportsNumberOfChanges(recvServerDevInf) == TRUE, NULL);
	sml_fail_unless(smlDevInfSupportsLargeObjs(recvServerDevInf) == TRUE, NULL);
	sml_fail_unless(smlDevInfSupportsUTC(recvServerDevInf) == FALSE, NULL);
	
	const char *content_types[] = {SML_ELEMENT_TEXT_VCARD, SML_ELEMENT_TEXT_VCAL, SML_ELEMENT_TEXT_PLAIN};
	int k = 0;
	for (k = 0; k < 3; k++)
	{
		smlDsSessionSendSync(clienttracker->dssessions[k], num_changes, _sync_reply, GINT_TO_POINTER(1), &error);
	
		unsigned int i = 0;
		for (i = 0; i < num_items; i++) {
			sml_fail_unless(smlDsSessionQueueChange(clienttracker->dssessions[k], SML_CHANGE_ADD, "uid", "data", 4, content_types[k], _add_reply, GINT_TO_POINTER(2), &error), NULL);
			sml_fail_unless(error == NULL, NULL);
			sml_fail_unless(smlDsSessionQueueChange(clienttracker->dssessions[k], SML_CHANGE_REPLACE, "uid", "newdata", 7, content_types[k], _modify_reply, GINT_TO_POINTER(1), &error), NULL);
			sml_fail_unless(error == NULL, NULL);
			sml_fail_unless(smlDsSessionQueueChange(clienttracker->dssessions[k], SML_CHANGE_DELETE, "uid", NULL, 0, content_types[k], _delete_reply, GINT_TO_POINTER(1), &error), NULL);
			sml_fail_unless(error == NULL, NULL);
		}

		smlDsSessionCloseSync(clienttracker->dssessions[k], &error);
	}
	
	sml_fail_unless(smlSessionFlush(clienttracker->session, TRUE, &error), NULL);
	
	smlDsSessionGetSync(servertracker->dssessions[0], _recv_sync, GINT_TO_POINTER(1));
	smlDsSessionGetSync(servertracker->dssessions[1], _recv_sync, GINT_TO_POINTER(1));
	smlDsSessionGetSync(servertracker->dssessions[2], _recv_sync, GINT_TO_POINTER(1));
	smlDsSessionGetChanges(servertracker->dssessions[0], _recv_changes, GINT_TO_POINTER(1));
	smlDsSessionGetChanges(servertracker->dssessions[1], _recv_changes, GINT_TO_POINTER(1));
	smlDsSessionGetChanges(servertracker->dssessions[2], _recv_changes, GINT_TO_POINTER(1));
	
	while (syncs_received < 3 || adds_received < num_items * 3 || deletes_received < num_items * 3 || modifies_received < num_items * 3) {
		smlDsSessionDispatch(servertracker->dssessions[0]);
		smlDsSessionDispatch(servertracker->dssessions[1]);
		smlDsSessionDispatch(servertracker->dssessions[2]);
		
		smlDsSessionDispatch(clienttracker->dssessions[0]);
		smlDsSessionDispatch(clienttracker->dssessions[1]);
		smlDsSessionDispatch(clienttracker->dssessions[2]);
		
		smlManagerDispatch(servermanager);
		smlManagerDispatch(clientmanager);
		usleep(100);
		sml_fail_unless(eventError == NULL, "%s", smlErrorPrint(&eventError));
	}
	
	for (k = 0; k < 3; k++)
	{
		smlDsSessionSendSync(servertracker->dssessions[k], num_changes, _sync_reply, GINT_TO_POINTER(1), &error);

		unsigned int i = 0;	
		for (i = 0; i < num_items; i++) {
			sml_fail_unless(smlDsSessionQueueChange(servertracker->dssessions[k], SML_CHANGE_ADD, "uid", "data", 4, content_types[k], _add_reply, GINT_TO_POINTER(1), &error), NULL);
			sml_fail_unless(error == NULL, NULL);
			sml_fail_unless(smlDsSessionQueueChange(servertracker->dssessions[k], SML_CHANGE_REPLACE, "uid", "newdata", 7, content_types[k], _modify_reply, GINT_TO_POINTER(1), &error), NULL);
			sml_fail_unless(error == NULL, NULL);
			sml_fail_unless(smlDsSessionQueueChange(servertracker->dssessions[k], SML_CHANGE_DELETE, "uid", NULL, 0, content_types[k], _delete_reply, GINT_TO_POINTER(1), &error), NULL);
			sml_fail_unless(error == NULL, NULL);
		}

		smlDsSessionCloseSync(servertracker->dssessions[k], &error);
	}
	
	sml_fail_unless(smlSessionFlush(servertracker->session, TRUE, &error), NULL);
	
	smlDsSessionGetSync(clienttracker->dssessions[0], _recv_sync, GINT_TO_POINTER(1));
	smlDsSessionGetSync(clienttracker->dssessions[1], _recv_sync, GINT_TO_POINTER(1));
	smlDsSessionGetSync(clienttracker->dssessions[2], _recv_sync, GINT_TO_POINTER(1));
	smlDsSessionGetChanges(clienttracker->dssessions[0], _recv_changes, GINT_TO_POINTER(1));
	smlDsSessionGetChanges(clienttracker->dssessions[1], _recv_changes, GINT_TO_POINTER(1));
	smlDsSessionGetChanges(clienttracker->dssessions[2], _recv_changes, GINT_TO_POINTER(1));
	
	while (syncs_received < 6 || adds_received < num_items * 6 || deletes_received < num_items * 6 || modifies_received < num_items * 6) {
		smlDsSessionDispatch(servertracker->dssessions[0]);
		smlDsSessionDispatch(servertracker->dssessions[1]);
		smlDsSessionDispatch(servertracker->dssessions[2]);
		
		smlDsSessionDispatch(clienttracker->dssessions[0]);
		smlDsSessionDispatch(clienttracker->dssessions[1]);
		smlDsSessionDispatch(clienttracker->dssessions[2]);
		
		smlManagerDispatch(servermanager);
		smlManagerDispatch(clientmanager);
		usleep(100);
		sml_fail_unless(eventError == NULL, "%s", smlErrorPrint(&eventError));
	}

	unsigned int i = 0;	
	for (i = 0; i < num_items; i++) {
		sml_fail_unless(smlDsSessionQueueMap(clienttracker->dssessions[0], "uid", "newuid", &error), NULL);
		sml_fail_unless(smlDsSessionQueueMap(clienttracker->dssessions[1], "uid", "newuid", &error), NULL);
		sml_fail_unless(smlDsSessionQueueMap(clienttracker->dssessions[2], "uid", "newuid", &error), NULL);
	}
	
	smlDsSessionCloseMap(clienttracker->dssessions[0], _map_reply, GINT_TO_POINTER(1), &error);
	smlDsSessionCloseMap(clienttracker->dssessions[1], _map_reply, GINT_TO_POINTER(1), &error);
	smlDsSessionCloseMap(clienttracker->dssessions[2], _map_reply, GINT_TO_POINTER(1), &error);
	
	sml_fail_unless(smlSessionFlush(clienttracker->session, TRUE, &error), NULL);
	
	while (delete_replies_received < num_items * 6) {
		smlDsSessionDispatch(servertracker->dssessions[0]);
		smlDsSessionDispatch(servertracker->dssessions[1]);
		smlDsSessionDispatch(servertracker->dssessions[2]);
		
		smlDsSessionDispatch(clienttracker->dssessions[0]);
		smlDsSessionDispatch(clienttracker->dssessions[1]);
		smlDsSessionDispatch(clienttracker->dssessions[2]);
		
		smlManagerDispatch(servermanager);
		smlManagerDispatch(clientmanager);
		usleep(100);
		sml_fail_unless(eventError == NULL, "%s", smlErrorPrint(&eventError));
	}
	
	
	sml_fail_unless(smlSessionGetRemoteMaxMsgSize(servertracker->session) == TEST_DEFAULT_MAX_MSG_SIZE, NULL);
	sml_fail_unless(smlSessionGetRemoteMaxObjSize(servertracker->session) == TEST_DEFAULT_MAX_OBJ_SIZE, NULL);
	
	sml_fail_unless(smlSessionGetRemoteMaxMsgSize(clienttracker->session) == TEST_DEFAULT_MAX_MSG_SIZE, NULL);
	sml_fail_unless(smlSessionGetRemoteMaxObjSize(clienttracker->session) == TEST_DEFAULT_MAX_OBJ_SIZE, NULL);
	
	sml_fail_unless(servertracker->session != NULL, NULL);
	sml_fail_unless(smlSessionEnd(servertracker->session, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	smlSessionUnref(servertracker->session); /* unref from ref at callback */
	smlSessionUnref(clienttracker->session); /* unref from ref at callback */
	smlSessionUnref(clienttracker->session); /* unref from smlSessionNew */
	
	while (num_end < 2 || replies_received < 15 || num_finals < 6) {
		smlManagerDispatch(servermanager);
		smlManagerDispatch(clientmanager);
		usleep(100);
		sml_fail_unless(eventError == NULL, "%s", smlErrorPrint(&eventError));
	}
	
	sml_fail_unless(init_alerts_received == 3, NULL);
	sml_fail_unless(alerts_received == 6, NULL);
	sml_fail_unless(syncs_received == 6, NULL);
	sml_fail_unless(sync_ends_received == 0, NULL);
	sml_fail_unless(changes_received == 0, NULL);
	sml_fail_unless(replies_received == 15, NULL);
	sml_fail_unless(adds_received == num_items * 6, NULL);
	sml_fail_unless(add_replies_received == num_items * 6, NULL);
	sml_fail_unless(deletes_received == num_items * 6, NULL);
	sml_fail_unless(delete_replies_received == num_items * 6, NULL);
	sml_fail_unless(modifies_received == num_items * 6, NULL);
	sml_fail_unless(modify_replies_received == num_items * 6, NULL);
	sml_fail_unless(transport_errors == 0, NULL);
	sml_fail_unless(num_sessions == 2, NULL);
	sml_fail_unless(num_finals == 6, NULL);
	sml_fail_unless(num_end == 2, NULL);
	sml_fail_unless(session_errors == 0, NULL);
	
	
	smlDsSessionUnref(clienttracker->dssessions[0]);
	smlDsSessionUnref(clienttracker->dssessions[1]);
	smlDsSessionUnref(clienttracker->dssessions[2]);
	
	smlDsSessionUnref(servertracker->dssessions[0]);
	smlDsSessionUnref(servertracker->dssessions[1]);
	smlDsSessionUnref(servertracker->dssessions[2]);
	
	smlDsServerFree(clienttracker->ds[0]);
	smlDsServerFree(clienttracker->ds[1]);
	smlDsServerFree(clienttracker->ds[2]);
	
	smlDsServerFree(servertracker->ds[0]);
	smlDsServerFree(servertracker->ds[1]);
	smlDsServerFree(servertracker->ds[2]);
	
	smlAuthFree(auth);
	smlAuthFree(auth2);
	
	g_free(clienttracker);
	g_free(servertracker);
	
	smlManagerStop(clientmanager);
	smlManagerStop(servermanager);
	
	while(num_disconnects < 2) {
		smlManagerDispatch(clientmanager);
		smlManagerDispatch(servermanager);
	}
	
	smlManagerFree(clientmanager);
	smlManagerFree(servermanager);
	
	sml_fail_unless(smlTransportFinalize(server, &error), NULL);
	sml_fail_unless(smlTransportFinalize(client, &error), "%s", smlErrorPrint(&error));
	
	smlTransportFree(server);
	smlTransportFree(client);

	smlDevInfAgentFree(clientagent);
	smlDevInfAgentFree(serveragent);
}
END_TEST

#ifdef ENABLE_OBEX
#ifdef ENABLE_OPENOBEX_TCP
START_TEST (sync_send_san)
{
	reset_testbed();

	unsigned int num_items = 10;
	num_changes = 3 * num_items;
	
	SmlError *error = NULL;
	
	/* The OMA DS client is an OBEX server. */
	SmlTransport *client = smlTransportNew(SML_TRANSPORT_OBEX_SERVER, &error);
	sml_fail_unless(smlTransportSetConnectionType(client, SML_TRANSPORT_CONNECTION_TYPE_NET, &error), NULL);
	sml_fail_unless(smlTransportSetConfigOption(client, "PORT", "12010", &error), "%s", smlErrorPrint(&error));

	/* The OMA DS server is an OBEX client. */
	SmlTransport *server = smlTransportNew(SML_TRANSPORT_OBEX_CLIENT, &error);
	sml_fail_unless(smlTransportSetConnectionType(server, SML_TRANSPORT_CONNECTION_TYPE_NET, &error), "%s", smlErrorPrint(&error));
	sml_fail_unless(smlTransportSetConfigOption(server, "PORT", "12010", &error), "%s", smlErrorPrint(&error));
	sml_fail_unless(smlTransportSetConfigOption(server, "URL", "127.0.0.1", &error), "%s", smlErrorPrint(&error));
	
	managerTracker *clienttracker = g_malloc0(sizeof(managerTracker));
	SmlManager *clientmanager = clienttracker->manager = smlManagerNew(client, &error);
	smlManagerSetEventCallback(clienttracker->manager, _manager_event, clienttracker);
	managerTracker *servertracker = g_malloc0(sizeof(managerTracker));
	SmlManager *servermanager = servertracker->manager = smlManagerNew(server, &error);
	smlManagerSetEventCallback(servertracker->manager, _manager_event, servertracker);
	smlManagerSetLocalMaxMsgSize(servertracker->manager, TEST_DEFAULT_MAX_MSG_SIZE);
	smlManagerSetLocalMaxObjSize(servertracker->manager, TEST_DEFAULT_MAX_OBJ_SIZE);
	
	sml_fail_unless(smlManagerStart(clientmanager, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	sml_fail_unless(smlManagerStart(servermanager, &error), NULL);
	sml_fail_unless(error == NULL, NULL);

	sml_fail_unless(smlTransportInitialize(client, &error), "%s", smlErrorPrint(&error));
	sml_fail_unless(smlTransportInitialize(server, &error), "%s", smlErrorPrint(&error));

	SmlLocation *loc = smlLocationNew("test", NULL, &error);
	sml_fail_unless(loc != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);
	
	SmlLocation *loc1 = smlLocationNew("test1", NULL, &error);
	sml_fail_unless(loc1 != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);
	
	SmlLocation *loc2 = smlLocationNew("test2", NULL, &error);
	sml_fail_unless(loc2 != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);
	
	SmlLocation *loc3 = smlLocationNew("test3", NULL, &error);
	sml_fail_unless(loc3 != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);
	
	clienttracker->ds[0] = smlDsClientNew(SML_ELEMENT_TEXT_VCARD, loc1, loc1, &error);
	clienttracker->ds[1] = smlDsClientNew(SML_ELEMENT_TEXT_VCAL, loc2, loc2, &error);
	clienttracker->ds[2] = smlDsClientNew(SML_ELEMENT_TEXT_PLAIN, loc3, loc3, &error);
	smlDsServerSetSanSessionCallback(clienttracker->ds[0], _recv_san, GINT_TO_POINTER(1));
	smlDsServerSetSanSessionCallback(clienttracker->ds[1], _recv_san, GINT_TO_POINTER(1));
	smlDsServerSetSanSessionCallback(clienttracker->ds[2], _recv_san, GINT_TO_POINTER(1));
	smlDsServerRegister(clienttracker->ds[0], clientmanager, &error);
	smlDsServerRegister(clienttracker->ds[1], clientmanager, &error);
	smlDsServerRegister(clienttracker->ds[2], clientmanager, &error);
				
	servertracker->ds[0] = smlDsServerNew(SML_ELEMENT_TEXT_VCARD, loc1, &error);
	servertracker->ds[1] = smlDsServerNew(SML_ELEMENT_TEXT_VCAL, loc2, &error);
	servertracker->ds[2] = smlDsServerNew(SML_ELEMENT_TEXT_PLAIN, loc3, &error);
	smlDsServerSetConnectCallback(servertracker->ds[0], _recv_init_alert, servertracker);
	smlDsServerSetConnectCallback(servertracker->ds[1], _recv_init_alert, servertracker);
	smlDsServerSetConnectCallback(servertracker->ds[2], _recv_init_alert, servertracker);
	smlDsServerRegister(servertracker->ds[0], servermanager, &error);
	smlDsServerRegister(servertracker->ds[1], servermanager, &error);
	smlDsServerRegister(servertracker->ds[2], servermanager, &error);
	
	smlLocationUnref(loc1);
	smlLocationUnref(loc2);
	smlLocationUnref(loc3);
	
	/* The authenticator */
	SmlAuthenticator *auth = smlAuthNew(&error);
	smlAuthSetEnable(auth, FALSE);
	smlAuthRegister(auth, clientmanager, &error);
	
	/* The authenticator */
	SmlAuthenticator *auth2 = smlAuthNew(&error);
	smlAuthSetEnable(auth2, FALSE);
	smlAuthRegister(auth2, servermanager, &error);
	
	sml_fail_unless(smlTransportConnect(server, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	SmlNotification *san = smlNotificationNew(SML_SAN_VERSION_11, SML_SAN_UIMODE_USER, SML_SAN_INITIATOR_SERVER, 65535, "test", "/", SML_MIMETYPE_XML, &error);
	sml_fail_unless(san != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);
	smlNotificationSetManager(san, servermanager);
	
	sml_fail_unless(smlDsServerAddSan(servertracker->ds[0], san, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	sml_fail_unless(smlDsServerAddSan(servertracker->ds[1], san, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	sml_fail_unless(smlDsServerAddSan(servertracker->ds[2], san, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	sml_fail_unless(smlNotificationSend(san, server, &error), NULL);
	sml_fail_unless(error == NULL, NULL);

	smlNotificationFree(san);
	
	while (sans_received < 3 || num_sessions < 2) {
		smlManagerDispatch(servermanager);
		smlManagerDispatch(clientmanager);
		usleep(100);
		sml_fail_unless(eventError == NULL, "%s", smlErrorPrint(&eventError));
	}
	
	sml_fail_unless(clienttracker->session != NULL, NULL);
	
	smlLocationUnref(loc);
	
	clienttracker->dssessions[0] = smlDsServerSendAlert(clienttracker->ds[0], clienttracker->session, SML_ALERT_TWO_WAY, "last", "next", _alert_reply, GINT_TO_POINTER(1), &error);
	clienttracker->dssessions[1] = smlDsServerSendAlert(clienttracker->ds[1], clienttracker->session, SML_ALERT_TWO_WAY, "last", "next", _alert_reply, GINT_TO_POINTER(1), &error);
	clienttracker->dssessions[2] = smlDsServerSendAlert(clienttracker->ds[2], clienttracker->session, SML_ALERT_TWO_WAY, "last", "next", _alert_reply, GINT_TO_POINTER(1), &error);
	
	sml_fail_unless(smlSessionFlush(clienttracker->session, TRUE, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	while (init_alerts_received < 3 || num_sessions < 2) {
		smlManagerDispatch(servermanager);
		smlManagerDispatch(clientmanager);
		usleep(100);
		sml_fail_unless(eventError == NULL, "%s", smlErrorPrint(&eventError));
	}
	
	smlDsSessionGetAlert(servertracker->dssessions[0], _recv_alert, GINT_TO_POINTER(1));
	smlDsSessionGetAlert(servertracker->dssessions[1], _recv_alert, GINT_TO_POINTER(1));
	smlDsSessionGetAlert(servertracker->dssessions[2], _recv_alert, GINT_TO_POINTER(1));
	
	smlDsSessionSendAlert(servertracker->dssessions[0], SML_ALERT_TWO_WAY, "last", "next", _alert_reply, GINT_TO_POINTER(1), &error);
	smlDsSessionSendAlert(servertracker->dssessions[1], SML_ALERT_TWO_WAY, "last", "next", _alert_reply, GINT_TO_POINTER(1), &error);
	smlDsSessionSendAlert(servertracker->dssessions[2], SML_ALERT_TWO_WAY, "last", "next", _alert_reply, GINT_TO_POINTER(1), &error);
	
	sml_fail_unless(servertracker->session != NULL, NULL);
	sml_fail_unless(smlSessionFlush(servertracker->session, TRUE, &error), NULL);
	
	smlDsSessionGetAlert(clienttracker->dssessions[0], _recv_alert, GINT_TO_POINTER(1));
	smlDsSessionGetAlert(clienttracker->dssessions[1], _recv_alert, GINT_TO_POINTER(1));
	smlDsSessionGetAlert(clienttracker->dssessions[2], _recv_alert, GINT_TO_POINTER(1));
	
	while (alerts_received < 6) {
		smlDsSessionDispatch(servertracker->dssessions[0]);
		smlDsSessionDispatch(servertracker->dssessions[1]);
		smlDsSessionDispatch(servertracker->dssessions[2]);
		
		smlDsSessionDispatch(clienttracker->dssessions[0]);
		smlDsSessionDispatch(clienttracker->dssessions[1]);
		smlDsSessionDispatch(clienttracker->dssessions[2]);
		
		smlManagerDispatch(servermanager);
		smlManagerDispatch(clientmanager);
		usleep(100);
		sml_fail_unless(eventError == NULL, "%s", smlErrorPrint(&eventError));
	}
	
	const char *content_types[] = {SML_ELEMENT_TEXT_VCARD, SML_ELEMENT_TEXT_VCAL, SML_ELEMENT_TEXT_PLAIN};
	int k = 0;
	for (k = 0; k < 3; k++)
	{
		smlDsSessionSendSync(clienttracker->dssessions[k], num_changes, _sync_reply, GINT_TO_POINTER(1), &error);
	
		unsigned int i = 0;
		for (i = 0; i < num_items; i++) {
			sml_fail_unless(smlDsSessionQueueChange(clienttracker->dssessions[k], SML_CHANGE_ADD, "uid", "data", 4, content_types[k], _add_reply, GINT_TO_POINTER(2), &error), NULL);
			sml_fail_unless(error == NULL, NULL);
			sml_fail_unless(smlDsSessionQueueChange(clienttracker->dssessions[k], SML_CHANGE_REPLACE, "uid", "newdata", 7, content_types[k], _modify_reply, GINT_TO_POINTER(1), &error), NULL);
			sml_fail_unless(error == NULL, NULL);
			sml_fail_unless(smlDsSessionQueueChange(clienttracker->dssessions[k], SML_CHANGE_DELETE, "uid", NULL, 0, content_types[k], _delete_reply, GINT_TO_POINTER(1), &error), NULL);
			sml_fail_unless(error == NULL, NULL);
		}

		smlDsSessionCloseSync(clienttracker->dssessions[k], &error);
	}
	
	sml_fail_unless(smlSessionFlush(clienttracker->session, TRUE, &error), NULL);
	
	smlDsSessionGetSync(servertracker->dssessions[0], _recv_sync, GINT_TO_POINTER(1));
	smlDsSessionGetSync(servertracker->dssessions[1], _recv_sync, GINT_TO_POINTER(1));
	smlDsSessionGetSync(servertracker->dssessions[2], _recv_sync, GINT_TO_POINTER(1));
	smlDsSessionGetChanges(servertracker->dssessions[0], _recv_changes, GINT_TO_POINTER(1));
	smlDsSessionGetChanges(servertracker->dssessions[1], _recv_changes, GINT_TO_POINTER(1));
	smlDsSessionGetChanges(servertracker->dssessions[2], _recv_changes, GINT_TO_POINTER(1));
	
	while (syncs_received < 3 || adds_received < num_items * 3 || deletes_received < num_items * 3 || modifies_received < num_items * 3) {
		smlDsSessionDispatch(servertracker->dssessions[0]);
		smlDsSessionDispatch(servertracker->dssessions[1]);
		smlDsSessionDispatch(servertracker->dssessions[2]);
		
		smlDsSessionDispatch(clienttracker->dssessions[0]);
		smlDsSessionDispatch(clienttracker->dssessions[1]);
		smlDsSessionDispatch(clienttracker->dssessions[2]);
		
		smlManagerDispatch(servermanager);
		smlManagerDispatch(clientmanager);
		usleep(100);
		sml_fail_unless(eventError == NULL, "%s", smlErrorPrint(&eventError));
	}
	
	sml_fail_unless(servertracker->session != NULL, NULL);
	sml_fail_unless(smlSessionEnd(servertracker->session, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	smlSessionUnref(servertracker->session);
	smlSessionUnref(clienttracker->session);
	
	while (num_end < 2 || replies_received < 9 || num_finals < 5) {
		smlManagerDispatch(servermanager);
		smlManagerDispatch(clientmanager);
		usleep(100);
		sml_fail_unless(eventError == NULL, "%s", smlErrorPrint(&eventError));
	}
	
	sml_fail_unless(init_alerts_received == 3, NULL);
	sml_fail_unless(alerts_received == 6, NULL);
	sml_fail_unless(syncs_received == 3, NULL);
	sml_fail_unless(sync_ends_received == 0, NULL);
	sml_fail_unless(changes_received == 0, NULL);
	sml_fail_unless(replies_received == 9, NULL);
	sml_fail_unless(adds_received == num_items * 3, NULL);
	sml_fail_unless(add_replies_received == num_items * 3, NULL);
	sml_fail_unless(deletes_received == num_items * 3, NULL);
	sml_fail_unless(delete_replies_received == num_items * 3, NULL);
	sml_fail_unless(modifies_received == num_items * 3, NULL);
	sml_fail_unless(modify_replies_received == num_items * 3, NULL);
	sml_fail_unless(transport_errors == 0, NULL);
	sml_fail_unless(num_sessions == 2, NULL);
	sml_fail_unless(num_finals == 5, NULL);
	sml_fail_unless(num_end == 2, NULL);
	sml_fail_unless(session_errors == 0, NULL);
	
	
	smlDsSessionUnref(clienttracker->dssessions[0]);
	smlDsSessionUnref(clienttracker->dssessions[1]);
	smlDsSessionUnref(clienttracker->dssessions[2]);
	
	smlDsSessionUnref(servertracker->dssessions[0]);
	smlDsSessionUnref(servertracker->dssessions[1]);
	smlDsSessionUnref(servertracker->dssessions[2]);
	
	smlDsServerFree(clienttracker->ds[0]);
	smlDsServerFree(clienttracker->ds[1]);
	smlDsServerFree(clienttracker->ds[2]);
	
	smlDsServerFree(servertracker->ds[0]);
	smlDsServerFree(servertracker->ds[1]);
	smlDsServerFree(servertracker->ds[2]);
	
	smlAuthFree(auth);
	smlAuthFree(auth2);
	
	g_free(clienttracker);
	g_free(servertracker);
	
	/* first we have to stop the OBEX client or we get errors */

	smlManagerStop(servermanager);
	while(num_disconnects < 1) {
		smlManagerDispatch(servermanager);
	}
	smlManagerStop(clientmanager);
	while(num_disconnects < 2) {
		smlManagerDispatch(clientmanager);
	}
	
	smlManagerFree(clientmanager);
	smlManagerFree(servermanager);
	
	sml_fail_unless(smlTransportFinalize(server, &error), "%s", smlErrorPrint(&error));
	sml_fail_unless(smlTransportFinalize(client, &error), NULL);
	
	smlTransportFree(server);
	smlTransportFree(client);
}
END_TEST
#endif
#endif

#ifdef ENABLE_OBEX
#ifdef ENABLE_OPENOBEX_TCP
START_TEST (sync_send_san12)
{
	reset_testbed();

	unsigned int num_items = 10;
	num_changes = 3 * num_items;
	
	SmlError *error = NULL;
	SmlTransport *server = smlTransportNew(SML_TRANSPORT_OBEX_CLIENT, &error);
	SmlTransport *client = smlTransportNew(SML_TRANSPORT_OBEX_SERVER, &error);
	
	sml_fail_unless(smlTransportSetConfigOption(client, "PORT", "12011", &error), NULL);
	sml_fail_unless(smlTransportSetConnectionType(client, SML_TRANSPORT_CONNECTION_TYPE_NET, &error), NULL);

	sml_fail_unless(smlTransportSetConfigOption(server, "URL", "127.0.0.1", &error), NULL);
	sml_fail_unless(smlTransportSetConfigOption(server, "PORT", "12011", &error), NULL);
	sml_fail_unless(smlTransportSetConnectionType(server, SML_TRANSPORT_CONNECTION_TYPE_NET, &error), NULL);
	
	managerTracker *clienttracker = g_malloc0(sizeof(managerTracker));
	SmlManager *clientmanager = clienttracker->manager = smlManagerNew(client, &error);
	smlManagerSetEventCallback(clienttracker->manager, _manager_event, clienttracker);
	managerTracker *servertracker = g_malloc0(sizeof(managerTracker));
	SmlManager *servermanager = servertracker->manager = smlManagerNew(server, &error);
	smlManagerSetEventCallback(servertracker->manager, _manager_event, servertracker);
	smlManagerSetLocalMaxMsgSize(servertracker->manager, TEST_DEFAULT_MAX_MSG_SIZE);
	smlManagerSetLocalMaxObjSize(servertracker->manager, TEST_DEFAULT_MAX_OBJ_SIZE);
	
	sml_fail_unless(smlManagerStart(clientmanager, &error), "%s", smlErrorPrint(&error));
	sml_fail_unless(error == NULL, NULL);
	sml_fail_unless(smlManagerStart(servermanager, &error), "%s", smlErrorPrint(&error));
	sml_fail_unless(error == NULL, NULL);

	sml_fail_unless(smlTransportInitialize(client, &error), "%s", smlErrorPrint(&error));
	sml_fail_unless(smlTransportInitialize(server, &error), "%s", smlErrorPrint(&error));

	SmlLocation *loc1 = smlLocationNew("test1", NULL, &error);
	sml_fail_unless(loc1 != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);
	
	SmlLocation *loc2 = smlLocationNew("test2", NULL, &error);
	sml_fail_unless(loc2 != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);
	
	SmlLocation *loc3 = smlLocationNew("test3", NULL, &error);
	sml_fail_unless(loc3 != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);
	
	clienttracker->ds[0] = smlDsClientNew(SML_ELEMENT_TEXT_VCARD, loc1, loc1, &error);
	clienttracker->ds[1] = smlDsClientNew(SML_ELEMENT_TEXT_VCAL, loc2, loc2, &error);
	clienttracker->ds[2] = smlDsClientNew(SML_ELEMENT_TEXT_PLAIN, loc3, loc3, &error);
	smlDsServerSetSanSessionCallback(clienttracker->ds[0], _recv_san, GINT_TO_POINTER(1));
	smlDsServerSetSanSessionCallback(clienttracker->ds[1], _recv_san, GINT_TO_POINTER(1));
	smlDsServerSetSanSessionCallback(clienttracker->ds[2], _recv_san, GINT_TO_POINTER(1));
	smlDsServerRegister(clienttracker->ds[0], clientmanager, &error);
	smlDsServerRegister(clienttracker->ds[1], clientmanager, &error);
	smlDsServerRegister(clienttracker->ds[2], clientmanager, &error);
				
	servertracker->ds[0] = smlDsServerNew(SML_ELEMENT_TEXT_VCARD, loc1, &error);
	servertracker->ds[1] = smlDsServerNew(SML_ELEMENT_TEXT_VCAL, loc2, &error);
	servertracker->ds[2] = smlDsServerNew(SML_ELEMENT_TEXT_PLAIN, loc3, &error);
	smlDsServerSetConnectCallback(servertracker->ds[0], _recv_init_alert, servertracker);
	smlDsServerSetConnectCallback(servertracker->ds[1], _recv_init_alert, servertracker);
	smlDsServerSetConnectCallback(servertracker->ds[2], _recv_init_alert, servertracker);
	smlDsServerRegister(servertracker->ds[0], servermanager, &error);
	smlDsServerRegister(servertracker->ds[1], servermanager, &error);
	smlDsServerRegister(servertracker->ds[2], servermanager, &error);
	
	smlLocationUnref(loc1);
	smlLocationUnref(loc2);
	smlLocationUnref(loc3);
	
	/* The authenticator */
	SmlAuthenticator *auth = smlAuthNew(&error);
	smlAuthSetEnable(auth, FALSE);
	smlAuthRegister(auth, clientmanager, &error);
	
	/* The authenticator */
	SmlAuthenticator *auth2 = smlAuthNew(&error);
	smlAuthSetEnable(auth2, FALSE);
	smlAuthRegister(auth2, servermanager, &error);
	
	sml_fail_unless(smlTransportConnect(server, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	SmlNotification *san = smlNotificationNew(SML_SAN_VERSION_12, SML_SAN_UIMODE_USER, SML_SAN_INITIATOR_SERVER, 65535, "test", "/", SML_MIMETYPE_XML, &error);
	sml_fail_unless(san != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);
	smlNotificationSetManager(san, servermanager);
	
	sml_fail_unless(smlDsServerAddSan(servertracker->ds[0], san, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	sml_fail_unless(smlDsServerAddSan(servertracker->ds[1], san, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	sml_fail_unless(smlDsServerAddSan(servertracker->ds[2], san, &error), NULL);
	sml_fail_unless(error == NULL, NULL);

	sml_fail_unless(smlNotificationSend(san, server, &error), "%s", smlErrorPrint(&error));
	sml_fail_unless(error == NULL, "%s", smlErrorPrint(&error));
	
	smlNotificationFree(san);
	
	while (sans_received < 3 || num_sessions < 1) {
		smlManagerDispatch(servermanager);
		smlManagerDispatch(clientmanager);
		usleep(100);
		sml_fail_unless(eventError == NULL, "%s", smlErrorPrint(&eventError));
	}
	
	sml_fail_unless(clienttracker->session != NULL, NULL);
	
	clienttracker->dssessions[0] = smlDsServerSendAlert(clienttracker->ds[0], clienttracker->session, SML_ALERT_TWO_WAY, "last", "next", _alert_reply, GINT_TO_POINTER(1), &error);
	clienttracker->dssessions[1] = smlDsServerSendAlert(clienttracker->ds[1], clienttracker->session, SML_ALERT_TWO_WAY, "last", "next", _alert_reply, GINT_TO_POINTER(1), &error);
	clienttracker->dssessions[2] = smlDsServerSendAlert(clienttracker->ds[2], clienttracker->session, SML_ALERT_TWO_WAY, "last", "next", _alert_reply, GINT_TO_POINTER(1), &error);
	
	sml_fail_unless(smlSessionFlush(clienttracker->session, TRUE, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	while (init_alerts_received < 3 || num_sessions < 2) {
		smlManagerDispatch(servermanager);
		smlManagerDispatch(clientmanager);
		usleep(100);
		sml_fail_unless(eventError == NULL, "%s", smlErrorPrint(&eventError));
	}
	
	smlDsSessionGetAlert(servertracker->dssessions[0], _recv_alert, GINT_TO_POINTER(1));
	smlDsSessionGetAlert(servertracker->dssessions[1], _recv_alert, GINT_TO_POINTER(1));
	smlDsSessionGetAlert(servertracker->dssessions[2], _recv_alert, GINT_TO_POINTER(1));
	
	smlDsSessionSendAlert(servertracker->dssessions[0], SML_ALERT_TWO_WAY, "last", "next", _alert_reply, GINT_TO_POINTER(1), &error);
	smlDsSessionSendAlert(servertracker->dssessions[1], SML_ALERT_TWO_WAY, "last", "next", _alert_reply, GINT_TO_POINTER(1), &error);
	smlDsSessionSendAlert(servertracker->dssessions[2], SML_ALERT_TWO_WAY, "last", "next", _alert_reply, GINT_TO_POINTER(1), &error);
	
	sml_fail_unless(servertracker->session != NULL, NULL);
	sml_fail_unless(smlSessionFlush(servertracker->session, TRUE, &error), NULL);
	
	smlDsSessionGetAlert(clienttracker->dssessions[0], _recv_alert, GINT_TO_POINTER(1));
	smlDsSessionGetAlert(clienttracker->dssessions[1], _recv_alert, GINT_TO_POINTER(1));
	smlDsSessionGetAlert(clienttracker->dssessions[2], _recv_alert, GINT_TO_POINTER(1));
	
	while (alerts_received < 6) {
		smlDsSessionDispatch(servertracker->dssessions[0]);
		smlDsSessionDispatch(servertracker->dssessions[1]);
		smlDsSessionDispatch(servertracker->dssessions[2]);
		
		smlDsSessionDispatch(clienttracker->dssessions[0]);
		smlDsSessionDispatch(clienttracker->dssessions[1]);
		smlDsSessionDispatch(clienttracker->dssessions[2]);
		
		smlManagerDispatch(servermanager);
		smlManagerDispatch(clientmanager);
		usleep(100);
		sml_fail_unless(eventError == NULL, "%s", smlErrorPrint(&eventError));
	}
	
	const char *content_types[] = {SML_ELEMENT_TEXT_VCARD, SML_ELEMENT_TEXT_VCAL, SML_ELEMENT_TEXT_PLAIN};
	int k = 0;
	for (k = 0; k < 3; k++)
	{
		smlDsSessionSendSync(clienttracker->dssessions[k], num_changes, _sync_reply, GINT_TO_POINTER(1), &error);
	
		unsigned int i = 0;
		for (i = 0; i < num_items; i++) {
			sml_fail_unless(smlDsSessionQueueChange(clienttracker->dssessions[k], SML_CHANGE_ADD, "uid", "data", 4, content_types[k], _add_reply, GINT_TO_POINTER(2), &error), NULL);
			sml_fail_unless(error == NULL, NULL);
			sml_fail_unless(smlDsSessionQueueChange(clienttracker->dssessions[k], SML_CHANGE_REPLACE, "uid", "newdata", 7, content_types[k], _modify_reply, GINT_TO_POINTER(1), &error), NULL);
			sml_fail_unless(error == NULL, NULL);
			sml_fail_unless(smlDsSessionQueueChange(clienttracker->dssessions[k], SML_CHANGE_DELETE, "uid", NULL, 0, content_types[k], _delete_reply, GINT_TO_POINTER(1), &error), NULL);
			sml_fail_unless(error == NULL, NULL);
		}

		smlDsSessionCloseSync(clienttracker->dssessions[k], &error);
	}
	
	sml_fail_unless(smlSessionFlush(clienttracker->session, TRUE, &error), "%s", smlErrorPrint(&error));
	
	smlDsSessionGetSync(servertracker->dssessions[0], _recv_sync, GINT_TO_POINTER(1));
	smlDsSessionGetSync(servertracker->dssessions[1], _recv_sync, GINT_TO_POINTER(1));
	smlDsSessionGetSync(servertracker->dssessions[2], _recv_sync, GINT_TO_POINTER(1));
	smlDsSessionGetChanges(servertracker->dssessions[0], _recv_changes, GINT_TO_POINTER(1));
	smlDsSessionGetChanges(servertracker->dssessions[1], _recv_changes, GINT_TO_POINTER(1));
	smlDsSessionGetChanges(servertracker->dssessions[2], _recv_changes, GINT_TO_POINTER(1));
	
	while (syncs_received < 3 || adds_received < num_items * 3 || deletes_received < num_items * 3 || modifies_received < num_items * 3) {
		smlDsSessionDispatch(servertracker->dssessions[0]);
		smlDsSessionDispatch(servertracker->dssessions[1]);
		smlDsSessionDispatch(servertracker->dssessions[2]);
		
		smlDsSessionDispatch(clienttracker->dssessions[0]);
		smlDsSessionDispatch(clienttracker->dssessions[1]);
		smlDsSessionDispatch(clienttracker->dssessions[2]);
		
		smlManagerDispatch(servermanager);
		smlManagerDispatch(clientmanager);
		usleep(100);
		sml_fail_unless(eventError == NULL, "%s", smlErrorPrint(&eventError));
	}
	
	sml_fail_unless(servertracker->session != NULL, NULL);
	sml_fail_unless(smlSessionEnd(servertracker->session, &error), "%s", smlErrorPrint(&error));
	sml_fail_unless(error == NULL, NULL);
	smlSessionUnref(servertracker->session);
	smlSessionUnref(clienttracker->session);
	
	while (num_end < 2 || replies_received < 9 || num_finals < 5) {
		smlManagerDispatch(servermanager);
		smlManagerDispatch(clientmanager);
		usleep(100);
		sml_fail_unless(eventError == NULL, "%s", smlErrorPrint(&eventError));
	}
	
	sml_fail_unless(init_alerts_received == 3, NULL);
	sml_fail_unless(alerts_received == 6, NULL);
	sml_fail_unless(syncs_received == 3, NULL);
	sml_fail_unless(sync_ends_received == 0, NULL);
	sml_fail_unless(changes_received == 0, NULL);
	sml_fail_unless(replies_received == 9, NULL);
	sml_fail_unless(adds_received == num_items * 3, NULL);
	sml_fail_unless(add_replies_received == num_items * 3, NULL);
	sml_fail_unless(deletes_received == num_items * 3, NULL);
	sml_fail_unless(delete_replies_received == num_items * 3, NULL);
	sml_fail_unless(modifies_received == num_items * 3, NULL);
	sml_fail_unless(modify_replies_received == num_items * 3, NULL);
	sml_fail_unless(transport_errors == 0, NULL);
	sml_fail_unless(num_sessions == 2, NULL);
	sml_fail_unless(num_finals == 5, NULL);
	sml_fail_unless(num_end == 2, NULL);
	sml_fail_unless(session_errors == 0, NULL);

	smlDsSessionUnref(clienttracker->dssessions[0]);
	smlDsSessionUnref(clienttracker->dssessions[1]);
	smlDsSessionUnref(clienttracker->dssessions[2]);
	
	smlDsSessionUnref(servertracker->dssessions[0]);
	smlDsSessionUnref(servertracker->dssessions[1]);
	smlDsSessionUnref(servertracker->dssessions[2]);
	
	smlDsServerFree(clienttracker->ds[0]);
	smlDsServerFree(clienttracker->ds[1]);
	smlDsServerFree(clienttracker->ds[2]);
	
	smlDsServerFree(servertracker->ds[0]);
	smlDsServerFree(servertracker->ds[1]);
	smlDsServerFree(servertracker->ds[2]);
	
	smlAuthFree(auth);
	smlAuthFree(auth2);
	
	g_free(clienttracker);
	g_free(servertracker);
	
	/* first we have to stop the OBEX client or we get errors */

	smlManagerStop(servermanager);
	while(num_disconnects < 1) {
		smlManagerDispatch(servermanager);
	}
	smlManagerStop(clientmanager);
	while(num_disconnects < 2) {
		smlManagerDispatch(clientmanager);
	}
	
	
	smlManagerFree(clientmanager);
	smlManagerFree(servermanager);
	
	sml_fail_unless(smlTransportFinalize(server, &error), "%s", smlErrorPrint(&error));
	sml_fail_unless(smlTransportFinalize(client, &error), "%s", smlErrorPrint(&error));
	
	smlTransportFree(server);
	smlTransportFree(client);
}
END_TEST
#endif
#endif

@SML_TESTCASE_CODE@

