/*
 * 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_transport_internals.h>
#include <libsyncml/transports/obex_internals.h>
#include <libsyncml/sml_error_internals.h>

#include <errno.h>
#include <unistd.h>

START_TEST (obex_get_result_macro)
{
	setup_testbed(NULL);
	errno = 0;

	sml_fail_unless(GET_OBEX_RESULT(0) == 0, "return: 0 => 0");
	sml_fail_unless(errno == 0, "errno: 0 => 0");

	sml_fail_unless(GET_OBEX_RESULT(1) == 1, "return: 1 => 1");
	sml_fail_unless(errno == 0, "errno: 1 => 0");

	sml_fail_unless(GET_OBEX_RESULT(-1) == -1, "return: -1 => -1");
	sml_fail_unless(errno == 1, "errno(%d): -1 => 1", errno);

	sml_fail_unless(GET_OBEX_RESULT(-10) == -10, "return: -10 => -10");
	sml_fail_unless(errno == 10, "errno: -10 => 10");

	errno = 20
	sml_fail_unless(GET_OBEX_RESULT(-1) == -20, "return: -1(20) => -20");
	sml_fail_unless(errno == 20, "errno: -1(20) => 20");

	errno = 0
	sml_fail_unless(GET_OBEX_RESULT(0) == 0, "return: 0 => 0");
	sml_fail_unless(errno == 0, "errno: 0 => 0");
}

END_TEST
START_TEST (obex_client_new)
{
	setup_testbed(NULL);
	
	GError *error = NULL;
	SmlTransport *tsp = smlTransportNew(SML_TRANSPORT_OBEX_CLIENT, &error);
	sml_fail_unless(tsp != NULL, NULL);
	sml_fail_unless(error == NULL, "%s", GET_ERROR_MESSAGE(error));
	
	smlTransportFree(tsp);
}
END_TEST

START_TEST (obex_client_init)
{
	setup_testbed(NULL);
	
	GError *error = NULL;
	SmlTransport *tsp = smlTransportNew(SML_TRANSPORT_OBEX_CLIENT, &error);
	sml_fail_unless(tsp != NULL, NULL);
	sml_fail_unless(error == NULL, "%s", GET_ERROR_MESSAGE(error));
	
	sml_fail_unless(smlTransportSetConfigOption(tsp, "URL", "127.0.0.1", &error), NULL);
	sml_fail_unless(smlTransportSetConnectionType(tsp, SML_TRANSPORT_CONNECTION_TYPE_NET, &error), NULL);
	
	sml_fail_unless(smlTransportInitialize(tsp, &error), "%s", GET_ERROR_MESSAGE(error));
	sml_fail_unless(error == NULL, "%s", GET_ERROR_MESSAGE(error));
	
	sml_fail_unless(smlTransportFinalize(tsp, &error), NULL);
	sml_fail_unless(error == NULL, "%s", GET_ERROR_MESSAGE(error));
	
	smlTransportFree(tsp);
}
END_TEST

START_TEST (obex_server_new)
{
	setup_testbed(NULL);
	
	GError *error = NULL;
	SmlTransport *tsp = smlTransportNew(SML_TRANSPORT_OBEX_SERVER, &error);
	sml_fail_unless(tsp != NULL, NULL);
	sml_fail_unless(error == NULL, "%s", GET_ERROR_MESSAGE(error));
	
	smlTransportFree(tsp);
}
END_TEST

int client_connect_done = 0;
int client_disconnect_done = 0;
int client_receives = 0;
int client_errors = 0;

gboolean _recv_client_event(SmlTransport *tsp, SmlLink *link_, SmlTransportEventType type, SmlTransportData *data, const GError *error, void *userdata)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %d)", __func__, tsp, link_, type);
	
	sml_fail_unless(GPOINTER_TO_INT(userdata) == 1, NULL);
	
	switch (type) {
		case SML_TRANSPORT_EVENT_CONNECT_DONE:
			g_atomic_int_inc(&client_connect_done);
			break;
		case SML_TRANSPORT_EVENT_DISCONNECT_DONE:
			g_atomic_int_inc(&client_disconnect_done);
			break;
		case SML_TRANSPORT_EVENT_DATA:
			sml_fail_unless(!strcmp(data->data, "answer"), NULL);
			sml_fail_unless(data->size == 7, NULL);
			sml_fail_unless(data->type == SML_MIMETYPE_XML, NULL);
			g_atomic_int_inc(&client_receives);
			break;
		case SML_TRANSPORT_EVENT_ERROR:
			sml_fail_unless(error != NULL, NULL);
			g_atomic_int_inc(&client_errors);
			break;
		default:
			sml_fail_unless(FALSE, "An unexpected transport event %d was received.", type);
			break;
	}
	
	smlTrace(TRACE_EXIT, "%s()", __func__);
	return TRUE;
}

int server_connect_done = 0;
int server_disconnect_done = 0;
int server_receives = 0;

gboolean _recv_server_event(SmlTransport *tsp, SmlLink *link_, SmlTransportEventType type, SmlTransportData *data, const GError *error, void *userdata)
{
	smlTrace(TRACE_ENTRY, "%s(%d, %p)", __func__, type, error);

	GError *locerror = NULL;

	switch (type) {
		case SML_TRANSPORT_EVENT_DATA:
			g_atomic_int_inc(&server_receives);
			
			if (!strcmp(data->data, "test")) {
				sml_fail_unless(data->size == 5, NULL);
				sml_fail_unless(data->type == SML_MIMETYPE_XML, NULL);
				sml_fail_unless(link_ != NULL, NULL);
				
				if (GPOINTER_TO_INT(userdata) == 1) {
					data = smlTransportDataNew((char *)"answer", 7, SML_MIMETYPE_XML, FALSE, &locerror);
					sml_fail_unless(data != NULL, NULL);
					sml_fail_unless(locerror == NULL, "%s", GET_ERROR_MESSAGE(locerror));
				
					sml_fail_unless(smlTransportSend(tsp, link_, data, &locerror), "%s", GET_ERROR_MESSAGE(locerror));
					
					smlTransportDataDeref(data);
				} else if (GPOINTER_TO_INT(userdata) == 2) {
					GError *newerror = NULL;
					SML_SET_ERROR(&newerror, SML_ERROR_GENERIC, "test");
					smlTransportSendError(tsp, link_, newerror);
					SML_ERROR_FREE(newerror);
				} else if (GPOINTER_TO_INT(userdata) == 3) {
					sml_fail_unless(smlTransportDisconnect(tsp, link_, &locerror), NULL);
				} else {
					sml_fail_unless(FALSE, "The userdata %d was not expected.", GPOINTER_TO_INT(userdata));
				}
			} else if (!strcmp(data->data, "error")) {
				sml_fail_unless(data->size == 6, NULL);
				sml_fail_unless(data->type == SML_MIMETYPE_XML, NULL);
				sml_fail_unless(link_ != NULL, NULL);
				
				GError *newerror = NULL;
				SML_SET_ERROR(&newerror, SML_ERROR_GENERIC, "test2");
				smlTransportSendError(tsp, link_, newerror);
				SML_ERROR_FREE(newerror);
			} else {
				sml_fail_unless(FALSE, "The received data was not expected (%s).", data->data);
			}
			break;
		case SML_TRANSPORT_EVENT_CONNECT_DONE:
			g_atomic_int_inc(&server_connect_done);
			if (GPOINTER_TO_INT(userdata) == 4) {
				smlTrace(TRACE_EXIT, "%s()", __func__);
				return FALSE;
			}
			break;
		case SML_TRANSPORT_EVENT_DISCONNECT_DONE:
			g_atomic_int_inc(&server_disconnect_done);
			break;
		case SML_TRANSPORT_EVENT_ERROR:
			sml_fail_unless(FALSE, "%s", GET_ERROR_MESSAGE(error));
			break;
		default:
			sml_fail_unless(FALSE, "An unexpected transport event %d was received.", type);
			break;
	}
	
	smlTrace(TRACE_EXIT, "%s()", __func__);
	return TRUE;
}

void reset_testbed()
{
	client_connect_done = 0;
	client_disconnect_done = 0;
	client_receives = 0;
	client_errors = 0;

	server_connect_done = 0;
	server_disconnect_done = 0;
	server_receives = 0;
	/* server errors are always a failure */

	setup_testbed(NULL);
}

