/* * libsyncml - A syncml protocol implementation * Copyright (C) 2005 Armin Bauer * Copyright (C) 2008 Michael Bell * * 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 #include #include #include #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); GError *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); GError *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, const GError *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); GError *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__, GET_ERROR_MESSAGE(error)); SML_ERROR_FREE(error); return; } START_TEST (manager_receive) { reset_testbed(); GError *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", GET_ERROR_MESSAGE(error)); sml_fail_unless(smlTransportInitialize(server, &error), "%s", GET_ERROR_MESSAGE(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 = ""; 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", GET_ERROR_MESSAGE(error)); smlTransportFree(client); smlTransportFree(server); } END_TEST START_TEST (manager_receive_session) { reset_testbed(); GError *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", GET_ERROR_MESSAGE(error)); sml_fail_unless(smlTransportInitialize(server, &error), "%s", GET_ERROR_MESSAGE(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/1.11.111testtest1/test/testNextlast200"; 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(); GError *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", GET_ERROR_MESSAGE(error)); sml_fail_unless(smlTransportInitialize(server, &error), "%s", GET_ERROR_MESSAGE(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/1.11.111testtest1/test/testtest1test2200"; 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); GError *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__, GET_ERROR_MESSAGE(error)); if (error) SML_ERROR_FREE(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(); GError *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", GET_ERROR_MESSAGE(error)); sml_fail_unless(smlTransportInitialize(server, &error), "%s", GET_ERROR_MESSAGE(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 = sml_location_new(); sml_fail_unless(loc != NULL, NULL); sml_location_set_uri(loc, "test"); 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, 0, 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); g_object_unref(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", GET_ERROR_MESSAGE(error)); sml_fail_unless(smlTransportFinalize(client, &error), "%s", GET_ERROR_MESSAGE(error)); smlTransportFree(server); smlTransportFree(client); } END_TEST START_TEST (manager_register) { reset_testbed(); GError *error = NULL; SmlLocation *loc = sml_location_new(); sml_fail_unless(loc != NULL, NULL); sml_location_set_uri(loc, "test"); 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", GET_ERROR_MESSAGE(error)); sml_fail_unless(smlTransportInitialize(server, &error), "%s", GET_ERROR_MESSAGE(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); g_object_unref(loc); smlTransportFree(server); smlTransportFree(client); } END_TEST @SML_TESTCASE_CODE@