/*
 * 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/sml_session_internals.h>

#define NUM_SESSIONS 30

unsigned int defaultMaxMsgSize = 10240;
unsigned int defaultMaxObjSize = 1024000;

typedef struct managerTracker {
	SmlManager *manager;
	SmlSession *sessions[NUM_SESSIONS];
	int num_sessions;
} managerTracker;

START_TEST (manager_new)
{
	setup_testbed(NULL);
	
	SmlError *error = NULL;
	SmlTransport *server = smlTransportNew(SML_TRANSPORT_HTTP_SERVER, &error);
	
	SmlManager *manager = smlManagerNew(server, &error);
	sml_fail_unless(manager != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);
	
	smlManagerFree(manager);
	
	smlTransportFree(server);
}
END_TEST

START_TEST (manager_run)
{
	setup_testbed(NULL);
	
	SmlError *error = NULL;
	SmlTransport *server = smlTransportNew(SML_TRANSPORT_HTTP_SERVER, &error);
	
	SmlManager *manager = smlManagerNew(server, &error);
	sml_fail_unless(manager != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);
	
	sml_fail_unless(smlManagerStart(manager, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	smlManagerStop(manager);
	
	smlManagerFree(manager);
	
	smlTransportFree(server);
}
END_TEST

unsigned int transport_errors;
unsigned int num_connects;
unsigned int num_disconnects;

unsigned int num_sessions;
unsigned int num_finals;
unsigned int num_end;
unsigned int session_errors;

unsigned int num_replies;
unsigned int num_alerts;
unsigned int num_syncs;
unsigned int num_changes;

void reset_testbed()
{
	transport_errors = 0;
	num_connects = 0;
	num_disconnects = 0;

	num_sessions = 0;
	num_finals = 0;
	num_end = 0;
	session_errors = 0;

	num_replies = 0;
	num_alerts = 0;
	num_syncs = 0;
	num_changes = 0;

	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_FLUSH:
			break;
		case SML_MANAGER_CONNECT_DONE:
			num_connects++;
			break;
		case SML_MANAGER_SESSION_ESTABLISHED:
			break;
		case SML_MANAGER_DISCONNECT_DONE:
			num_disconnects++;
			break;
		case SML_MANAGER_TRANSPORT_ERROR:
			transport_errors++;
			break;
		case SML_MANAGER_SESSION_NEW:
			smlAssert(session);
			tracker->sessions[tracker->num_sessions] = session;
			tracker->num_sessions++;
			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:
			session_errors++;
			break;
	}
	
	smlTrace(TRACE_EXIT, "%s", __func__);
}

static void _header_callback(SmlSession *session, SmlHeader *header, SmlCred *cred, void *userdata)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p, %p)", __func__, session, header, cred, userdata);
	smlAssert(session);
	SmlError *error = NULL;

	SmlStatus *reply = smlStatusNew(SML_AUTH_ACCEPTED, 0, session->lastReceivedMessageID, session->source, session->target, SML_COMMAND_TYPE_HEADER, &error);
	if (!reply)
		goto error;
	
	if (!smlSessionSendReply(session, reply, &error))
		goto error;
	
	smlStatusUnref(reply);

	session->established = TRUE;
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return;

error:
	smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, smlErrorPrint(&error));
	smlErrorDeref(&error);
	return;
}

START_TEST (manager_receive)
{
	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:12020", &error), NULL);

	sml_fail_unless(smlTransportSetConfigOption(server, "PORT", "12020", &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);
	
	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);

	const char *datastr = "<SyncML></SyncML>";
	SmlTransportData *data = smlTransportDataNew((char *)datastr, strlen(datastr), SML_MIMETYPE_XML, FALSE, &error);
	sml_fail_unless(data != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);

	sml_fail_unless(smlTransportSend(client, NULL, data, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	smlTransportDataDeref(data);
	
	while (session_errors < 1) {
		smlManagerDispatch(servermanager);
		smlManagerDispatch(clientmanager);
		usleep(100);
	}
	
	/* If the server disconnects actively without a response
	 * then the service is unavailable.
	 * The HTTP server transport sends status 503 in this case.
	 * This is a transport error.
	 * If the machine is fast enough or a SMP machine
	 * then the transport error is signalled fast enough.
	 */
	sml_fail_unless(transport_errors <= 1, NULL);
	sml_fail_unless(num_sessions == 0, NULL);
	sml_fail_unless(num_finals == 0, NULL);
	sml_fail_unless(num_end == 0, NULL);
	sml_fail_unless(session_errors == 1, NULL);

	smlManagerStop(clientmanager);
	smlManagerStop(servermanager);
	
	smlManagerFree(clientmanager);
	smlManagerFree(servermanager);
	
	g_free(clienttracker);
	g_free(servertracker);

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