START_TEST (obex_server_init)
{
	setup_testbed(NULL);
	
	GError *error = NULL;
	SmlTransport *tsp = smlTransportNew(SML_TRANSPORT_OBEX_SERVER, &error);
	sml_fail_unless(tsp != NULL, NULL);
	sml_fail_unless(error == NULL, "%s", GET_ERROR_MESSAGE(error));
	
	sml_fail_unless(smlTransportSetConnectionType(tsp, SML_TRANSPORT_CONNECTION_TYPE_NET, &error), NULL);
	sml_fail_unless(error == NULL, "%s", GET_ERROR_MESSAGE(error));

	/* Don't use the default port 650 here because this is a reserved port in Unix.
	 * Therefore the port is usually only allowed for the root user.
	 */
	sml_fail_unless(smlTransportSetConfigOption(tsp, "PORT", "10104", &error), NULL);

	smlTransportSetEventCallback(tsp, _recv_server_event, GINT_TO_POINTER(1));

	sml_fail_unless(smlTransportInitialize(tsp, &error), "%s", GET_ERROR_MESSAGE(error));
	sml_fail_unless(error == NULL, "%s", GET_ERROR_MESSAGE(error));
	
	sml_fail_unless(smlTransportFinalize(tsp, &error), "%s", GET_ERROR_MESSAGE(error));
	sml_fail_unless(error == NULL, "%s", GET_ERROR_MESSAGE(error));
	
	smlTransportFree(tsp);
}
END_TEST

START_TEST (obex_connect)
{
	reset_testbed();
	
	GError *error = NULL;
	SmlTransport *server = smlTransportNew(SML_TRANSPORT_OBEX_SERVER, &error);
	SmlTransport *client = smlTransportNew(SML_TRANSPORT_OBEX_CLIENT, &error);
	
	sml_fail_unless(smlTransportSetConfigOption(client, "URL", "127.0.0.1", &error), NULL);
	sml_fail_unless(smlTransportSetConfigOption(client, "PORT", "10105", &error), NULL);
	sml_fail_unless(smlTransportSetConnectionType(client, SML_TRANSPORT_CONNECTION_TYPE_NET, &error), NULL);

	sml_fail_unless(smlTransportSetConfigOption(server, "PORT", "10105", &error), NULL);
	sml_fail_unless(smlTransportSetConnectionType(server, SML_TRANSPORT_CONNECTION_TYPE_NET, &error), NULL);

	smlTransportSetEventCallback(client, _recv_client_event, GINT_TO_POINTER(1));
	smlTransportSetEventCallback(server, _recv_server_event, GINT_TO_POINTER(1));

	sml_fail_unless(smlTransportInitialize(client, &error), "%s", GET_ERROR_MESSAGE(error));
	sml_fail_unless(smlTransportInitialize(server, &error), "%s", GET_ERROR_MESSAGE(error));
	sml_fail_unless(error == NULL, "%s", GET_ERROR_MESSAGE(error));

	sml_fail_unless(smlTransportConnect(client, &error), NULL);
	sml_fail_unless(error == NULL, "%s", GET_ERROR_MESSAGE(error));

	while (client_connect_done < 1 || server_connect_done < 1) { usleep(50); };

	sml_fail_unless(smlTransportDisconnect(client, NULL, &error), NULL);
	sml_fail_unless(error == NULL, "%s", GET_ERROR_MESSAGE(error));
	
	while (client_disconnect_done < 1 || server_disconnect_done < 1) { usleep(50); };
	
	sml_fail_unless(client_receives == 0, NULL);
	sml_fail_unless(client_connect_done == 1, NULL);
	sml_fail_unless(client_disconnect_done == 1, NULL);
	sml_fail_unless(server_receives == 0, NULL);
	sml_fail_unless(server_connect_done == 1, NULL);
	sml_fail_unless(server_disconnect_done == 1, NULL);
	
	sml_fail_unless(smlTransportFinalize(server, &error), NULL);
	sml_fail_unless(smlTransportFinalize(client, &error), NULL);
	
	smlTransportFree(server);
	smlTransportFree(client);
}
END_TEST

