diff --git a/lib/Formatter.cpp b/lib/Formatter.cpp
index 2f1631ba4..ed4bd9f61 100644
--- a/lib/Formatter.cpp
+++ b/lib/Formatter.cpp
@@ -110,15 +110,6 @@ Formatter::getState ()
return _state;
}
-bool
-Formatter::startWebServices ()
-{
- if (_opts.webservice && !_webservices->isStarted ())
- return _webservices->start ();
- else
- return false;
-}
-
bool
Formatter::start (const string &file, string *errmsg)
{
@@ -129,8 +120,8 @@ Formatter::start (const string &file, string *errmsg)
if (_state != GINGA_STATE_STOPPED)
return false;
- if (_opts.webservice && !_webservices->isStarted ())
- this->startWebServices ();
+ if (_opts.webservices)
+ this->_webservices->start ();
// Parse document.
g_assert_null (_doc);
@@ -182,6 +173,10 @@ Formatter::start (const string &file, string *errmsg)
// Sets formatter state.
_state = GINGA_STATE_PLAYING;
+ // start webservices
+ if (_opts.webservices)
+ _webservices->start ();
+
return true;
}
@@ -450,7 +445,7 @@ Formatter::Formatter (const GingaOptions *opts) : Ginga (opts)
_opts.width = 800;
_opts.height = 600;
_opts.debug = false;
- _opts.webservice = false;
+ _opts.webservices = false;
_opts.opengl = false;
_opts.experimental = false;
};
@@ -492,6 +487,16 @@ Formatter::getDocument ()
return _doc;
}
+/**
+ * @brief Gets WebServices.
+ * @return WebServices or null (no current document).
+ */
+WebServices *
+Formatter::getWebServices ()
+{
+ return _webservices;
+}
+
/**
* @brief Gets EOS flag.
* @return EOS flag.
@@ -557,6 +562,24 @@ Formatter::setOptionDebug (Formatter *self, const string &name, bool value)
TRACE ("%s:=%s", name.c_str (), strbool (value));
}
+/**
+ * @brief Sets WebServices option option of the given Formatter.
+ * @param self Formatter.
+ * @param name Must be the string "webservies".
+ * @param value webservies flag value.
+ */
+void
+Formatter::setOptionWebServices (Formatter *self, const string &name,
+ bool value)
+{
+ g_assert (name == "webservices");
+ if (value)
+ self->_webservices->start ();
+ else
+ self->_webservices->stop ();
+ TRACE ("%s:=%s", name.c_str (), strbool (value));
+}
+
/**
* @brief Sets the experimental option of the given Formatter.
* @param self Formatter.
diff --git a/lib/Formatter.h b/lib/Formatter.h
index 63c9fb78a..636bd07ba 100644
--- a/lib/Formatter.h
+++ b/lib/Formatter.h
@@ -43,7 +43,6 @@ class Formatter : public Ginga
bool start (const std::string &, std::string *);
bool stop ();
- bool startWebServices ();
void resize (int, int);
void redraw (cairo_t *);
@@ -64,11 +63,13 @@ class Formatter : public Ginga
~Formatter ();
Document *getDocument ();
+ WebServices *getWebServices ();
bool getEOS ();
void setEOS (bool);
static void setOptionBackground (Formatter *, const string &, string);
static void setOptionDebug (Formatter *, const string &, bool);
+ static void setOptionWebServices (Formatter *, const string &, bool);
static void setOptionExperimental (Formatter *, const string &, bool);
static void setOptionOpenGL (Formatter *, const string &, bool);
static void setOptionSize (Formatter *, const string &, int);
@@ -97,7 +98,7 @@ class Formatter : public Ginga
/// @brief Current WebServices.
WebServices *_webservices;
-
+
/// @brief Current document tree.
Document *_doc;
diff --git a/lib/PlayerRemote.cpp b/lib/PlayerRemote.cpp
index 4b23b01c5..aaa0d904d 100644
--- a/lib/PlayerRemote.cpp
+++ b/lib/PlayerRemote.cpp
@@ -25,8 +25,6 @@ along with Ginga. If not, see . */
GINGA_NAMESPACE_BEGIN
-#define REMOTE_PLAYER_ACT_JSON "{ \"action\" : \"%s\", \"delay\" : \"%d\"}"
-
/**
* @brief Creates PlayerRemote
* @param fmt Formatter
@@ -50,7 +48,7 @@ PlayerRemote::doSetProperty (Property code, const string &name,
if (_url != nullptr)
g_free (_url);
_url = g_strdup_printf ("http://%s/scene/nodes/%s", value.c_str (),
- _media->getId ().c_str());
+ _media->getId ().c_str ());
break;
default:
return Player::doSetProperty (code, name, value);
@@ -61,15 +59,29 @@ PlayerRemote::doSetProperty (Property code, const string &name,
PlayerRemote::~PlayerRemote ()
{
g_free (_url);
+ g_object_unref (_session);
}
-bool
+static void
+ws_action_callback (SoupSession *session, SoupMessage *msg,
+ gpointer user_data)
+{
+ string url;
+ auto player = (PlayerRemote *) user_data;
+ url = player->getProperty ("remotePlayerBaseURL");
+ if (!msg->status_code == SOUP_STATUS_OK)
+ WARNING ("Failed to perform request %s", url.c_str ());
+ TRACE_SOUP_REQ_MSG (msg);
+}
+
+void
PlayerRemote::sendAction (const string &action)
{
guint status;
SoupMessage *msg;
char *body;
+ g_assert_nonnull (_url);
if (!_sessionStarted)
{
_session
@@ -77,50 +89,44 @@ PlayerRemote::sendAction (const string &action)
SOUP_TYPE_CONTENT_SNIFFER, NULL);
_sessionStarted = true;
}
- g_assert_nonnull (_url);
msg = soup_message_new ("POST", _url);
- body = g_strdup_printf (REMOTE_PLAYER_ACT_JSON, action.c_str (), 0);
+ body = g_strdup_printf (REMOTE_PLAYER_JSON_ACT, action.c_str (), 0);
soup_message_set_request (msg, "application/json", SOUP_MEMORY_COPY, body,
strlen (body));
- status = soup_session_send_message (_session, msg);
- if(!status){
- WARNING ("Failed perform request %s", _url);
- }
+ soup_session_queue_message (_session, msg, ws_action_callback, this);
g_free (msg);
g_free (body);
- g_free (msg);
- return true;
}
void
PlayerRemote::start ()
{
- g_assert (sendAction ("start"));
+ sendAction ("start");
}
void
PlayerRemote::startPreparation ()
{
- g_assert (sendAction ("prepare"));
+ sendAction ("prepare");
}
void
PlayerRemote::stop ()
{
- g_assert (sendAction ("stop"));
+ sendAction ("stop");
}
void
PlayerRemote::pause ()
{
- g_assert (sendAction ("pause"));
+ sendAction ("pause");
}
void
PlayerRemote::resume ()
{
- g_assert (sendAction ("resume"));
+ sendAction ("resume");
}
GINGA_NAMESPACE_END
\ No newline at end of file
diff --git a/lib/PlayerRemote.h b/lib/PlayerRemote.h
index 5f6a73590..00f7e67d5 100644
--- a/lib/PlayerRemote.h
+++ b/lib/PlayerRemote.h
@@ -25,6 +25,13 @@ GINGA_NAMESPACE_BEGIN
class WebServices;
+#define REMOTE_PLAYER_JSON_ACT \
+ "{\
+ \"action\" : \"%s\",\
+ \"delay\" : \"%d\"\
+ }"
+
+
class PlayerRemote : public Player
{
public:
@@ -38,7 +45,7 @@ class PlayerRemote : public Player
protected:
bool doSetProperty (Property, const string &, const string &);
- bool sendAction (const string &);
+ void sendAction (const string &);
char * _url;
bool _sessionStarted;
WebServices *_ws;
diff --git a/lib/WebServices.cpp b/lib/WebServices.cpp
index 09898c47a..5496575e7 100644
--- a/lib/WebServices.cpp
+++ b/lib/WebServices.cpp
@@ -8,15 +8,6 @@
#include
#include
-#define WS_ROURTE_LOC "/location"
-#define WS_ROURTE_RPLAYER "/remote-mediaplayer"
-#define WS_ROURTE_APPS "/current-service/apps/"
-#define WS_PORT 44642
-#define SSDP_UUID "uuid:b16f8e7e-8050-11eb-8036-00155dfe4f40"
-#define SSDP_DEVICE "upnp:rootdevice"
-#define SSDP_NAME "TeleMidia GingaCCWebServices"
-#define SSDP_USN "urn:schemas-sbtvd-org:service:GingaCCWebServices:1"
-
#define WS_ADD_ROUTE(ws, r, f) \
G_STMT_START \
{ \
@@ -24,19 +15,6 @@
} \
G_STMT_END
-#define TRACE_RQ_HEADERS(msg) \
- G_STMT_START \
- { \
- SoupMessageHeadersIter it; \
- const gchar *name; \
- const gchar *value; \
- soup_message_headers_iter_init (&it, msg->request_headers); \
- while (soup_message_headers_iter_next (&it, &name, &value)) \
- TRACE ("req %s: %s", name, value); \
- TRACE ("req body:\n%s\n", name, value, msg->request_body->data); \
- } \
- G_STMT_END
-
/**
* @brief Creates a new WebServices.
* @return New #WebServices.
@@ -44,19 +22,31 @@
WebServices::WebServices (Formatter *fmt)
{
_formatter = fmt;
- _started = false;
+ _resource_group = nullptr;
+ _client = nullptr;
+ _ws = nullptr;
+ _state = WS_STATE_STOPPED;
}
WebServices::~WebServices ()
{
g_object_unref (_resource_group);
g_object_unref (_client);
+ g_object_unref (_ws);
+}
+
+bool
+WebServices::stop ()
+{
+ _state = WS_STATE_STOPPED;
+ gssdp_resource_group_set_available (_resource_group, FALSE);
+ return true;
}
bool
WebServices::machMediaThenSetPlayerRemote (PlayerRemoteData &data)
{
- if (!getCurrentDocument ())
+ if (!_formatter->getDocument ())
return false;
auto mrts = _formatter->getDocument ()->getMediasRemote ();
@@ -89,16 +79,16 @@ WebServices::machMediaThenSetPlayerRemote (PlayerRemoteData &data)
return found;
}
-bool
-WebServices::isStarted ()
+WebServicesState
+WebServices::getState ()
{
- return _started;
+ return _state;
}
-Document *
-WebServices::getCurrentDocument ()
+Formatter *
+WebServices::getFormatter ()
{
- return _formatter->getDocument ();
+ return _formatter;
}
static void
@@ -122,16 +112,15 @@ ws_loc_callback (SoupServer *server, SoupMessage *msg, const char *path,
soup_message_set_status (msg, SOUP_STATUS_OK);
soup_message_set_response (msg, "text/plan", SOUP_MEMORY_COPY, NULL, 0);
- // Add GingaCC-WS headers
- value = g_strdup_printf ("http://%s:%d", ws->_host_addr, WS_PORT);
+ // add GingaCC-Server-* headers
+ value = g_strdup_printf ("http://%s:%d", ws->host_addr, WS_PORT);
soup_message_headers_append (msg->response_headers,
"GingaCC-Server-BaseURL", value);
-
- value = g_strdup_printf ("https://%s:%d", ws->_host_addr, WS_PORT);
+ g_free (value);
+ value = g_strdup_printf ("https://%s:%d", ws->host_addr, WS_PORT);
soup_message_headers_append (msg->response_headers,
"GingaCC-Server-SecureBaseURL", value);
g_free (value);
-
soup_message_headers_append (
msg->response_headers, "GingaCC-Server-PairingMethods", "qcode,kex");
}
@@ -148,7 +137,7 @@ ws_remoteplayer_callback (SoupServer *server, SoupMessage *msg,
WebServices *ws = (WebServices *) user_data;
bool status;
- TRACE_RQ_HEADERS (msg);
+ TRACE_SOUP_REQ_MSG (msg);
if (!reader->parse (msg->request_body->data,
msg->request_body->data + msg->request_body->length,
@@ -183,19 +172,21 @@ ws_apps_callback (SoupServer *server, SoupMessage *msg, const char *path,
string action, interface, value;
Object *node;
Event *evt;
+ Document *doc;
const char *target = path + strlen (WS_ROURTE_APPS);
gchar **params = g_strsplit (target, "/", 3);
+
+ doc = ws->getFormatter ()->getDocument ();
+ g_assert_nonnull (doc);
const char *appId = params[0];
const char *docId = params[1];
const char *nodeId = params[2];
-
- if (!ws->getCurrentDocument () || !strlen (appId) || !strlen (docId)
- || !strlen (nodeId))
+ if (!strlen (appId) || !strlen (docId) || !strlen (nodeId))
goto fail;
TRACE ("request %s: app=%s, docId=%s nodeId=%s", WS_ROURTE_APPS, appId,
docId, nodeId);
- TRACE_RQ_HEADERS (msg);
+ TRACE_SOUP_REQ_MSG (msg);
if (!reader->parse (msg->request_body->data,
msg->request_body->data + msg->request_body->length,
@@ -208,34 +199,35 @@ ws_apps_callback (SoupServer *server, SoupMessage *msg, const char *path,
interface = root["interface"].asString ();
value = root["value"].asString ();
- node = ws->getCurrentDocument ()->getObjectById (nodeId);
- g_assert_nonnull (node);
+ node = doc->getObjectById (nodeId);
+ g_strfreev (params);
+
+ if (!node)
+ goto fail;
- if (xstrcasecmp (action, "select") == 0)
+ if (action == "select")
{
evt = node->getSelectionEvent (value);
- ws->getCurrentDocument ()->evalAction (evt, Event::START);
- ws->getCurrentDocument ()->evalAction (evt, Event::STOP);
+ doc->evalAction (evt, Event::START);
+ doc->evalAction (evt, Event::STOP);
}
- else if (xstrcasecmp (action, "lookAt") == 0)
+ else if (action == "lookAt")
{
evt = node->getLookAtEvent ("@lambda");
- ws->getCurrentDocument ()->evalAction (evt, Event::START);
+ doc->evalAction (evt, Event::START);
}
- else if (xstrcasecmp (action, "lookAway") == 0)
+ else if (action == "lookAway")
{
evt = node->getLookAtEvent ("@lambda");
- ws->getCurrentDocument ()->evalAction (evt, Event::STOP);
+ doc->evalAction (evt, Event::STOP);
}
else
- {
- goto fail;
- }
+ goto fail;
soup_message_set_status (msg, SOUP_STATUS_OK);
+ return;
fail:
- g_strfreev (params);
soup_message_set_status (msg, SOUP_STATUS_NOT_FOUND);
}
@@ -256,10 +248,10 @@ WebServices::start ()
}
_resource_group = gssdp_resource_group_new (_client);
g_assert (_resource_group);
- _host_addr = gssdp_client_get_host_ip (_client);
+ host_addr = gssdp_client_get_host_ip (_client);
// ssdp avaliable
- location = g_strdup_printf ("http://%s:%d/location", _host_addr, WS_PORT);
+ location = g_strdup_printf ("http://%s:%d/location", host_addr, WS_PORT);
gssdp_resource_group_add_resource_simple (_resource_group, SSDP_USN,
SSDP_USN, location);
g_free (location);
@@ -284,7 +276,7 @@ WebServices::start ()
WS_ADD_ROUTE (_ws, WS_ROURTE_APPS, ws_apps_callback);
WS_ADD_ROUTE (_ws, nullptr, ws_null_callback);
- _started = true;
+ _state = WS_STATE_STARTED;
return true;
fail:
g_error_free (error);
diff --git a/lib/WebServices.h b/lib/WebServices.h
index 982766433..6d40feff2 100644
--- a/lib/WebServices.h
+++ b/lib/WebServices.h
@@ -21,12 +21,22 @@ along with Ginga. If not, see . */
#include "aux-ginga.h"
#include
#include
+#include "Event.h"
GINGA_NAMESPACE_BEGIN
class Formatter;
class Document;
class Media;
+/**
+ * @brief WevServices states.
+ */
+typedef enum
+{
+ WS_STATE_STARTED,
+ WS_STATE_STOPPED,
+} WebServicesState;
+
/**
* @brief PlayerRemoteData.
*/
@@ -38,6 +48,16 @@ typedef struct
list recognizedableEvents;
} PlayerRemoteData;
+
+#define WS_ROURTE_LOC "/location"
+#define WS_ROURTE_RPLAYER "/remote-mediaplayer"
+#define WS_ROURTE_APPS "/current-service/apps/"
+#define WS_PORT 44642
+#define SSDP_UUID "uuid:b16f8e7e-8050-11eb-8036-00155dfe4f40"
+#define SSDP_DEVICE "upnp:rootdevice"
+#define SSDP_NAME "TeleMidia GingaCCWebServices"
+#define SSDP_USN "urn:schemas-sbtvd-org:service:GingaCCWebServices:1"
+
/**
* @brief WebSercices.
*/
@@ -48,14 +68,15 @@ class WebServices
explicit WebServices (Formatter *);
~WebServices ();
bool start ();
- bool isStarted ();
- const char *_host_addr;
+ bool stop ();
+ WebServicesState getState ();
bool machMediaThenSetPlayerRemote (PlayerRemoteData &);
- Document *getCurrentDocument ();
+ Formatter *getFormatter ();
+ const char *host_addr;
private:
Formatter *_formatter;
- bool _started;
+ WebServicesState _state;
map _playerMap;
GSSDPClient *_client;
GSSDPResourceGroup *_resource_group;
diff --git a/lib/aux-ginga.h b/lib/aux-ginga.h
index 80be9db2c..9cad6c8c6 100644
--- a/lib/aux-ginga.h
+++ b/lib/aux-ginga.h
@@ -113,6 +113,19 @@ string __ginga_strfunc (const string &);
#define ERROR_NOT_IMPLEMENTED(fmt, ...)\
ERROR ("not implemented: " fmt, ## __VA_ARGS__)
+#define TRACE_SOUP_REQ_MSG(msg) \
+ G_STMT_START \
+ { \
+ SoupMessageHeadersIter it; \
+ const gchar *name; \
+ const gchar *value; \
+ soup_message_headers_iter_init (&it, msg->request_headers); \
+ while (soup_message_headers_iter_next (&it, &name, &value)) \
+ TRACE ("request header %s: %s", name, value); \
+ TRACE ("request body:\n%s\n", name, value, msg->request_body->data); \
+ } \
+ G_STMT_END
+
// Internal types.
typedef GdkRGBA Color;
typedef GdkRectangle Rect;
diff --git a/lib/ginga.h b/lib/ginga.h
index 890c86329..2f8223ba2 100644
--- a/lib/ginga.h
+++ b/lib/ginga.h
@@ -53,7 +53,7 @@ struct GingaOptions
bool debug;
/// @brief Whether to enable debug mode.
- bool webservice;
+ bool webservices;
/// @brief Whether to enable experimental features.
bool experimental;
@@ -89,7 +89,6 @@ class Ginga
virtual GingaState getState () = 0;
virtual bool start (const std::string &path, std::string *errmsg) = 0;
virtual bool stop () = 0;
- virtual bool startWebServices () = 0;
virtual void resize (int width, int height) = 0;
virtual void redraw (cairo_t *cr) = 0;
diff --git a/src/ginga.cpp b/src/ginga.cpp
index 78f009864..a1fb634b6 100644
--- a/src/ginga.cpp
+++ b/src/ginga.cpp
@@ -43,7 +43,7 @@ static Ginga *GINGA = nullptr;
static gboolean opt_debug = FALSE; // toggle debug
static gboolean opt_experimental = FALSE; // toggle experimental stuff
static gboolean opt_fullscreen = FALSE; // toggle fullscreen-mode
-static gboolean opt_ws = FALSE; // toggle webservices-only-mode
+static gboolean opt_webservices = FALSE; // toggle webservices-only-mode
static gboolean opt_opengl = FALSE; // toggle OpenGL backend
static string opt_background = ""; // background color
static gint opt_width = 800; // initial window width
@@ -101,7 +101,7 @@ static GOptionEntry options[]
"Enable debugging", NULL },
{ "fullscreen", 'f', 0, G_OPTION_ARG_NONE, &opt_fullscreen,
"Enable full-screen mode", NULL },
- { "ws", 'w', 0, G_OPTION_ARG_NONE, &opt_ws,
+ { "ws", 'w', 0, G_OPTION_ARG_NONE, &opt_webservices,
"Enable WebService and turn file param optional.", NULL },
{ "opengl", 'g', 0, G_OPTION_ARG_NONE, &opt_opengl,
"Use OpenGL backend", NULL },
@@ -343,7 +343,7 @@ main (int argc, char **argv)
_exit (0);
}
- if (!opt_ws && saved_argc < 2)
+ if (!opt_webservices && saved_argc < 2)
{
usage_error ("Missing file operand");
_exit (0);
@@ -406,7 +406,7 @@ main (int argc, char **argv)
opts.width = opt_width;
opts.height = opt_height;
opts.debug = opt_debug;
- opts.webservice = opt_ws;
+ opts.webservices = opt_webservices;
opts.experimental = opt_experimental;
opts.opengl = opt_opengl;
opts.background = string (opt_background);
@@ -414,15 +414,15 @@ main (int argc, char **argv)
g_assert_nonnull (GINGA);
int fail_count = 0;
- // Run only GingaCC-WebServices
- if (opt_ws && saved_argc < 2)
- {
- GINGA->startWebServices ();
- GMainLoop * main_loop = g_main_loop_new (NULL, FALSE);
- g_main_loop_run (main_loop);
- g_main_loop_unref (main_loop);
- goto done;
- }
+ // // Run only GingaCC-WebServices
+ // if (opt_webservices && saved_argc < 2)
+ // {
+ // GINGA->startWebServices ();
+ // GMainLoop * main_loop = g_main_loop_new (NULL, FALSE);
+ // g_main_loop_run (main_loop);
+ // g_main_loop_unref (main_loop);
+ // goto done;
+ // }
// Run each NCL file, one after another.
for (int i = 1; i < saved_argc; i++)
diff --git a/tests/test-Ginga-getOptions.cpp b/tests/test-Ginga-getOptions.cpp
index 10c6c7427..3c983d0e2 100644
--- a/tests/test-Ginga-getOptions.cpp
+++ b/tests/test-Ginga-getOptions.cpp
@@ -27,7 +27,7 @@ main (void)
opts.height = 20;
opts.debug = false;
opts.experimental = false;
- opts.webservice = false;
+ opts.webservices = false;
opts.opengl = false;
opts.background = "green";
Ginga *ginga = Ginga::create (&opts);
@@ -37,7 +37,7 @@ main (void)
g_assert (out->width == opts.width);
g_assert (out->height == opts.height);
g_assert (out->debug == opts.debug);
- g_assert (out->webservice == opts.webservice);
+ g_assert (out->webservices == opts.webservices);
g_assert (out->experimental == opts.experimental);
g_assert (out->opengl == opts.opengl);
g_assert (out->background == opts.background);
diff --git a/tests/test-WebServices.cpp b/tests/test-WebServices.cpp
new file mode 100644
index 000000000..8a527cbef
--- /dev/null
+++ b/tests/test-WebServices.cpp
@@ -0,0 +1,215 @@
+/* Copyright (C) 2006-2018 PUC-Rio/Laboratorio TeleMidia
+
+This file is part of Ginga (Ginga-NCL).
+
+Ginga is free software: you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+Ginga 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 General Public
+License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Ginga. If not, see . */
+
+#include "tests.h"
+#include "WebServices.h"
+#include "PlayerRemote.h"
+#include
+
+Formatter *fmt;
+
+static void
+cb_endloop (SoupSession *session, SoupMessage *msg, gpointer loop)
+{
+ g_assert (msg->status_code == SOUP_STATUS_OK);
+ g_main_loop_quit ((GMainLoop *) loop);
+}
+
+static void
+cb_test_location_endloop (SoupSession *session, SoupMessage *msg,
+ gpointer loop)
+{
+ SoupMessageHeadersIter it;
+ const gchar *name;
+ const gchar *value;
+ char *url1;
+ char *url2;
+ const char *host_addr = fmt->getWebServices ()->host_addr;
+ SoupMessageHeaders *hdrs;
+
+ g_assert (msg->status_code == SOUP_STATUS_OK);
+
+ url1 = g_strdup_printf ("http://%s:%d", host_addr, WS_PORT);
+ url2 = g_strdup_printf ("https://%s:%d", host_addr, WS_PORT);
+
+ hdrs = msg->response_headers;
+ g_assert_cmpstr (
+ soup_message_headers_get_one (hdrs, "GingaCC-Server-BaseURL"), ==,
+ url1);
+ g_assert_cmpstr (
+ soup_message_headers_get_one (hdrs, "GingaCC-Server-SecureBaseURL"),
+ ==, url2);
+ g_assert_cmpstr (
+ soup_message_headers_get_one (hdrs, "GingaCC-Server-PairingMethods"),
+ ==, "qcode,kex");
+ g_free (url1);
+ g_free (url2);
+ g_main_loop_quit ((GMainLoop *) loop);
+}
+
+void
+test_ws_get_location (GMainLoop *loop)
+{
+ SoupMessage *msg;
+ SoupSession *session;
+ gchar *url;
+
+ session = soup_session_new ();
+ g_assert_nonnull (session);
+ url = g_strdup_printf ("http://localhost:%d%s", WS_PORT, WS_ROURTE_LOC);
+ g_assert_nonnull (url);
+ msg = soup_message_new ("GET", url);
+ g_assert_nonnull (msg);
+ soup_session_queue_message (session, msg, cb_test_location_endloop, loop);
+
+ g_free (url);
+ g_object_unref (session);
+}
+
+void
+test_ws_post_action (const char *node, const char *action, GMainLoop *loop)
+{
+ guint status;
+ SoupMessage *msg;
+ SoupSession *session;
+ gchar *body, *url;
+
+ session = soup_session_new ();
+ g_assert_nonnull (session);
+ url = g_strdup_printf ("http://localhost:%d%s1/docId/%s", WS_PORT,
+ WS_ROURTE_APPS, node);
+ g_assert_nonnull (url);
+ msg = soup_message_new ("POST", url);
+ g_assert_nonnull (msg);
+ body = g_strdup_printf (REMOTE_PLAYER_JSON_ACT, action, 0);
+ g_assert_nonnull (body);
+ soup_message_set_request (msg, "application/json", SOUP_MEMORY_COPY, body,
+ strlen (body));
+ soup_session_queue_message (session, msg, cb_endloop, loop);
+
+ g_free (url);
+ g_free (body);
+ g_object_unref (session);
+}
+
+int
+main (int argc, char **argv)
+{
+ Document *doc;
+ tests_parse_and_start (&fmt, &doc, "\
+\n\
+ \n\
+ \n\
+ \n\
+ \n\
+ \n\
+ \n\
+ \n\
+ \n\
+ \n\
+ \n\
+ \n\
+
\n\
+ \n\
+ \n\
+ \n\
+ \n\
+ \n\
+ \n\
+ \n\
+ \n\
+ \n\
+ \n\
+ \n\
+ \n\
+ \n\
+ \n\
+");
+ Formatter::setOptionDebug (fmt, "debug", true);
+ Formatter::setOptionWebServices (fmt, "webservices", true);
+
+ GMainLoop *loop;
+ loop = g_main_loop_new (NULL, FALSE);
+
+ // test get location
+ test_ws_get_location (loop);
+ g_main_loop_run (loop);
+
+ // test post actions: lookAt and lookAway
+ {
+ Context *body = cast (Context *, doc->getRoot ());
+ g_assert_nonnull (body);
+ Event *body_lambda = body->getLambda ();
+ g_assert_nonnull (body_lambda);
+
+ Media *m1 = cast (Media *, doc->getObjectById ("m1"));
+ g_assert_nonnull (m1);
+ Event *m1_lambda = m1->getLambda ();
+ g_assert_nonnull (m1_lambda);
+ Media *m2 = cast (Media *, doc->getObjectById ("m2"));
+ g_assert_nonnull (m2);
+ Event *m2_lambda = m2->getLambda ();
+ g_assert_nonnull (m2_lambda);
+ Media *m3 = cast (Media *, doc->getObjectById ("m3"));
+ g_assert_nonnull (m3);
+ Event *m3_lambda = m3->getLambda ();
+ g_assert_nonnull (m3_lambda);
+
+ // when document is started, only the body_lambda is OCCURING
+ g_assert_cmpint ((body_lambda)->getState (), ==, Event::OCCURRING);
+ g_assert_cmpint ((m1_lambda)->getState (), ==, Event::SLEEPING);
+ g_assert_cmpint ((m2_lambda)->getState (), ==, Event::SLEEPING);
+ g_assert_cmpint ((m3_lambda)->getState (), ==, Event::SLEEPING);
+
+ // when advance time, m1_lambda is OCCURRING
+ (fmt)->sendTick (0, 0, 0);
+ g_assert_cmpint ((body_lambda)->getState (), ==, Event::OCCURRING);
+ g_assert_cmpint ((m1_lambda)->getState (), ==, Event::OCCURRING);
+ g_assert_cmpint ((m2_lambda)->getState (), ==, Event::SLEEPING);
+ g_assert_cmpint ((m3_lambda)->getState (), ==, Event::SLEEPING);
+
+ // START is done
+ Event *evtOnLookAt = m1->getLookAtEvent ("@lambda");
+ g_assert_nonnull (evtOnLookAt);
+
+ // lookAt
+ test_ws_post_action ("m1", "lookAt", loop);
+ g_main_loop_run (loop);
+
+ // after START, m1_onLooAt m2 OCCURRING
+ (fmt)->sendTick (0, 0, 0);
+ g_assert_cmpint ((body_lambda)->getState (), ==, Event::OCCURRING);
+ g_assert_cmpint ((m1_lambda)->getState (), ==, Event::OCCURRING);
+ g_assert_cmpint ((m2_lambda)->getState (), ==, Event::OCCURRING);
+ g_assert_cmpint ((m3_lambda)->getState (), ==, Event::SLEEPING);
+
+ // lookAway
+ test_ws_post_action ("m1", "lookAway", loop);
+ g_main_loop_run (loop);
+
+ // after START, m1_onLooAway m3 are OCCURRING
+ (fmt)->sendTick (0, 0, 0);
+ g_assert_cmpint ((body_lambda)->getState (), ==, Event::OCCURRING);
+ g_assert_cmpint ((m1_lambda)->getState (), ==, Event::OCCURRING);
+ g_assert_cmpint ((m2_lambda)->getState (), ==, Event::OCCURRING);
+ g_assert_cmpint ((m3_lambda)->getState (), ==, Event::OCCURRING);
+ }
+
+ g_main_loop_unref (loop);
+ delete fmt;
+ exit (EXIT_SUCCESS);
+}