START_TEST (manager_receive_session)
{
	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:12001", &error), NULL);

	sml_fail_unless(smlTransportSetConfigOption(server, "PORT", "12001", &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, defaultMaxMsgSize);
	smlManagerSetLocalMaxObjSize(servertracker->manager, defaultMaxObjSize);
	smlManagerRegisterHeaderHandler(servermanager, _header_callback, NULL, NULL);
	
	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);

	const char *datastr = "<SyncML><SyncHdr><VerProto>SyncML/1.1</VerProto><VerDTD>1.1</VerDTD><MsgID>1</MsgID><SessionID>1</SessionID><Target><LocURI>test</LocURI></Target><Source><LocURI>test</LocURI></Source></SyncHdr><SyncBody><Alert><CmdID>1</CmdID><Item><Target><LocURI>/test</LocURI></Target><Source><LocURI>/test</LocURI></Source><Meta><Anchor xmlns=\"syncml:metinf\"><Next>Next</Next><Last>last</Last></Anchor></Meta></Item><Data>200</Data></Alert><Final></Final></SyncBody></SyncML>";
	SmlTransportData *data = smlTransportDataNew((char *)datastr, strlen(datastr), SML_MIMETYPE_XML, FALSE, &error);
	sml_fail_unless(data != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);

	sml_fail_unless(smlTransportSend(client, NULL, data, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	smlTransportDataDeref(data);
	
	/* The server detects that there is no matching datastore and
	 * client receives an error from the server as the result.
	 */
	while (num_finals < 1 || session_errors < 1) {
		smlManagerDispatch(servermanager);
		smlManagerDispatch(clientmanager);
		usleep(100);
	}
	
	sml_fail_unless(transport_errors == 0, NULL);
	sml_fail_unless(num_sessions == 1, NULL);
	sml_fail_unless(num_finals == 1, NULL);
	sml_fail_unless(num_end == 0, NULL);
	sml_fail_unless(session_errors == 1, "session errors: %d", session_errors);
	
	sml_fail_unless(servertracker->sessions[0] != NULL, NULL);
	smlSessionUnref(servertracker->sessions[0]);
	
	g_free(clienttracker);
	g_free(servertracker);
	
	smlManagerStop(clientmanager);
	smlManagerStop(servermanager);
	
	smlManagerFree(clientmanager);
	smlManagerFree(servermanager);
	
	/* The session is not registered correctly at the client
	 * because HTTP clients create their sessions always by
	 * themselves and not via automatic registration at the
	 * manager. Therefore the client transport must be
	 * explicitely disconnected to avoid warnings from the
	 * transport layer.
	 */
	// sml_fail_unless(smlTransportDisconnect(client, NULL, &error), NULL);
	sml_fail_unless(smlTransportFinalize(client, &error), NULL);
	sml_fail_unless(smlTransportFinalize(server, &error), NULL);
	
	smlTransportFree(server);
	smlTransportFree(client);
}
END_TEST

START_TEST (manager_end_session)
{
	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:12002", &error), NULL);

	sml_fail_unless(smlTransportSetConfigOption(server, "PORT", "12002", &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, defaultMaxMsgSize);
	smlManagerSetLocalMaxObjSize(servertracker->manager, defaultMaxObjSize);
	smlManagerRegisterHeaderHandler(servermanager, _header_callback, NULL, NULL);
	
	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);

	/* the client send its data to the server */

	const char *datastr = "<SyncML><SyncHdr><VerProto>SyncML/1.1</VerProto><VerDTD>1.1</VerDTD><MsgID>1</MsgID><SessionID>1</SessionID><Target><LocURI>test</LocURI></Target><Source><LocURI>test</LocURI></Source></SyncHdr><SyncBody><Alert><CmdID>1</CmdID><Item><Target><LocURI>/test</LocURI></Target><Source><LocURI>/test</LocURI></Source><Meta><Anchor xmlns=\"syncml:metinf\"><Next>test1</Next><Last>test2</Last></Anchor></Meta></Item><Data>200</Data></Alert><Final></Final></SyncBody></SyncML>";
	SmlTransportData *data = smlTransportDataNew((char *)datastr, strlen(datastr), SML_MIMETYPE_XML, FALSE, &error);
	sml_fail_unless(data != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);

	sml_fail_unless(smlTransportSend(client, NULL, data, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	smlTransportDataDeref(data);

	/* the server received the data and has no matching datastore */
	
	while (num_connects < 1 || num_finals < 1 || session_errors < 1) {
		smlManagerDispatch(servermanager);
		usleep(100);
	}

	sml_fail_unless(transport_errors == 0, NULL);
	sml_fail_unless(num_sessions == 1, NULL);
	sml_fail_unless(num_finals == 1, NULL);
	sml_fail_unless(num_end == 0, NULL);
	sml_fail_unless(session_errors == 1, NULL);

	/* the server sends the answer */
	
	sml_fail_unless(servertracker->sessions[0] != NULL, NULL);
	sml_fail_unless(smlSessionEnd(servertracker->sessions[0], &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	smlSessionUnref(servertracker->sessions[0]);

	/* 1. the server sends the error message
	 * 2. the server declares session end internally
	 * 3. the server aborts the connection
	 */
	
	while (num_end < 1) {
		smlManagerDispatch(servermanager);
		usleep(100);
	}
	sml_fail_unless(TRUE, "server is passive now");

	/* 1. the client receives the connect
	 * 2. the client receives the error message
	 *    but the client has no appropriate session
	 *
	 * SO WHAT SHOULD HAPPEN !?
	 *
	 */

	while (num_connects < 2 || session_errors < 2) {
		smlManagerDispatch(clientmanager);
		usleep(100);
	}
	
	sml_fail_unless(transport_errors == 0, NULL);
	sml_fail_unless(num_sessions == 1, NULL);
	sml_fail_unless(num_finals == 1, NULL);
	sml_fail_unless(num_end == 1, NULL);
	sml_fail_unless(session_errors == 2, "Session errors: %d", session_errors);
	sml_fail_unless(num_connects == 2, NULL);

	g_free(clienttracker);
	g_free(servertracker);
	
	sml_fail_unless(TRUE, "trackers freed");

	/* first the client must be stopped */

	/* The session is not registered correctly at the client
	 * because HTTP clients create their sessions always by
	 * themselves and not via automatic registration at the
	 * manager. Therefore the client transport must be
	 * explicitely disconnected to avoid warnings from the
	 * transport layer.
	 */
	sml_fail_unless(smlTransportDisconnect(client, NULL, &error), NULL);
	while(num_disconnects < 1) {
		smlManagerDispatch(clientmanager);
	}

	smlManagerStop(clientmanager);
	smlManagerFree(clientmanager);

	sml_fail_unless(smlTransportFinalize(client, &error), NULL);
	smlTransportFree(client);
	
	/* second the server must be stopped */

	smlManagerStop(servermanager);
	smlManagerFree(servermanager);
	sml_fail_unless(smlTransportFinalize(server, &error), NULL);
	smlTransportFree(server);
}
END_TEST

static void _recv_alert(SmlSession *session, SmlCommand *cmd, void *userdata)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, session, cmd, userdata);
	SmlError *error = NULL;
	
	num_alerts++;
	
	SmlStatus *reply = smlCommandNewReply(cmd, SML_NO_ERROR, &error);
	if (!reply)
		goto error;
	
	if (!smlSessionSendReply(session, reply, &error)) {
		smlStatusUnref(reply);
		goto error;
	}
	
	smlStatusUnref(reply);
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return;

error:
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(&error));
	if (error)
		smlErrorDeref(&error);
}

static void _status_reply(SmlSession *session, SmlStatus *status, void *userdata)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, session, status, userdata);
	
	smlAssert(session != NULL);
	smlAssert(status != NULL);
	
	if (GPOINTER_TO_INT(userdata) == 2) {
		smlAssert(smlStatusGetCode(status) >= 500);
	} else if (GPOINTER_TO_INT(userdata) == 1) {
		smlAssert(smlStatusGetClass(status) == SML_ERRORCLASS_SUCCESS);
	} else
		abort();
	
	num_replies++;

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

START_TEST (manager_start_session)
{
	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:12004", &error), NULL);

	sml_fail_unless(smlTransportSetConfigOption(server, "PORT", "12004", &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, defaultMaxMsgSize);
	smlManagerSetLocalMaxObjSize(servertracker->manager, defaultMaxObjSize);
	
	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);
	
	sml_fail_unless(smlManagerObjectRegister(servermanager, SML_COMMAND_TYPE_ALERT, NULL, loc, NULL, NULL, _recv_alert, NULL, NULL, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	smlManagerRegisterHeaderHandler(servermanager, _header_callback, NULL, NULL);
	
	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);
	
	SmlCommand *cmd = smlCommandNewAlert(SML_ALERT_TWO_WAY, loc, loc, "last", "next", NULL, &error);
	sml_fail_unless(cmd != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);
	
	smlLocationUnref(loc);
	
	sml_fail_unless(smlSessionSendCommand(session, cmd, NULL, _status_reply, GINT_TO_POINTER(1), &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	smlCommandUnref(cmd);
	
	sml_fail_unless(smlSessionFlush(session, TRUE, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	/* unref session from smlSessionNew */
	smlSessionUnref(session);
	session = NULL;
	
	while (num_finals < 1 || num_alerts < 1 || num_sessions < 2) {
		smlManagerDispatch(servermanager);
		smlManagerDispatch(clientmanager);
		usleep(100);
	}
	
	sml_fail_unless(transport_errors == 0, NULL);
	sml_fail_unless(num_sessions == 2, NULL);
	sml_fail_unless(num_finals == 1, NULL);
	sml_fail_unless(num_end == 0, NULL);
	sml_fail_unless(session_errors == 0, NULL);
	
	sml_fail_unless(servertracker->sessions[0] != NULL, NULL);
	sml_fail_unless(smlSessionEnd(servertracker->sessions[0], &error), NULL);
	sml_fail_unless(error == NULL, NULL);

	/* cleanup references on sessions because of SML_SESSION_NEW event */
	smlSessionUnref(clienttracker->sessions[0]);
	smlSessionUnref(servertracker->sessions[0]);
	
	while (num_end != 2 || num_replies != 1 || num_finals != 2) {
		smlManagerDispatch(servermanager);
		smlManagerDispatch(clientmanager);
		usleep(100);
	}
	
	
	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);
	sml_fail_unless(num_replies == 1, NULL);
	sml_fail_unless(num_alerts == 1, NULL);
	sml_fail_unless(num_syncs == 0, NULL);
	sml_fail_unless(num_changes == 0, NULL);
	
	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), "%s", smlErrorPrint(&error));
	sml_fail_unless(smlTransportFinalize(client, &error), "%s", smlErrorPrint(&error));
	
	smlTransportFree(server);
	smlTransportFree(client);
}
END_TEST

START_TEST (manager_register)
{
	reset_testbed();
	
	SmlError *error = NULL;
	SmlLocation *loc = smlLocationNew("test", NULL, &error);
	
	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:12005", &error), NULL);

	sml_fail_unless(smlTransportSetConfigOption(server, "PORT", "12005", &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);
	
	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);

	sml_fail_unless(smlManagerObjectRegister(clientmanager, SML_COMMAND_TYPE_ALERT, NULL, loc, NULL, NULL, _recv_alert, NULL, NULL, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	g_free(clienttracker);
	g_free(servertracker);
	
	smlManagerStop(clientmanager);
	smlManagerStop(servermanager);
	
	smlManagerFree(clientmanager);
	smlManagerFree(servermanager);
	
	sml_fail_unless(smlTransportFinalize(server, &error), NULL);
	sml_fail_unless(smlTransportFinalize(client, &error), NULL);
	
	smlLocationUnref(loc);
	
	smlTransportFree(server);
	smlTransportFree(client);
}
END_TEST

@SML_TESTCASE_CODE@