START_TEST (obex_send)
{
	reset_testbed();
	
	GError *error = NULL;
	SmlTransport *server = smlTransportNew(SML_TRANSPORT_OBEX_SERVER, &error);
	SmlTransport *client = smlTransportNew(SML_TRANSPORT_OBEX_CLIENT, &error);
	
	sml_fail_unless(smlTransportSetConfigOption(client, "URL", "127.0.0.1", &error), NULL);
	sml_fail_unless(smlTransportSetConfigOption(client, "PORT", "10106", &error), NULL);
	sml_fail_unless(smlTransportSetConnectionType(client, SML_TRANSPORT_CONNECTION_TYPE_NET, &error), NULL);

	sml_fail_unless(smlTransportSetConfigOption(server, "PORT", "10106", &error), NULL);
	sml_fail_unless(smlTransportSetConnectionType(server, SML_TRANSPORT_CONNECTION_TYPE_NET, &error), NULL);

	smlTransportSetEventCallback(client, _recv_client_event, GINT_TO_POINTER(1));
	smlTransportSetEventCallback(server, _recv_server_event, GINT_TO_POINTER(3));

	sml_fail_unless(smlTransportInitialize(client, &error), "%s", GET_ERROR_MESSAGE(error));
	sml_fail_unless(smlTransportInitialize(server, &error), "%s", GET_ERROR_MESSAGE(error));
	sml_fail_unless(error == NULL, "%s", GET_ERROR_MESSAGE(error));

	sml_fail_unless(smlTransportConnect(client, &error), NULL);
	sml_fail_unless(error == NULL, "%s", GET_ERROR_MESSAGE(error));

	while (client_connect_done < 1 || server_connect_done < 1) { usleep(50); };

	SmlTransportData *data = smlTransportDataNew((char *)"test", 5, SML_MIMETYPE_XML, FALSE, &error);
	sml_fail_unless(data != NULL, NULL);
	sml_fail_unless(error == NULL, "%s", GET_ERROR_MESSAGE(error));

	sml_fail_unless(smlTransportSend(client, NULL, data, &error), "%s", GET_ERROR_MESSAGE(error));
	sml_fail_unless(error == NULL, "%s", GET_ERROR_MESSAGE(error));
	
	smlTransportDataDeref(data);

	while (server_receives < 1) { usleep(50); };

	sml_fail_unless(smlTransportDisconnect(client, NULL, &error), NULL);
	sml_fail_unless(error == NULL, "%s", GET_ERROR_MESSAGE(error));
	
	while ((client_disconnect_done < 1 ||
	        server_disconnect_done < 1
	       ) && client_errors < 1) { usleep(50); };
	
	sml_fail_unless(client_receives == 0, NULL);
	sml_fail_unless(server_receives == 1, NULL);
	sml_fail_unless(client_connect_done == 1, NULL);
	sml_fail_unless(server_connect_done == 1, NULL);

	/* If we stop the while loop like some lines before
	 * then only client_disconnect_done or client_errors are
	 * guaranteed to finish correctly. If this thread is fast
	 * enough then the checks for equal (==2 && ==1) are reached
	 * before the disconnects and error detection completed.
	 * Such a behaviour would be a typical race condition.
	 * The solution is to wait until there are enough disconnects
	 * or more errors than expected.
	 */
	while ( (client_disconnect_done + client_errors) < 2 ||
	        server_disconnect_done < 1) { usleep(50); };

	sml_fail_unless(client_disconnect_done == 1, NULL);
	sml_fail_unless(server_disconnect_done == 1, NULL);
	/* There must be an error but it depends on the speed of the
	 * machine if two or one error are detected by the bluetooth
	 * stack.
	 */
	sml_fail_unless(client_errors > 0, "There must be a client error.");
	
	sml_fail_unless(smlTransportFinalize(server, &error), NULL);
	sml_fail_unless(smlTransportFinalize(client, &error), NULL);
	
	smlTransportFree(server);
	smlTransportFree(client);
}
END_TEST

START_TEST (obex_reply)
{
	reset_testbed();
	
	GError *error = NULL;
	SmlTransport *server = smlTransportNew(SML_TRANSPORT_OBEX_SERVER, &error);
	SmlTransport *client = smlTransportNew(SML_TRANSPORT_OBEX_CLIENT, &error);
	
	sml_fail_unless(smlTransportSetConfigOption(client, "URL", "127.0.0.1", &error), NULL);
	sml_fail_unless(smlTransportSetConfigOption(client, "PORT", "10107", &error), NULL);
	sml_fail_unless(smlTransportSetConnectionType(client, SML_TRANSPORT_CONNECTION_TYPE_NET, &error), NULL);

	sml_fail_unless(smlTransportSetConfigOption(server, "PORT", "10107", &error), NULL);
	sml_fail_unless(smlTransportSetConnectionType(server, SML_TRANSPORT_CONNECTION_TYPE_NET, &error), NULL);

	smlTransportSetEventCallback(client, _recv_client_event, GINT_TO_POINTER(1));
	smlTransportSetEventCallback(server, _recv_server_event, GINT_TO_POINTER(1));

	sml_fail_unless(smlTransportInitialize(client, &error), "%s", GET_ERROR_MESSAGE(error));
	sml_fail_unless(smlTransportInitialize(server, &error), "%s", GET_ERROR_MESSAGE(error));
	sml_fail_unless(error == NULL, "%s", GET_ERROR_MESSAGE(error));

	sml_fail_unless(smlTransportConnect(client, &error), NULL);
	sml_fail_unless(error == NULL, "%s", GET_ERROR_MESSAGE(error));

	while (client_connect_done < 1 || server_connect_done < 1) { usleep(50); };

	SmlTransportData *data = smlTransportDataNew((char *)"test", 5, SML_MIMETYPE_XML, FALSE, &error);
	sml_fail_unless(data != NULL, NULL);
	sml_fail_unless(error == NULL, "%s", GET_ERROR_MESSAGE(error));

	sml_fail_unless(smlTransportSend(client, NULL, data, &error), "%s", GET_ERROR_MESSAGE(error));
	sml_fail_unless(error == NULL, "%s", GET_ERROR_MESSAGE(error));
	
	smlTransportDataDeref(data);

	sml_fail_unless(smlTransportDisconnect(client, NULL, &error), NULL);
	sml_fail_unless(error == NULL, "%s", GET_ERROR_MESSAGE(error));
	
	while (client_disconnect_done < 1 || server_disconnect_done < 1) { usleep(50); };
	
	sml_fail_unless(client_receives == 1, NULL);
	sml_fail_unless(client_connect_done == 1, NULL);
	sml_fail_unless(client_disconnect_done == 1, NULL);
	sml_fail_unless(client_errors == 0, NULL);
	sml_fail_unless(server_receives == 1, NULL);
	sml_fail_unless(server_connect_done == 1, NULL);
	sml_fail_unless(server_disconnect_done == 1, NULL);
	
	sml_fail_unless(smlTransportFinalize(server, &error), NULL);
	sml_fail_unless(smlTransportFinalize(client, &error), NULL);
	
	smlTransportFree(server);
	smlTransportFree(client);
}
END_TEST

START_TEST (obex_talk)
{
	reset_testbed();
	
	int num = 10;
	
	GError *error = NULL;
	SmlTransport *server = smlTransportNew(SML_TRANSPORT_OBEX_SERVER, &error);
	SmlTransport *client = smlTransportNew(SML_TRANSPORT_OBEX_CLIENT, &error);
	
	sml_fail_unless(smlTransportSetConfigOption(client, "URL", "127.0.0.1", &error), NULL);
	sml_fail_unless(smlTransportSetConfigOption(client, "PORT", "10108", &error), NULL);
	sml_fail_unless(smlTransportSetConnectionType(client, SML_TRANSPORT_CONNECTION_TYPE_NET, &error), NULL);

	sml_fail_unless(smlTransportSetConfigOption(server, "PORT", "10108", &error), NULL);
	sml_fail_unless(smlTransportSetConnectionType(server, SML_TRANSPORT_CONNECTION_TYPE_NET, &error), NULL);
	
	smlTransportSetEventCallback(client, _recv_client_event, GINT_TO_POINTER(1));
	smlTransportSetEventCallback(server, _recv_server_event, GINT_TO_POINTER(1));

	sml_fail_unless(smlTransportInitialize(client, &error), "%s", GET_ERROR_MESSAGE(error));
	sml_fail_unless(smlTransportInitialize(server, &error), "%s", GET_ERROR_MESSAGE(error));
	sml_fail_unless(error == NULL, "%s", GET_ERROR_MESSAGE(error));

	sml_fail_unless(smlTransportConnect(client, &error), NULL);
	sml_fail_unless(error == NULL, "%s", GET_ERROR_MESSAGE(error));

	while (client_connect_done < 1 || server_connect_done < 1) { usleep(50); };

	SmlTransportData *data = NULL;
	
	int i;
	for (i = 0; i < num; i++) {
		data = smlTransportDataNew((char *)"test", 5, SML_MIMETYPE_XML, FALSE, &error);
		sml_fail_unless(data != NULL, NULL);
		sml_fail_unless(error == NULL, "%s", GET_ERROR_MESSAGE(error));
	
		sml_fail_unless(smlTransportSend(client, NULL, data, &error), "%s", GET_ERROR_MESSAGE(error));
		sml_fail_unless(error == NULL, "%s", GET_ERROR_MESSAGE(error));
		
		smlTransportDataDeref(data);
		
		while (1) {
			usleep(100);
			if (client_receives == i+1)
				break;
		}
	}

	sml_fail_unless(smlTransportDisconnect(client, NULL, &error), NULL);
	sml_fail_unless(error == NULL, "%s", GET_ERROR_MESSAGE(error));
	
	while (client_disconnect_done < 1 || server_disconnect_done < 1) { usleep(50); };
	
	sml_fail_unless(client_receives == num, NULL);
	sml_fail_unless(client_connect_done == 1, NULL);
	sml_fail_unless(client_disconnect_done == 1, NULL);
	sml_fail_unless(client_errors == 0, NULL);
	sml_fail_unless(server_receives == num, NULL);
	sml_fail_unless(server_connect_done == 1, NULL);
	sml_fail_unless(server_disconnect_done == 1, NULL);
	
	sml_fail_unless(smlTransportFinalize(server, &error), NULL);
	sml_fail_unless(smlTransportFinalize(client, &error), NULL);
	
	smlTransportFree(server);
	smlTransportFree(client);
}
END_TEST

START_TEST (obex_multi_connect)
{
	reset_testbed();
	
	GError *error = NULL;
	SmlTransport *server = smlTransportNew(SML_TRANSPORT_OBEX_SERVER, &error);
	SmlTransport *client1 = smlTransportNew(SML_TRANSPORT_OBEX_CLIENT, &error);
	SmlTransport *client2 = smlTransportNew(SML_TRANSPORT_OBEX_CLIENT, &error);
	SmlTransport *client3 = smlTransportNew(SML_TRANSPORT_OBEX_CLIENT, &error);
	
	sml_fail_unless(smlTransportSetConfigOption(client1, "URL", "127.0.0.1", &error), NULL);
	sml_fail_unless(smlTransportSetConfigOption(client1, "PORT", "10109", &error), NULL);
	sml_fail_unless(smlTransportSetConnectionType(client1, SML_TRANSPORT_CONNECTION_TYPE_NET, &error), NULL);

	sml_fail_unless(smlTransportSetConfigOption(client2, "URL", "127.0.0.1", &error), NULL);
	sml_fail_unless(smlTransportSetConfigOption(client2, "PORT", "10109", &error), NULL);
	sml_fail_unless(smlTransportSetConnectionType(client2, SML_TRANSPORT_CONNECTION_TYPE_NET, &error), NULL);

	sml_fail_unless(smlTransportSetConfigOption(client3, "URL", "127.0.0.1", &error), NULL);
	sml_fail_unless(smlTransportSetConfigOption(client3, "PORT", "10109", &error), NULL);
	sml_fail_unless(smlTransportSetConnectionType(client3, SML_TRANSPORT_CONNECTION_TYPE_NET, &error), NULL);

	sml_fail_unless(smlTransportSetConfigOption(server, "PORT", "10109", &error), NULL);
	sml_fail_unless(smlTransportSetConnectionType(server, SML_TRANSPORT_CONNECTION_TYPE_NET, &error), NULL);
	
	smlTransportSetEventCallback(client1, _recv_client_event, GINT_TO_POINTER(1));
	smlTransportSetEventCallback(client2, _recv_client_event, GINT_TO_POINTER(1));
	smlTransportSetEventCallback(client3, _recv_client_event, GINT_TO_POINTER(1));
	smlTransportSetEventCallback(server, _recv_server_event, GINT_TO_POINTER(1));

	sml_fail_unless(smlTransportInitialize(client1, &error), "%s", GET_ERROR_MESSAGE(error));
	sml_fail_unless(smlTransportInitialize(client2, &error), "%s", GET_ERROR_MESSAGE(error));
	sml_fail_unless(smlTransportInitialize(client3, &error), "%s", GET_ERROR_MESSAGE(error));
	sml_fail_unless(smlTransportInitialize(server, &error), "%s", GET_ERROR_MESSAGE(error));
	sml_fail_unless(error == NULL, "%s", GET_ERROR_MESSAGE(error));

	sml_fail_unless(smlTransportConnect(client1, &error), NULL);
	sml_fail_unless(smlTransportConnect(client2, &error), NULL);
	sml_fail_unless(smlTransportConnect(client3, &error), NULL);
	sml_fail_unless(error == NULL, "%s", GET_ERROR_MESSAGE(error));

	while (client_connect_done < 3 || server_connect_done < 3) { usleep(50); };

	SmlTransportData *data = smlTransportDataNew((char *)"test", 5, SML_MIMETYPE_XML, FALSE, &error);
	sml_fail_unless(data != NULL, NULL);
	sml_fail_unless(error == NULL, "%s", GET_ERROR_MESSAGE(error));
	sml_fail_unless(smlTransportSend(client1, NULL, data, &error), "%s", GET_ERROR_MESSAGE(error));
	sml_fail_unless(error == NULL, "%s", GET_ERROR_MESSAGE(error));
	smlTransportDataDeref(data);
	
	data = smlTransportDataNew((char *)"test", 5, SML_MIMETYPE_XML, FALSE, &error);
	sml_fail_unless(data != NULL, NULL);
	sml_fail_unless(error == NULL, "%s", GET_ERROR_MESSAGE(error));
	sml_fail_unless(smlTransportSend(client2, NULL, data, &error), "%s", GET_ERROR_MESSAGE(error));
	sml_fail_unless(error == NULL, "%s", GET_ERROR_MESSAGE(error));
	smlTransportDataDeref(data);
	
	data = smlTransportDataNew((char *)"test", 5, SML_MIMETYPE_XML, FALSE, &error);
	sml_fail_unless(data != NULL, NULL);
	sml_fail_unless(error == NULL, "%s", GET_ERROR_MESSAGE(error));
	sml_fail_unless(smlTransportSend(client3, NULL, data, &error), "%s", GET_ERROR_MESSAGE(error));
	sml_fail_unless(error == NULL, "%s", GET_ERROR_MESSAGE(error));
	smlTransportDataDeref(data);

	sml_fail_unless(smlTransportDisconnect(client1, NULL, &error), NULL);
	sml_fail_unless(smlTransportDisconnect(client2, NULL, &error), NULL);
	sml_fail_unless(smlTransportDisconnect(client3, NULL, &error), NULL);
	sml_fail_unless(error == NULL, "%s", GET_ERROR_MESSAGE(error));
	
	while (client_disconnect_done < 3 || server_disconnect_done < 3) { usleep(50); };
	
	sml_fail_unless(client_receives == 3, NULL);
	sml_fail_unless(client_connect_done == 3, NULL);
	sml_fail_unless(client_disconnect_done == 3, NULL);
	sml_fail_unless(client_errors == 0, NULL);
	sml_fail_unless(server_receives == 3, NULL);
	sml_fail_unless(server_connect_done == 3, NULL);
	sml_fail_unless(server_disconnect_done == 3, NULL);
	
	sml_fail_unless(smlTransportFinalize(server, &error), NULL);
	sml_fail_unless(smlTransportFinalize(client1, &error), NULL);
	sml_fail_unless(smlTransportFinalize(client2, &error), NULL);
	sml_fail_unless(smlTransportFinalize(client3, &error), NULL);
	
	smlTransportFree(server);
	smlTransportFree(client1);
	smlTransportFree(client2);
	smlTransportFree(client3);
}
END_TEST

START_TEST (obex_multi_stress)
{
	reset_testbed();
	
	int num = 1000;
	
	GError *error = NULL;
	SmlTransport *server = smlTransportNew(SML_TRANSPORT_OBEX_SERVER, &error);
	SmlTransport *client1 = smlTransportNew(SML_TRANSPORT_OBEX_CLIENT, &error);
	SmlTransport *client2 = smlTransportNew(SML_TRANSPORT_OBEX_CLIENT, &error);
	SmlTransport *client3 = smlTransportNew(SML_TRANSPORT_OBEX_CLIENT, &error);
	
	sml_fail_unless(smlTransportSetConfigOption(client1, "URL", "127.0.0.1", &error), NULL);
	sml_fail_unless(smlTransportSetConfigOption(client1, "PORT", "10110", &error), NULL);
	sml_fail_unless(smlTransportSetConnectionType(client1, SML_TRANSPORT_CONNECTION_TYPE_NET, &error), NULL);

	sml_fail_unless(smlTransportSetConfigOption(client2, "URL", "127.0.0.1", &error), NULL);
	sml_fail_unless(smlTransportSetConfigOption(client2, "PORT", "10110", &error), NULL);
	sml_fail_unless(smlTransportSetConnectionType(client2, SML_TRANSPORT_CONNECTION_TYPE_NET, &error), NULL);

	sml_fail_unless(smlTransportSetConfigOption(client3, "URL", "127.0.0.1", &error), NULL);
	sml_fail_unless(smlTransportSetConfigOption(client3, "PORT", "10110", &error), NULL);
	sml_fail_unless(smlTransportSetConnectionType(client3, SML_TRANSPORT_CONNECTION_TYPE_NET, &error), NULL);

	sml_fail_unless(smlTransportSetConfigOption(server, "PORT", "10110", &error), NULL);
	sml_fail_unless(smlTransportSetConnectionType(server, SML_TRANSPORT_CONNECTION_TYPE_NET, &error), NULL);
	
	smlTransportSetEventCallback(client1, _recv_client_event, GINT_TO_POINTER(1));
	smlTransportSetEventCallback(client2, _recv_client_event, GINT_TO_POINTER(1));
	smlTransportSetEventCallback(client3, _recv_client_event, GINT_TO_POINTER(1));
	smlTransportSetEventCallback(server, _recv_server_event, GINT_TO_POINTER(1));

	/* Always start the server first. */
	sml_fail_unless(error == NULL, "%s", GET_ERROR_MESSAGE(error));
	sml_fail_unless(smlTransportInitialize(server, &error), "%s", GET_ERROR_MESSAGE(error));
	sml_fail_unless(error == NULL, "%s", GET_ERROR_MESSAGE(error));
	sml_fail_unless(smlTransportInitialize(client1, &error), "%s", GET_ERROR_MESSAGE(error));
	sml_fail_unless(error == NULL, "%s", GET_ERROR_MESSAGE(error));
	sml_fail_unless(smlTransportInitialize(client2, &error), "%s", GET_ERROR_MESSAGE(error));
	sml_fail_unless(error == NULL, "%s", GET_ERROR_MESSAGE(error));
	sml_fail_unless(smlTransportInitialize(client3, &error), "%s", GET_ERROR_MESSAGE(error));
	sml_fail_unless(error == NULL, "%s", GET_ERROR_MESSAGE(error));

	sml_fail_unless(smlTransportConnect(client1, &error), NULL);
	sml_fail_unless(smlTransportConnect(client2, &error), NULL);
	sml_fail_unless(smlTransportConnect(client3, &error), NULL);
	sml_fail_unless(error == NULL, "%s", GET_ERROR_MESSAGE(error));

	while (client_connect_done < 3 || server_connect_done < 3) { usleep(50); };

	SmlTransportData *data = NULL;
	
	int i;
	for (i = 0; i < num; i++) {
		data = smlTransportDataNew((char *)"test", 5, SML_MIMETYPE_XML, FALSE, &error);
		sml_fail_unless(data != NULL, NULL);
		sml_fail_unless(error == NULL, "%s", GET_ERROR_MESSAGE(error));
		sml_fail_unless(smlTransportSend(client1, NULL, data, &error), "%s", GET_ERROR_MESSAGE(error));
		sml_fail_unless(error == NULL, "%s", GET_ERROR_MESSAGE(error));
		smlTransportDataDeref(data);
		
		data = smlTransportDataNew((char *)"test", 5, SML_MIMETYPE_XML, FALSE, &error);
		sml_fail_unless(data != NULL, NULL);
		sml_fail_unless(error == NULL, "%s", GET_ERROR_MESSAGE(error));
		sml_fail_unless(smlTransportSend(client2, NULL, data, &error), "%s", GET_ERROR_MESSAGE(error));
		sml_fail_unless(error == NULL, "%s", GET_ERROR_MESSAGE(error));
		smlTransportDataDeref(data);
		
		data = smlTransportDataNew((char *)"test", 5, SML_MIMETYPE_XML, FALSE, &error);
		sml_fail_unless(data != NULL, NULL);
		sml_fail_unless(error == NULL, "%s", GET_ERROR_MESSAGE(error));
		sml_fail_unless(smlTransportSend(client3, NULL, data, &error), "%s", GET_ERROR_MESSAGE(error));
		sml_fail_unless(error == NULL, "%s", GET_ERROR_MESSAGE(error));
		smlTransportDataDeref(data);
		
		while (1) {
			usleep(1);
			if (client_receives == (i+1)*3)
				break;
		}
	}

	sml_fail_unless(smlTransportDisconnect(client1, NULL, &error), NULL);
	sml_fail_unless(smlTransportDisconnect(client2, NULL, &error), NULL);
	sml_fail_unless(smlTransportDisconnect(client3, NULL, &error), NULL);
	sml_fail_unless(error == NULL, "%s", GET_ERROR_MESSAGE(error));
	
	while (client_disconnect_done < 3 || server_disconnect_done < 3) { usleep(50); };
	
	sml_fail_unless(client_receives == num * 3, NULL);
	sml_fail_unless(client_connect_done == 3, NULL);
	sml_fail_unless(client_disconnect_done == 3, NULL);
	sml_fail_unless(client_errors == 0, NULL);
	sml_fail_unless(server_receives == num * 3, NULL);
	sml_fail_unless(server_connect_done == 3, NULL);
	sml_fail_unless(server_disconnect_done == 3, NULL);
	
	sml_fail_unless(smlTransportFinalize(server, &error), NULL);
	sml_fail_unless(smlTransportFinalize(client1, &error), NULL);
	sml_fail_unless(smlTransportFinalize(client2, &error), NULL);
	sml_fail_unless(smlTransportFinalize(client3, &error), NULL);
	
	smlTransportFree(server);
	smlTransportFree(client1);
	smlTransportFree(client2);
	smlTransportFree(client3);
}
END_TEST

START_TEST (obex_connect_error)
{
	reset_testbed();
	
	GError *error = NULL;
	SmlTransport *client = smlTransportNew(SML_TRANSPORT_OBEX_CLIENT, &error);
	
	sml_fail_unless(smlTransportSetConfigOption(client, "URL", "127.0.0.1", &error), NULL);
	sml_fail_unless(smlTransportSetConfigOption(client, "PORT", "10111", &error), NULL);
	sml_fail_unless(smlTransportSetConnectionType(client, SML_TRANSPORT_CONNECTION_TYPE_NET, &error), NULL);
	
	smlTransportSetEventCallback(client, _recv_client_event, GINT_TO_POINTER(1));

	sml_fail_unless(smlTransportInitialize(client, &error), "%s", GET_ERROR_MESSAGE(error));
	sml_fail_unless(error == NULL, "%s", GET_ERROR_MESSAGE(error));

	sml_fail_unless(smlTransportConnect(client, &error), NULL);
	sml_fail_unless(error == NULL, "%s", GET_ERROR_MESSAGE(error));
	// usleep(50);

	while (client_errors < 1) { usleep(50); };

	sml_fail_unless(!smlTransportDisconnect(client, NULL, &error), NULL);
	sml_fail_unless(error != NULL, NULL);
	SML_ERROR_FREE(error);

	/* The client is stateful and so disconnect is false
	 * because the client was never connected.
	 */
	sml_fail_unless(client_receives == 0, NULL);
	sml_fail_unless(server_receives == 0, NULL);
	sml_fail_unless(client_connect_done == 0, NULL);
	sml_fail_unless(client_disconnect_done == 0, NULL);
	sml_fail_unless(client_errors == 1, NULL);
	
	sml_fail_unless(smlTransportFinalize(client, &error), NULL);
	
	smlTransportFree(client);
}
END_TEST

START_TEST (obex_reject)
{
	reset_testbed();
	
	GError *error = NULL;
	SmlTransport *server = smlTransportNew(SML_TRANSPORT_OBEX_SERVER, &error);
	SmlTransport *client = smlTransportNew(SML_TRANSPORT_OBEX_CLIENT, &error);

	sml_fail_unless(smlTransportSetConfigOption(client, "URL", "127.0.0.1", &error), NULL);
	sml_fail_unless(smlTransportSetConfigOption(client, "PORT", "10113", &error), NULL);
	sml_fail_unless(smlTransportSetConnectionType(client, SML_TRANSPORT_CONNECTION_TYPE_NET, &error), NULL);

	sml_fail_unless(smlTransportSetConfigOption(server, "PORT", "10113", &error), NULL);
	sml_fail_unless(smlTransportSetConnectionType(server, SML_TRANSPORT_CONNECTION_TYPE_NET, &error), NULL);

	smlTransportSetEventCallback(client, _recv_client_event, GINT_TO_POINTER(1));
	smlTransportSetEventCallback(server, _recv_server_event, GINT_TO_POINTER(4));

	sml_fail_unless(smlTransportInitialize(client, &error), "%s", GET_ERROR_MESSAGE(error));
	sml_fail_unless(smlTransportInitialize(server, &error), "%s", GET_ERROR_MESSAGE(error));
	sml_fail_unless(error == NULL, "%s", GET_ERROR_MESSAGE(error));

	sml_fail_unless(smlTransportConnect(client, &error), NULL);
	sml_fail_unless(error == NULL, "%s", GET_ERROR_MESSAGE(error));

	while (client_errors < 1 || server_connect_done < 1) { usleep(50); };

	sml_fail_unless(!smlTransportDisconnect(client, NULL, &error), NULL);
	sml_fail_unless(error != NULL, NULL);
	SML_ERROR_FREE(error);
	
	while (server_disconnect_done < 1) { usleep(50); };

	sml_fail_unless(client_receives == 0, NULL);
	sml_fail_unless(client_connect_done == 0, NULL);
	sml_fail_unless(client_disconnect_done == 0, NULL);
	sml_fail_unless(client_errors == 1, NULL);
	sml_fail_unless(server_receives == 0, NULL);
	sml_fail_unless(server_connect_done == 1, NULL);
	sml_fail_unless(server_disconnect_done == 1, NULL);
	
	sml_fail_unless(smlTransportFinalize(server, &error), NULL);
	sml_fail_unless(smlTransportFinalize(client, &error), NULL);
	
	smlTransportFree(server);
	smlTransportFree(client);
}
END_TEST

START_TEST (obex_multi_partial_error)
{
	reset_testbed();
	
	GError *error = NULL;
	SmlTransport *server = smlTransportNew(SML_TRANSPORT_OBEX_SERVER, &error);
	SmlTransport *client1 = smlTransportNew(SML_TRANSPORT_OBEX_CLIENT, &error);
	SmlTransport *client2 = smlTransportNew(SML_TRANSPORT_OBEX_CLIENT, &error);
	SmlTransport *client3 = smlTransportNew(SML_TRANSPORT_OBEX_CLIENT, &error);

	sml_fail_unless(smlTransportSetConfigOption(client1, "URL", "127.0.0.1", &error), NULL);
	sml_fail_unless(smlTransportSetConfigOption(client1, "PORT", "10114", &error), NULL);
	sml_fail_unless(smlTransportSetConnectionType(client1, SML_TRANSPORT_CONNECTION_TYPE_NET, &error), NULL);

	sml_fail_unless(smlTransportSetConfigOption(client2, "URL", "127.0.0.1", &error), NULL);
	sml_fail_unless(smlTransportSetConfigOption(client2, "PORT", "10114", &error), NULL);
	sml_fail_unless(smlTransportSetConnectionType(client2, SML_TRANSPORT_CONNECTION_TYPE_NET, &error), NULL);

	sml_fail_unless(smlTransportSetConfigOption(client3, "URL", "127.0.0.1", &error), NULL);
	sml_fail_unless(smlTransportSetConfigOption(client3, "PORT", "10114", &error), NULL);
	sml_fail_unless(smlTransportSetConnectionType(client3, SML_TRANSPORT_CONNECTION_TYPE_NET, &error), NULL);

	sml_fail_unless(smlTransportSetConfigOption(server, "PORT", "10114", &error), NULL);
	sml_fail_unless(smlTransportSetConnectionType(server, SML_TRANSPORT_CONNECTION_TYPE_NET, &error), NULL);
	
	smlTransportSetEventCallback(client1, _recv_client_event, GINT_TO_POINTER(1));
	smlTransportSetEventCallback(client2, _recv_client_event, GINT_TO_POINTER(1));
	smlTransportSetEventCallback(client3, _recv_client_event, GINT_TO_POINTER(1));
	smlTransportSetEventCallback(server, _recv_server_event, GINT_TO_POINTER(1));

	sml_fail_unless(smlTransportInitialize(client1, &error), "%s", GET_ERROR_MESSAGE(error));
	sml_fail_unless(smlTransportInitialize(client2, &error), "%s", GET_ERROR_MESSAGE(error));
	sml_fail_unless(smlTransportInitialize(client3, &error), "%s", GET_ERROR_MESSAGE(error));
	sml_fail_unless(smlTransportInitialize(server, &error), "%s", GET_ERROR_MESSAGE(error));
	sml_fail_unless(error == NULL, "%s", GET_ERROR_MESSAGE(error));

	sml_fail_unless(smlTransportConnect(client1, &error), NULL);
	sml_fail_unless(smlTransportConnect(client2, &error), NULL);
	sml_fail_unless(smlTransportConnect(client3, &error), NULL);
	sml_fail_unless(error == NULL, "%s", GET_ERROR_MESSAGE(error));

	while (client_connect_done < 3 || server_connect_done < 3) { usleep(50); };

	SmlTransportData *data = smlTransportDataNew((char *)"test", 5, SML_MIMETYPE_XML, FALSE, &error);
	sml_fail_unless(data != NULL, NULL);
	sml_fail_unless(error == NULL, "%s", GET_ERROR_MESSAGE(error));
	sml_fail_unless(smlTransportSend(client1, NULL, data, &error), "%s", GET_ERROR_MESSAGE(error));
	sml_fail_unless(error == NULL, "%s", GET_ERROR_MESSAGE(error));
	smlTransportDataDeref(data);
	
	data = smlTransportDataNew((char *)"error", 6, SML_MIMETYPE_XML, FALSE, &error);
	sml_fail_unless(data != NULL, NULL);
	sml_fail_unless(error == NULL, "%s", GET_ERROR_MESSAGE(error));
	sml_fail_unless(smlTransportSend(client2, NULL, data, &error), "%s", GET_ERROR_MESSAGE(error));
	sml_fail_unless(error == NULL, "%s", GET_ERROR_MESSAGE(error));
	smlTransportDataDeref(data);
	
	data = smlTransportDataNew((char *)"error", 6, SML_MIMETYPE_XML, FALSE, &error);
	sml_fail_unless(data != NULL, NULL);
	sml_fail_unless(error == NULL, "%s", GET_ERROR_MESSAGE(error));
	sml_fail_unless(smlTransportSend(client3, NULL, data, &error), "%s", GET_ERROR_MESSAGE(error));
	sml_fail_unless(error == NULL, "%s", GET_ERROR_MESSAGE(error));
	smlTransportDataDeref(data);

	sml_fail_unless(smlTransportDisconnect(client1, NULL, &error), NULL);
	sml_fail_unless(smlTransportDisconnect(client2, NULL, &error), NULL);
	sml_fail_unless(smlTransportDisconnect(client3, NULL, &error), NULL);
	sml_fail_unless(error == NULL, "%s", GET_ERROR_MESSAGE(error));
	
	while (client_disconnect_done < 3 || server_disconnect_done < 3) { usleep(50); };
	
	sml_fail_unless(client_receives == 1, NULL);
	sml_fail_unless(client_connect_done == 3, NULL);
	sml_fail_unless(client_disconnect_done == 3, NULL);
	sml_fail_unless(client_errors == 2, NULL);
	sml_fail_unless(server_receives == 3, NULL);
	sml_fail_unless(server_connect_done == 3, NULL);
	sml_fail_unless(server_disconnect_done == 3, NULL);
	
	sml_fail_unless(smlTransportFinalize(server, &error), NULL);
	sml_fail_unless(smlTransportFinalize(client1, &error), NULL);
	sml_fail_unless(smlTransportFinalize(client2, &error), NULL);
	sml_fail_unless(smlTransportFinalize(client3, &error), NULL);
	
	smlTransportFree(server);
	smlTransportFree(client1);
	smlTransportFree(client2);
	smlTransportFree(client3);
}
END_TEST

@SML_TESTCASE_CODE@

