From 65a755959f20509220016295fb219dc4e3690c4b Mon Sep 17 00:00:00 2001 From: Simon Chung Date: Tue, 17 Dec 2024 15:48:30 +0000 Subject: [PATCH 01/21] RDKECMF-213 Add CODEOWNERS file --- .github/CODEOWNERS | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .github/CODEOWNERS diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000..c7b23a3d --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,5 @@ +# These owners will be the default owners for everything in +# the repo. Unless a later match takes precedence, +# @global-owner1 and @global-owner2 will be requested for +# review when someone opens a pull request. +* @rdkcentral/xdialserver-maintainers From 8efac8e97bb559ba246dcc354c1410dd2b88d093 Mon Sep 17 00:00:00 2001 From: apatel859 Date: Mon, 3 Feb 2025 12:26:20 -0500 Subject: [PATCH 02/21] RDKEMW-254: libsoup3 support Signed-off-by: apatel859 --- server/CMakeLists.txt | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt index 7c56967d..c506e0d7 100644 --- a/server/CMakeLists.txt +++ b/server/CMakeLists.txt @@ -27,6 +27,7 @@ find_package (PkgConfig REQUIRED) pkg_search_module (GLIB REQUIRED glib-2.0) pkg_search_module (GIO REQUIRED gio-2.0) pkg_search_module (GSSDP12 gssdp-1.2) +pkg_search_module (LIBSOUP3 libsoup-3.0) if (GSSDP12_FOUND) pkg_search_module (GSSDP REQUIRED gssdp-1.2) add_definitions(-DHAVE_GSSDP_VERSION_1_2_OR_NEWER) @@ -34,7 +35,12 @@ if (GSSDP12_FOUND) else() pkg_search_module (GSSDP REQUIRED gssdp-1.0) endif() -pkg_search_module (SOUP REQUIRED libsoup-2.4) +if (LIBSOUP3_FOUND) + pkg_search_module (SOUP REQUIRED libsoup-3.0) + message("Using libsoup-3.0") +else() + pkg_search_module (SOUP REQUIRED libsoup-2.4) +endif() pkg_search_module (XML2 REQUIRED libxml-2.0) set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -g") @@ -101,4 +107,4 @@ target_link_libraries (gdial-server add_executable (gdial-server-ut gdialserver_ut.cpp) -target_link_libraries (gdial-server-ut gdial-plat gdial-server) \ No newline at end of file +target_link_libraries (gdial-server-ut gdial-plat gdial-server) From 55650a2f912c89dc7ad8f47aafb3cc3a2350ce23 Mon Sep 17 00:00:00 2001 From: yuvaramachandran_gurusamy Date: Tue, 4 Mar 2025 13:07:03 +0000 Subject: [PATCH 03/21] RDKTV-35185: Add sync between ssdp_http_server_callback and gdial_ssdp_set_friendlyname Signed-off-by: yuvaramachandran_gurusamy --- server/gdial-ssdp.c | 94 +++++++++++++++++++++++++++++++++------------ 1 file changed, 70 insertions(+), 24 deletions(-) diff --git a/server/gdial-ssdp.c b/server/gdial-ssdp.c index 4cf34433..4b9f2119 100644 --- a/server/gdial-ssdp.c +++ b/server/gdial-ssdp.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -74,6 +75,7 @@ static const char ssdp_device_xml_template[] = "" static gchar *dd_xml_response_str_ = NULL; static gchar *app_friendly_name = NULL; static gchar *app_random_uuid = NULL; +static pthread_mutex_t ssdpServerEventSync = PTHREAD_MUTEX_INITIALIZER; static void ssdp_http_server_callback(SoupServer *server, SoupMessage *msg, const char *path, GHashTable *query, SoupClientContext *client, gpointer user_data) { /* @@ -85,6 +87,8 @@ static void ssdp_http_server_callback(SoupServer *server, SoupMessage *msg, cons GDIAL_DEBUG("warning: SSDP HTTP Method is not GET"); return; } + + pthread_mutex_lock(&ssdpServerEventSync); /* * there is no variant here, so we can cache the response. */ @@ -94,24 +98,54 @@ static void ssdp_http_server_callback(SoupServer *server, SoupMessage *msg, cons const gchar *manufacturer= gdial_plat_dev_get_manufacturer(); const gchar *model = gdial_plat_dev_get_model(); - if (manufacturer == NULL) {manufacturer = gdial_options_->manufacturer;} - if (model == NULL) {model = gdial_options_->model_name;} - if(gdial_options_->feature_friendlyname && strlen(app_friendly_name)) + if (manufacturer == NULL) { + manufacturer = gdial_options_->manufacturer; + } + + if (model == NULL) { + model = gdial_options_->model_name; + } + + if(gdial_options_->feature_friendlyname && app_friendly_name && strlen(app_friendly_name)) + { dd_xml_response_str_ = g_strdup_printf(ssdp_device_xml_template, app_friendly_name, manufacturer, model, gdial_options_->uuid); + } else + { dd_xml_response_str_ = g_strdup_printf(ssdp_device_xml_template, gdial_options_->friendly_name, manufacturer, model, gdial_options_->uuid); + } - dd_xml_response_str_len = strlen(dd_xml_response_str_); + if ( dd_xml_response_str_ ) { + dd_xml_response_str_len = strlen(dd_xml_response_str_); + } + else { + GDIAL_LOGERROR("Failed to allocate memory for dd.xml response"); + soup_message_set_status(msg, SOUP_STATUS_INTERNAL_SERVER_ERROR); + } } - gchar *application_url_str = g_strdup_printf("http://%s:%d/%s/", iface_ipv4_address, GDIAL_REST_HTTP_PORT,app_random_uuid); - soup_message_headers_replace (msg->response_headers, "Application-URL", application_url_str); - g_free(application_url_str); - application_url_str = NULL; - soup_message_set_response(msg, "text/xml; charset=utf-8", SOUP_MEMORY_STATIC, dd_xml_response_str_, dd_xml_response_str_len); - soup_message_set_status(msg, SOUP_STATUS_OK); - GDIAL_CHECK("Content-Type:text/xml"); - GDIAL_CHECK("Application-URL: exist"); + if ( dd_xml_response_str_ ) { + gchar *application_url_str = g_strdup_printf("http://%s:%d/%s/", iface_ipv4_address, GDIAL_REST_HTTP_PORT,app_random_uuid); + + if ( application_url_str ) + { + soup_message_headers_replace (msg->response_headers, "Application-URL", application_url_str); + g_free(application_url_str); + application_url_str = NULL; + + soup_message_set_response(msg, "text/xml; charset=utf-8", SOUP_MEMORY_STATIC, dd_xml_response_str_, dd_xml_response_str_len); + soup_message_set_status(msg, SOUP_STATUS_OK); + + GDIAL_CHECK("Content-Type:text/xml"); + GDIAL_CHECK("Application-URL: exist"); + } + else + { + GDIAL_LOGERROR("Failed to allocate memory for response_headers"); + soup_message_set_status(msg, SOUP_STATUS_INTERNAL_SERVER_ERROR); + } + } + pthread_mutex_unlock(&ssdpServerEventSync); } void gdial_ssdp_networkstandbymode_handler(const bool nwstandby) @@ -138,6 +172,12 @@ int gdial_ssdp_new(SoupServer *ssdp_http_server, GDialOptions *options, const gc g_return_val_if_fail(options != NULL, -1); g_return_val_if_fail(options->iface_name != NULL, -1); + if (0 != pthread_mutex_init(&ssdpServerEventSync, NULL)) + { + GDIAL_LOGERROR("Failed to initializing mutex"); + return EXIT_FAILURE; + } + gdial_options_ = options; GDIAL_LOGINFO("gdial_options_->friendly_name[%p]",gdial_options_->friendly_name); @@ -273,6 +313,7 @@ int gdial_ssdp_destroy() { g_object_unref(ssdp_client_); ssdp_client_ = NULL; } + pthread_mutex_destroy(&ssdpServerEventSync); GDIAL_LOGTRACE("Exiting ..."); return 0; } @@ -287,15 +328,20 @@ int gdial_ssdp_set_available(bool activation_status, const gchar *friendlyname) int gdial_ssdp_set_friendlyname(const gchar *friendlyname) { - if(gdial_options_ && gdial_options_->feature_friendlyname && friendlyname) - { - if (app_friendly_name != NULL) g_free(app_friendly_name); - app_friendly_name = g_strdup(friendlyname); - GDIAL_LOGINFO("gdial_ssdp_set_friendlyname app_friendly_name :%s ",app_friendly_name); - if (dd_xml_response_str_!= NULL){ - g_free(dd_xml_response_str_); - dd_xml_response_str_ = NULL; - } - } - return 0; -} + pthread_mutex_lock(&ssdpServerEventSync); + if(gdial_options_ && gdial_options_->feature_friendlyname && friendlyname) + { + if (app_friendly_name != NULL) { + g_free(app_friendly_name); + } + app_friendly_name = g_strdup(friendlyname); + + GDIAL_LOGINFO("gdial_ssdp_set_friendlyname app_friendly_name :%s ",app_friendly_name); + if (dd_xml_response_str_!= NULL){ + g_free(dd_xml_response_str_); + dd_xml_response_str_ = NULL; + } + } + pthread_mutex_unlock(&ssdpServerEventSync); + return 0; +} \ No newline at end of file From 84b8951a72c46aab831fb2efb21bb475e3c9d286 Mon Sep 17 00:00:00 2001 From: hgfell683 <107510770+hgfell683@users.noreply.github.com> Date: Mon, 17 Mar 2025 13:12:39 -0400 Subject: [PATCH 04/21] RDKEMW-2033: Fix coverity related issue Reason for change: Fix issues identified within xcast Test Procedure: Risks: low Priority: P1 Signed-off-by:Hayden Gfeller --- server/plat/gdial.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/server/plat/gdial.cpp b/server/plat/gdial.cpp index 465cf5dc..81f38cfe 100644 --- a/server/plat/gdial.cpp +++ b/server/plat/gdial.cpp @@ -78,6 +78,11 @@ class GDialCastObject reterror = GDIAL_CAST_ERROR_NONE; } } + if (nullptr != AppObj) + { + delete AppObj; + AppObj = nullptr; + } GDIAL_LOGTRACE("Exiting ..."); return reterror; } From 9257820f36ad448d08a34191cf9f227578b867e0 Mon Sep 17 00:00:00 2001 From: yuvaramachandran_gurusamy Date: Wed, 19 Mar 2025 13:47:45 +0000 Subject: [PATCH 05/21] RDK-55044: [RDKE] Implement DIAL requirement to use on EU product Reason for change: Implemented setManufacturerName and setModelName APIs for DIAL Server name configuration maintained the additional data url to specific app Test Procedure: DIAL should work Risks: None Priority: P1 Signed-off-by: yuvaramachandran_gurusamy --- server/gdial-rest.c | 28 +++-- server/gdial-rest.h | 2 +- server/gdial-ssdp.c | 46 ++++++- server/gdial-ssdp.h | 2 + server/gdialservice.cpp | 59 +++++++++ server/include/gdial-plat-app.h | 6 + server/include/gdial-plat-dev.h | 2 - server/include/gdial_app_registry.h | 3 + server/include/gdialservice.h | 4 +- server/include/gdialserviceimpl.h | 4 + server/plat/CMakeLists.txt | 2 +- server/plat/gdial-os-app.h | 2 + server/plat/gdial-plat-app.c | 30 ++++- server/plat/gdial-plat-dev.c | 8 -- server/plat/gdial.cpp | 181 ++++++++++++++++++++++------ server/plat/gdial.hpp | 5 + server/plat/gdial_app_registry.c | 34 +++++- 17 files changed, 355 insertions(+), 63 deletions(-) diff --git a/server/gdial-rest.c b/server/gdial-rest.c index 4ed904ae..2312b85a 100644 --- a/server/gdial-rest.c +++ b/server/gdial-rest.c @@ -155,9 +155,12 @@ static void gdial_soup_message_set_http_error(SoupMessage *msg, guint state_code g_string_free(value_buf, FALSE); \ } -static GList *gdial_rest_server_registered_apps_clear(GList *registered_apps, GList *found) { +static GList *gdial_rest_server_registered_apps_clear(GDialRestServer *self, GList *registered_apps, GList *found) { + GDialRestServerPrivate *priv = gdial_rest_server_get_instance_private(self); GDialAppRegistry *app_registry = (GDialAppRegistry *)found->data; registered_apps = g_list_remove_link(registered_apps, found); + GDIAL_LOGINFO("Removing handler for app[%s]", app_registry->name); + soup_server_remove_handler(priv->local_soup_instance, app_registry->app_uri); gdial_app_regstry_dispose (app_registry); g_list_free(found); return registered_apps; @@ -281,7 +284,7 @@ GDIAL_STATIC gboolean gdial_rest_server_is_allowed_origin(GDialRestServer *self, return is_allowed; } -GDIAL_STATIC gchar *gdial_rest_server_new_additional_data_url(guint listening_port, const gchar *app_name, gboolean encode) { +GDIAL_STATIC gchar *gdial_rest_server_new_additional_data_url(guint listening_port, const gchar *app_name, gboolean encode , const gchar* app_uri) { /* * The specifciation of additionalDataUrl in form of /apps//dial_data * thus the instance data must be included in the query or payload, not the path. @@ -291,7 +294,7 @@ GDIAL_STATIC gchar *gdial_rest_server_new_additional_data_url(guint listening_po * for MIME type application/x-www-form-urlencoded.]] */ GString *url_buf = g_string_new(""); - g_string_printf(url_buf, "http://%s:%d%s/%s%s", "localhost", listening_port, GDIAL_REST_HTTP_APPS_URI, app_name, GDIAL_REST_HTTP_DIAL_DATA_URI); + g_string_printf(url_buf, "http://%s:%d%s%s", "localhost", listening_port, app_uri, GDIAL_REST_HTTP_DIAL_DATA_URI); gchar *unencoded = g_string_free(url_buf, FALSE); if (encode) { gchar *encoded = soup_uri_encode(unencoded, NULL); @@ -433,7 +436,7 @@ static void gdial_rest_server_handle_POST(GDialRestServer *gdial_rest_server, So if (new_app_instance) { gchar *additional_data_url = NULL; if (app_registry->use_additional_data) { - additional_data_url = gdial_rest_server_new_additional_data_url(listening_port, app_registry->name, FALSE); + additional_data_url = gdial_rest_server_new_additional_data_url(listening_port, app_registry->name, FALSE, app_registry->app_uri ); } gchar *additional_data_url_safe = soup_uri_encode(additional_data_url, NULL); GDIAL_LOGINFO("additionalDataUrl = %s, %s", additional_data_url, additional_data_url_safe); @@ -981,7 +984,7 @@ static void gdial_rest_server_dispose(GObject *object) { g_object_unref(priv->soup_instance); g_object_unref(priv->local_soup_instance); while (priv->registered_apps) { - priv->registered_apps = gdial_rest_server_registered_apps_clear(priv->registered_apps, priv->registered_apps); + priv->registered_apps = gdial_rest_server_registered_apps_clear(object, priv->registered_apps, priv->registered_apps); } G_OBJECT_CLASS (gdial_rest_server_parent_class)->dispose (object); } @@ -1102,10 +1105,7 @@ GDialRestServer *gdial_rest_server_new(SoupServer *rest_http_server,SoupServer * */ soup_server_add_handler(rest_http_server, "/apps/system", gdial_rest_http_server_system_callback, object, NULL); #endif - GDIAL_LOGINFO("gdial_local_rest_http_server_callback add handler"); GDIAL_REST_HTTP_APPS_URI = g_strdup_printf("/%s", random_id); - - soup_server_add_handler(local_rest_http_server, GDIAL_REST_HTTP_APPS_URI, gdial_local_rest_http_server_callback, object, NULL); return object; } @@ -1147,6 +1147,12 @@ gboolean gdial_rest_server_register_app(GDialRestServer *self, const gchar *app_ } g_return_val_if_fail(priv->registered_apps != NULL, FALSE); g_return_val_if_fail(gdial_rest_server_is_app_registered(self, app_name), FALSE); + + if( 0 != strcmp(app_name,"system")) + { + GDIAL_LOGINFO("gdial_local_rest_http_server_callback add handler for app_name:[%s]",app_name); + soup_server_add_handler(priv->local_soup_instance, app_registry->app_uri, gdial_local_rest_http_server_callback, self, NULL); + } GDIAL_LOGTRACE("Exiting ..."); return TRUE; } @@ -1204,7 +1210,7 @@ gboolean gdial_rest_server_unregister_all_apps(GDialRestServer *self) { priv->registered_apps = registered_apps_head; /*Remove all registered apps before*/ while (priv->registered_apps) { - priv->registered_apps = gdial_rest_server_registered_apps_clear(priv->registered_apps, priv->registered_apps); + priv->registered_apps = gdial_rest_server_registered_apps_clear(self, priv->registered_apps, priv->registered_apps); } GDIAL_LOGTRACE("Exiting ..."); return TRUE; @@ -1219,7 +1225,7 @@ gboolean gdial_rest_server_unregister_app(GDialRestServer *self, const gchar *ap GDialRestServerPrivate *priv = gdial_rest_server_get_instance_private(self); GList *found = g_list_find_custom(priv->registered_apps, app_name, GCompareFunc_match_registry_app_name); if (found == NULL) return FALSE; - priv->registered_apps = gdial_rest_server_registered_apps_clear(priv->registered_apps, found); + priv->registered_apps = gdial_rest_server_registered_apps_clear(self, priv->registered_apps, found); return TRUE; } @@ -1334,4 +1340,4 @@ GDIAL_STATIC_INLINE void GET_APP_response_builder_destroy(void *builder) { g_free(rbuilder->additionalData); g_free(builder); -} +} \ No newline at end of file diff --git a/server/gdial-rest.h b/server/gdial-rest.h index ca6f3db9..770d2463 100644 --- a/server/gdial-rest.h +++ b/server/gdial-rest.h @@ -50,7 +50,7 @@ gboolean gdial_rest_server_unregister_all_apps(GDialRestServer *self); typedef struct _GDialAppRegistry GDialAppRegistry; GDIAL_STATIC gboolean gdial_rest_server_is_allowed_origin(GDialRestServer *self, const gchar *header_origin, const gchar *app_name); -GDIAL_STATIC gchar *gdial_rest_server_new_additional_data_url(guint listening_port, const gchar *app_name, gboolean encode); +GDIAL_STATIC gchar *gdial_rest_server_new_additional_data_url(guint listening_port, const gchar *app_name, gboolean encode, const gchar* app_uri); GDIAL_STATIC GDialAppRegistry *gdial_rest_server_find_app_registry(GDialRestServer *self, const gchar *app_name); G_END_DECLS diff --git a/server/gdial-ssdp.c b/server/gdial-ssdp.c index 4b9f2119..d2ed7808 100644 --- a/server/gdial-ssdp.c +++ b/server/gdial-ssdp.c @@ -75,6 +75,8 @@ static const char ssdp_device_xml_template[] = "" static gchar *dd_xml_response_str_ = NULL; static gchar *app_friendly_name = NULL; static gchar *app_random_uuid = NULL; +static gchar *app_manufacturer_name = NULL; +static gchar *app_model_name = NULL; static pthread_mutex_t ssdpServerEventSync = PTHREAD_MUTEX_INITIALIZER; static void ssdp_http_server_callback(SoupServer *server, SoupMessage *msg, const char *path, GHashTable *query, SoupClientContext *client, gpointer user_data) { @@ -95,8 +97,8 @@ static void ssdp_http_server_callback(SoupServer *server, SoupMessage *msg, cons static size_t dd_xml_response_str_len = 0; if (!dd_xml_response_str_) { - const gchar *manufacturer= gdial_plat_dev_get_manufacturer(); - const gchar *model = gdial_plat_dev_get_model(); + const gchar *manufacturer= app_manufacturer_name; + const gchar *model = app_model_name; if (manufacturer == NULL) { manufacturer = gdial_options_->manufacturer; @@ -344,4 +346,44 @@ int gdial_ssdp_set_friendlyname(const gchar *friendlyname) } pthread_mutex_unlock(&ssdpServerEventSync); return 0; +} + +int gdial_ssdp_set_manufacturername(const gchar *manufacturer_name) +{ + pthread_mutex_lock(&ssdpServerEventSync); + if(manufacturer_name) + { + if (app_manufacturer_name != NULL) { + g_free(app_manufacturer_name); + } + app_manufacturer_name = g_strdup(manufacturer_name); + + GDIAL_LOGINFO("app_manufacturer_name :%s ",app_manufacturer_name); + if (dd_xml_response_str_!= NULL){ + g_free(dd_xml_response_str_); + dd_xml_response_str_ = NULL; + } + } + pthread_mutex_unlock(&ssdpServerEventSync); + return 0; +} + +int gdial_ssdp_set_modelname(const gchar *model_name) +{ + pthread_mutex_lock(&ssdpServerEventSync); + if(model_name) + { + if (app_model_name != NULL) { + g_free(app_model_name); + } + app_model_name = g_strdup(model_name); + + GDIAL_LOGINFO("app_model_name :%s ",app_model_name); + if (dd_xml_response_str_!= NULL){ + g_free(dd_xml_response_str_); + dd_xml_response_str_ = NULL; + } + } + pthread_mutex_unlock(&ssdpServerEventSync); + return 0; } \ No newline at end of file diff --git a/server/gdial-ssdp.h b/server/gdial-ssdp.h index 838b148f..3522e6df 100644 --- a/server/gdial-ssdp.h +++ b/server/gdial-ssdp.h @@ -30,6 +30,8 @@ int gdial_ssdp_new(SoupServer *server, GDialOptions *options, const gchar *rando int gdial_ssdp_destroy(); int gdial_ssdp_set_available(bool activationStatus, const gchar *friendlyName); int gdial_ssdp_set_friendlyname(const gchar *friendlyName); +int gdial_ssdp_set_manufacturername(const gchar *manufacturer_name); +int gdial_ssdp_set_modelname(const gchar *model_name); G_END_DECLS #endif diff --git a/server/gdialservice.cpp b/server/gdialservice.cpp index f0b6ee88..c41cf79d 100644 --- a/server/gdialservice.cpp +++ b/server/gdialservice.cpp @@ -171,6 +171,16 @@ static void server_friendlyname_handler(const gchar * friendlyname) gdial_ssdp_set_friendlyname(friendlyname); } +static void server_manufacturername_handler(const gchar * manufacturername) +{ + gdial_ssdp_set_manufacturername(manufacturername); +} + +static void server_modelname_handler(const gchar * modelname) +{ + gdial_ssdp_set_modelname(modelname); +} + static void server_powerstate_handler(const gchar * powerState) { gdialServiceImpl::getInstance()->updatePowerState(std::string(powerState)); @@ -268,6 +278,10 @@ int gdialServiceImpl::start_GDialServer(int argc, char *argv[]) gdail_plat_register_activation_cb(server_activation_handler); gdail_plat_register_friendlyname_cb(server_friendlyname_handler); + + gdail_plat_register_manufacturername_cb(server_manufacturername_handler); + gdail_plat_register_modelname_cb(server_modelname_handler); + gdail_plat_register_registerapps_cb (server_register_application); gdail_plat_dev_register_powerstate_cb(server_powerstate_handler); @@ -678,6 +692,18 @@ void *gdialServiceImpl::requestHandlerThread(void *ctx) gdial_plat_application_update_network_standby_mode((gboolean)reqHdlrPayload.user_param1); } break; + case UPDATE_MANUFACTURER_NAME: + { + GDIAL_LOGINFO("UPDATE_MANUFACTURER_NAME : data:%s",reqHdlrPayload.manufacturer.c_str()); + gdial_plat_application_update_manufacturer_name(reqHdlrPayload.manufacturer.c_str()); + } + break; + case UPDATE_MODEL_NAME: + { + GDIAL_LOGINFO("UPDATE_MODEL_NAME : data:%s",reqHdlrPayload.model.c_str()); + gdial_plat_application_update_model_name(reqHdlrPayload.model.c_str()); + } + break; default: { @@ -1043,6 +1069,39 @@ void gdialService::setNetworkStandbyMode(bool nwStandbymode) GDIAL_LOGTRACE("Exiting ..."); } + +GDIAL_SERVICE_ERROR_CODES gdialService::setManufacturerName(string manufacturer) +{ + gdialServiceImpl* gdialImplInstance = gdialServiceImpl::getInstance(); + GDIAL_LOGTRACE("Entering ..."); + GDIAL_LOGINFO("Manufacturer[%s]",manufacturer.c_str()); + if ((nullptr != m_gdialService ) && (nullptr != gdialImplInstance)) + { + RequestHandlerPayload payload; + payload.event = UPDATE_MANUFACTURER_NAME; + payload.manufacturer = manufacturer; + gdialImplInstance->sendRequest(payload); + } + GDIAL_LOGTRACE("Exiting ..."); + return GDIAL_SERVICE_ERROR_NONE; +} + +GDIAL_SERVICE_ERROR_CODES gdialService::setModelName(string model) +{ + gdialServiceImpl* gdialImplInstance = gdialServiceImpl::getInstance(); + GDIAL_LOGTRACE("Entering ..."); + GDIAL_LOGINFO("Model[%s]",model.c_str()); + if ((nullptr != m_gdialService ) && (nullptr != gdialImplInstance)) + { + RequestHandlerPayload payload; + payload.event = UPDATE_MODEL_NAME; + payload.model = model; + gdialImplInstance->sendRequest(payload); + } + GDIAL_LOGTRACE("Exiting ..."); + return GDIAL_SERVICE_ERROR_NONE; +} + void gdialServiceImpl::sendRequest( const RequestHandlerPayload& payload ) { GDIAL_LOGTRACE("Entering ..."); diff --git a/server/include/gdial-plat-app.h b/server/include/gdial-plat-app.h index 8a5134a9..8382f3ab 100644 --- a/server/include/gdial-plat-app.h +++ b/server/include/gdial-plat-app.h @@ -32,9 +32,13 @@ typedef void * gdial_async_handler_t; typedef void (*gdial_plat_activation_cb)(gboolean, const gchar *); typedef void (*gdial_plat_friendlyname_cb)(const gchar *); typedef void (*gdial_plat_registerapps_cb)(gpointer); +typedef void (*gdial_plat_manufacturername_cb)(const gchar *); +typedef void (*gdial_plat_modelname_cb)(const gchar *); void gdail_plat_register_activation_cb(gdial_plat_activation_cb cb); void gdail_plat_register_friendlyname_cb(gdial_plat_friendlyname_cb cb); void gdail_plat_register_registerapps_cb(gdial_plat_registerapps_cb cb); +void gdail_plat_register_manufacturername_cb(gdial_plat_manufacturername_cb cb); +void gdail_plat_register_modelname_cb(gdial_plat_modelname_cb cb); GDialAppError gdial_plat_application_start(const gchar *app_name, const gchar *payload, const gchar *query, const gchar *additional_data_url, gint *instance_id); GDialAppError gdial_plat_application_hide(const gchar *app_name, gint instance_id); @@ -48,6 +52,8 @@ GDialAppError gdial_plat_application_friendlyname_changed(const char *friendlyna const char* gdial_plat_application_get_protocol_version(); GDialAppError gdial_plat_application_register_applications(void*); void gdial_plat_application_update_network_standby_mode(gboolean nwstandbyMode); +GDialAppError gdial_plat_application_update_manufacturer_name(const char *manufacturer); +GDialAppError gdial_plat_application_update_model_name(const char *model); GDialAppError gdial_plat_application_service_notification(gboolean isNotifyRequired, void* notifier); void * gdial_plat_application_start_async(const gchar *app_name, const gchar *payload, const gchar *query, const gchar *additional_data_url, void *user_data); diff --git a/server/include/gdial-plat-dev.h b/server/include/gdial-plat-dev.h index 99ca0e53..05b607d4 100644 --- a/server/include/gdial-plat-dev.h +++ b/server/include/gdial-plat-dev.h @@ -26,8 +26,6 @@ extern "C" { #endif -const char * gdial_plat_dev_get_manufacturer(); -const char * gdial_plat_dev_get_model(); bool gdial_plat_dev_set_power_state_on(); bool gdial_plat_dev_set_power_state_off(); bool gdial_plat_dev_toggle_power_state(); diff --git a/server/include/gdial_app_registry.h b/server/include/gdial_app_registry.h index 02fbae09..1a701318 100644 --- a/server/include/gdial_app_registry.h +++ b/server/include/gdial_app_registry.h @@ -32,7 +32,10 @@ G_BEGIN_DECLS +#define APP_MAX_UUID_SIZE 64 + typedef struct _GDialAppRegistry { + gchar app_uri[APP_MAX_UUID_SIZE+1]; // for storing uri gchar *name; gboolean use_additional_data; gboolean is_singleton; diff --git a/server/include/gdialservice.h b/server/include/gdialservice.h index 179a1d47..e3523868 100644 --- a/server/include/gdialservice.h +++ b/server/include/gdialservice.h @@ -41,10 +41,12 @@ class gdialService string getProtocolVersion(void); GDIAL_SERVICE_ERROR_CODES RegisterApplications(RegisterAppEntryList* appConfigList); void setNetworkStandbyMode(bool nwStandbymode); + GDIAL_SERVICE_ERROR_CODES setManufacturerName(string manufacturer); + GDIAL_SERVICE_ERROR_CODES setModelName(string model); private: GDialNotifier* m_observer{nullptr}; gdialService(){}; virtual ~gdialService(){}; }; -#endif /* gdialService_hpp */ \ No newline at end of file +#endif /* gdialService_hpp */ diff --git a/server/include/gdialserviceimpl.h b/server/include/gdialserviceimpl.h index 29fd6168..5733b43e 100644 --- a/server/include/gdialserviceimpl.h +++ b/server/include/gdialserviceimpl.h @@ -37,6 +37,8 @@ typedef enum _AppRequestEvents FRIENDLYNAME_CHANGED, REGISTER_APPLICATIONS, UPDATE_NW_STANDBY, + UPDATE_MANUFACTURER_NAME, + UPDATE_MODEL_NAME, INVALID_REQUEST } AppRequestEvents; @@ -59,6 +61,8 @@ typedef struct _RequestHandlerPayload std::string appIdOractivation; std::string state; std::string error; + std::string manufacturer; + std::string model; void* data_param; bool user_param1; AppRequestEvents event; diff --git a/server/plat/CMakeLists.txt b/server/plat/CMakeLists.txt index 341fee74..c6fa074d 100644 --- a/server/plat/CMakeLists.txt +++ b/server/plat/CMakeLists.txt @@ -69,4 +69,4 @@ if(PLATFORM) endif() add_library(gdial-plat SHARED ${GDIAL_PLAT_LIB_SOURCE_FILES}) -target_link_Libraries(gdial-plat PRIVATE ${GLIB_LIBRARIES} ${GOBJECT_LIBRARIES} -lpthread -lWPEFrameworkCore -lWPEFrameworkDefinitions -lWPEFrameworkCOM -lWPEFrameworkPlugins -lWPEFrameworkSecurityUtil -lIARMBus) +target_link_Libraries(gdial-plat PRIVATE ${GLIB_LIBRARIES} ${GOBJECT_LIBRARIES} -lpthread -lWPEFrameworkCore -lWPEFrameworkDefinitions -lWPEFrameworkCOM -lWPEFrameworkPlugins -lWPEFrameworkSecurityUtil -lIARMBus -luuid) diff --git a/server/plat/gdial-os-app.h b/server/plat/gdial-os-app.h index 43229602..c2ed67e1 100644 --- a/server/plat/gdial-os-app.h +++ b/server/plat/gdial-os-app.h @@ -39,6 +39,8 @@ int gdial_os_application_friendlyname_changed(const char *friendlyname); const char* gdial_os_application_get_protocol_version(); int gdial_os_application_register_applications(void*); void gdial_os_application_update_network_standby_mode(gboolean nwstandbyMode); +int gdial_os_application_update_manufacturer_name(const char *manufacturer); +int gdial_os_application_update_model_name(const char *model); int gdial_os_application_service_notification(gboolean isNotifyRequired, void* notifier); #ifdef __cplusplus diff --git a/server/plat/gdial-plat-app.c b/server/plat/gdial-plat-app.c index 56fef49a..0d2d0eea 100644 --- a/server/plat/gdial-plat-app.c +++ b/server/plat/gdial-plat-app.c @@ -35,6 +35,8 @@ static gpointer gdial_app_state_cb_user_data_ = NULL; static gdial_plat_activation_cb g_activation_cb = NULL; static gdial_plat_friendlyname_cb g_friendlyname_cb = NULL; static gdial_plat_registerapps_cb g_registerapps_cb = NULL; +static gdial_plat_manufacturername_cb g_manufacturername_cb = NULL; +static gdial_plat_modelname_cb g_modelname_cb = NULL; #define GDIAL_PLAT_APP_ASYNC_CONTEXT_TYPE_COMMON 0 #define GDIAL_PLAT_APP_ASYNC_CONTEXT_TYPE_START 1 @@ -151,6 +153,18 @@ void gdail_plat_register_registerapps_cb(gdial_plat_registerapps_cb cb) gdial_register_registerapps_cb(cb); } +void gdail_plat_register_manufacturername_cb(gdial_plat_manufacturername_cb cb) +{ + g_manufacturername_cb = cb; + gdial_register_manufacturername_cb(cb); +} + +void gdail_plat_register_modelname_cb(gdial_plat_modelname_cb cb) +{ + g_modelname_cb = cb; + gdial_register_modelname_cb(cb); +} + gint gdial_plat_init(GMainContext *main_context) { g_return_val_if_fail(main_context != NULL, GDIAL_APP_ERROR_INTERNAL); g_return_val_if_fail((g_main_context_ == NULL || g_main_context_ == main_context), GDIAL_APP_ERROR_INTERNAL); @@ -336,8 +350,22 @@ void gdial_plat_application_update_network_standby_mode(gboolean nwstandby) gdial_os_application_update_network_standby_mode(nwstandby); } +GDialAppError gdial_plat_application_update_manufacturer_name(const char *manufacturer) +{ + g_return_val_if_fail(manufacturer != NULL, GDIAL_APP_ERROR_BAD_REQUEST); + + return gdial_os_application_update_manufacturer_name(manufacturer); +} + +GDialAppError gdial_plat_application_update_model_name(const char *model) +{ + g_return_val_if_fail(model != NULL, GDIAL_APP_ERROR_BAD_REQUEST); + + return gdial_os_application_update_model_name(model); +} + GDialAppError gdial_plat_application_service_notification(gboolean isNotifyRequired, void* notifier) { g_return_val_if_fail((notifier != NULL)||(false == isNotifyRequired), GDIAL_APP_ERROR_BAD_REQUEST); return gdial_os_application_service_notification(isNotifyRequired,notifier); -} +} \ No newline at end of file diff --git a/server/plat/gdial-plat-dev.c b/server/plat/gdial-plat-dev.c index 55d4c40f..baef465a 100644 --- a/server/plat/gdial-plat-dev.c +++ b/server/plat/gdial-plat-dev.c @@ -25,14 +25,6 @@ static gdial_plat_dev_nwstandbymode_cb g_nwstandbymode_cb = NULL; static gdial_plat_dev_powerstate_cb g_powerstate_cb = NULL; -const char * gdial_plat_dev_get_manufacturer() { - return g_getenv("GDIAL_DEV_MANUFACTURER"); -} - -const char * gdial_plat_dev_get_model() { - return g_getenv("GDIAL_DEV_MODEL"); -} - void gdial_plat_dev_nwstandby_mode_change(gboolean NetworkStandbyMode) { if(g_nwstandbymode_cb) g_nwstandbymode_cb(NetworkStandbyMode); diff --git a/server/plat/gdial.cpp b/server/plat/gdial.cpp index 465cf5dc..175b8782 100644 --- a/server/plat/gdial.cpp +++ b/server/plat/gdial.cpp @@ -49,6 +49,8 @@ GDialAppStatusCache* AppCache = nullptr; static gdial_activation_cb g_activation_cb = NULL; static gdial_friendlyname_cb g_friendlyname_cb = NULL; static gdial_registerapps_cb g_registerapps_cb = NULL; +static gdial_manufacturername_cb g_manufacturername_cb = NULL; +static gdial_modelname_cb g_modelname_cb = NULL; #define DIAL_MAX_NUM_OF_APPS (64) #define DIAL_MAX_NUM_OF_APP_NAMES (64) @@ -68,7 +70,7 @@ class GDialCastObject AppInfo* AppObj = new AppInfo(applicationName,applicationId,state,error); if ((nullptr != AppObj) && (nullptr != AppCache)) { - GDIAL_LOGINFO("AppName : %s AppID : %s State : %s Error : %s", + GDIAL_LOGINFO("AppName[%s] AppID[%s] State[%s] Error[%s]", AppObj->appName.c_str(), AppObj->appId.c_str(), AppObj->appState.c_str(), @@ -88,7 +90,7 @@ class GDialCastObject GDialErrorCode error = GDIAL_CAST_ERROR_INTERNAL; if( g_friendlyname_cb && friendlyname ) { - GDIAL_LOGINFO("GDialCastObject::friendlyNameChanged :%s ",friendlyname); + GDIAL_LOGINFO("GDialCastObject::friendlyNameChanged:[%s]",friendlyname); g_friendlyname_cb(friendlyname); error = GDIAL_CAST_ERROR_NONE; } @@ -111,7 +113,7 @@ class GDialCastObject { break; } - GDIAL_LOGINFO("Application: %d ", i); + GDIAL_LOGINFO("Application:[%d]", i); gAppPrefxes = g_list_prepend (gAppPrefxes, g_strdup(appEntry->prefixes.c_str())); GDIAL_LOGINFO("%s, ", appEntry->prefixes.c_str()); GDIAL_LOGINFO(""); @@ -123,7 +125,7 @@ class GDialCastObject GHashTable *gProperties = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); std::string appAllowStop = appEntry->allowStop ? "true" : "false"; g_hash_table_insert(gProperties,g_strdup("allowStop"),g_strdup(appAllowStop.c_str())); - GDIAL_LOGINFO("allowStop: %s", appAllowStop.c_str()); + GDIAL_LOGINFO("allowStop:[%s]", appAllowStop.c_str()); GDIAL_LOGINFO(""); GDialAppRegistry* app_registry = gdial_app_registry_new( g_strdup(appEntry->Names.c_str()), @@ -165,7 +167,7 @@ class GDialCastObject { GDIAL_LOGTRACE("Entering ..."); GDialErrorCode error = GDIAL_CAST_ERROR_INTERNAL; - GDIAL_LOGINFO("status: %s friendlyname: %s ",status.c_str(),friendlyName.c_str()); + GDIAL_LOGINFO("status[%s] friendlyname[%s]",status.c_str(),friendlyName.c_str()); if( g_activation_cb ) { if(!strcmp(status.c_str(), "true")) @@ -176,7 +178,7 @@ class GDialCastObject { g_activation_cb(0,friendlyName.c_str()); } - GDIAL_LOGINFO("status: %s g_activation_cb :%p",status.c_str(), g_activation_cb); + GDIAL_LOGINFO("status[%s] g_activation_cb[%p]",status.c_str(), g_activation_cb); error = GDIAL_CAST_ERROR_NONE; } GDIAL_LOGTRACE("Exiting ..."); @@ -189,14 +191,48 @@ class GDialCastObject gdial_plat_dev_nwstandby_mode_change(nwstandbyMode); GDIAL_LOGTRACE("Exiting ..."); } + + GDialErrorCode updateManufacturerName(const char* manufacturerName) + { + GDIAL_LOGTRACE("Entering ..."); + GDialErrorCode error = GDIAL_CAST_ERROR_INTERNAL; + if( g_manufacturername_cb && manufacturerName ) + { + GDIAL_LOGINFO("Manufacturer[%s]",manufacturerName); + g_manufacturername_cb(manufacturerName); + error = GDIAL_CAST_ERROR_NONE; + } + GDIAL_LOGTRACE("Exiting ..."); + return error; + } + + GDialErrorCode updateModelName(const char* modelName) + { + GDIAL_LOGTRACE("Entering ..."); + GDialErrorCode error = GDIAL_CAST_ERROR_INTERNAL; + if( g_modelname_cb && modelName ) + { + GDIAL_LOGINFO("ModelName[%s]",modelName); + g_modelname_cb(modelName); + error = GDIAL_CAST_ERROR_NONE; + } + GDIAL_LOGTRACE("Exiting ..."); + return error; + } GDialErrorCode launchApplication(const char* appName, const char* args) { GDIAL_LOGTRACE("Entering ..."); std::string applicationName = "", parameter = ""; - if (nullptr!=appName) applicationName = appName; - if (nullptr!=args) parameter = args; + + if (nullptr!=appName){ + applicationName = appName; + } + + if (nullptr!=args){ + parameter = args; + } GDIAL_LOGINFO("App[%s] param[%s] observer[%p]",applicationName.c_str(),parameter.c_str(),m_observer); if (nullptr!=m_observer) @@ -215,10 +251,18 @@ class GDialCastObject additionalDataUrl = ""; GDIAL_LOGTRACE("Entering ..."); - if (nullptr!=appName) applicationName = appName; - if (nullptr!=argPayload) payLoad = argPayload; - if (nullptr!=argQueryString) queryString = argQueryString; - if (nullptr!=argAdditionalDataUrl) additionalDataUrl = argAdditionalDataUrl; + if (nullptr!=appName){ + applicationName = appName; + } + if (nullptr!=argPayload){ + payLoad = argPayload; + } + if (nullptr!=argQueryString){ + queryString = argQueryString; + } + if (nullptr!=argAdditionalDataUrl){ + additionalDataUrl = argAdditionalDataUrl; + } GDIAL_LOGINFO("App[%s] payload[%s] query_string[%s] additional_data_url[%s]observer[%p]", applicationName.c_str(), @@ -242,8 +286,12 @@ class GDialCastObject GDIAL_LOGTRACE("Entering ..."); std::string applicationName = "", applicationId = ""; - if (nullptr!=appName) applicationName = appName; - if (nullptr!=appID) applicationId = appID; + if (nullptr!=appName){ + applicationName = appName; + } + if (nullptr!=appID){ + applicationId = appID; + } GDIAL_LOGINFO("App[%s]ID[%s]observer[%p]",applicationName.c_str(),applicationId.c_str(),m_observer); if (nullptr!=m_observer) @@ -260,8 +308,12 @@ class GDialCastObject std::string applicationName = "", applicationId = ""; - if (nullptr!=appName) applicationName = appName; - if (nullptr!=appID) applicationId = appID; + if (nullptr!=appName){ + applicationName = appName; + } + if (nullptr!=appID){ + applicationId = appID; + } GDIAL_LOGINFO("App[%s]ID[%s]observer[%p]",applicationName.c_str(),applicationId.c_str(),m_observer); if (nullptr!=m_observer) @@ -278,8 +330,12 @@ class GDialCastObject std::string applicationName = "", applicationId = ""; - if (nullptr!=appName) applicationName = appName; - if (nullptr!=appID) applicationId = appID; + if (nullptr!=appName){ + applicationName = appName; + } + if (nullptr!=appID){ + applicationId = appID; + } GDIAL_LOGINFO("App[%s]ID[%s]observer[%p]",applicationName.c_str(),applicationId.c_str(),m_observer); if (nullptr!=m_observer) @@ -296,8 +352,12 @@ class GDialCastObject std::string applicationName = "", applicationId = ""; - if (nullptr!=appName) applicationName = appName; - if (nullptr!=appID) applicationId = appID; + if (nullptr!=appName){ + applicationName = appName; + } + if (nullptr!=appID){ + applicationId = appID; + } GDIAL_LOGINFO("App[%s]ID[%s]observer[%p]",applicationName.c_str(),applicationId.c_str(),m_observer); if (nullptr!=m_observer) @@ -333,6 +393,16 @@ void gdial_register_registerapps_cb(gdial_registerapps_cb cb) g_registerapps_cb = cb; } +void gdial_register_manufacturername_cb(gdial_manufacturername_cb cb) +{ + g_manufacturername_cb = cb; +} + +void gdial_register_modelname_cb(gdial_modelname_cb cb) +{ + g_modelname_cb = cb; +} + bool gdial_init(GMainContext *context) { bool returnValue = false; @@ -435,7 +505,7 @@ map parse_query(const char* query_string) { int gdial_os_application_start(const char *app_name, const char *payload, const char *query_string, const char *additional_data_url, int *instance_id) { GDIAL_LOGTRACE("Entering ..."); - GDIAL_LOGINFO("App launch req: appName: %s query: [%s], payload: [%s], additionalDataUrl [%s] instance[%p]", + GDIAL_LOGINFO("App launch req: appName[%s] query[%s], payload[%s], additionalDataUrl [%s] instance[%p]", app_name, query_string, payload, additional_data_url,instance_id); if (strcmp(app_name,"system") == 0) { @@ -508,20 +578,20 @@ std::string GetCurrentState() { } else { string sToken = (char*)buffer; string query = "token=" + sToken; - GDIAL_LOGINFO("Security token = %s ",query.c_str()); + GDIAL_LOGINFO("Security token[%s] ",query.c_str()); controllerRemoteObject = new JSONRPC::LinkType(std::string(), false, query); } } std::string nfxstatus = "status@" + nfx_callsign; if(controllerRemoteObject->Get(1000, _T(nfxstatus), pluginResponse) == Core::ERROR_NONE) { - GDIAL_LOGINFO("Obtained netflix status = %s",nfxstatus.c_str()); + GDIAL_LOGINFO("Obtained netflix status[%s]",nfxstatus.c_str()); Core::JSON::ArrayType::Iterator index(pluginResponse.Elements()); while (index.Next() == true) { netflixState = index.Current().JSONState.Data(); } //end of while loop } //end of if case for querrying - GDIAL_LOGINFO("Netflix State = %s",netflixState.c_str()); + GDIAL_LOGINFO("Netflix State[%s]",netflixState.c_str()); return netflixState; } void stop_netflix() @@ -541,7 +611,7 @@ void stop_netflix() int gdial_os_application_stop(const char *app_name, int instance_id) { bool enable_stop = false; GDIAL_LOGTRACE("Entering ..."); - GDIAL_LOGINFO("AppName = %s appID = %s",app_name,std::to_string(instance_id).c_str()); + GDIAL_LOGINFO("AppName[%s] appID[%s]",app_name,std::to_string(instance_id).c_str()); if((strcmp(app_name,"system") == 0)){ GDIAL_LOGINFO("delete not supported for system app return GDIAL_APP_ERROR_BAD_REQUEST"); return GDIAL_APP_ERROR_BAD_REQUEST; @@ -591,7 +661,7 @@ int gdial_os_application_hide(const char *app_name, int instance_id) return GDIAL_APP_ERROR_NONE; } #if 0 - GDIAL_LOGINFO("gdial_os_application_hide-->stop: appName = %s appID = %s",app_name,std::to_string(instance_id).c_str()); + GDIAL_LOGINFO("gdial_os_application_hide-->stop: appName[%s] appID[%s]",app_name,std::to_string(instance_id).c_str()); std::string State = AppCache->SearchAppStatusInCache(app_name); if (0 && State != "running") { return GDIAL_APP_ERROR_BAD_REQUEST; @@ -607,7 +677,7 @@ int gdial_os_application_hide(const char *app_name, int instance_id) } return GDIAL_APP_ERROR_NONE; #else - GDIAL_LOGINFO("gdial_os_application_hide: appName = %s appID = %s",app_name,std::to_string(instance_id).c_str()); + GDIAL_LOGINFO("gdial_os_application_hide: appName[%s] appID[%s]",app_name,std::to_string(instance_id).c_str()); std::string State = AppCache->SearchAppStatusInCache(app_name); if (State != "running") { @@ -635,7 +705,7 @@ int gdial_os_application_hide(const char *app_name, int instance_id) int gdial_os_application_resume(const char *app_name, int instance_id) { GDIAL_LOGTRACE("Entering ..."); - GDIAL_LOGINFO("appName = %s appID = %s",app_name,std::to_string(instance_id).c_str()); + GDIAL_LOGINFO("appName[%s] appID[%s]",app_name,std::to_string(instance_id).c_str()); if((strcmp(app_name,"system") == 0)){ GDIAL_LOGINFO("system app can not be resume"); @@ -668,7 +738,7 @@ int gdial_os_application_resume(const char *app_name, int instance_id) { int gdial_os_application_state(const char *app_name, int instance_id, GDialAppState *state) { GDIAL_LOGTRACE("Entering ..."); - GDIAL_LOGINFO("App = %s Id = %d",app_name,instance_id); + GDIAL_LOGINFO("App[%s] Id[%d]",app_name,instance_id); if((strcmp(app_name,"system") == 0)){ *state = GDIAL_APP_STATE_HIDE; GDIAL_LOGINFO("getApplicationState: AppState = suspended "); @@ -676,7 +746,7 @@ int gdial_os_application_state(const char *app_name, int instance_id, GDialAppSt return GDIAL_APP_ERROR_NONE; } std::string State = AppCache->SearchAppStatusInCache(app_name); - GDIAL_LOGINFO("getApplicationState: AppState = %s ",State.c_str()); + GDIAL_LOGINFO("getApplicationState: AppState[%s] ",State.c_str()); /* * return cache, but also trigger a refresh */ @@ -710,7 +780,7 @@ int gdial_os_application_state(const char *app_name, int instance_id, GDialAppSt if ( enable_stop != NULL ) { if (strcmp(app_name,"Netflix") == 0 && strcmp(enable_stop,"true") == 0) { std::string app_state = GetCurrentState(); - GDIAL_LOGINFO("Presence of Netflix thunder plugin state = %s to confirm state", app_state.c_str()); + GDIAL_LOGINFO("Presence of Netflix thunder plugin state[%s] to confirm state", app_state.c_str()); if (app_state == "deactivated") { *state = GDIAL_APP_STATE_STOPPED; GDIAL_LOGINFO("app [%s] state converted to [%d]", app_name, *state); @@ -732,7 +802,7 @@ int gdial_os_application_state(const char *app_name, int instance_id, GDialAppSt int gdial_os_application_state_changed(const char *applicationName, const char *applicationId, const char *state, const char *error) { GDIAL_LOGTRACE("Entering ..."); - GDIAL_LOGINFO("appName: %s appId: [%s], state: [%s], error [%s]",applicationName, applicationId, state, error); + GDIAL_LOGINFO("appName[%s] appId[%s], state[%s], error [%s]",applicationName, applicationId, state, error); if (nullptr == GDialObjHandle) { @@ -755,7 +825,7 @@ int gdial_os_application_state_changed(const char *applicationName, const char * int gdial_os_application_activation_changed(const char *activation, const char *friendlyname) { GDIAL_LOGTRACE("Entering ..."); - GDIAL_LOGINFO("activation: %s friendlyname: [%s]",activation, friendlyname); + GDIAL_LOGINFO("activation[%s] friendlyname[%s]",activation, friendlyname); if (nullptr == GDialObjHandle) { @@ -778,7 +848,7 @@ int gdial_os_application_activation_changed(const char *activation, const char * int gdial_os_application_friendlyname_changed(const char *friendlyname) { GDIAL_LOGTRACE("Entering ..."); - GDIAL_LOGINFO("friendlyname: [%s]",friendlyname); + GDIAL_LOGINFO("friendlyname[%s]",friendlyname); if (nullptr == GDialObjHandle) { @@ -849,6 +919,47 @@ void gdial_os_application_update_network_standby_mode(gboolean nwstandbymode) GDIAL_LOGTRACE("Exiting ..."); } +int gdial_os_application_update_manufacturer_name(const char *manufacturer) +{ + GDialErrorCode returnValue = GDIAL_CAST_ERROR_INTERNAL; + GDIAL_LOGTRACE("Entering ..."); + if ((nullptr == GDialObjHandle)||(nullptr == manufacturer)) + { + GDIAL_LOGERROR("NULL GDialObjHandle[%p] manufacturer[%p]",GDialObjHandle,manufacturer); + } + else + { + GDIAL_LOGINFO("Manufacturer[%s]", manufacturer); + returnValue = GDialObjHandle->updateManufacturerName(manufacturer); + if (returnValue != GDIAL_CAST_ERROR_NONE) { + GDIAL_LOGERROR("Failed to updateManufacturerName. Error=%x",returnValue); + } + } + + GDIAL_LOGTRACE("Exiting ..."); + return returnValue; +} + +int gdial_os_application_update_model_name(const char *model) +{ + GDialErrorCode returnValue = GDIAL_CAST_ERROR_INTERNAL; + GDIAL_LOGTRACE("Entering ..."); + if ((nullptr == GDialObjHandle)||(nullptr == model)) + { + GDIAL_LOGERROR("NULL GDialObjHandle[%p] manufacturer[%p]",GDialObjHandle,model); + } + else + { + GDIAL_LOGINFO("Model[%s]", model); + returnValue = GDialObjHandle->updateModelName(model); + if (returnValue != GDIAL_CAST_ERROR_NONE) { + GDIAL_LOGERROR("Failed to updateModelName. Error=%x",returnValue); + } + } + GDIAL_LOGTRACE("Exiting ..."); + return returnValue; +} + int gdial_os_application_service_notification(gboolean isNotifyRequired, void* notifier) { GDIAL_LOGTRACE("Entering ..."); @@ -870,4 +981,4 @@ int gdial_os_application_service_notification(gboolean isNotifyRequired, void* n } GDIAL_LOGTRACE("Exiting ..."); return GDIAL_APP_ERROR_NONE; -} +} \ No newline at end of file diff --git a/server/plat/gdial.hpp b/server/plat/gdial.hpp index dcd36d2e..6570e974 100644 --- a/server/plat/gdial.hpp +++ b/server/plat/gdial.hpp @@ -45,9 +45,14 @@ void gdial_term(); typedef void (*gdial_activation_cb)(bool, const gchar *); typedef void (*gdial_friendlyname_cb)(const gchar *); typedef void (*gdial_registerapps_cb)(gpointer); +typedef void (*gdial_manufacturername_cb)(const gchar *); +typedef void (*gdial_modelname_cb)(const gchar *); + void gdial_register_activation_cb(gdial_activation_cb cb); void gdial_register_friendlyname_cb(gdial_friendlyname_cb cb); void gdial_register_registerapps_cb(gdial_registerapps_cb cb); +void gdial_register_manufacturername_cb(gdial_manufacturername_cb cb); +void gdial_register_modelname_cb(gdial_manufacturername_cb cb); #ifdef __cplusplus } diff --git a/server/plat/gdial_app_registry.c b/server/plat/gdial_app_registry.c index c3346470..2ef69c77 100644 --- a/server/plat/gdial_app_registry.c +++ b/server/plat/gdial_app_registry.c @@ -19,9 +19,12 @@ * limitations under the License. */ +#include #include #include "gdialservicelogging.h" +#define UUID_FILE_TEMPLATE "/tmp/.dial_%s_uuid.txt" + #define GDIAL_STR_ENDS_WITH(s1, s2) ((s1 != NULL) && (s2 != NULL) && ((strlen(s2) == 0) || (g_str_has_suffix(s1, s2)))) void gdial_app_regstry_dispose (GDialAppRegistry *app_registry) { @@ -92,5 +95,34 @@ GDialAppRegistry* gdial_app_registry_new (const gchar *app_name, const GList *ap app_registry->allowed_origins = g_list_prepend(app_registry->allowed_origins, g_strdup(allowed_origins->data)); allowed_origins = allowed_origins->next; } + + char app_uuid_file_path[64] = {0}; + char uuid_data[APP_MAX_UUID_SIZE] = {0}; + snprintf( app_uuid_file_path, sizeof(app_uuid_file_path), UUID_FILE_TEMPLATE , app_name); + + FILE *fuuid = fopen(app_uuid_file_path, "r"); + if (fuuid == NULL) + { + uuid_t random_uuid; + uuid_generate_random(random_uuid); + uuid_unparse(random_uuid, uuid_data); + GDIAL_LOGINFO("generated uuid:[%s]", uuid_data); + + fuuid = fopen(app_uuid_file_path, "w"); + if (fuuid != NULL) + { + fprintf(fuuid, "%s", uuid_data); + fclose(fuuid); + } + } + else + { + fgets(uuid_data, sizeof(uuid_data), fuuid); + fclose(fuuid); + } + + snprintf( app_registry->app_uri, sizeof(app_registry->app_uri), "/%s" , uuid_data); + GDIAL_LOGINFO("App:[%s] uuid:[%s] per Bootup", app_name, uuid_data); + return app_registry; -} +} \ No newline at end of file From a503f9e66afaaf89a31ab3ba148d130625fa3fd7 Mon Sep 17 00:00:00 2001 From: yuvaramachandran_gurusamy Date: Fri, 21 Mar 2025 15:55:22 +0000 Subject: [PATCH 06/21] RDK-56672: Fix parsing issue while restrict addtionalData url per app Signed-off-by: yuvaramachandran_gurusamy --- server/gdial-rest.c | 56 ++++++++++++++++++++++++++++++--------------- server/gdial-rest.h | 1 + 2 files changed, 39 insertions(+), 18 deletions(-) diff --git a/server/gdial-rest.c b/server/gdial-rest.c index 2312b85a..ea9959a4 100644 --- a/server/gdial-rest.c +++ b/server/gdial-rest.c @@ -159,7 +159,7 @@ static GList *gdial_rest_server_registered_apps_clear(GDialRestServer *self, GLi GDialRestServerPrivate *priv = gdial_rest_server_get_instance_private(self); GDialAppRegistry *app_registry = (GDialAppRegistry *)found->data; registered_apps = g_list_remove_link(registered_apps, found); - GDIAL_LOGINFO("Removing handler for app[%s]", app_registry->name); + GDIAL_LOGINFO("gdial_local_rest_http_server_callback handler Removed for App[%s]instance[%x]", app_registry->name,priv->local_soup_instance); soup_server_remove_handler(priv->local_soup_instance, app_registry->app_uri); gdial_app_regstry_dispose (app_registry); g_list_free(found); @@ -211,6 +211,14 @@ static gint GCompareFunc_match_registry_app_name(gconstpointer a, gconstpointer return is_matched ? 0 : 1; } +static gint GCompareFunc_match_registry_app_uuid(gconstpointer a, gconstpointer b) { + GDialAppRegistry *app_registry = (GDialAppRegistry *)a; + int is_matched = 0; + /* match by exact uuid */ + is_matched = (g_strcmp0(&app_registry->app_uri[1], b) == 0); + return is_matched ? 0 : 1; +} + GDIAL_STATIC GDialAppRegistry *gdial_rest_server_find_app_registry(GDialRestServer *self, const gchar *app_name) { g_return_val_if_fail(self != NULL && app_name != NULL, FALSE); GDialRestServerPrivate *priv = gdial_rest_server_get_instance_private(self); @@ -221,6 +229,16 @@ GDIAL_STATIC GDialAppRegistry *gdial_rest_server_find_app_registry(GDialRestServ return NULL; } +GDIAL_STATIC GDialAppRegistry *gdial_rest_server_find_app_registry_by_uuid(GDialRestServer *self, const gchar *app_uuid) { + g_return_val_if_fail(self != NULL && app_uuid != NULL, FALSE); + GDialRestServerPrivate *priv = gdial_rest_server_get_instance_private(self); + GList *found = g_list_find_custom(priv->registered_apps, app_uuid, GCompareFunc_match_registry_app_uuid); + if (found) { + return (GDialAppRegistry *)found->data; + } + return NULL; + } + GDIAL_STATIC gboolean gdial_rest_server_is_allowed_youtube_origin(GDialRestServer *self, const gchar *header_origin, const gchar *app_name) { if (self == NULL) return FALSE; @@ -676,13 +694,12 @@ static void gdial_local_rest_http_server_callback(SoupServer *server, SoupMessage *msg, const gchar *path, GHashTable *query, SoupClientContext *client, gpointer user_data) { gchar *remote_address_str = g_inet_address_to_string(g_inet_socket_address_get_address(G_INET_SOCKET_ADDRESS(soup_client_context_get_remote_address(client)))); - g_print_with_timestamp("gdial_local_rest_http_server_callback() %s path=%s recv from [%s], in thread %lx", msg->method, path, remote_address_str, pthread_self()); + GDIAL_LOGINFO("method[%s] path[%s] recv from [%s], in thread %lx", msg->method, path, remote_address_str, pthread_self()); g_free(remote_address_str); GDialRestServer *gdial_rest_server = (GDIAL_REST_SERVER(user_data)); - gchar **elements = g_strsplit(&path[1], "/", 4); + gchar **elements = g_strsplit(&path[1], "/", 3); gdial_rest_server_http_return_if_fail(elements != NULL, msg, SOUP_STATUS_NOT_IMPLEMENTED); gchar base[GDIAL_REST_HTTP_PATH_COMPONENT_MAX_LEN] = {0}; - gchar app_name[GDIAL_REST_HTTP_PATH_COMPONENT_MAX_LEN] = {0}; gchar instance[GDIAL_REST_HTTP_PATH_COMPONENT_MAX_LEN] = {0}; gchar last_elem[GDIAL_REST_HTTP_PATH_COMPONENT_MAX_LEN] = {0}; int i = 0; @@ -694,37 +711,35 @@ static void gdial_local_rest_http_server_callback(SoupServer *server, GDIAL_LOGWARNING("Warn: empty elements in URI path"); continue; } - if (j == 0) g_strlcpy(base, elements[i], sizeof(base)); - else if (j == 1) { - ret = g_strlcpy(app_name, elements[i], sizeof(app_name)); - if (ret >= sizeof(app_name)) { - g_printerr("Warn: app_name too long\r\n"); - } + if (j == 0) { + g_strlcpy(base, elements[i], sizeof(base)); } - else if (j == 2) { + else if (j == 1) { ret = g_strlcpy(instance, elements[i], sizeof(instance)); if (ret >= sizeof(instance)) { - g_printerr("Warn: instance too long\r\n"); + GDIAL_LOGERROR("Warn: instance too long"); } } ret = g_strlcpy(last_elem, elements[i], sizeof(last_elem)); if (ret >= sizeof(last_elem)) { - g_printerr("Warn: last_elem too long\r\n"); + GDIAL_LOGERROR("Warn: last_elem too long"); } + GDIAL_LOGINFO("last_elem[%s]", last_elem); j++; } g_strfreev(elements); const int element_num = j; GDIAL_LOGINFO("there are %d non-empty elems", element_num); - if(element_num == 3 && g_strcmp0(instance,"dial_data") == 0) + if(element_num == 2 && g_strcmp0(instance,"dial_data") == 0) { - GDialAppRegistry *app_registry = gdial_rest_server_find_app_registry(gdial_rest_server, app_name); + GDialAppRegistry *app_registry = gdial_rest_server_find_app_registry_by_uuid(gdial_rest_server, base); + gdial_rest_server_http_return_if_fail(app_registry, msg, SOUP_STATUS_NOT_FOUND); if (msg->method == SOUP_METHOD_POST) { - gdial_rest_server_handle_POST_dial_data(gdial_rest_server, msg, query, app_name); + gdial_rest_server_handle_POST_dial_data(gdial_rest_server, msg, query, app_registry->name); } else if (msg->method == SOUP_METHOD_GET) { - gdial_rest_server_handle_GET_app(gdial_rest_server, msg, query, app_name, GDIAL_APP_INSTANCE_NULL); + gdial_rest_server_handle_GET_app(gdial_rest_server, msg, query, app_registry->name, GDIAL_APP_INSTANCE_NULL); } else { gdial_rest_server_http_return_if_fail(msg->method == SOUP_METHOD_POST, msg, SOUP_STATUS_NOT_IMPLEMENTED); @@ -1150,7 +1165,7 @@ gboolean gdial_rest_server_register_app(GDialRestServer *self, const gchar *app_ if( 0 != strcmp(app_name,"system")) { - GDIAL_LOGINFO("gdial_local_rest_http_server_callback add handler for app_name:[%s]",app_name); + GDIAL_LOGINFO("gdial_local_rest_http_server_callback handler added for App[%s]uri[%s]instance[%x]",app_name,app_registry->app_uri,priv->local_soup_instance); soup_server_add_handler(priv->local_soup_instance, app_registry->app_uri, gdial_local_rest_http_server_callback, self, NULL); } GDIAL_LOGTRACE("Exiting ..."); @@ -1180,6 +1195,11 @@ gboolean gdial_rest_server_register_app_registry(GDialRestServer *self, GDialApp g_return_val_if_fail(priv->registered_apps != NULL, FALSE); g_return_val_if_fail(gdial_rest_server_is_app_registered(self, app_registry->name), FALSE); + if( 0 != strcmp(app_registry->name,"system")) + { + GDIAL_LOGINFO("gdial_local_rest_http_server_callback handler added for App[%s]uri[%s]instance[%x]",app_registry->name,app_registry->app_uri,priv->local_soup_instance); + soup_server_add_handler(priv->local_soup_instance, app_registry->app_uri, gdial_local_rest_http_server_callback, self, NULL); + } return TRUE; } diff --git a/server/gdial-rest.h b/server/gdial-rest.h index 770d2463..8aa4f829 100644 --- a/server/gdial-rest.h +++ b/server/gdial-rest.h @@ -52,6 +52,7 @@ typedef struct _GDialAppRegistry GDialAppRegistry; GDIAL_STATIC gboolean gdial_rest_server_is_allowed_origin(GDialRestServer *self, const gchar *header_origin, const gchar *app_name); GDIAL_STATIC gchar *gdial_rest_server_new_additional_data_url(guint listening_port, const gchar *app_name, gboolean encode, const gchar* app_uri); GDIAL_STATIC GDialAppRegistry *gdial_rest_server_find_app_registry(GDialRestServer *self, const gchar *app_name); +GDIAL_STATIC GDialAppRegistry *gdial_rest_server_find_app_registry_by_uuid(GDialRestServer *self, const gchar *app_uuid); G_END_DECLS From 8e05add4289eba25842ebe05f5dadc3191b8347c Mon Sep 17 00:00:00 2001 From: tabbas651 Date: Mon, 14 Apr 2025 15:25:01 +0000 Subject: [PATCH 07/21] RDKEMW-2278: Removal of WPEFrameworkSecurity Agent Utility Reason for change: Added DISABLE_SECURITY_TOKEN Flag to disable the WPEFrameworkSecurity Token generation changes Test Procedure: please referred from the ticket Risks: Medium Signed-off-by: Thamim Razith --- Makefile | 2 +- server/plat/CMakeLists.txt | 12 +++++++++++- server/plat/gdial.cpp | 19 +++++++++++++------ 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index 2ffd032e..32f94877 100644 --- a/Makefile +++ b/Makefile @@ -20,6 +20,6 @@ DIRS:= server all: - for dir in $(DIRS); do (cd $$dir && cmake $(PLATFORM_FLAGS) . && make || exit 1) || exit 1; done + for dir in $(DIRS); do (cd $$dir && cmake $(DISABLE_SECURITY_TOKEN) $(PLATFORM_FLAGS) . && make || exit 1) || exit 1; done clean: for dir in $(DIRS); do (cd $$dir && make clean || exit 1) || exit 1; done diff --git a/server/plat/CMakeLists.txt b/server/plat/CMakeLists.txt index c6fa074d..dcb8215c 100644 --- a/server/plat/CMakeLists.txt +++ b/server/plat/CMakeLists.txt @@ -27,6 +27,12 @@ set (GDIAL_PLAT_DEPEND_LIBRARIES "${GLIB_LIBRARIES} ${GOBJECT_LIBRARIES}") set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} ${CMAKE_CXX_FLAGS} -std=c++11 -Wno-nonnull -DRT_PLATFORM_LINUX") +option(DISABLE_SECURITY_TOKEN "Disable security token" OFF) + +if(DISABLE_SECURITY_TOKEN) + add_definitions(-DDISABLE_SECURITY_TOKEN) +endif() + # # Template that generates .pc file # @@ -69,4 +75,8 @@ if(PLATFORM) endif() add_library(gdial-plat SHARED ${GDIAL_PLAT_LIB_SOURCE_FILES}) -target_link_Libraries(gdial-plat PRIVATE ${GLIB_LIBRARIES} ${GOBJECT_LIBRARIES} -lpthread -lWPEFrameworkCore -lWPEFrameworkDefinitions -lWPEFrameworkCOM -lWPEFrameworkPlugins -lWPEFrameworkSecurityUtil -lIARMBus -luuid) +if(DISABLE_SECURITY_TOKEN) + target_link_Libraries(gdial-plat PRIVATE ${GLIB_LIBRARIES} ${GOBJECT_LIBRARIES} -lpthread -lWPEFrameworkCore -lWPEFrameworkDefinitions -lWPEFrameworkCOM -lWPEFrameworkPlugins -lIARMBus -luuid) +else() + target_link_libraries(gdial-plat PRIVATE ${GLIB_LIBRARIES} ${GOBJECT_LIBRARIES} -lpthread -lWPEFrameworkCore -lWPEFrameworkDefinitions -lWPEFrameworkCOM -lWPEFrameworkPlugins -lWPEFrameworkSecurityUtil -lIARMBus -luuid) +endif() diff --git a/server/plat/gdial.cpp b/server/plat/gdial.cpp index d9a2f0d3..1415a2e5 100644 --- a/server/plat/gdial.cpp +++ b/server/plat/gdial.cpp @@ -32,7 +32,9 @@ #include #include #include +#ifndef DISABLE_SECURITY_TOKEN #include +#endif #include "gdial-app.h" #include "gdial-plat-dev.h" #include "gdial-os-app.h" @@ -572,21 +574,26 @@ std::string GetCurrentState() { std::string netflixState = ""; Core::JSON::ArrayType pluginResponse; Core::SystemInfo::SetEnvironment(_T("THUNDER_ACCESS"), (_T("127.0.0.1:9998"))); + string sToken = ""; + string query = ""; +#ifdef DISABLE_SECURITY_TOKEN + query = "token=" + sToken; +#else unsigned char buffer[MAX_LENGTH] = {0}; //Obtaining controller object if (NULL == controllerRemoteObject) { int ret = GetSecurityToken(MAX_LENGTH,buffer); - if(ret<0) + if(ret>0) { - controllerRemoteObject = new JSONRPC::LinkType(std::string()); - } else { - string sToken = (char*)buffer; - string query = "token=" + sToken; + sToken = (char*)buffer; + query = "token=" + sToken; GDIAL_LOGINFO("Security token[%s] ",query.c_str()); - controllerRemoteObject = new JSONRPC::LinkType(std::string(), false, query); } } +#endif + controllerRemoteObject = new JSONRPC::LinkType(std::string(), false, query); + std::string nfxstatus = "status@" + nfx_callsign; if(controllerRemoteObject->Get(1000, _T(nfxstatus), pluginResponse) == Core::ERROR_NONE) { From 81f4b8948f10b2a696c78cb16c0528a8d66f731b Mon Sep 17 00:00:00 2001 From: yuvaramachandran_gurusamy Date: Thu, 5 Jun 2025 15:31:43 +0000 Subject: [PATCH 08/21] RDKEMW-4129: Prepare native build environment Signed-off-by: yuvaramachandran_gurusamy --- .github/workflows/native_full_build.yml | 25 +++++++ build_dependencies.sh | 95 +++++++++++++++++++++++++ cov_build.sh | 23 ++++++ 3 files changed, 143 insertions(+) create mode 100755 .github/workflows/native_full_build.yml create mode 100755 build_dependencies.sh create mode 100755 cov_build.sh diff --git a/.github/workflows/native_full_build.yml b/.github/workflows/native_full_build.yml new file mode 100755 index 00000000..757298bf --- /dev/null +++ b/.github/workflows/native_full_build.yml @@ -0,0 +1,25 @@ +name: Build Component in Native Environment + +on: + push: + branches: [ main, 'sprint/**', 'release/**', topic/*, develop, topic/RDKEMW-4129 ] + pull_request: + branches: [ main, 'sprint/**', 'release/**', topic/*, develop, topic/RDKEMW-4129 ] + +jobs: + build-entservices-on-pr: + name: Build xdialserver component in github rdkcentral + runs-on: ubuntu-latest + container: + image: ghcr.io/rdkcentral/docker-rdk-ci:latest + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: native build + run: | + sh -x build_dependencies.sh + sh -x cov_build.sh + env: + GITHUB_TOKEN: ${{ secrets.RDKCM_RDKE }} diff --git a/build_dependencies.sh b/build_dependencies.sh new file mode 100755 index 00000000..c755d997 --- /dev/null +++ b/build_dependencies.sh @@ -0,0 +1,95 @@ +#!/bin/bash +set -x +set -e +############################## +GITHUB_WORKSPACE="${PWD}" +ls -la ${GITHUB_WORKSPACE} +cd ${GITHUB_WORKSPACE} + +# # ############################# +#1. Install Dependencies and packages + +apt update +#apt install -y libsqlite3-dev libcurl4-openssl-dev valgrind lcov clang libsystemd-dev libboost-all-dev libwebsocketpp-dev meson libcunit1 libcunit1-dev curl protobuf-compiler-grpc libgrpc-dev libgrpc++-dev libunwind-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev +pip install jsonref + +############################ +# Build trevor-base64 +if [ ! -d "trower-base64" ]; then +git clone https://github.com/xmidt-org/trower-base64.git +fi +cd trower-base64 +meson setup --warnlevel 3 --werror build +ninja -C build +ninja -C build install +cd .. +########################################### +# Clone the required repositories + + +git clone --branch R4.4.3 https://github.com/rdkcentral/ThunderTools.git + +git clone --branch R4.4.1 https://github.com/rdkcentral/Thunder.git + +git clone --branch main https://github.com/rdkcentral/entservices-apis.git + +git clone https://$GITHUB_TOKEN@github.com/rdkcentral/entservices-testframework.git + +############################ +# Build Thunder-Tools +echo "======================================================================================" +echo "buliding thunderTools" +cd ThunderTools +patch -p1 < $GITHUB_WORKSPACE/entservices-testframework/patches/00010-R4.4-Add-support-for-project-dir.patch +cd - + + +cmake -G Ninja -S ThunderTools -B build/ThunderTools \ + -DEXCEPTIONS_ENABLE=ON \ + -DCMAKE_INSTALL_PREFIX="/usr" \ + -DCMAKE_MODULE_PATH="$GITHUB_WORKSPACE/install/tools/cmake" \ + -DGENERIC_CMAKE_MODULE_PATH="$GITHUB_WORKSPACE/install/tools/cmake" \ + +cmake --build build/ThunderTools --target install + + +############################ +# Build Thunder +echo "======================================================================================" +echo "buliding thunder" + +cd Thunder +patch -p1 < $GITHUB_WORKSPACE/entservices-testframework/patches/Use_Legact_Alt_Based_On_ThunderTools_R4.4.3.patch +patch -p1 < $GITHUB_WORKSPACE/entservices-testframework/patches/error_code_R4_4.patch +patch -p1 < $GITHUB_WORKSPACE/entservices-testframework/patches/1004-Add-support-for-project-dir.patch +patch -p1 < $GITHUB_WORKSPACE/entservices-testframework/patches/RDKEMW-733-Add-ENTOS-IDS.patch +cd - + +cmake -G Ninja -S Thunder -B build/Thunder \ + -DMESSAGING=ON \ + -DCMAKE_INSTALL_PREFIX="/usr" \ + -DCMAKE_MODULE_PATH="$GITHUB_WORKSPACE/install/tools/cmake" \ + -DGENERIC_CMAKE_MODULE_PATH="$GITHUB_WORKSPACE/install/tools/cmake" \ + -DBUILD_TYPE=Debug \ + -DBINDING=127.0.0.1 \ + -DPORT=55555 \ + -DEXCEPTIONS_ENABLE=ON \ + +cmake --build build/Thunder --target install + + +############################ +# Build entservices-apis +echo "======================================================================================" +echo "buliding entservices-apis" +cd entservices-apis +rm -rf jsonrpc/DTV.json +cd .. + +cmake -G Ninja -S entservices-apis -B build/entservices-apis \ + -DEXCEPTIONS_ENABLE=ON \ + -DCMAKE_INSTALL_PREFIX="/usr" \ + -DCMAKE_MODULE_PATH="$GITHUB_WORKSPACE/install/tools/cmake" \ + +cmake --build build/entservices-apis --target install + diff --git a/cov_build.sh b/cov_build.sh new file mode 100755 index 00000000..54f3fb84 --- /dev/null +++ b/cov_build.sh @@ -0,0 +1,23 @@ +#!/bin/bash +set -x +set -e +############################## +GITHUB_WORKSPACE="${PWD}" +ls -la ${GITHUB_WORKSPACE} +############################ +# Build xdialserver +echo "buliding xdialserver" + +cd ${GITHUB_WORKSPACE} +cmake -G Ninja -S "$GITHUB_WORKSPACE" -B build/xdialserver \ +-DUSE_THUNDER_R4=ON \ +-DCMAKE_INSTALL_PREFIX="$GITHUB_WORKSPACE/install/usr" \ +-DCMAKE_MODULE_PATH="$GITHUB_WORKSPACE/install/tools/cmake" \ +-DCMAKE_VERBOSE_MAKEFILE=ON \ +-DCMAKE_CXX_FLAGS="-DEXCEPTIONS_ENABLE=ON \ +-I ${GITHUB_WORKSPACE}/Thunder/Source \ +-I ${GITHUB_WORKSPACE}/Thunder/Source/core" \ + +cmake --build build/xdialserver --target install +echo "======================================================================================" +exit 0 From 6750ebaf408e15a46cad36f53fae7966e8f869aa Mon Sep 17 00:00:00 2001 From: yuvaramachandran_gurusamy Date: Thu, 5 Jun 2025 19:20:11 +0000 Subject: [PATCH 09/21] RDKEMW-4129: Test Signed-off-by: yuvaramachandran_gurusamy --- build_dependencies.sh | 45 ++++++- cov_build.sh | 11 +- ...010-R4.4-Add-support-for-project-dir.patch | 116 ++++++++++++++++++ .../1004-Add-support-for-project-dir.patch | 42 +++++++ .../patches/RDKEMW-733-Add-ENTOS-IDS.patch | 21 ++++ ...act_Alt_Based_On_ThunderTools_R4.4.3.patch | 20 +++ .../patches/error_code_R4_4.patch | 64 ++++++++++ server/include/gdial-config.h | 10 +- stubs/iarm_stubs.cpp | 68 ++++++++++ stubs/securityagent/SecurityTokenUtil.cpp | 56 +++++++++ stubs/securityagent/SecurityTokenUtil.h | 50 ++++++++ 11 files changed, 482 insertions(+), 21 deletions(-) create mode 100755 entservices-testframework/patches/00010-R4.4-Add-support-for-project-dir.patch create mode 100755 entservices-testframework/patches/1004-Add-support-for-project-dir.patch create mode 100755 entservices-testframework/patches/RDKEMW-733-Add-ENTOS-IDS.patch create mode 100755 entservices-testframework/patches/Use_Legact_Alt_Based_On_ThunderTools_R4.4.3.patch create mode 100644 entservices-testframework/patches/error_code_R4_4.patch create mode 100644 stubs/iarm_stubs.cpp create mode 100644 stubs/securityagent/SecurityTokenUtil.cpp create mode 100644 stubs/securityagent/SecurityTokenUtil.h diff --git a/build_dependencies.sh b/build_dependencies.sh index c755d997..041b10da 100755 --- a/build_dependencies.sh +++ b/build_dependencies.sh @@ -3,14 +3,14 @@ set -x set -e ############################## GITHUB_WORKSPACE="${PWD}" -ls -la ${GITHUB_WORKSPACE} + cd ${GITHUB_WORKSPACE} # # ############################# #1. Install Dependencies and packages apt update -#apt install -y libsqlite3-dev libcurl4-openssl-dev valgrind lcov clang libsystemd-dev libboost-all-dev libwebsocketpp-dev meson libcunit1 libcunit1-dev curl protobuf-compiler-grpc libgrpc-dev libgrpc++-dev libunwind-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev +apt install -y ninja-build meson curl libsoup2.4-dev libxml2-dev libglib2.0-dev gobject-introspection libgirepository1.0-dev libgtk-3-dev valac pandoc pip install jsonref ############################ @@ -26,14 +26,36 @@ cd .. ########################################### # Clone the required repositories +rm -rf iarmbus +git clone https://github.com/rdkcentral/iarmbus.git +export IARMBUS_PATH=$GITHUB_WORKSPACE/iarmbus +rm -rf ThunderTools git clone --branch R4.4.3 https://github.com/rdkcentral/ThunderTools.git +rm -rf Thunder git clone --branch R4.4.1 https://github.com/rdkcentral/Thunder.git +rm -rf entservices-apis git clone --branch main https://github.com/rdkcentral/entservices-apis.git -git clone https://$GITHUB_TOKEN@github.com/rdkcentral/entservices-testframework.git +#git clone https://$GITHUB_TOKEN@github.com/rdkcentral/entservices-testframework.git + +rm -rf gssdp +git clone --branch gssdp-1.2.3 https://gitlab.gnome.org/GNOME/gssdp.git + +############################ +# Build gssdp-1.2 +echo "======================================================================================" +echo "buliding gssdp-1.2" +cd gssdp + +rm -rf build +meson setup build + +ninja -C build +ninja -C build install +cd - ############################ # Build Thunder-Tools @@ -77,7 +99,6 @@ cmake -G Ninja -S Thunder -B build/Thunder \ cmake --build build/Thunder --target install - ############################ # Build entservices-apis echo "======================================================================================" @@ -93,3 +114,19 @@ cmake -G Ninja -S entservices-apis -B build/entservices-apis \ cmake --build build/entservices-apis --target install +############################ + +############################ +# Build and deploy stubs for IARMBus and WPEFramework securityagent +mkdir -p /usr/include/WPEFramework/securityagent +cp $GITHUB_WORKSPACE/stubs/securityagent/* /usr/include/WPEFramework/securityagent/ -v + +echo "======================================================================================" +echo "Building IARMBus and WPEFramework securityagent stubs" +cd $GITHUB_WORKSPACE +cd ./stubs +g++ -fPIC -shared -o libIARMBus.so iarm_stubs.cpp -I$GITHUB_WORKSPACE/stubs -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -I$IARMBUS_PATH/core -I$IARMBUS_PATH/core/include -fpermissive +g++ -fPIC -shared -o libWPEFrameworkSecurityUtil.so securityagent/SecurityTokenUtil.cpp -I$GITHUB_WORKSPACE/stubs -fpermissive + +cp libIARMBus.so /usr/local/lib/ +cp libWPEFrameworkSecurityUtil.so /usr/local/lib/ diff --git a/cov_build.sh b/cov_build.sh index 54f3fb84..da227aba 100755 --- a/cov_build.sh +++ b/cov_build.sh @@ -9,15 +9,8 @@ ls -la ${GITHUB_WORKSPACE} echo "buliding xdialserver" cd ${GITHUB_WORKSPACE} -cmake -G Ninja -S "$GITHUB_WORKSPACE" -B build/xdialserver \ --DUSE_THUNDER_R4=ON \ --DCMAKE_INSTALL_PREFIX="$GITHUB_WORKSPACE/install/usr" \ --DCMAKE_MODULE_PATH="$GITHUB_WORKSPACE/install/tools/cmake" \ --DCMAKE_VERBOSE_MAKEFILE=ON \ --DCMAKE_CXX_FLAGS="-DEXCEPTIONS_ENABLE=ON \ --I ${GITHUB_WORKSPACE}/Thunder/Source \ --I ${GITHUB_WORKSPACE}/Thunder/Source/core" \ -cmake --build build/xdialserver --target install +make + echo "======================================================================================" exit 0 diff --git a/entservices-testframework/patches/00010-R4.4-Add-support-for-project-dir.patch b/entservices-testframework/patches/00010-R4.4-Add-support-for-project-dir.patch new file mode 100755 index 00000000..dbb94881 --- /dev/null +++ b/entservices-testframework/patches/00010-R4.4-Add-support-for-project-dir.patch @@ -0,0 +1,116 @@ +diff --git a/JsonGenerator/source/class_emitter.py b/JsonGenerator/source/class_emitter.py +index 7f2fb01..67a6469 100644 +--- a/JsonGenerator/source/class_emitter.py ++++ b/JsonGenerator/source/class_emitter.py +@@ -113,7 +113,6 @@ + emit.Line("#include ") + emit.Line() + +- emit.Line("#include \"definitions.h\"") + + if not config.NO_INCLUDES: + if if_file.endswith(".h"): +diff --git a/ProxyStubGenerator/StubGenerator.py b/ProxyStubGenerator/StubGenerator.py +index 6e079b6..d2de681 100755 +--- a/ProxyStubGenerator/StubGenerator.py ++++ b/ProxyStubGenerator/StubGenerator.py +@@ -193,7 +193,7 @@ + + + # Generate interface information in lua +-def GenerateLuaData(emit, interfaces_list, enums_list, source_file=None, tree=None, ns=None): ++def GenerateLuaData(emit, interfaces_list, enums_list, project_dir, source_file=None, tree=None, ns=None): + + if not source_file: + assert(tree==None) +@@ -535,7 +535,7 @@ + + return tree + +-def GenerateStubs2(output_file, source_file, tree, ns, scan_only=False): ++def GenerateStubs2(output_file, source_file, project_dir, tree, ns, scan_only=False): + log.Info("Scanning '%s' (in %s)..." % (source_file, ns)) + + if not FORCE and (os.path.exists(output_file) and (os.path.getmtime(source_file) < os.path.getmtime(output_file))): +@@ -579,7 +579,7 @@ + emit.Line + emit.Line() + +- if os.path.isfile(os.path.join(os.path.dirname(source_file), "Module.h")): ++ if os.path.isfile(os.path.join(project_dir, "Module.h")): + emit.Line('#include "Module.h"') + + if os.path.isfile(os.path.join(os.path.dirname(source_file), interface_header_name)): +@@ -2085,6 +2085,8 @@ + help="include an additional C++ header file, may be used multiple times (default: include 'Ids.h')") + argparser.add_argument('-I', dest="includePaths", metavar="INCLUDE_DIR", action='append', default=[], type=str, + help='add an include search path, can be used multiple times') ++ argparser.add_argument("--projectdir",dest="project_dir",metavar="DIR",type=str,default="", ++ help="specify the project directory") + + args = argparser.parse_args(sys.argv[1:]) + SHOW_WARNINGS = not args.no_warnings +@@ -2221,7 +2223,10 @@ + + for source_file in interface_files: + try: +- _extra_includes = [ os.path.join("@" + os.path.dirname(source_file), IDS_DEFINITIONS_FILE) ] ++ if args.project_dir is not None: ++ _extra_includes = [ os.path.join("@" + args.project_dir, IDS_DEFINITIONS_FILE) ] ++ else: ++ _extra_includes = [ os.path.join("@" + os.path.dirname(source_file), IDS_DEFINITIONS_FILE) ] + _extra_includes.extend(args.extra_includes) + + tree = Parse(source_file, args.includePaths, +@@ -2242,7 +2247,7 @@ + some_omitted = False + + for ns in INTERFACE_NAMESPACES: +- output, some_omitted = GenerateStubs2(output_file, source_file, tree, ns, scan_only) ++ output, some_omitted = GenerateStubs2(output_file, source_file, args.project_dir, tree, ns, scan_only) + + new_faces += output + +@@ -2265,7 +2270,7 @@ + log.Print("(lua generator) Scanning %s..." % os.path.basename(source_file)) + + for ns in INTERFACE_NAMESPACES: +- GenerateLuaData(Emitter(lua_file, INDENT_SIZE), lua_interfaces, lua_enums, source_file, tree, ns) ++ GenerateLuaData(Emitter(lua_file, INDENT_SIZE), lua_interfaces, lua_enums, args.project_dir, source_file, tree, ns) + + except NotModifiedException as err: + log.Print("skipped file %s, up-to-date" % os.path.basename(output_file)) +@@ -2316,7 +2321,7 @@ + if args.lua_code: + # Epilogue + for ns in INTERFACE_NAMESPACES: +- GenerateLuaData(Emitter(lua_file, INDENT_SIZE), lua_interfaces, lua_enums) ++ GenerateLuaData(Emitter(lua_file, INDENT_SIZE), lua_interfaces, lua_enums,args.project_dir) + log.Print("Created %s (%s interfaces, %s enums)" % (lua_file.name, len(lua_interfaces), len(lua_enums))) + + else: + +diff --git a/cmake/FindProxyStubGenerator.cmake.in b/cmake/FindProxyStubGenerator.cmake.in +index 1344452..a8dbcea 100644 +--- a/cmake/FindProxyStubGenerator.cmake.in ++++ b/cmake/FindProxyStubGenerator.cmake.in. +@@ -31,7 +31,7 @@ function(ProxyStubGenerator) + endif() + + set(optionsArgs SECURE COHERENT TRACES VERBOSE NO_WARNINGS KEEP FORCE_GENERATE) +- set(oneValueArgs OUTDIR) ++ set(oneValueArgs OUTDIR PROJECT_DIR) + set(multiValueArgs INPUT INCLUDE INCLUDE_PATH NAMESPACE) + + cmake_parse_arguments(Argument "${optionsArgs}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} ) +@@ -81,6 +81,10 @@ function(ProxyStubGenerator) + list(APPEND _execute_command "--outdir" "${Argument_OUTDIR}") + endif() + ++ if(Argument_PROJECT_DIR) ++ list(APPEND _execute_command "--projectdir" "${Argument_PROJECT_DIR}") ++ endif() ++ + foreach(_include ${Argument_INCLUDE}) + list(APPEND _execute_command "-I" "${_include}") + endforeach(_include) diff --git a/entservices-testframework/patches/1004-Add-support-for-project-dir.patch b/entservices-testframework/patches/1004-Add-support-for-project-dir.patch new file mode 100755 index 00000000..26fdee12 --- /dev/null +++ b/entservices-testframework/patches/1004-Add-support-for-project-dir.patch @@ -0,0 +1,42 @@ +diff --git a/Source/plugins/CMakeLists.txt b/Source/plugins/CMakeLists.txt +index 4d362d545..9357c647c 100644 +--- a/Source/plugins/CMakeLists.txt ++++ b/Source/plugins/CMakeLists.txt +@@ -20,14 +20,14 @@ + string(TOLOWER ${NAMESPACE} NAMESPACE_LIB) + + option(VIRTUALINPUT_TOOLS "Build VirtualInput tools" OFF) +- +-ProxyStubGenerator(NAMESPACE "WPEFramework::PluginHost" INPUT "${CMAKE_CURRENT_SOURCE_DIR}/IPlugin.h" OUTDIR "${CMAKE_CURRENT_BINARY_DIR}/generated") +-ProxyStubGenerator(NAMESPACE "WPEFramework::PluginHost" INPUT "${CMAKE_CURRENT_SOURCE_DIR}/IShell.h" OUTDIR "${CMAKE_CURRENT_BINARY_DIR}/generated") +-ProxyStubGenerator(NAMESPACE "WPEFramework::Exchange" INPUT "${CMAKE_CURRENT_SOURCE_DIR}/IController.h" OUTDIR "${CMAKE_CURRENT_BINARY_DIR}/generated" INCLUDE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/..") +-ProxyStubGenerator(NAMESPACE "WPEFramework::PluginHost" INPUT "${CMAKE_CURRENT_SOURCE_DIR}/IControllerDeprecated.h" OUTDIR "${CMAKE_CURRENT_BINARY_DIR}/generated" INCLUDE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/..") +-ProxyStubGenerator(NAMESPACE "WPEFramework::PluginHost" INPUT "${CMAKE_CURRENT_SOURCE_DIR}/IStateControl.h" OUTDIR "${CMAKE_CURRENT_BINARY_DIR}/generated") +-ProxyStubGenerator(NAMESPACE "WPEFramework::PluginHost" INPUT "${CMAKE_CURRENT_SOURCE_DIR}/ISubSystem.h" OUTDIR "${CMAKE_CURRENT_BINARY_DIR}/generated") +-ProxyStubGenerator(NAMESPACE "WPEFramework::PluginHost" INPUT "${CMAKE_CURRENT_SOURCE_DIR}/IDispatcher.h" OUTDIR "${CMAKE_CURRENT_BINARY_DIR}/generated") ++set(PROJECT_DIR "${CMAKE_CURRENT_SOURCE_DIR}") ++ProxyStubGenerator(NAMESPACE "WPEFramework::PluginHost" INPUT "${CMAKE_CURRENT_SOURCE_DIR}/IPlugin.h" OUTDIR "${CMAKE_CURRENT_BINARY_DIR}/generated" PROJECT_DIR ${PROJECT_DIR}) ++ProxyStubGenerator(NAMESPACE "WPEFramework::PluginHost" INPUT "${CMAKE_CURRENT_SOURCE_DIR}/IShell.h" OUTDIR "${CMAKE_CURRENT_BINARY_DIR}/generated" PROJECT_DIR ${PROJECT_DIR}) ++ProxyStubGenerator(NAMESPACE "WPEFramework::Exchange" INPUT "${CMAKE_CURRENT_SOURCE_DIR}/IController.h" OUTDIR "${CMAKE_CURRENT_BINARY_DIR}/generated" INCLUDE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/.." PROJECT_DIR ${PROJECT_DIR}) ++ProxyStubGenerator(NAMESPACE "WPEFramework::PluginHost" INPUT "${CMAKE_CURRENT_SOURCE_DIR}/IControllerDeprecated.h" OUTDIR "${CMAKE_CURRENT_BINARY_DIR}/generated" INCLUDE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/.." PROJECT_DIR ${PROJECT_DIR}) ++ProxyStubGenerator(NAMESPACE "WPEFramework::PluginHost" INPUT "${CMAKE_CURRENT_SOURCE_DIR}/IStateControl.h" OUTDIR "${CMAKE_CURRENT_BINARY_DIR}/generated" PROJECT_DIR ${PROJECT_DIR}) ++ProxyStubGenerator(NAMESPACE "WPEFramework::PluginHost" INPUT "${CMAKE_CURRENT_SOURCE_DIR}/ISubSystem.h" OUTDIR "${CMAKE_CURRENT_BINARY_DIR}/generated" PROJECT_DIR ${PROJECT_DIR}) ++ProxyStubGenerator(NAMESPACE "WPEFramework::PluginHost" INPUT "${CMAKE_CURRENT_SOURCE_DIR}/IDispatcher.h" OUTDIR "${CMAKE_CURRENT_BINARY_DIR}/generated" PROJECT_DIR ${PROJECT_DIR}) + + JsonGenerator(CODE NAMESPACE WPEFramework::Exchange::Controller INPUT ${CMAKE_CURRENT_SOURCE_DIR}/IController.h OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/generated/jsonrpc" INCLUDE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/.." NO_INCLUDES LEGACY_ALT) + +diff --git a/Source/com/CMakeLists.txt b/Source/com/CMakeLists.txt +index 1de9ac176..bcf9489d1 100644 +--- a/Source/com/CMakeLists.txt ++++ b/Source/com/CMakeLists.txt +@@ -16,8 +16,8 @@ + # limitations under the License. + + set(TARGET ${NAMESPACE}COM) +- +-ProxyStubGenerator(NAMESPACE "WPEFramework::RPC" INPUT "${CMAKE_CURRENT_SOURCE_DIR}/ICOM.h" OUTDIR "${CMAKE_CURRENT_BINARY_DIR}/generated") ++set(PROJECT_DIR "${CMAKE_CURRENT_SOURCE_DIR}") ++ProxyStubGenerator(NAMESPACE "WPEFramework::RPC" INPUT "${CMAKE_CURRENT_SOURCE_DIR}/ICOM.h" OUTDIR "${CMAKE_CURRENT_BINARY_DIR}/generated" PROJECT_DIR ${PROJECT_DIR}) + + add_library(${TARGET} SHARED + Administrator.cpp diff --git a/entservices-testframework/patches/RDKEMW-733-Add-ENTOS-IDS.patch b/entservices-testframework/patches/RDKEMW-733-Add-ENTOS-IDS.patch new file mode 100755 index 00000000..c8fd3213 --- /dev/null +++ b/entservices-testframework/patches/RDKEMW-733-Add-ENTOS-IDS.patch @@ -0,0 +1,21 @@ +From: Sitaram, Soundaryaa +Date: 05 Feb 2025 17:24:43 +0000 +Subject: [PATCH] ENTOS IDs offset to be adjusted in rdkservices-apis + +Upstream-Status: None +Signed-off-by: ssitar583 +--- +diff --git a/Source/com/Ids.h b/Source/com/Ids.h +index cf2060333..75d235bbd 100644 +--- a/Source/com/Ids.h ++++ b/Source/com/Ids.h +@@ -74,7 +74,8 @@ namespace RPC { + ID_COMREQUEST_NOTIFICATION = (ID_OFFSET_INTERNAL + 0x003D), + ID_SYSTEM_METADATA = (ID_OFFSET_INTERNAL + 0x003E), + +- ID_EXTERNAL_INTERFACE_OFFSET = (ID_OFFSET_INTERNAL + 0x0040) ++ ID_EXTERNAL_INTERFACE_OFFSET = (ID_OFFSET_INTERNAL + 0x0040), ++ ID_EXTERNAL_CC_INTERFACE_OFFSET = (0xCC00) // ends on 0xDFFF + }; + } + } diff --git a/entservices-testframework/patches/Use_Legact_Alt_Based_On_ThunderTools_R4.4.3.patch b/entservices-testframework/patches/Use_Legact_Alt_Based_On_ThunderTools_R4.4.3.patch new file mode 100755 index 00000000..c5b4a36f --- /dev/null +++ b/entservices-testframework/patches/Use_Legact_Alt_Based_On_ThunderTools_R4.4.3.patch @@ -0,0 +1,20 @@ +commit 810aae64cb31c907698e468b615797750094b847 +Author: Pesala Lakshmi Jwala Priya +Date: Thu Aug 29 12:31:30 2024 +0530 + + Alt change + +diff --git a/Source/plugins/CMakeLists.txt b/Source/plugins/CMakeLists.txt +index 4d362d54..cdb53cdc 100644 +--- a/Source/plugins/CMakeLists.txt ++++ b/Source/plugins/CMakeLists.txt +@@ -29,7 +29,7 @@ ProxyStubGenerator(NAMESPACE "WPEFramework::PluginHost" INPUT "${CMAKE_CURRENT_S + ProxyStubGenerator(NAMESPACE "WPEFramework::PluginHost" INPUT "${CMAKE_CURRENT_SOURCE_DIR}/ISubSystem.h" OUTDIR "${CMAKE_CURRENT_BINARY_DIR}/generated") + ProxyStubGenerator(NAMESPACE "WPEFramework::PluginHost" INPUT "${CMAKE_CURRENT_SOURCE_DIR}/IDispatcher.h" OUTDIR "${CMAKE_CURRENT_BINARY_DIR}/generated") + +-JsonGenerator(CODE NAMESPACE WPEFramework::Exchange::Controller INPUT ${CMAKE_CURRENT_SOURCE_DIR}/IController.h OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/generated/jsonrpc" INCLUDE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/.." NO_INCLUDES) ++JsonGenerator(CODE NAMESPACE WPEFramework::Exchange::Controller INPUT ${CMAKE_CURRENT_SOURCE_DIR}/IController.h OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/generated/jsonrpc" INCLUDE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/.." NO_INCLUDES LEGACY_ALT) + + add_library(${TARGET} SHARED + Channel.cpp + diff --git a/entservices-testframework/patches/error_code_R4_4.patch b/entservices-testframework/patches/error_code_R4_4.patch new file mode 100644 index 00000000..954ca083 --- /dev/null +++ b/entservices-testframework/patches/error_code_R4_4.patch @@ -0,0 +1,64 @@ +diff --git a/Source/WPEFramework/PluginServer.h b/Source/WPEFramework/PluginServer.h +index e87933d9a..591385222 100644 +--- a/Source/WPEFramework/PluginServer.h ++++ b/Source/WPEFramework/PluginServer.h +@@ -1535,6 +1535,22 @@ namespace PluginHost { + response->Error.SetError(Core::ERROR_ILLEGAL_STATE); + response->Error.Text = _T("The service is in an illegal state!!!."); + break; ++ case Core::ERROR_INVALID_PARAMETER: ++ response->Error.SetError(Core::ERROR_INVALID_SIGNATURE); ++ response->Error.Text = _T("Invalid Parameter"); ++ break; ++ case Core::ERROR_INVALID_DEVICENAME: ++ response->Error.SetError(Core::ERROR_INVALID_DEVICENAME); ++ response->Error.Text = _T("Invalid device name"); ++ break; ++ case Core::ERROR_INVALID_MOUNTPOINT: ++ response->Error.SetError(Core::ERROR_INVALID_MOUNTPOINT); ++ response->Error.Text = _T("Invalid mount path"); ++ break; ++ case Core::ERROR_FIRMWAREUPDATE_INPROGRESS: ++ response->Error.SetError(Core::ERROR_FIRMWAREUPDATE_INPROGRESS); ++ response->Error.Text = _T("Firmware update already in progress"); ++ break; + case static_cast(~0): + response.Release(); + break; +diff --git a/Source/core/JSONRPC.h b/Source/core/JSONRPC.h +index a9c4360fb..5fcef161d 100644 +--- a/Source/core/JSONRPC.h ++++ b/Source/core/JSONRPC.h +@@ -98,6 +98,15 @@ namespace Core { + case Core::ERROR_TIMEDOUT: + Code = -32000; // Server defined, now mapped to Timed out + break; ++ case Core::ERROR_INVALID_DEVICENAME: ++ Code = -32001; // Invalid device name ++ break; ++ case Core::ERROR_INVALID_MOUNTPOINT: ++ Code = -32002; // Invalid mount path ++ break; ++ case Core::ERROR_FIRMWAREUPDATE_INPROGRESS: ++ Code = -32003; // Firmware update inprogress ++ break; + default: + Code = static_cast(frameworkError); + break; +diff --git a/Source/core/Portability.h b/Source/core/Portability.h +index 01e34d398..a5728eebf 100644 +--- a/Source/core/Portability.h ++++ b/Source/core/Portability.h +@@ -874,7 +874,11 @@ namespace Core { + ERROR_CODE(ERROR_HIBERNATED, 46) \ + ERROR_CODE(ERROR_INPROC, 47) \ + ERROR_CODE(ERROR_FAILED_REGISTERED, 48) \ +- ERROR_CODE(ERROR_FAILED_UNREGISTERED, 49) ++ ERROR_CODE(ERROR_FAILED_UNREGISTERED, 49) \ ++ ERROR_CODE(ERROR_INVALID_PARAMETER, 54) \ ++ ERROR_CODE(ERROR_INVALID_DEVICENAME, 59) \ ++ ERROR_CODE(ERROR_INVALID_MOUNTPOINT, 60) \ ++ ERROR_CODE(ERROR_FIRMWAREUPDATE_INPROGRESS,61) + + #define ERROR_CODE(CODE, VALUE) CODE = VALUE, + diff --git a/server/include/gdial-config.h b/server/include/gdial-config.h index c6b9a9bc..13dd4172 100644 --- a/server/include/gdial-config.h +++ b/server/include/gdial-config.h @@ -17,8 +17,8 @@ * limitations under the License. */ -#ifndef GDIAL_H_ -#define GDIAL_H_ +#ifndef _GDIAL_CONFIG_H_ +#define _GDIAL_CONFIG_H_ #ifdef __cplusplus extern "C" { @@ -81,12 +81,6 @@ extern "C" { #define GDIAL_THROTTLE_DELAY_US 100000 #define GDIAL_DEBUG g_print -enum { - GDIAL_ERROR_NONE = 0, - GDIAL_ERROR_NOT_REGISTERED, - GDIAL_ERROR_FAIL_TO_START, -} GDialError; - #define GDIAL_GERROR_CHECK_AND_FREE(err, msg) \ {\ if (err) {\ diff --git a/stubs/iarm_stubs.cpp b/stubs/iarm_stubs.cpp new file mode 100644 index 00000000..ef10fc2d --- /dev/null +++ b/stubs/iarm_stubs.cpp @@ -0,0 +1,68 @@ +#include "libIBus.h" +#include "libIARMCore.h" +using namespace std; + +IARM_Result_t IARM_Malloc(IARM_MemType_t type, size_t size, void **ptr) +{ + return IARM_RESULT_SUCCESS; +} + +IARM_Result_t IARM_Free(IARM_MemType_t type, void *alloc) +{ + return IARM_RESULT_SUCCESS; +} + +IARM_Result_t IARM_Bus_BroadcastEvent(const char *ownerName, IARM_EventId_t eventId, void *arg, size_t argLen) +{ + return IARM_RESULT_SUCCESS; +} + +IARM_Result_t IARM_Bus_Init(const char* name) +{ + return IARM_RESULT_SUCCESS; +} + +IARM_Result_t IARM_Bus_Connect() +{ + return IARM_RESULT_SUCCESS; +} + +IARM_Result_t IARM_Bus_IsConnected(const char* memberName, int* isRegistered) +{ + return IARM_RESULT_SUCCESS; +} + +IARM_Result_t IARM_Bus_RegisterEventHandler(const char* ownerName, IARM_EventId_t eventId, IARM_EventHandler_t handler) +{ + return IARM_RESULT_SUCCESS; +} + +IARM_Result_t IARM_Bus_UnRegisterEventHandler(const char* ownerName, IARM_EventId_t eventId) +{ + return IARM_RESULT_SUCCESS; +} + +IARM_Result_t IARM_Bus_RemoveEventHandler(const char* ownerName, IARM_EventId_t eventId, IARM_EventHandler_t handler) +{ + return IARM_RESULT_SUCCESS; +} +IARM_Result_t IARM_Bus_RegisterCall(const char *methodName, IARM_BusCall_t handler) +{ +return IARM_RESULT_SUCCESS; +} +IARM_Result_t IARM_Bus_Term(void) +{ +return IARM_RESULT_SUCCESS; +} +IARM_Result_t IARM_Bus_Disconnect(void) +{ +return IARM_RESULT_SUCCESS; +} +IARM_Result_t IARM_Bus_RegisterEvent(IARM_EventId_t maxEventId) +{ +return IARM_RESULT_SUCCESS; +} +IARM_Result_t IARM_Bus_Call(const char* ownerName, const char* methodName, void* arg, size_t argLen) +{ + return IARM_RESULT_SUCCESS; +} diff --git a/stubs/securityagent/SecurityTokenUtil.cpp b/stubs/securityagent/SecurityTokenUtil.cpp new file mode 100644 index 00000000..a743d52f --- /dev/null +++ b/stubs/securityagent/SecurityTokenUtil.cpp @@ -0,0 +1,56 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2020 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include "SecurityTokenUtil.h" +/* + * Send thunder security token for localhost. + * This token can be used by native applications to securely access rdkservices. + */ + +using namespace std; + +extern "C" { + /* + * GetSecurityToken - function to obtain a token from SecurityAgent + * + * Parameters + * maxLength - holds the maximum uint8_t length of the buffer + * Id - Buffer to hold the token. + * + * Return value + * < 0 - failure, absolute value returned is the length required to store the token + * > 0 - success, char length of the returned token + * + * Post-condition; return value 0 should not occur + * + */ + int GetSecurityToken(unsigned short maxLength, unsigned char buffer[]) + { + // get a localhost token + string payload = "http://localhost"; + + size_t len = payload.length(); + + if(!memcpy(buffer,payload.c_str(),len)) + return -1; + return 0; + } +} diff --git a/stubs/securityagent/SecurityTokenUtil.h b/stubs/securityagent/SecurityTokenUtil.h new file mode 100644 index 00000000..b32894f5 --- /dev/null +++ b/stubs/securityagent/SecurityTokenUtil.h @@ -0,0 +1,50 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2020 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#ifdef __WINDOWS__ +#undef EXTERNAL +#ifdef SECURITYUTILITY_EXPORTS +#define EXTERNAL EXTERNAL_EXPORT +#else +#define EXTERNAL EXTERNAL_IMPORT +#endif +#else +#undef EXTERNAL +#define EXTERNAL +#endif + +extern "C" { + /* + * GetSecurityToken - function to obtain a token from SecurityAgent + * + * Parameters + * maxLength - holds the maximum uint8_t length of the buffer + * buffer - Buffer to hold the token. + * + * Return value + * < 0 - failure, absolute value returned is the length required to store the token + * > 0 - success, char length of the returned token + * + * Post-condition; return value 0 should not occur + * + */ + int EXTERNAL GetSecurityToken(unsigned short maxLength, unsigned char buffer[]); +} From 83a1518bfc88b4528c95bdb0c65214c5157b77eb Mon Sep 17 00:00:00 2001 From: yuvaramachandran_gurusamy Date: Thu, 5 Jun 2025 19:20:11 +0000 Subject: [PATCH 10/21] RDKEMW-4129: Prepare native build script Signed-off-by: yuvaramachandran_gurusamy --- .github/workflows/native_full_build.yml | 4 +- build_dependencies.sh | 41 +++++++++++++- cov_build.sh | 11 +--- server/include/gdial-config.h | 10 +--- stubs/iarm_stubs.cpp | 68 +++++++++++++++++++++++ stubs/securityagent/SecurityTokenUtil.cpp | 56 +++++++++++++++++++ stubs/securityagent/SecurityTokenUtil.h | 50 +++++++++++++++++ 7 files changed, 218 insertions(+), 22 deletions(-) create mode 100644 stubs/iarm_stubs.cpp create mode 100644 stubs/securityagent/SecurityTokenUtil.cpp create mode 100644 stubs/securityagent/SecurityTokenUtil.h diff --git a/.github/workflows/native_full_build.yml b/.github/workflows/native_full_build.yml index 757298bf..b241c8d2 100755 --- a/.github/workflows/native_full_build.yml +++ b/.github/workflows/native_full_build.yml @@ -2,9 +2,9 @@ name: Build Component in Native Environment on: push: - branches: [ main, 'sprint/**', 'release/**', topic/*, develop, topic/RDKEMW-4129 ] + branches: [ main, 'sprint/**', 'release/**', topic/*, develop ] pull_request: - branches: [ main, 'sprint/**', 'release/**', topic/*, develop, topic/RDKEMW-4129 ] + branches: [ main, 'sprint/**', 'release/**', topic/*, develop ] jobs: build-entservices-on-pr: diff --git a/build_dependencies.sh b/build_dependencies.sh index c755d997..86d6c353 100755 --- a/build_dependencies.sh +++ b/build_dependencies.sh @@ -3,14 +3,14 @@ set -x set -e ############################## GITHUB_WORKSPACE="${PWD}" -ls -la ${GITHUB_WORKSPACE} + cd ${GITHUB_WORKSPACE} # # ############################# #1. Install Dependencies and packages apt update -#apt install -y libsqlite3-dev libcurl4-openssl-dev valgrind lcov clang libsystemd-dev libboost-all-dev libwebsocketpp-dev meson libcunit1 libcunit1-dev curl protobuf-compiler-grpc libgrpc-dev libgrpc++-dev libunwind-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev +apt install -y ninja-build meson curl libsoup2.4-dev libxml2-dev libglib2.0-dev gobject-introspection libgirepository1.0-dev libgtk-3-dev valac pandoc pip install jsonref ############################ @@ -26,6 +26,11 @@ cd .. ########################################### # Clone the required repositories +rm -rf iarmbus ThunderTools Thunder entservices-apis entservices-testframework gssdp + + +git clone https://github.com/rdkcentral/iarmbus.git +export IARMBUS_PATH=$GITHUB_WORKSPACE/iarmbus git clone --branch R4.4.3 https://github.com/rdkcentral/ThunderTools.git @@ -35,6 +40,21 @@ git clone --branch main https://github.com/rdkcentral/entservices-apis.git git clone https://$GITHUB_TOKEN@github.com/rdkcentral/entservices-testframework.git +git clone --branch gssdp-1.2.3 https://gitlab.gnome.org/GNOME/gssdp.git + +############################ +# Build gssdp-1.2 +echo "======================================================================================" +echo "buliding gssdp-1.2" +cd gssdp + +rm -rf build +meson setup build + +ninja -C build +ninja -C build install +cd - + ############################ # Build Thunder-Tools echo "======================================================================================" @@ -77,7 +97,6 @@ cmake -G Ninja -S Thunder -B build/Thunder \ cmake --build build/Thunder --target install - ############################ # Build entservices-apis echo "======================================================================================" @@ -93,3 +112,19 @@ cmake -G Ninja -S entservices-apis -B build/entservices-apis \ cmake --build build/entservices-apis --target install +############################ + +############################ +# Build and deploy stubs for IARMBus and WPEFramework securityagent +mkdir -p /usr/include/WPEFramework/securityagent +cp $GITHUB_WORKSPACE/stubs/securityagent/* /usr/include/WPEFramework/securityagent/ -v + +echo "======================================================================================" +echo "Building IARMBus and WPEFramework securityagent stubs" +cd $GITHUB_WORKSPACE +cd ./stubs +g++ -fPIC -shared -o libIARMBus.so iarm_stubs.cpp -I$GITHUB_WORKSPACE/stubs -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -I$IARMBUS_PATH/core -I$IARMBUS_PATH/core/include -fpermissive +g++ -fPIC -shared -o libWPEFrameworkSecurityUtil.so securityagent/SecurityTokenUtil.cpp -I$GITHUB_WORKSPACE/stubs -fpermissive + +cp libIARMBus.so /usr/local/lib/ +cp libWPEFrameworkSecurityUtil.so /usr/local/lib/ diff --git a/cov_build.sh b/cov_build.sh index 54f3fb84..da227aba 100755 --- a/cov_build.sh +++ b/cov_build.sh @@ -9,15 +9,8 @@ ls -la ${GITHUB_WORKSPACE} echo "buliding xdialserver" cd ${GITHUB_WORKSPACE} -cmake -G Ninja -S "$GITHUB_WORKSPACE" -B build/xdialserver \ --DUSE_THUNDER_R4=ON \ --DCMAKE_INSTALL_PREFIX="$GITHUB_WORKSPACE/install/usr" \ --DCMAKE_MODULE_PATH="$GITHUB_WORKSPACE/install/tools/cmake" \ --DCMAKE_VERBOSE_MAKEFILE=ON \ --DCMAKE_CXX_FLAGS="-DEXCEPTIONS_ENABLE=ON \ --I ${GITHUB_WORKSPACE}/Thunder/Source \ --I ${GITHUB_WORKSPACE}/Thunder/Source/core" \ -cmake --build build/xdialserver --target install +make + echo "======================================================================================" exit 0 diff --git a/server/include/gdial-config.h b/server/include/gdial-config.h index c6b9a9bc..13dd4172 100644 --- a/server/include/gdial-config.h +++ b/server/include/gdial-config.h @@ -17,8 +17,8 @@ * limitations under the License. */ -#ifndef GDIAL_H_ -#define GDIAL_H_ +#ifndef _GDIAL_CONFIG_H_ +#define _GDIAL_CONFIG_H_ #ifdef __cplusplus extern "C" { @@ -81,12 +81,6 @@ extern "C" { #define GDIAL_THROTTLE_DELAY_US 100000 #define GDIAL_DEBUG g_print -enum { - GDIAL_ERROR_NONE = 0, - GDIAL_ERROR_NOT_REGISTERED, - GDIAL_ERROR_FAIL_TO_START, -} GDialError; - #define GDIAL_GERROR_CHECK_AND_FREE(err, msg) \ {\ if (err) {\ diff --git a/stubs/iarm_stubs.cpp b/stubs/iarm_stubs.cpp new file mode 100644 index 00000000..ef10fc2d --- /dev/null +++ b/stubs/iarm_stubs.cpp @@ -0,0 +1,68 @@ +#include "libIBus.h" +#include "libIARMCore.h" +using namespace std; + +IARM_Result_t IARM_Malloc(IARM_MemType_t type, size_t size, void **ptr) +{ + return IARM_RESULT_SUCCESS; +} + +IARM_Result_t IARM_Free(IARM_MemType_t type, void *alloc) +{ + return IARM_RESULT_SUCCESS; +} + +IARM_Result_t IARM_Bus_BroadcastEvent(const char *ownerName, IARM_EventId_t eventId, void *arg, size_t argLen) +{ + return IARM_RESULT_SUCCESS; +} + +IARM_Result_t IARM_Bus_Init(const char* name) +{ + return IARM_RESULT_SUCCESS; +} + +IARM_Result_t IARM_Bus_Connect() +{ + return IARM_RESULT_SUCCESS; +} + +IARM_Result_t IARM_Bus_IsConnected(const char* memberName, int* isRegistered) +{ + return IARM_RESULT_SUCCESS; +} + +IARM_Result_t IARM_Bus_RegisterEventHandler(const char* ownerName, IARM_EventId_t eventId, IARM_EventHandler_t handler) +{ + return IARM_RESULT_SUCCESS; +} + +IARM_Result_t IARM_Bus_UnRegisterEventHandler(const char* ownerName, IARM_EventId_t eventId) +{ + return IARM_RESULT_SUCCESS; +} + +IARM_Result_t IARM_Bus_RemoveEventHandler(const char* ownerName, IARM_EventId_t eventId, IARM_EventHandler_t handler) +{ + return IARM_RESULT_SUCCESS; +} +IARM_Result_t IARM_Bus_RegisterCall(const char *methodName, IARM_BusCall_t handler) +{ +return IARM_RESULT_SUCCESS; +} +IARM_Result_t IARM_Bus_Term(void) +{ +return IARM_RESULT_SUCCESS; +} +IARM_Result_t IARM_Bus_Disconnect(void) +{ +return IARM_RESULT_SUCCESS; +} +IARM_Result_t IARM_Bus_RegisterEvent(IARM_EventId_t maxEventId) +{ +return IARM_RESULT_SUCCESS; +} +IARM_Result_t IARM_Bus_Call(const char* ownerName, const char* methodName, void* arg, size_t argLen) +{ + return IARM_RESULT_SUCCESS; +} diff --git a/stubs/securityagent/SecurityTokenUtil.cpp b/stubs/securityagent/SecurityTokenUtil.cpp new file mode 100644 index 00000000..a743d52f --- /dev/null +++ b/stubs/securityagent/SecurityTokenUtil.cpp @@ -0,0 +1,56 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2020 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include "SecurityTokenUtil.h" +/* + * Send thunder security token for localhost. + * This token can be used by native applications to securely access rdkservices. + */ + +using namespace std; + +extern "C" { + /* + * GetSecurityToken - function to obtain a token from SecurityAgent + * + * Parameters + * maxLength - holds the maximum uint8_t length of the buffer + * Id - Buffer to hold the token. + * + * Return value + * < 0 - failure, absolute value returned is the length required to store the token + * > 0 - success, char length of the returned token + * + * Post-condition; return value 0 should not occur + * + */ + int GetSecurityToken(unsigned short maxLength, unsigned char buffer[]) + { + // get a localhost token + string payload = "http://localhost"; + + size_t len = payload.length(); + + if(!memcpy(buffer,payload.c_str(),len)) + return -1; + return 0; + } +} diff --git a/stubs/securityagent/SecurityTokenUtil.h b/stubs/securityagent/SecurityTokenUtil.h new file mode 100644 index 00000000..b32894f5 --- /dev/null +++ b/stubs/securityagent/SecurityTokenUtil.h @@ -0,0 +1,50 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2020 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#ifdef __WINDOWS__ +#undef EXTERNAL +#ifdef SECURITYUTILITY_EXPORTS +#define EXTERNAL EXTERNAL_EXPORT +#else +#define EXTERNAL EXTERNAL_IMPORT +#endif +#else +#undef EXTERNAL +#define EXTERNAL +#endif + +extern "C" { + /* + * GetSecurityToken - function to obtain a token from SecurityAgent + * + * Parameters + * maxLength - holds the maximum uint8_t length of the buffer + * buffer - Buffer to hold the token. + * + * Return value + * < 0 - failure, absolute value returned is the length required to store the token + * > 0 - success, char length of the returned token + * + * Post-condition; return value 0 should not occur + * + */ + int EXTERNAL GetSecurityToken(unsigned short maxLength, unsigned char buffer[]); +} From c66ef14dcc294c10ecde5e94e3b8d12ad18d90ad Mon Sep 17 00:00:00 2001 From: apatel859 Date: Mon, 9 Jun 2025 13:12:46 -0400 Subject: [PATCH 11/21] remmove duplicates from build_xxx script Signed-off-by: apatel859 --- build_dependencies.sh | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/build_dependencies.sh b/build_dependencies.sh index 20029a67..86d6c353 100755 --- a/build_dependencies.sh +++ b/build_dependencies.sh @@ -55,21 +55,6 @@ ninja -C build ninja -C build install cd - -git clone --branch gssdp-1.2.3 https://gitlab.gnome.org/GNOME/gssdp.git - -############################ -# Build gssdp-1.2 -echo "======================================================================================" -echo "buliding gssdp-1.2" -cd gssdp - -rm -rf build -meson setup build - -ninja -C build -ninja -C build install -cd - - ############################ # Build Thunder-Tools echo "======================================================================================" From 7302ede2a9fafc17ca36b2c25999fe2156d43f89 Mon Sep 17 00:00:00 2001 From: ssitar583 Date: Thu, 26 Jun 2025 11:19:31 +0530 Subject: [PATCH 12/21] Update gdial.cpp --- server/plat/gdial.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/server/plat/gdial.cpp b/server/plat/gdial.cpp index 1415a2e5..71b9cfd1 100644 --- a/server/plat/gdial.cpp +++ b/server/plat/gdial.cpp @@ -82,11 +82,6 @@ class GDialCastObject reterror = GDIAL_CAST_ERROR_NONE; } } - if (nullptr != AppObj) - { - delete AppObj; - AppObj = nullptr; - } GDIAL_LOGTRACE("Exiting ..."); return reterror; } @@ -993,4 +988,4 @@ int gdial_os_application_service_notification(gboolean isNotifyRequired, void* n } GDIAL_LOGTRACE("Exiting ..."); return GDIAL_APP_ERROR_NONE; -} \ No newline at end of file +} From 3202ca6bbf15d9567f3ab746ba37ce7eccd18c85 Mon Sep 17 00:00:00 2001 From: Yuvaramachandran Gurusamy <123441336+yuvaramachandran-gurusamy@users.noreply.github.com> Date: Tue, 5 Aug 2025 17:19:16 +0530 Subject: [PATCH 13/21] RDKEMW-5927: Crash fix during soup_uri_to_string API call (#163) Signed-off-by: yuvaramachandran_gurusamy --- server/gdialservice.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/server/gdialservice.cpp b/server/gdialservice.cpp index c41cf79d..97606bf2 100644 --- a/server/gdialservice.cpp +++ b/server/gdialservice.cpp @@ -490,10 +490,16 @@ int gdialServiceImpl::start_GDialServer(int argc, char *argv[]) GSList *uris = soup_server_get_uris(m_servers[i]); for (GSList *uri = uris; uri != NULL; uri = uri->next) { - char *uri_string = soup_uri_to_string(uri->data, FALSE); + SoupURI *origin_uri = (SoupURI *)uri->data; + if (!origin_uri) + { + GDIAL_LOGWARNING("Failed to get SoupURI from SoupServer at index [%d]", i); + continue; + } + char *uri_string = soup_uri_to_string(origin_uri, FALSE); GDIAL_LOGINFO("Listening on %s", uri_string); g_free(uri_string); - soup_uri_free(uri->data); + soup_uri_free(origin_uri); } g_slist_free(uris); } From 206d13783924f8ea90d48dbbcc9c199e45ffa783 Mon Sep 17 00:00:00 2001 From: mukesh972 Date: Thu, 4 Sep 2025 01:34:56 +0530 Subject: [PATCH 14/21] RDKEMW-6891:Coverity errors fix for xdial (#166) * RDKEMW-6891: Coverity errors fix for xdial * Update gdial.cpp Fixed review comments --- server/gdial-rest.c | 15 ++++++++++----- server/gdialserver_ut.cpp | 8 ++++---- server/plat/gdial-plat-util.c | 2 +- server/plat/gdial.cpp | 26 +++++++++++++++++--------- server/plat/gdialappcache.cpp | 8 ++++---- 5 files changed, 36 insertions(+), 23 deletions(-) diff --git a/server/gdial-rest.c b/server/gdial-rest.c index ea9959a4..8af53e50 100644 --- a/server/gdial-rest.c +++ b/server/gdial-rest.c @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include #include @@ -159,7 +161,7 @@ static GList *gdial_rest_server_registered_apps_clear(GDialRestServer *self, GLi GDialRestServerPrivate *priv = gdial_rest_server_get_instance_private(self); GDialAppRegistry *app_registry = (GDialAppRegistry *)found->data; registered_apps = g_list_remove_link(registered_apps, found); - GDIAL_LOGINFO("gdial_local_rest_http_server_callback handler Removed for App[%s]instance[%x]", app_registry->name,priv->local_soup_instance); + GDIAL_LOGINFO("gdial_local_rest_http_server_callback handler Removed for App[%s]instance[%x]", app_registry->name,(uintptr_t)priv->local_soup_instance); soup_server_remove_handler(priv->local_soup_instance, app_registry->app_uri); gdial_app_regstry_dispose (app_registry); g_list_free(found); @@ -712,7 +714,10 @@ static void gdial_local_rest_http_server_callback(SoupServer *server, continue; } if (j == 0) { - g_strlcpy(base, elements[i], sizeof(base)); + ret = g_strlcpy(base, elements[i], sizeof(base)); + if (ret >= sizeof(base)) { + GDIAL_LOGERROR("Warn: base too long"); + } } else if (j == 1) { ret = g_strlcpy(instance, elements[i], sizeof(instance)); @@ -1165,7 +1170,7 @@ gboolean gdial_rest_server_register_app(GDialRestServer *self, const gchar *app_ if( 0 != strcmp(app_name,"system")) { - GDIAL_LOGINFO("gdial_local_rest_http_server_callback handler added for App[%s]uri[%s]instance[%x]",app_name,app_registry->app_uri,priv->local_soup_instance); + GDIAL_LOGINFO("gdial_local_rest_http_server_callback handler added for App[%s]uri[%s]instance[%x]",app_name,app_registry->app_uri,(uintptr_t)priv->local_soup_instance); soup_server_add_handler(priv->local_soup_instance, app_registry->app_uri, gdial_local_rest_http_server_callback, self, NULL); } GDIAL_LOGTRACE("Exiting ..."); @@ -1197,7 +1202,7 @@ gboolean gdial_rest_server_register_app_registry(GDialRestServer *self, GDialApp g_return_val_if_fail(gdial_rest_server_is_app_registered(self, app_registry->name), FALSE); if( 0 != strcmp(app_registry->name,"system")) { - GDIAL_LOGINFO("gdial_local_rest_http_server_callback handler added for App[%s]uri[%s]instance[%x]",app_registry->name,app_registry->app_uri,priv->local_soup_instance); + GDIAL_LOGINFO("gdial_local_rest_http_server_callback handler added for App[%s]uri[%s]instance[%x]",app_registry->name,app_registry->app_uri,(uintptr_t)priv->local_soup_instance); soup_server_add_handler(priv->local_soup_instance, app_registry->app_uri, gdial_local_rest_http_server_callback, self, NULL); } return TRUE; @@ -1360,4 +1365,4 @@ GDIAL_STATIC_INLINE void GET_APP_response_builder_destroy(void *builder) { g_free(rbuilder->additionalData); g_free(builder); -} \ No newline at end of file +} diff --git a/server/gdialserver_ut.cpp b/server/gdialserver_ut.cpp index 4669ac65..217eccfa 100644 --- a/server/gdialserver_ut.cpp +++ b/server/gdialserver_ut.cpp @@ -43,7 +43,7 @@ class gdialServiceTest: public GDialNotifier { GDIAL_LOGINFO("Entering ..."); std::string activation = status ? "true" : "false"; - service->ActivationChanged(activation,"SampleTest"); + service->ActivationChanged(std::move(activation),"SampleTest"); GDIAL_LOGINFO("Exiting ..."); } @@ -137,9 +137,9 @@ class gdialServiceTest: public GDialNotifier break; } - appReq->Names = Names; + appReq->Names = std::move(Names); appReq->prefixes = prefixes; - appReq->cors = prefixes; + appReq->cors = std::move(prefixes); appReq->allowStop = allowStop; appReqList->pushBack(appReq); @@ -253,4 +253,4 @@ int main(int argc, char *argv[]) delete testObject; return 0; -} \ No newline at end of file +} diff --git a/server/plat/gdial-plat-util.c b/server/plat/gdial-plat-util.c index 9f4a07b7..774bd9ea 100644 --- a/server/plat/gdial-plat-util.c +++ b/server/plat/gdial-plat-util.c @@ -128,4 +128,4 @@ void gdial_plat_util_log(gdial_plat_util_LogLevel level, func, formatted); fflush(stderr); -} \ No newline at end of file +} diff --git a/server/plat/gdial.cpp b/server/plat/gdial.cpp index 71b9cfd1..4876b746 100644 --- a/server/plat/gdial.cpp +++ b/server/plat/gdial.cpp @@ -82,6 +82,14 @@ class GDialCastObject reterror = GDIAL_CAST_ERROR_NONE; } } + else + { + if (nullptr != AppObj) + { + delete AppObj; + AppObj = nullptr; + } + } GDIAL_LOGTRACE("Exiting ..."); return reterror; } @@ -239,7 +247,7 @@ class GDialCastObject GDIAL_LOGINFO("App[%s] param[%s] observer[%p]",applicationName.c_str(),parameter.c_str(),m_observer); if (nullptr!=m_observer) { - m_observer->onApplicationLaunchRequest(applicationName,parameter); + m_observer->onApplicationLaunchRequest(std::move(applicationName),std::move(parameter)); } GDIAL_LOGTRACE("Exiting ..."); return GDIAL_CAST_ERROR_NONE; @@ -274,10 +282,10 @@ class GDialCastObject m_observer); if (nullptr!=m_observer) { - m_observer->onApplicationLaunchRequestWithLaunchParam( applicationName, - payLoad, - queryString, - additionalDataUrl ); + m_observer->onApplicationLaunchRequestWithLaunchParam( std::move(applicationName), + std::move(payLoad), + std::move(queryString), + std::move(additionalDataUrl) ); } GDIAL_LOGTRACE("Exiting ..."); return GDIAL_CAST_ERROR_NONE; @@ -298,7 +306,7 @@ class GDialCastObject GDIAL_LOGINFO("App[%s]ID[%s]observer[%p]",applicationName.c_str(),applicationId.c_str(),m_observer); if (nullptr!=m_observer) { - m_observer->onApplicationHideRequest(applicationName,applicationId); + m_observer->onApplicationHideRequest(std::move(applicationName),std::move(applicationId)); } GDIAL_LOGTRACE("Exiting ..."); return GDIAL_CAST_ERROR_NONE; @@ -320,7 +328,7 @@ class GDialCastObject GDIAL_LOGINFO("App[%s]ID[%s]observer[%p]",applicationName.c_str(),applicationId.c_str(),m_observer); if (nullptr!=m_observer) { - m_observer->onApplicationResumeRequest(applicationName,applicationId); + m_observer->onApplicationResumeRequest(std::move(applicationName),std::move(applicationId)); } GDIAL_LOGTRACE("Exiting ..."); return GDIAL_CAST_ERROR_NONE; @@ -342,7 +350,7 @@ class GDialCastObject GDIAL_LOGINFO("App[%s]ID[%s]observer[%p]",applicationName.c_str(),applicationId.c_str(),m_observer); if (nullptr!=m_observer) { - m_observer->onApplicationStopRequest(applicationName,applicationId); + m_observer->onApplicationStopRequest(std::move(applicationName),std::move(applicationId)); } GDIAL_LOGTRACE("Exiting ..."); return GDIAL_CAST_ERROR_NONE; @@ -364,7 +372,7 @@ class GDialCastObject GDIAL_LOGINFO("App[%s]ID[%s]observer[%p]",applicationName.c_str(),applicationId.c_str(),m_observer); if (nullptr!=m_observer) { - m_observer->onApplicationStateRequest(applicationName,applicationId); + m_observer->onApplicationStateRequest(std::move(applicationName),std::move(applicationId)); } GDIAL_LOGTRACE("Exiting ..."); return GDIAL_CAST_ERROR_NONE; diff --git a/server/plat/gdialappcache.cpp b/server/plat/gdialappcache.cpp index 287d53e7..dfd08dde 100644 --- a/server/plat/gdialappcache.cpp +++ b/server/plat/gdialappcache.cpp @@ -44,12 +44,12 @@ void GDialAppStatusCache :: setAppCacheId(std::string app_name,std::string id) GDIAL_LOGTRACE("Entering ..."); if(!strcmp(app_name.c_str(),"Netflix")) { - GDialAppStatusCache::Netflix_AppCacheId = id; + GDialAppStatusCache::Netflix_AppCacheId = std::move(id); GDIAL_LOGINFO("App cache Id of Netflix updated to %s",GDialAppStatusCache::Netflix_AppCacheId.c_str()); } else if(!strcmp(app_name.c_str(),"YouTube")) { - GDialAppStatusCache::Youtube_AppCacheId = id; + GDialAppStatusCache::Youtube_AppCacheId = std::move(id); GDIAL_LOGINFO("App cache Id of Youtube updated to %s",GDialAppStatusCache::Youtube_AppCacheId.c_str()); } else @@ -75,7 +75,7 @@ AppCacheErrorCodes GDialAppStatusCache::UpdateAppStatusCache(AppInfo* appEntry) GDIAL_LOGINFO("erasing old data"); err = ObjectCache->erase(id); } - err = ObjectCache->insert(id,appEntry); + err = ObjectCache->insert(std::move(id),appEntry); GDIAL_LOGTRACE("Exiting ..."); return err; } @@ -96,7 +96,7 @@ std::string GDialAppStatusCache::SearchAppStatusInCache(const char* app_name) appEntry->appId.c_str(), appEntry->appError.c_str()); } - GDIAL_LOGINFO("App State = ",state.c_str()); + GDIAL_LOGINFO("App State = %s ",state.c_str()); GDIAL_LOGTRACE("Exiting ..."); return state; } From c48dff76657733cb73a81de73a2a886d86c1dde2 Mon Sep 17 00:00:00 2001 From: Yuvaramachandran Gurusamy <123441336+yuvaramachandran-gurusamy@users.noreply.github.com> Date: Wed, 12 Nov 2025 06:31:34 +0530 Subject: [PATCH 15/21] RDKEMW-9964: Removing onStopped GDial notification (#177) * RDKEMW-9964: Removing onStopped GDial notification handling Signed-off-by: yuvaramachandran_gurusamy * RDKEMW-9964: Fixed coverity issues Signed-off-by: yuvaramachandran_gurusamy * RDKEMW-9964: Fixed coverity issues Signed-off-by: yuvaramachandran_gurusamy * RDKEMW-9964: Fixed coverity issues Signed-off-by: yuvaramachandran_gurusamy --------- Signed-off-by: yuvaramachandran_gurusamy --- server/gdialserver_ut.cpp | 5 -- server/gdialservice.cpp | 115 ++++++++++++++-------------- server/include/gdialservicecommon.h | 1 - server/include/gdialserviceimpl.h | 3 +- 4 files changed, 60 insertions(+), 64 deletions(-) diff --git a/server/gdialserver_ut.cpp b/server/gdialserver_ut.cpp index 217eccfa..88ff7999 100644 --- a/server/gdialserver_ut.cpp +++ b/server/gdialserver_ut.cpp @@ -181,11 +181,6 @@ class gdialServiceTest: public GDialNotifier GDIAL_LOGINFO("App:%s appID:%s",appName.c_str(),appID.c_str()); } - virtual void onStopped() override - { - GDIAL_LOGINFO("~~~~~~~~~~~"); - } - virtual void updatePowerState(string powerState) override { GDIAL_LOGINFO("powerState : %s",powerState.c_str()); diff --git a/server/gdialservice.cpp b/server/gdialservice.cpp index 97606bf2..fdd64ed9 100644 --- a/server/gdialservice.cpp +++ b/server/gdialservice.cpp @@ -292,7 +292,7 @@ int gdialServiceImpl::start_GDialServer(int argc, char *argv[]) soup_server_add_handler(ssdp_http_server, "/", gdial_http_server_throttle_callback, NULL, NULL); GSocketAddress *listen_address = g_inet_socket_address_new_from_string(iface_ipv4_address_, GDIAL_REST_HTTP_PORT); - SoupServerListenOptions option = 0; + SoupServerListenOptions option = (SoupServerListenOptions)0; gboolean success = soup_server_listen(rest_http_server, listen_address, option, &error); g_object_unref (listen_address); if (!success) @@ -376,7 +376,7 @@ int gdialServiceImpl::start_GDialServer(int argc, char *argv[]) if (g_strstr_len(app_list_low, app_list_len, "netflix")) { GDIAL_LOGINFO("netflix is enabled from cmdline"); - GList *allowed_origins = g_list_prepend(NULL, ".netflix.com"); + GList *allowed_origins = g_list_prepend(NULL, (gpointer)".netflix.com"); gdial_rest_server_register_app(dial_rest_server, "Netflix", NULL, NULL, TRUE, TRUE, allowed_origins); g_list_free(allowed_origins); } @@ -388,7 +388,7 @@ int gdialServiceImpl::start_GDialServer(int argc, char *argv[]) if (g_strstr_len(app_list_low, app_list_len, "youtube")) { GDIAL_LOGINFO("youtube is enabled from cmdline"); - GList *allowed_origins = g_list_prepend(NULL, ".youtube.com"); + GList *allowed_origins = g_list_prepend(NULL, (gpointer)".youtube.com"); gdial_rest_server_register_app(dial_rest_server, "YouTube", NULL, NULL, TRUE, TRUE, allowed_origins); g_list_free(allowed_origins); } @@ -400,7 +400,7 @@ int gdialServiceImpl::start_GDialServer(int argc, char *argv[]) if (g_strstr_len(app_list_low, app_list_len, "youtubetv")) { GDIAL_LOGINFO("youtubetv is enabled from cmdline"); - GList *allowed_origins = g_list_prepend(NULL, ".youtube.com"); + GList *allowed_origins = g_list_prepend(NULL, (gpointer)".youtube.com"); gdial_rest_server_register_app(dial_rest_server, "YouTubeTV", NULL, NULL, TRUE, TRUE, allowed_origins); g_list_free(allowed_origins); } @@ -412,7 +412,7 @@ int gdialServiceImpl::start_GDialServer(int argc, char *argv[]) if (g_strstr_len(app_list_low, app_list_len, "youtubekids")) { GDIAL_LOGINFO("youtubekids is enabled from cmdline"); - GList *allowed_origins = g_list_prepend(NULL, ".youtube.com"); + GList *allowed_origins = g_list_prepend(NULL, (gpointer)".youtube.com"); gdial_rest_server_register_app(dial_rest_server, "YouTubeKids", NULL, NULL, TRUE, TRUE, allowed_origins); g_list_free(allowed_origins); } @@ -424,7 +424,7 @@ int gdialServiceImpl::start_GDialServer(int argc, char *argv[]) if (g_strstr_len(app_list_low, app_list_len, "amazoninstantvideo")) { GDIAL_LOGINFO("AmazonInstantVideo is enabled from cmdline"); - GList *allowed_origins = g_list_prepend(NULL, ".amazonprime.com"); + GList *allowed_origins = g_list_prepend(NULL, (gpointer)".amazonprime.com"); gdial_rest_server_register_app(dial_rest_server, "AmazonInstantVideo", NULL, NULL, TRUE, TRUE, allowed_origins); g_list_free(allowed_origins); } @@ -436,8 +436,8 @@ int gdialServiceImpl::start_GDialServer(int argc, char *argv[]) if (g_strstr_len(app_list_low, app_list_len, "spotify")) { GDIAL_LOGINFO("spotify is enabled from cmdline"); - GList *app_prefixes= g_list_prepend(NULL, "com.spotify"); - GList *allowed_origins = g_list_prepend(NULL, ".spotify.com"); + GList *app_prefixes= g_list_prepend(NULL, (gpointer)"com.spotify"); + GList *allowed_origins = g_list_prepend(NULL, (gpointer)".spotify.com"); gdial_rest_server_register_app(dial_rest_server, "com.spotify.Spotify.TV", app_prefixes, NULL, TRUE, TRUE, allowed_origins); g_list_free(allowed_origins); g_list_free(app_prefixes); @@ -450,7 +450,7 @@ int gdialServiceImpl::start_GDialServer(int argc, char *argv[]) if (g_strstr_len(app_list_low, app_list_len, "pairing")) { GDIAL_LOGINFO("pairing is enabled from cmdline"); - GList *allowed_origins = g_list_prepend(NULL, ".comcast.com"); + GList *allowed_origins = g_list_prepend(NULL, (gpointer)".comcast.com"); gdial_rest_server_register_app(dial_rest_server, "Pairing", NULL, NULL, TRUE, TRUE, allowed_origins); g_list_free(allowed_origins); } @@ -625,7 +625,7 @@ void *gdialServiceImpl::requestHandlerThread(void *ctx) { GDIAL_LOGTRACE("Entering ..."); gdialServiceImpl *_instance = (gdialServiceImpl *)ctx; - RequestHandlerPayload reqHdlrPayload; + RequestHandlerPayload reqHdlrPayload = {}; while(!_instance->m_RequestHandlerThreadExit) { reqHdlrPayload.appNameOrfriendlyname = ""; @@ -773,7 +773,7 @@ void *gdialServiceImpl::responseHandlerThread(void *ctx) payload.c_str(), query.c_str(), AddDataUrl.c_str()); - _instance->m_observer->onApplicationLaunchRequestWithLaunchParam(appName,payload,query,AddDataUrl); + _instance->m_observer->onApplicationLaunchRequestWithLaunchParam(std::move(appName),std::move(payload),std::move(query),std::move(AddDataUrl)); } break; case APP_LAUNCH_REQUEST: @@ -781,7 +781,7 @@ void *gdialServiceImpl::responseHandlerThread(void *ctx) std::string appName = response_data.appName, parameter = response_data.parameterOrPayload; GDIAL_LOGINFO("AppLaunch : appName:%s parameter:%s",appName.c_str(),parameter.c_str()); - _instance->m_observer->onApplicationLaunchRequest(appName,parameter); + _instance->m_observer->onApplicationLaunchRequest(std::move(appName),std::move(parameter)); } break; case APP_STOP_REQUEST: @@ -789,7 +789,7 @@ void *gdialServiceImpl::responseHandlerThread(void *ctx) std::string appName = response_data.appName, appId = response_data.appIdOrQuery; GDIAL_LOGINFO("AppStop : appName:%s appId:%s",appName.c_str(),appId.c_str()); - _instance->m_observer->onApplicationStopRequest(appName,appId); + _instance->m_observer->onApplicationStopRequest(std::move(appName),std::move(appId)); } break; case APP_HIDE_REQUEST: @@ -797,7 +797,7 @@ void *gdialServiceImpl::responseHandlerThread(void *ctx) std::string appName = response_data.appName, appId = response_data.appIdOrQuery; GDIAL_LOGINFO("AppHide : appName:%s appId:%s",appName.c_str(),appId.c_str()); - _instance->m_observer->onApplicationHideRequest(appName,appId); + _instance->m_observer->onApplicationHideRequest(std::move(appName),std::move(appId)); } break; case APP_STATE_REQUEST: @@ -805,7 +805,7 @@ void *gdialServiceImpl::responseHandlerThread(void *ctx) std::string appName = response_data.appName, appId = response_data.appIdOrQuery; GDIAL_LOGINFO("AppState : appName:%s appId:%s",appName.c_str(),appId.c_str()); - _instance->m_observer->onApplicationStateRequest(appName,appId); + _instance->m_observer->onApplicationStateRequest(std::move(appName),std::move(appId)); } break; case APP_RESUME_REQUEST: @@ -813,7 +813,7 @@ void *gdialServiceImpl::responseHandlerThread(void *ctx) std::string appName = response_data.appName, appId = response_data.appIdOrQuery; GDIAL_LOGINFO("AppResume : appName:%s appId:%s",appName.c_str(),appId.c_str()); - _instance->m_observer->onApplicationResumeRequest(appName,appId); + _instance->m_observer->onApplicationResumeRequest(std::move(appName),std::move(appId)); } break; default: @@ -825,7 +825,6 @@ void *gdialServiceImpl::responseHandlerThread(void *ctx) } } _instance->m_gdialserver_response_handler_thread = 0; - _instance->m_observer->onStopped(); pthread_exit(nullptr); } @@ -982,13 +981,14 @@ GDIAL_SERVICE_ERROR_CODES gdialService::ApplicationStateChanged(string applicati error.c_str()); if ((nullptr != m_gdialService ) && (nullptr != gdialImplInstance)) { - RequestHandlerPayload payload; + RequestHandlerPayload payload = {}; payload.event = APP_STATE_CHANGED; - - payload.appNameOrfriendlyname = applicationName; - payload.appIdOractivation = applicationId; - payload.state = appState; - payload.error = error; + payload.appNameOrfriendlyname = std::move(applicationName); + payload.appIdOractivation = std::move(applicationId); + payload.state = std::move(appState); + payload.error = std::move(error); + payload.data_param = nullptr; + payload.user_param1 = false; gdialImplInstance->sendRequest(payload); } GDIAL_LOGTRACE("Exiting ..."); @@ -1002,11 +1002,12 @@ GDIAL_SERVICE_ERROR_CODES gdialService::ActivationChanged(string activation, str GDIAL_LOGINFO("activation[%s] friendlyname[%s]",activation.c_str(),friendlyname.c_str()); if ((nullptr != m_gdialService ) && (nullptr != gdialImplInstance)) { - RequestHandlerPayload payload; + RequestHandlerPayload payload = {}; payload.event = ACTIVATION_CHANGED; - - payload.appNameOrfriendlyname = friendlyname; - payload.appIdOractivation = activation; + payload.appNameOrfriendlyname = std::move(friendlyname); + payload.appIdOractivation = std::move(activation); + payload.data_param = nullptr; + payload.user_param1 = false; GDIAL_LOGINFO("ACTIVATION_CHANGED request sent"); gdialImplInstance->sendRequest(payload); } @@ -1021,10 +1022,11 @@ GDIAL_SERVICE_ERROR_CODES gdialService::FriendlyNameChanged(string friendlyname) GDIAL_LOGINFO("friendlyname[%s]",friendlyname.c_str()); if ((nullptr != m_gdialService ) && (nullptr != gdialImplInstance)) { - RequestHandlerPayload payload; + RequestHandlerPayload payload = {}; payload.event = FRIENDLYNAME_CHANGED; - - payload.appNameOrfriendlyname = friendlyname; + payload.appNameOrfriendlyname = std::move(friendlyname); + payload.data_param = nullptr; + payload.user_param1 = false; gdialImplInstance->sendRequest(payload); } GDIAL_LOGTRACE("Exiting ..."); @@ -1051,9 +1053,10 @@ GDIAL_SERVICE_ERROR_CODES gdialService::RegisterApplications(RegisterAppEntryLis GDIAL_LOGINFO("appConfigList[%p]",appConfigList); if ((nullptr != m_gdialService ) && (nullptr != gdialImplInstance)) { - RequestHandlerPayload payload; + RequestHandlerPayload payload = {}; payload.event = REGISTER_APPLICATIONS; payload.data_param = appConfigList; + payload.user_param1 = false; gdialImplInstance->sendRequest(payload); } GDIAL_LOGTRACE("Exiting ..."); @@ -1067,9 +1070,10 @@ void gdialService::setNetworkStandbyMode(bool nwStandbymode) GDIAL_LOGINFO("nwStandbymode[%u]",nwStandbymode); if ((nullptr != m_gdialService ) && (nullptr != gdialImplInstance)) { - RequestHandlerPayload payload; + RequestHandlerPayload payload = {}; payload.event = UPDATE_NW_STANDBY; payload.user_param1 = (bool)nwStandbymode; + payload.data_param = nullptr; gdialImplInstance->sendRequest(payload); } GDIAL_LOGTRACE("Exiting ..."); @@ -1083,9 +1087,11 @@ GDIAL_SERVICE_ERROR_CODES gdialService::setManufacturerName(string manufacturer) GDIAL_LOGINFO("Manufacturer[%s]",manufacturer.c_str()); if ((nullptr != m_gdialService ) && (nullptr != gdialImplInstance)) { - RequestHandlerPayload payload; + RequestHandlerPayload payload = {}; payload.event = UPDATE_MANUFACTURER_NAME; - payload.manufacturer = manufacturer; + payload.manufacturer = std::move(manufacturer); + payload.data_param = nullptr; + payload.user_param1 = false; gdialImplInstance->sendRequest(payload); } GDIAL_LOGTRACE("Exiting ..."); @@ -1099,9 +1105,11 @@ GDIAL_SERVICE_ERROR_CODES gdialService::setModelName(string model) GDIAL_LOGINFO("Model[%s]",model.c_str()); if ((nullptr != m_gdialService ) && (nullptr != gdialImplInstance)) { - RequestHandlerPayload payload; + RequestHandlerPayload payload = {}; payload.event = UPDATE_MODEL_NAME; - payload.model = model; + payload.model = std::move(model); + payload.data_param = nullptr; + payload.user_param1 = false; gdialImplInstance->sendRequest(payload); } GDIAL_LOGTRACE("Exiting ..."); @@ -1134,10 +1142,10 @@ void gdialServiceImpl::onApplicationLaunchRequestWithLaunchParam(string appName, GDIAL_LOGTRACE("Entering ..."); payload.event = APP_LAUNCH_REQUEST_WITH_PARAMS; - payload.appName = appName; - payload.parameterOrPayload = strPayLoad; - payload.appIdOrQuery = strQuery; - payload.AddDataUrl = strAddDataUrl; + payload.appName = std::move(appName); + payload.parameterOrPayload = std::move(strPayLoad); + payload.appIdOrQuery = std::move(strQuery); + payload.AddDataUrl = std::move(strAddDataUrl); notifyResponse(payload); GDIAL_LOGTRACE("Exiting ..."); } @@ -1148,8 +1156,8 @@ void gdialServiceImpl::onApplicationLaunchRequest(string appName, string paramet GDIAL_LOGTRACE("Entering ..."); payload.event = APP_LAUNCH_REQUEST; - payload.appName = appName; - payload.parameterOrPayload = parameter; + payload.appName = std::move(appName); + payload.parameterOrPayload = std::move(parameter); notifyResponse(payload); GDIAL_LOGTRACE("Exiting ..."); } @@ -1160,8 +1168,8 @@ void gdialServiceImpl::onApplicationStopRequest(string appName, string appID) GDIAL_LOGTRACE("Entering ..."); payload.event = APP_STOP_REQUEST; - payload.appName = appName; - payload.appIdOrQuery = appID; + payload.appName = std::move(appName); + payload.appIdOrQuery = std::move(appID); notifyResponse(payload); GDIAL_LOGTRACE("Exiting ..."); } @@ -1172,8 +1180,8 @@ void gdialServiceImpl::onApplicationHideRequest(string appName, string appID) GDIAL_LOGTRACE("Entering ..."); payload.event = APP_HIDE_REQUEST; - payload.appName = appName; - payload.appIdOrQuery = appID; + payload.appName = std::move(appName); + payload.appIdOrQuery = std::move(appID); notifyResponse(payload); GDIAL_LOGTRACE("Exiting ..."); } @@ -1184,8 +1192,8 @@ void gdialServiceImpl::onApplicationResumeRequest(string appName, string appID) GDIAL_LOGTRACE("Entering ..."); payload.event = APP_RESUME_REQUEST; - payload.appName = appName; - payload.appIdOrQuery = appID; + payload.appName = std::move(appName); + payload.appIdOrQuery = std::move(appID); notifyResponse(payload); GDIAL_LOGTRACE("Exiting ..."); } @@ -1196,24 +1204,19 @@ void gdialServiceImpl::onApplicationStateRequest(string appName, string appID) GDIAL_LOGTRACE("Entering ..."); payload.event = APP_STATE_REQUEST; - payload.appName = appName; - payload.appIdOrQuery = appID; + payload.appName = std::move(appName); + payload.appIdOrQuery = std::move(appID); notifyResponse(payload); GDIAL_LOGTRACE("Exiting ..."); } -void gdialServiceImpl::onStopped() -{ - // -} - void gdialServiceImpl::updatePowerState(string powerState) { GDIAL_LOGTRACE("Entering ..."); GDIAL_LOGINFO("powerState : %s",powerState.c_str()); if (m_observer) { - m_observer->updatePowerState(powerState); + m_observer->updatePowerState(std::move(powerState)); } GDIAL_LOGTRACE("Exiting ..."); } \ No newline at end of file diff --git a/server/include/gdialservicecommon.h b/server/include/gdialservicecommon.h index 3f9dfe49..fb463373 100644 --- a/server/include/gdialservicecommon.h +++ b/server/include/gdialservicecommon.h @@ -78,7 +78,6 @@ class GDialNotifier virtual void onApplicationHideRequest(string appName, string appID)=0; virtual void onApplicationResumeRequest(string appName, string appID)=0; virtual void onApplicationStateRequest(string appName, string appID)=0; - virtual void onStopped(void)=0; virtual void updatePowerState(string powerState)=0; }; #endif /* _GDIAL_SERVICE_COMMON_H_ */ \ No newline at end of file diff --git a/server/include/gdialserviceimpl.h b/server/include/gdialserviceimpl.h index 5733b43e..26f4b430 100644 --- a/server/include/gdialserviceimpl.h +++ b/server/include/gdialserviceimpl.h @@ -97,14 +97,13 @@ class gdialServiceImpl: public GDialNotifier virtual void onApplicationHideRequest(string appName, string appID) override; virtual void onApplicationResumeRequest(string appName, string appID) override; virtual void onApplicationStateRequest(string appName, string appID) override; - virtual void onStopped() override; virtual void updatePowerState(string powerState) override; private: gdialServiceImpl(){}; virtual ~gdialServiceImpl(){}; - GDialNotifier *m_observer; + GDialNotifier *m_observer{nullptr}; pthread_t m_gdialserver_main_thread{0}; static void *mainThread(void *ctx); From 3da15c8c1d2d3bbc6f341f6451ce52ae019e4a4d Mon Sep 17 00:00:00 2001 From: srinibas15 <113517102+srinibas15@users.noreply.github.com> Date: Fri, 5 Dec 2025 07:27:04 +0530 Subject: [PATCH 16/21] RDKEMW-11024 - Using copilot identify and fix the static code analysis issues in xdial (#182) * RDKEMW-11024 - Using copilot identify and fix the static code analysis issues in xdial Reason for Change: Resolving the static code issues scanned by copilot Test Procedure: Compiled and Verified Risks: Low Priority: P1 version: minor Signed-off-by: smohap466 --------- Signed-off-by: smohap466 Co-authored-by: smohap466 Co-authored-by: dkumar798 --- server/gdial-rest.c | 5 ++++- server/plat/gdialappcache.cpp | 17 +++++++++++------ 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/server/gdial-rest.c b/server/gdial-rest.c index 8af53e50..6902dbd3 100644 --- a/server/gdial-rest.c +++ b/server/gdial-rest.c @@ -540,6 +540,8 @@ static void gdial_rest_server_handle_POST(GDialRestServer *gdial_rest_server, So } else { g_object_unref(app); + // FIX(Copilot): Set app to NULL after unref to prevent use-after-free + app = NULL; gdial_rest_server_http_return_if(start_error == GDIAL_APP_ERROR_FORBIDDEN, msg, SOUP_STATUS_FORBIDDEN); gdial_rest_server_http_return_if(start_error == GDIAL_APP_ERROR_UNAUTH, msg, SOUP_STATUS_UNAUTHORIZED); gdial_rest_server_http_return_if(TRUE, msg, SOUP_STATUS_SERVICE_UNAVAILABLE); @@ -653,7 +655,8 @@ static void gdial_rest_server_handle_POST_dial_data(GDialRestServer *gdial_rest_ if (query) { GHashTable *dupQuery = gdial_util_str_str_hashtable_dup(query); body_query = query ? gdial_util_str_str_hashtable_merge(body_query, dupQuery) : body_query; - g_hash_table_destory(dupQuery); + // FIX(Copilot): Correct typo g_hash_table_destory -> g_hash_table_destroy + g_hash_table_destroy(dupQuery); } #endif gdial_app_set_additional_dial_data(app, body_query); diff --git a/server/plat/gdialappcache.cpp b/server/plat/gdialappcache.cpp index dfd08dde..d8c380c7 100644 --- a/server/plat/gdialappcache.cpp +++ b/server/plat/gdialappcache.cpp @@ -89,12 +89,17 @@ std::string GDialAppStatusCache::SearchAppStatusInCache(const char* app_name) if(doIdExist(id)) { AppInfo* appEntry = ObjectCache->findObject(id); - - state = appEntry->appState; - GDIAL_LOGINFO("APPCache: App Name[%s] AppID[%s] Error[%s]", - appEntry->appName.c_str(), - appEntry->appId.c_str(), - appEntry->appError.c_str()); + // FIX(Copilot): Add NULL check for appEntry + if (appEntry) { + state = appEntry->appState; + GDIAL_LOGINFO("APPCache: App Name[%s] AppID[%s] Error[%s]", + appEntry->appName.c_str(), + appEntry->appId.c_str(), + appEntry->appError.c_str()); + } + else { + GDIAL_LOGERROR("Cache entry exists but findObject returned NOT_FOUND"); + } } GDIAL_LOGINFO("App State = %s ",state.c_str()); GDIAL_LOGTRACE("Exiting ..."); From 29f678989ac24e57118b5274208fa9bfaf4c22a3 Mon Sep 17 00:00:00 2001 From: dkumar798 Date: Thu, 8 Jan 2026 23:05:06 +0530 Subject: [PATCH 17/21] RDKEMW-12059: Fix Coverity identified issues (#183) * RDKEMW-12059: Fix Coverity identified issues --- server/gdial-rest.c | 6 +++--- server/gdial-ssdp.c | 4 +++- server/plat/gdial-plat-util.c | 4 ++-- server/plat/gdial.cpp | 2 +- server/plat/gdialappcache.hpp | 2 +- 5 files changed, 10 insertions(+), 8 deletions(-) diff --git a/server/gdial-rest.c b/server/gdial-rest.c index 6902dbd3..c9604e98 100644 --- a/server/gdial-rest.c +++ b/server/gdial-rest.c @@ -161,7 +161,7 @@ static GList *gdial_rest_server_registered_apps_clear(GDialRestServer *self, GLi GDialRestServerPrivate *priv = gdial_rest_server_get_instance_private(self); GDialAppRegistry *app_registry = (GDialAppRegistry *)found->data; registered_apps = g_list_remove_link(registered_apps, found); - GDIAL_LOGINFO("gdial_local_rest_http_server_callback handler Removed for App[%s]instance[%x]", app_registry->name,(uintptr_t)priv->local_soup_instance); + GDIAL_LOGINFO("gdial_local_rest_http_server_callback handler Removed for App[%s]instance[%p]", app_registry->name,(void*)priv->local_soup_instance); soup_server_remove_handler(priv->local_soup_instance, app_registry->app_uri); gdial_app_regstry_dispose (app_registry); g_list_free(found); @@ -1173,7 +1173,7 @@ gboolean gdial_rest_server_register_app(GDialRestServer *self, const gchar *app_ if( 0 != strcmp(app_name,"system")) { - GDIAL_LOGINFO("gdial_local_rest_http_server_callback handler added for App[%s]uri[%s]instance[%x]",app_name,app_registry->app_uri,(uintptr_t)priv->local_soup_instance); + GDIAL_LOGINFO("gdial_local_rest_http_server_callback handler added for App[%s]uri[%s]instance[%p]",app_name,app_registry->app_uri,(void*)priv->local_soup_instance); soup_server_add_handler(priv->local_soup_instance, app_registry->app_uri, gdial_local_rest_http_server_callback, self, NULL); } GDIAL_LOGTRACE("Exiting ..."); @@ -1205,7 +1205,7 @@ gboolean gdial_rest_server_register_app_registry(GDialRestServer *self, GDialApp g_return_val_if_fail(gdial_rest_server_is_app_registered(self, app_registry->name), FALSE); if( 0 != strcmp(app_registry->name,"system")) { - GDIAL_LOGINFO("gdial_local_rest_http_server_callback handler added for App[%s]uri[%s]instance[%x]",app_registry->name,app_registry->app_uri,(uintptr_t)priv->local_soup_instance); + GDIAL_LOGINFO("gdial_local_rest_http_server_callback handler added for App[%s]uri[%s]instance[%p]",app_registry->name,app_registry->app_uri,(void*)priv->local_soup_instance); soup_server_add_handler(priv->local_soup_instance, app_registry->app_uri, gdial_local_rest_http_server_callback, self, NULL); } return TRUE; diff --git a/server/gdial-ssdp.c b/server/gdial-ssdp.c index d2ed7808..107b42cf 100644 --- a/server/gdial-ssdp.c +++ b/server/gdial-ssdp.c @@ -257,6 +257,7 @@ int gdial_ssdp_new(SoupServer *ssdp_http_server, GDialOptions *options, const gc int gdial_ssdp_destroy() { GDIAL_LOGTRACE("Entering ..."); + pthread_mutex_lock(&ssdpServerEventSync); if (ssdp_http_server_) { soup_server_remove_handler(ssdp_http_server_, "/dd.xml"); @@ -315,6 +316,7 @@ int gdial_ssdp_destroy() { g_object_unref(ssdp_client_); ssdp_client_ = NULL; } + pthread_mutex_unlock(&ssdpServerEventSync); pthread_mutex_destroy(&ssdpServerEventSync); GDIAL_LOGTRACE("Exiting ..."); return 0; @@ -386,4 +388,4 @@ int gdial_ssdp_set_modelname(const gchar *model_name) } pthread_mutex_unlock(&ssdpServerEventSync); return 0; -} \ No newline at end of file +} diff --git a/server/plat/gdial-plat-util.c b/server/plat/gdial-plat-util.c index 774bd9ea..fd6e9594 100644 --- a/server/plat/gdial-plat-util.c +++ b/server/plat/gdial-plat-util.c @@ -120,8 +120,8 @@ void gdial_plat_util_log(gdial_plat_util_LogLevel level, va_start(argptr, format); vsnprintf(formatted, kFormatMessageSize, format, argptr); va_end(argptr); - fprintf(stderr, "[GDIAL][%d] %s [%s:%d] %s: %s \n", - (int)syscall(SYS_gettid), + fprintf(stderr, "[GDIAL][%ld] %s [%s:%d] %s: %s \n", + (long)syscall(SYS_gettid), levelMap[level], basename(file), line, diff --git a/server/plat/gdial.cpp b/server/plat/gdial.cpp index 4876b746..6e294fde 100644 --- a/server/plat/gdial.cpp +++ b/server/plat/gdial.cpp @@ -383,7 +383,7 @@ class GDialCastObject m_observer = service; } private: - GDialNotifier *m_observer; + GDialNotifier *m_observer{nullptr}; }; GDialCastObject* GDialObjHandle = nullptr; diff --git a/server/plat/gdialappcache.hpp b/server/plat/gdialappcache.hpp index a3191f5e..276b79f3 100644 --- a/server/plat/gdialappcache.hpp +++ b/server/plat/gdialappcache.hpp @@ -56,7 +56,7 @@ class GDialAppStatusCache private: GDialObjectCacheHelper* ObjectCache; - GDialNotifier* m_observer; + GDialNotifier* m_observer{nullptr}; static std::string Netflix_AppCacheId; static std::string Youtube_AppCacheId; }; From 48461dc2b8a21975cc598c4956a9fd5c621482a8 Mon Sep 17 00:00:00 2001 From: agampa263 <133222558+agampa263@users.noreply.github.com> Date: Tue, 20 Jan 2026 21:50:59 +0530 Subject: [PATCH 18/21] RDKEMW-12555 : Fix coveirty workflow scan in xdialserver repo (#187) * RDKEMW-12555 : Fix coveirty workflow scan in xdialserver repo Reason for Change: Fix coveirty scan workflow failure in xdialserver repo Test Procedure: Verify coveirty workflow Risks: Low Priority: P1 version: minor Signed-off-by:AkshayKumar_Gampa AkshayKumar_Gampa@comcast.com * RDKEMW-12555 : Fix coveirty workflow scan in xdialserver repo Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .github/workflows/native_full_build.yml | 4 ++-- build_dependencies.sh | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/native_full_build.yml b/.github/workflows/native_full_build.yml index b241c8d2..8869a3ea 100755 --- a/.github/workflows/native_full_build.yml +++ b/.github/workflows/native_full_build.yml @@ -11,7 +11,7 @@ jobs: name: Build xdialserver component in github rdkcentral runs-on: ubuntu-latest container: - image: ghcr.io/rdkcentral/docker-rdk-ci:latest + image: ubuntu:22.04 steps: - name: Checkout code @@ -22,4 +22,4 @@ jobs: sh -x build_dependencies.sh sh -x cov_build.sh env: - GITHUB_TOKEN: ${{ secrets.RDKCM_RDKE }} + GITHUB_TOKEN: ${{ secrets.RDKCM_RDKE }} \ No newline at end of file diff --git a/build_dependencies.sh b/build_dependencies.sh index 86d6c353..1dad0973 100755 --- a/build_dependencies.sh +++ b/build_dependencies.sh @@ -10,7 +10,7 @@ cd ${GITHUB_WORKSPACE} #1. Install Dependencies and packages apt update -apt install -y ninja-build meson curl libsoup2.4-dev libxml2-dev libglib2.0-dev gobject-introspection libgirepository1.0-dev libgtk-3-dev valac pandoc +apt install -y git python3 python3-pip cmake ninja-build meson curl libsoup2.4-dev libxml2-dev libglib2.0-dev gobject-introspection libgirepository1.0-dev libgtk-3-dev libcurl4-openssl-dev libcunit1-dev valac pandoc pip install jsonref ############################ From 6500318ad3bb438e2970336b754de42eb5ffe47a Mon Sep 17 00:00:00 2001 From: dkumar798 Date: Fri, 27 Mar 2026 18:11:04 +0530 Subject: [PATCH 19/21] RDKEMW-12059: Fix Coverity identified issues (#192) * RDKEMW-12059: Fix Coverity identified issues * Update gdial-plat-util.c * Update gdial-rest.c * Update gdial-plat-util.c * Update gdial-plat-util.c --- server/gdial-rest.c | 4 ++++ server/gdialserver_ut.cpp | 1 - server/plat/gdial-plat-util.c | 1 + server/plat/gdialappcache.cpp | 4 ++++ 4 files changed, 9 insertions(+), 1 deletion(-) diff --git a/server/gdial-rest.c b/server/gdial-rest.c index c9604e98..f00094ef 100644 --- a/server/gdial-rest.c +++ b/server/gdial-rest.c @@ -501,6 +501,7 @@ static void gdial_rest_server_handle_POST(GDialRestServer *gdial_rest_server, So * start_error = NONE; * app exist, and could be in hidden state, so resume; */ + /* coverity[deadcode : FALSE] */ start_error = gdial_app_start(app, NULL, NULL, NULL, gdial_rest_server); } @@ -535,6 +536,7 @@ static void gdial_rest_server_handle_POST(GDialRestServer *gdial_rest_server, So } } else { + /* coverity[deadcode : FALSE] */ soup_message_set_status(msg, SOUP_STATUS_OK); } } @@ -880,7 +882,9 @@ static void gdial_rest_http_server_apps_callback(SoupServer *server, const gchar *header_origin = soup_message_headers_get_one(msg->request_headers, "Origin"); GDIAL_LOGERROR("Origin %s, Host: %s, Method: %s", header_origin, header_host, msg->method); + /* coverity[url_manipulation : FALSE] */ if (!gdial_rest_server_is_allowed_origin(gdial_rest_server, header_origin, app_name)) { + /* coverity[PW.PARAMETER_HIDDEN : FALSE] */ gdial_rest_server_http_print_and_return_if_fail(FALSE, msg, SOUP_STATUS_FORBIDDEN, "origin %s is not allowed", header_origin); } /* diff --git a/server/gdialserver_ut.cpp b/server/gdialserver_ut.cpp index 88ff7999..20e4eec5 100644 --- a/server/gdialserver_ut.cpp +++ b/server/gdialserver_ut.cpp @@ -224,7 +224,6 @@ int main(int argc, char *argv[]) if (nullptr != testObject) { delete testObject; - testObject = nullptr; } testObject = new gdialServiceTest(gdial_args); } diff --git a/server/plat/gdial-plat-util.c b/server/plat/gdial-plat-util.c index fd6e9594..2e6c2f35 100644 --- a/server/plat/gdial-plat-util.c +++ b/server/plat/gdial-plat-util.c @@ -123,6 +123,7 @@ void gdial_plat_util_log(gdial_plat_util_LogLevel level, fprintf(stderr, "[GDIAL][%ld] %s [%s:%d] %s: %s \n", (long)syscall(SYS_gettid), levelMap[level], + /* coverity[PRINTF_ARGS : FALSE] */ basename(file), line, func, diff --git a/server/plat/gdialappcache.cpp b/server/plat/gdialappcache.cpp index d8c380c7..3f22ef73 100644 --- a/server/plat/gdialappcache.cpp +++ b/server/plat/gdialappcache.cpp @@ -74,6 +74,10 @@ AppCacheErrorCodes GDialAppStatusCache::UpdateAppStatusCache(AppInfo* appEntry) if(doIdExist(id)) { GDIAL_LOGINFO("erasing old data"); err = ObjectCache->erase(id); + if(err != AppCacheError_OK) + { + GDIAL_LOGINFO("erasing failed"); + } } err = ObjectCache->insert(std::move(id),appEntry); GDIAL_LOGTRACE("Exiting ..."); From a2496f07c35c9bf1fe98c66b50a55e7adaf66855 Mon Sep 17 00:00:00 2001 From: dkumar798 Date: Tue, 7 Apr 2026 19:54:23 +0530 Subject: [PATCH 20/21] RDKEMW-16256: Suppress coverity false positive issue (#198) --- server/gdial-rest.c | 3 +++ server/plat/gdial-plat-util.c | 1 + 2 files changed, 4 insertions(+) diff --git a/server/gdial-rest.c b/server/gdial-rest.c index f00094ef..7266a8c7 100644 --- a/server/gdial-rest.c +++ b/server/gdial-rest.c @@ -536,6 +536,7 @@ static void gdial_rest_server_handle_POST(GDialRestServer *gdial_rest_server, So } } else { + /* marking as intended */ /* coverity[deadcode : FALSE] */ soup_message_set_status(msg, SOUP_STATUS_OK); } @@ -882,8 +883,10 @@ static void gdial_rest_http_server_apps_callback(SoupServer *server, const gchar *header_origin = soup_message_headers_get_one(msg->request_headers, "Origin"); GDIAL_LOGERROR("Origin %s, Host: %s, Method: %s", header_origin, header_host, msg->method); + /* marking as intended */ /* coverity[url_manipulation : FALSE] */ if (!gdial_rest_server_is_allowed_origin(gdial_rest_server, header_origin, app_name)) { + /* marking as intended */ /* coverity[PW.PARAMETER_HIDDEN : FALSE] */ gdial_rest_server_http_print_and_return_if_fail(FALSE, msg, SOUP_STATUS_FORBIDDEN, "origin %s is not allowed", header_origin); } diff --git a/server/plat/gdial-plat-util.c b/server/plat/gdial-plat-util.c index 2e6c2f35..6d793dfb 100644 --- a/server/plat/gdial-plat-util.c +++ b/server/plat/gdial-plat-util.c @@ -123,6 +123,7 @@ void gdial_plat_util_log(gdial_plat_util_LogLevel level, fprintf(stderr, "[GDIAL][%ld] %s [%s:%d] %s: %s \n", (long)syscall(SYS_gettid), levelMap[level], + /* marking as intended */ /* coverity[PRINTF_ARGS : FALSE] */ basename(file), line, From d0acf4610dbbd98ea578d176fa552ca96b348e76 Mon Sep 17 00:00:00 2001 From: hgfell683 <107510770+hgfell683@users.noreply.github.com> Date: Tue, 28 Apr 2026 16:20:46 +0000 Subject: [PATCH 21/21] RDKEMW-16911 - L1 unit tests for xdialserver (#199) * RDKEMW-16911 - L1 unit tests for xdialserver --- .github/workflows/L1-tests.yml | 252 ++++++++++ Makefile.am | 20 + configure.ac | 72 +++ tests/L1Tests/Makefile.am | 96 ++++ tests/L1Tests/mocks/gssdp_mock.c | 77 +++ tests/L1Tests/plat/gdial_os_stubs.cpp | 268 ++++++++++ tests/L1Tests/plat/test_gdialAppCache.cpp | 63 +++ tests/L1Tests/plat/test_gdialCpp.cpp | 494 +++++++++++++++++++ tests/L1Tests/plat/test_gdialPlat.cpp | 193 ++++++++ tests/L1Tests/plat/test_gdialPlatApp.cpp | 421 ++++++++++++++++ tests/L1Tests/plat/test_gdialPlatDev.cpp | 164 ++++++ tests/L1Tests/plat/test_gdialPlatUtil.cpp | 115 +++++ tests/L1Tests/server/test_gdialRest.cpp | 486 ++++++++++++++++++ tests/L1Tests/server/test_gdialServer.cpp | 286 +++++++++++ tests/L1Tests/server/test_gdialService.cpp | 476 ++++++++++++++++++ tests/L1Tests/server/test_gdialShield.cpp | 166 +++++++ tests/L1Tests/server/test_gdialSsdp.cpp | 247 ++++++++++ tests/L1Tests/server/test_gdialserver_ut.cpp | 160 ++++++ tests/L1Tests/stubs/gdial_cpp_test_stubs.hpp | 130 +++++ tests/L1Tests/test_main.cpp | 39 ++ tests/L1Tests/tests/gdialserver_ut.cpp | 53 ++ tests/L1Tests/utils/test_gdialUtil.cpp | 314 ++++++++++++ tests/Makefile.am | 22 + tests/README.md | 134 ++++- 24 files changed, 4747 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/L1-tests.yml create mode 100644 Makefile.am create mode 100644 configure.ac create mode 100644 tests/L1Tests/Makefile.am create mode 100644 tests/L1Tests/mocks/gssdp_mock.c create mode 100644 tests/L1Tests/plat/gdial_os_stubs.cpp create mode 100644 tests/L1Tests/plat/test_gdialAppCache.cpp create mode 100644 tests/L1Tests/plat/test_gdialCpp.cpp create mode 100644 tests/L1Tests/plat/test_gdialPlat.cpp create mode 100644 tests/L1Tests/plat/test_gdialPlatApp.cpp create mode 100644 tests/L1Tests/plat/test_gdialPlatDev.cpp create mode 100644 tests/L1Tests/plat/test_gdialPlatUtil.cpp create mode 100644 tests/L1Tests/server/test_gdialRest.cpp create mode 100644 tests/L1Tests/server/test_gdialServer.cpp create mode 100644 tests/L1Tests/server/test_gdialService.cpp create mode 100644 tests/L1Tests/server/test_gdialShield.cpp create mode 100644 tests/L1Tests/server/test_gdialSsdp.cpp create mode 100644 tests/L1Tests/server/test_gdialserver_ut.cpp create mode 100644 tests/L1Tests/stubs/gdial_cpp_test_stubs.hpp create mode 100644 tests/L1Tests/test_main.cpp create mode 100644 tests/L1Tests/tests/gdialserver_ut.cpp create mode 100644 tests/L1Tests/utils/test_gdialUtil.cpp create mode 100644 tests/Makefile.am diff --git a/.github/workflows/L1-tests.yml b/.github/workflows/L1-tests.yml new file mode 100644 index 00000000..bc17965c --- /dev/null +++ b/.github/workflows/L1-tests.yml @@ -0,0 +1,252 @@ +name: L1-tests + +on: + push: + branches: + - develop + - main + pull_request: + branches: + - develop + - main + workflow_call: + secrets: + RDKCM_RDKE: + required: true + workflow_dispatch: + +permissions: + contents: read + +env: + BUILD_TYPE: Debug + +jobs: + L1-tests: + name: Build and run unit tests + runs-on: ubuntu-22.04 + + steps: + - name: Set up CMake + uses: jwlawson/actions-setup-cmake@v1.13 + with: + cmake-version: '3.16.x' + github-api-token: '' + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.x' + - run: pip install jsonref + + - name: ACK External Trigger + run: | + echo "Message: External Trigger Received for L1 Tests" + echo "Trigger Source: ${{ inputs.caller_source }}" + + - name: Set up CMake + uses: jwlawson/actions-setup-cmake@v1.13 + with: + cmake-version: '3.16.x' + + - name: Install packages + run: > + sudo apt update + && + sudo apt install -y + autoconf automake libtool pkg-config + libgtest-dev libgmock-dev + build-essential g++ cmake + valgrind lcov clang ninja-build + libglib2.0-dev libdbus-1-dev + libgssdp-1.2-dev libsoup2.4-dev libxml2-dev + uuid-dev libcurl4-openssl-dev + libsystemd-dev libboost-all-dev libwebsocketpp-dev + meson libcunit1 libcunit1-dev curl + protobuf-compiler-grpc libgrpc-dev libgrpc++-dev + libyajl-dev + + - name: Build trower-base64 + run: | + if [ ! -d "trower-base64" ]; then + git clone https://github.com/xmidt-org/trower-base64.git + fi + cd trower-base64 + meson setup --warnlevel 3 --werror build + ninja -C build + sudo ninja -C build install + + - name: Checkout xdialserver + uses: actions/checkout@v3 + with: + path: xdialserver + + # - name: Checkout entservices-testframework + # uses: actions/checkout@v3 + # with: + # repository: rdkcentral/entservices-testframework + # path: entservices-testframework + # token: ${{ secrets.RDKCM_RDKE }} + # ref: feature/RDKEMW-14961-2 + + - name: Checkout googletest + uses: actions/checkout@v3 + with: + repository: google/googletest + path: googletest + ref: v1.15.0 + + - name: Build googletest + run: | + cmake -S "$GITHUB_WORKSPACE/googletest" \ + -B build/googletest \ + -DCMAKE_INSTALL_PREFIX="$GITHUB_WORKSPACE/install/usr" \ + -DBUILD_GMOCK=ON \ + -DBUILD_SHARED_LIBS=OFF \ + -DCMAKE_POSITION_INDEPENDENT_CODE=ON + cmake --build build/googletest -j$(nproc) + cmake --install build/googletest + + # - name: Generate external headers + # # Empty headers to mute errors + # run: > + # cd "$GITHUB_WORKSPACE/entservices-testframework/Tests/" + # && + # mkdir -p + # headers + # headers/WPEFramework + # headers/WPEFramework/core + # headers/WPEFramework/interfaces + # headers/rdk/iarmbus + # headers/rdk/iarmmgrs-hal + # headers/uuid + # && + # cd headers + # && + # touch + # WPEFramework/core.h + # WPEFramework/interfaces.h + # WPEFramework/plugins.h + # rdk/iarmbus/libIBus.h + # rdk/iarmbus/libIBusDaemon.h + # rdk/iarmmgrs-hal/iarmmgrs_hal.h + # uuid/uuid.h + # && + # mkdir -p proc + # && + # touch proc/readproc.h + + - name: Generate stub headers + # Individual shadow headers that source files #include are generated + # here as thin wrappers so they never need to be committed. + # Truly empty stubs are created with touch. + # C++ shim definitions for gdial.cpp live in + # tests/L1Tests/stubs/gdial_cpp_test_stubs.hpp and are included by + # lightweight wrappers generated here. + run: | + STUBS="$GITHUB_WORKSPACE/xdialserver/tests/L1Tests/stubs" + WRAPPER='#pragma once' + CPP_WRAPPER='#pragma once\n#include "gdial_cpp_test_stubs.hpp"' + + # Create stubs directory if it doesn't exist + mkdir -p "$STUBS" + + # Shadow headers: block the real headers and redirect to the + # combined stub. + for f in \ + libIBus.h libIARMCore.h; do + printf "$WRAPPER\n" > "$STUBS/$f" + done + + # Empty stubs: headers from WPEFramework and IARM that are unused + # in the test paths. + touch \ + "$STUBS/libIARMBus.h" \ + "$STUBS/libIBusDaemon.h" \ + "$STUBS/libIARMUtil.h" + + # gdial.cpp wrappers: keep source definitions in a committed mock + # file and generate only include shims here. + mkdir -p "$STUBS/com" "$STUBS/json" "$STUBS/securityagent" + for f in core.h plugins.h securityagent/SecurityTokenUtil.h; do + printf "$CPP_WRAPPER\n" > "$STUBS/$f" + done + + # Truly empty stubs for unused headers. + touch \ + "$STUBS/com/Ids.h" \ + "$STUBS/json/JsonData_Netflix.h" \ + "$STUBS/json/JsonData_StateControl.h" + + - name: Build xdialserver L1 tests + run: | + cd "$GITHUB_WORKSPACE/xdialserver" + autoreconf -if + + # Compiler / linker flags for coverage instrumentation + export CFLAGS="-fprofile-arcs -ftest-coverage -g -O0" + export CXXFLAGS="-fprofile-arcs -ftest-coverage -g -O0" + export LDFLAGS="--coverage" + + # Tell pkg-config / the build where to find googletest + export PKG_CONFIG_PATH="$GITHUB_WORKSPACE/install/usr/lib/pkgconfig:${PKG_CONFIG_PATH:-}" + export CPPFLAGS="-I$GITHUB_WORKSPACE/install/usr/include" + export LIBRARY_PATH="$GITHUB_WORKSPACE/install/usr/lib:${LIBRARY_PATH:-}" + + ./configure --enable-l1tests + # TESTFRAMEWORK_DIR="$GITHUB_WORKSPACE/entservices-testframework" + make -C tests/L1Tests + + - name: Run unit tests without valgrind + run: | + cd "$GITHUB_WORKSPACE/xdialserver/tests/L1Tests" + ./run_L1Tests --gtest_output="json:$GITHUB_WORKSPACE/xdialserverL1TestResults.json" + cp "$GITHUB_WORKSPACE/xdialserverL1TestResults.json" \ + "$GITHUB_WORKSPACE/xdialserverL1TestResultsWithoutValgrind.json" + + - name: Run unit tests with valgrind + if: ${{ !env.ACT }} + run: | + cd "$GITHUB_WORKSPACE/xdialserver/tests/L1Tests" + valgrind \ + --tool=memcheck \ + --log-file="$GITHUB_WORKSPACE/valgrind_log" \ + --leak-check=yes \ + --show-reachable=yes \ + --track-fds=yes \ + --fair-sched=try \ + ./run_L1Tests --gtest_output="json:$GITHUB_WORKSPACE/xdialserverL1TestResultsWithValgrind.json" + + - name: Generate coverage + if: ${{ !env.ACT }} + run: | + lcov -c \ + -o coverage.info \ + -d "$GITHUB_WORKSPACE/xdialserver" + + lcov -r coverage.info \ + '/usr/include/*' \ + '*/install/usr/include/*' \ + '*/googletest/*' \ + '*/entservices-testframework/*' \ + '*/tests/*' \ + '*/mocks/*' \ + '*/stubs/*' \ + -o filtered_coverage.info + + genhtml \ + -o coverage \ + -t "xdialserver L1 coverage" \ + filtered_coverage.info + + - name: Upload artifacts + if: ${{ !env.ACT }} + uses: actions/upload-artifact@v4 + with: + name: artifacts-L1-xdialserver + path: | + coverage/ + valgrind_log + xdialserverL1TestResultsWithoutValgrind.json + xdialserverL1TestResultsWithValgrind.json + if-no-files-found: warn diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 00000000..55aaab31 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,20 @@ +########################################################################## +# If not stated otherwise in this file or this component's LICENSE +# file the following copyright and licenses apply: +# +# Copyright 2019 RDK Management +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +########################################################################## + +SUBDIRS = tests diff --git a/configure.ac b/configure.ac new file mode 100644 index 00000000..88d83336 --- /dev/null +++ b/configure.ac @@ -0,0 +1,72 @@ +########################################################################## +# If not stated otherwise in this file or this component's LICENSE +# file the following copyright and licenses apply: +# +# Copyright 2019 RDK Management +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +########################################################################## + +AC_INIT([xdialserver], [1.0.0]) +AM_INIT_AUTOMAKE([-Wall -Werror foreign subdir-objects]) + +AC_PROG_CC +AC_PROG_CXX +AC_PROG_RANLIB +AM_PROG_AR + +# Check for pkg-config +PKG_CHECK_MODULES([GLIB], [glib-2.0]) +PKG_CHECK_MODULES([GIO], [gio-2.0]) + +# Check for DIAL protocol dependencies +PKG_CHECK_MODULES([GSSDP], [gssdp-1.2], + [], + [PKG_CHECK_MODULES([GSSDP], [gssdp-1.0], + [], + [AC_MSG_WARN([gssdp library not found])])]) + +PKG_CHECK_MODULES([SOUP], [libsoup-2.4], + [], + [PKG_CHECK_MODULES([SOUP], [libsoup-3.0], + [], + [AC_MSG_WARN([libsoup library not found])])]) + +PKG_CHECK_MODULES([XML2], [libxml-2.0]) + +# Optional RDK-specific packages +PKG_CHECK_MODULES([WPEFRAMEWORK], [WPEFramework], [], + [AC_MSG_WARN([WPEFramework development headers not found - platform tests may be limited])]) +PKG_CHECK_MODULES([IARMBUS], [libIARMBus], [], + [AC_MSG_WARN([libIARMBus development headers not found - IARM tests may be limited])]) + +# L1 Tests support +AC_ARG_ENABLE([l1tests], + [AS_HELP_STRING([--enable-l1tests], [Enable L1 unit tests])], + [enable_l1tests="$enableval"], + [enable_l1tests="no"]) + +AM_CONDITIONAL([ENABLE_L1TESTS], [test "x$enable_l1tests" = "xyes"]) + +if test "x$enable_l1tests" = "xyes"; then + PKG_CHECK_MODULES([GTEST], [gtest], [], [AC_MSG_WARN([gtest not found in pkg-config])]) + PKG_CHECK_MODULES([GMOCK], [gmock], [], [AC_MSG_WARN([gmock not found in pkg-config])]) +fi + +AC_CONFIG_FILES([ + Makefile + tests/Makefile + tests/L1Tests/Makefile +]) + +AC_OUTPUT diff --git a/tests/L1Tests/Makefile.am b/tests/L1Tests/Makefile.am new file mode 100644 index 00000000..c74d0e21 --- /dev/null +++ b/tests/L1Tests/Makefile.am @@ -0,0 +1,96 @@ +########################################################################## +# If not stated otherwise in this file or this component's LICENSE +# file the following copyright and licenses apply: +# +# Copyright 2024 RDK Management +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +########################################################################## + +# Include paths: +# - tests/L1Tests/stubs : shadow headers that redirect to test stubs +# - tests/L1Tests/mocks : shared mock implementations +# - server/include : xdialserver public API headers +# - server : xdialserver implementation headers +# - server/plat : platform-specific headers +# +# Note: WPEFramework and IARM bus headers are optional and mocked in the test +# environment. The test stubs provide mock implementations, so the actual +# development packages (libwpeframework-dev, libiarmbus-dev) are not required +# for basic testing. + +AM_CPPFLAGS = -I$(top_srcdir)/tests/L1Tests/stubs \ + -I$(top_srcdir)/tests/L1Tests/mocks \ + -I$(top_srcdir)/server/include \ + -I$(top_srcdir)/server \ + -I$(top_srcdir)/server/plat \ + -DGDIAL_STATIC=extern \ + -DGDIAL_STATIC_INLINE=extern \ + -D_GNU_SOURCE \ + -DHAVE_GSSDP_VERSION_1_2_OR_NEWER \ + $(GLIB_CFLAGS) \ + $(GIO_CFLAGS) \ + $(GSSDP_CFLAGS) \ + $(SOUP_CFLAGS) \ + $(XML2_CFLAGS) + +AM_CXXFLAGS = -Wall -std=c++14 -g + +AM_LDFLAGS = -pthread + +# ---------- Test binary ---------- +noinst_PROGRAMS = run_L1Tests + +# New test checklist: +# 1) Add new test_*.cpp to run_L1Tests_SOURCES below. +# 2) Keep tests in the matching folder (server/, plat/, utils/). +# 3) If new stubs are required, add them under tests/L1Tests/stubs and/or +# tests/L1Tests/mocks, and ensure CI wrapper generation covers them. +# 4) For modules with static/global state, add explicit fixture cleanup to +# avoid cross-test contamination in the single-process run_L1Tests binary. +run_L1Tests_SOURCES = \ + test_main.cpp \ + server/test_gdialServer.cpp \ + server/test_gdialService.cpp \ + server/test_gdialserver_ut.cpp \ + server/test_gdialRest.cpp \ + server/test_gdialShield.cpp \ + server/test_gdialSsdp.cpp \ + mocks/gssdp_mock.c \ + plat/test_gdialPlat.cpp \ + plat/test_gdialAppCache.cpp \ + plat/test_gdialCpp.cpp \ + plat/test_gdialPlatApp.cpp \ + plat/test_gdialPlatDev.cpp \ + plat/test_gdialPlatUtil.cpp \ + utils/test_gdialUtil.cpp \ + plat/gdial_os_stubs.cpp \ + $(top_srcdir)/server/gdialservice.cpp \ + $(top_srcdir)/server/gdial-util.c \ + $(top_srcdir)/server/gdial-app.c \ + $(top_srcdir)/server/gdial-rest.c \ + $(top_srcdir)/server/gdial-shield.c \ + $(top_srcdir)/server/gdial-ssdp.c \ + $(top_srcdir)/server/plat/gdial-plat-app.c \ + $(top_srcdir)/server/plat/gdial-plat-dev.c \ + $(top_srcdir)/server/plat/gdial-plat-util.c \ + $(top_srcdir)/server/plat/gdialappcache.cpp \ + $(top_srcdir)/server/plat/gdial_app_registry.c + +run_L1Tests_LDADD = -lgtest -lgmock $(GLIB_LIBS) $(GIO_LIBS) $(SOUP_LIBS) $(XML2_LIBS) -luuid + +# Run tests on 'make check' +TESTS = run_L1Tests + +# Clean up test artifacts +CLEANFILES = *.log *.trs diff --git a/tests/L1Tests/mocks/gssdp_mock.c b/tests/L1Tests/mocks/gssdp_mock.c new file mode 100644 index 00000000..c8406b64 --- /dev/null +++ b/tests/L1Tests/mocks/gssdp_mock.c @@ -0,0 +1,77 @@ +/* + * Minimal GSSDP mocks for L1 tests. + * These avoid runtime dependency on host network/GSSDP behavior while + * allowing gdial-ssdp.c code paths to execute for coverage. + */ + +#include +#include + +#ifndef HAVE_GSSDP_VERSION_1_2_OR_NEWER +GSSDPClient *gssdp_client_new(GMainContext *main_context, const char *iface, GError **error) +{ + (void)main_context; + (void)iface; + if (error) { + *error = NULL; + } + return (GSSDPClient *)g_object_new(G_TYPE_OBJECT, NULL); +} +#else +GSSDPClient *gssdp_client_new(const char *iface, GError **error) +{ + (void)iface; + if (error) { + *error = NULL; + } + return (GSSDPClient *)g_object_new(G_TYPE_OBJECT, NULL); +} +#endif + +void gssdp_client_append_header(GSSDPClient *client, const char *name, const char *value) +{ + (void)client; + (void)name; + (void)value; +} + +void gssdp_client_remove_header(GSSDPClient *client, const char *name) +{ + (void)client; + (void)name; +} + +void gssdp_client_clear_headers(GSSDPClient *client) +{ + (void)client; +} + +GSSDPResourceGroup *gssdp_resource_group_new(GSSDPClient *client) +{ + (void)client; + return (GSSDPResourceGroup *)g_object_new(G_TYPE_OBJECT, NULL); +} + +guint gssdp_resource_group_add_resource_simple(GSSDPResourceGroup *resource_group, + const char *target, + const char *usn, + const char *location) +{ + (void)resource_group; + (void)target; + (void)usn; + (void)location; + return 1; +} + +void gssdp_resource_group_set_available(GSSDPResourceGroup *resource_group, gboolean available) +{ + (void)resource_group; + (void)available; +} + +void gssdp_resource_group_remove_resource(GSSDPResourceGroup *resource_group, guint resource_id) +{ + (void)resource_group; + (void)resource_id; +} diff --git a/tests/L1Tests/plat/gdial_os_stubs.cpp b/tests/L1Tests/plat/gdial_os_stubs.cpp new file mode 100644 index 00000000..38c012dc --- /dev/null +++ b/tests/L1Tests/plat/gdial_os_stubs.cpp @@ -0,0 +1,268 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2024 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file gdial_os_stubs.cpp + * + * Stubs for the layer *below* gdial-plat-app.c: + * - gdial_os_application_* (gdial-os-app.h) + * - gdial_init / gdial_term / gdial_register_* (gdial.hpp) + * + * Also provides two gdial_plat_application_*_async symbols that are declared in + * gdial-plat-app.h but intentionally absent from gdial-plat-app.c: + * - gdial_plat_application_hide_async + * - gdial_plat_application_resume_async + * + * The gdial_plat_stub_reset_behavior / _set_app_state / _set_errors control + * interface is preserved at this layer so that any future test code that uses + * those helpers continues to work. + */ + +#include + +#include "gdial-app.h" /* GDialAppState, GDialAppError */ +#include "gdial_app_registry.h" +#include "gdial-os-app.h" +#include "gdial-plat-app.h" /* hide_async / resume_async declarations */ +#include "gdialservicecommon.h" +#include "gdial.hpp" + +/* ------------------------------------------------------------------ */ +/* Injectable error / state controls */ +/* ------------------------------------------------------------------ */ + +static GDialAppState s_app_state = GDIAL_APP_STATE_STOPPED; +static int s_start_err = 0; +static int s_hide_err = 0; +static int s_resume_err = 0; +static int s_stop_err = 0; +static int s_state_err = 0; +static gdial_registerapps_cb s_registerapps_cb = nullptr; + +static GDialAppRegistry *create_registry_from_entry(const RegisterAppEntry *entry) +{ + if (!entry || entry->Names.empty()) { + return nullptr; + } + + GList *app_prefixes = nullptr; + GList *allowed_origins = nullptr; + GHashTable *properties = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + const char *allow_stop = entry->allowStop ? "true" : "false"; + + if (!entry->prefixes.empty()) { + app_prefixes = g_list_prepend(app_prefixes, g_strdup(entry->prefixes.c_str())); + } + if (!entry->cors.empty()) { + allowed_origins = g_list_prepend(allowed_origins, g_strdup(entry->cors.c_str())); + } + g_hash_table_insert(properties, g_strdup("allowStop"), g_strdup(allow_stop)); + + return gdial_app_registry_new( + entry->Names.c_str(), + app_prefixes, + properties, + TRUE, + TRUE, + allowed_origins); +} + +static GList *build_app_registry_list(const RegisterAppEntryList *app_list) +{ + if (!app_list) { + return nullptr; + } + + GList *g_app_list = nullptr; + for (const RegisterAppEntry *entry : app_list->getValues()) { + GDialAppRegistry *registry = create_registry_from_entry(entry); + if (registry) { + g_app_list = g_list_append(g_app_list, registry); + } + } + return g_app_list; +} + +static void free_app_registry_list_nodes(GList *g_app_list) +{ + // Ownership of GDialAppRegistry entries is transferred to callback consumers + // (same semantics as production gdial.cpp). Only free the list nodes here. + g_list_free(g_app_list); +} + +extern "C" void gdial_plat_stub_reset_behavior(void) +{ + s_app_state = GDIAL_APP_STATE_STOPPED; + s_start_err = 0; + s_hide_err = 0; + s_resume_err = 0; + s_stop_err = 0; + s_state_err = 0; + s_registerapps_cb = nullptr; +} + +extern "C" void gdial_plat_stub_set_app_state(GDialAppState state) +{ + s_app_state = state; +} + +extern "C" void gdial_plat_stub_set_errors( + GDialAppError start_err, + GDialAppError hide_err, + GDialAppError resume_err, + GDialAppError stop_err, + GDialAppError state_err) +{ + s_start_err = (int)start_err; + s_hide_err = (int)hide_err; + s_resume_err = (int)resume_err; + s_stop_err = (int)stop_err; + s_state_err = (int)state_err; +} + +/* ------------------------------------------------------------------ */ +/* gdial.hpp — GLib integration layer called by gdial-plat-app.c */ +/* ------------------------------------------------------------------ */ + +bool gdial_init(GMainContext *context) { (void)context; return true; } +void gdial_term(void) {} + +void gdial_register_activation_cb(gdial_activation_cb cb) { (void)cb; } +void gdial_register_friendlyname_cb(gdial_friendlyname_cb cb) { (void)cb; } +void gdial_register_registerapps_cb(gdial_registerapps_cb cb) { s_registerapps_cb = cb; } +void gdial_register_manufacturername_cb(gdial_manufacturername_cb cb) { (void)cb; } +void gdial_register_modelname_cb(gdial_manufacturername_cb cb) { (void)cb; } + +/* ------------------------------------------------------------------ */ +/* gdial_os_application_* — one level below gdial-plat-app.c */ +/* ------------------------------------------------------------------ */ + +int gdial_os_application_start( + const char *name, const char *payload, + const char *query, const char *url, int *instance_id) +{ + (void)name; (void)payload; (void)query; (void)url; + if (s_start_err) return s_start_err; + if (instance_id) *instance_id = 1; + return 0; +} + +int gdial_os_application_hide(const char *name, int id) +{ + (void)name; (void)id; + return s_hide_err; +} + +int gdial_os_application_resume(const char *name, int id) +{ + (void)name; (void)id; + return s_resume_err; +} + +int gdial_os_application_stop(const char *name, int id) +{ + (void)name; (void)id; + return s_stop_err; +} + +int gdial_os_application_state(const char *name, int id, GDialAppState *state) +{ + (void)name; (void)id; + if (s_state_err) return s_state_err; + if (state) *state = s_app_state; + return 0; +} + +int gdial_os_application_state_changed( + const char *name, const char *id, const char *state, const char *error) +{ + (void)name; (void)id; (void)state; (void)error; + return 0; +} + +int gdial_os_application_activation_changed( + const char *activation, const char *friendly) +{ + (void)activation; (void)friendly; + return 0; +} + +int gdial_os_application_friendlyname_changed(const char *name) +{ + (void)name; + return 0; +} + +const char *gdial_os_application_get_protocol_version(void) +{ + return "2.2.1"; +} + +int gdial_os_application_register_applications(void *p) +{ + if (s_registerapps_cb && p) { + const RegisterAppEntryList *app_config_list = static_cast(p); + GList *g_app_list = build_app_registry_list(app_config_list); + s_registerapps_cb(g_app_list); + free_app_registry_list_nodes(g_app_list); + } + return 0; +} + +void gdial_os_application_update_network_standby_mode(gboolean mode) +{ + (void)mode; +} + +int gdial_os_application_update_manufacturer_name(const char *name) +{ + (void)name; + return 0; +} + +int gdial_os_application_update_model_name(const char *model) +{ + (void)model; + return 0; +} + +int gdial_os_application_service_notification(gboolean req, void *notifier) +{ + (void)req; (void)notifier; + return 0; +} + +/* ------------------------------------------------------------------ */ +/* Async stubs — declared in gdial-plat-app.h but absent from */ +/* gdial-plat-app.c */ +/* ------------------------------------------------------------------ */ + +void *gdial_plat_application_hide_async( + const gchar *name, gint id, void *user_data) +{ + (void)name; (void)id; (void)user_data; + return nullptr; +} + +void *gdial_plat_application_resume_async( + const gchar *name, gint id, void *user_data) +{ + (void)name; (void)id; (void)user_data; + return nullptr; +} diff --git a/tests/L1Tests/plat/test_gdialAppCache.cpp b/tests/L1Tests/plat/test_gdialAppCache.cpp new file mode 100644 index 00000000..799ef0da --- /dev/null +++ b/tests/L1Tests/plat/test_gdialAppCache.cpp @@ -0,0 +1,63 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2026 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +#include "gdialappcache.hpp" + +class GDialAppCacheTest : public ::testing::Test { +protected: + GDialAppStatusCache cache; + std::string original_netflix_id; + std::string original_youtube_id; + + void SetUp() override + { + original_netflix_id = cache.getAppCacheId("Netflix"); + original_youtube_id = cache.getAppCacheId("YouTube"); + } + + void TearDown() override + { + // Restore static cache IDs so tests remain isolated. + cache.setAppCacheId("Netflix", original_netflix_id); + cache.setAppCacheId("YouTube", original_youtube_id); + } +}; + +TEST_F(GDialAppCacheTest, SetAppCacheId_UpdatesNetflixId) +{ + cache.setAppCacheId("Netflix", "DialNetflix_L1"); + EXPECT_EQ(cache.getAppCacheId("Netflix"), "DialNetflix_L1"); +} + +TEST_F(GDialAppCacheTest, SetAppCacheId_UpdatesYoutubeId) +{ + cache.setAppCacheId("YouTube", "DialYouTube_L1"); + EXPECT_EQ(cache.getAppCacheId("YouTube"), "DialYouTube_L1"); +} + +TEST_F(GDialAppCacheTest, SetAppCacheId_UnknownAppDoesNotChangeKnownIds) +{ + cache.setAppCacheId("UnknownApp", "Unknown_L1"); + EXPECT_EQ(cache.getAppCacheId("Netflix"), original_netflix_id); + EXPECT_EQ(cache.getAppCacheId("YouTube"), original_youtube_id); +} diff --git a/tests/L1Tests/plat/test_gdialCpp.cpp b/tests/L1Tests/plat/test_gdialCpp.cpp new file mode 100644 index 00000000..f23a5620 --- /dev/null +++ b/tests/L1Tests/plat/test_gdialCpp.cpp @@ -0,0 +1,494 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2026 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include + +extern "C" { +#include +#include "gdial-plat-dev.h" +} + +#include "gdialservicecommon.h" + +/* + * Pull real gdial.cpp into this test TU, but rename exported symbols to avoid + * collisions with gdial_os_stubs.cpp used by the rest of the L1 suite. + */ +#define gdial_register_activation_cb gdial_cpp_test_register_activation_cb +#define gdial_register_friendlyname_cb gdial_cpp_test_register_friendlyname_cb +#define gdial_register_registerapps_cb gdial_cpp_test_register_registerapps_cb +#define gdial_register_manufacturername_cb gdial_cpp_test_register_manufacturername_cb +#define gdial_register_modelname_cb gdial_cpp_test_register_modelname_cb +#define gdial_init gdial_cpp_test_init +#define gdial_term gdial_cpp_test_term +#define parse_query gdial_cpp_test_parse_query +#define gdial_os_application_start gdial_cpp_test_os_application_start +#define gdial_os_application_stop gdial_cpp_test_os_application_stop +#define gdial_os_application_hide gdial_cpp_test_os_application_hide +#define gdial_os_application_resume gdial_cpp_test_os_application_resume +#define gdial_os_application_state gdial_cpp_test_os_application_state +#define gdial_os_application_state_changed gdial_cpp_test_os_application_state_changed +#define gdial_os_application_activation_changed gdial_cpp_test_os_application_activation_changed +#define gdial_os_application_friendlyname_changed gdial_cpp_test_os_application_friendlyname_changed +#define gdial_os_application_get_protocol_version gdial_cpp_test_os_application_get_protocol_version +#define gdial_os_application_register_applications gdial_cpp_test_os_application_register_applications +#define gdial_os_application_update_network_standby_mode gdial_cpp_test_os_application_update_network_standby_mode +#define gdial_os_application_update_manufacturer_name gdial_cpp_test_os_application_update_manufacturer_name +#define gdial_os_application_update_model_name gdial_cpp_test_os_application_update_model_name +#define gdial_os_application_service_notification gdial_cpp_test_os_application_service_notification +#include "../../../server/plat/gdial.cpp" +#undef gdial_register_activation_cb +#undef gdial_register_friendlyname_cb +#undef gdial_register_registerapps_cb +#undef gdial_register_manufacturername_cb +#undef gdial_register_modelname_cb +#undef gdial_init +#undef gdial_term +#undef parse_query +#undef gdial_os_application_start +#undef gdial_os_application_stop +#undef gdial_os_application_hide +#undef gdial_os_application_resume +#undef gdial_os_application_state +#undef gdial_os_application_state_changed +#undef gdial_os_application_activation_changed +#undef gdial_os_application_friendlyname_changed +#undef gdial_os_application_get_protocol_version +#undef gdial_os_application_register_applications +#undef gdial_os_application_update_network_standby_mode +#undef gdial_os_application_update_manufacturer_name +#undef gdial_os_application_update_model_name +#undef gdial_os_application_service_notification + +namespace { + +static int g_power_cb_calls = 0; +static std::string g_last_power_state; +static int g_manufacturer_cb_calls = 0; +static int g_model_cb_calls = 0; +static int g_activation_cb_calls = 0; +static bool g_last_activation_state = false; +static std::string g_last_activation_friendlyname; +static int g_friendlyname_cb_calls = 0; +static std::string g_last_friendlyname; +static int g_registerapps_cb_calls = 0; +static gpointer g_last_registerapps_payload = nullptr; +static int g_nwstandby_cb_calls = 0; +static bool g_last_nwstandby_mode = false; + +static void test_power_cb(const char *state) +{ + ++g_power_cb_calls; + g_last_power_state = state ? state : ""; +} + +static void test_manufacturer_cb(const char *) +{ + ++g_manufacturer_cb_calls; +} + +static void test_model_cb(const char *) +{ + ++g_model_cb_calls; +} + +static void test_activation_cb(bool state, const gchar *friendlyname) +{ + ++g_activation_cb_calls; + g_last_activation_state = state; + g_last_activation_friendlyname = friendlyname ? friendlyname : ""; +} + +static void test_friendlyname_cb(const gchar *friendlyname) +{ + ++g_friendlyname_cb_calls; + g_last_friendlyname = friendlyname ? friendlyname : ""; +} + +static void test_registerapps_cb(gpointer payload) +{ + ++g_registerapps_cb_calls; + g_last_registerapps_payload = payload; +} + +static void test_nwstandby_cb(const bool mode) +{ + ++g_nwstandby_cb_calls; + g_last_nwstandby_mode = mode; +} + +class DummyNotifier : public GDialNotifier { +public: + int launch_calls = 0; + int launch_with_params_calls = 0; + int stop_calls = 0; + int hide_calls = 0; + int resume_calls = 0; + int state_calls = 0; + + void onApplicationLaunchRequest(std::string, std::string) override + { + ++launch_calls; + } + void onApplicationLaunchRequestWithLaunchParam(std::string, std::string, std::string, std::string) override + { + ++launch_with_params_calls; + } + void onApplicationStopRequest(std::string, std::string) override + { + ++stop_calls; + } + void onApplicationHideRequest(std::string, std::string) override + { + ++hide_calls; + } + void onApplicationResumeRequest(std::string, std::string) override + { + ++resume_calls; + } + void onApplicationStateRequest(std::string, std::string) override + { + ++state_calls; + } + void updatePowerState(std::string) override {} +}; + +class GDialCppTest : public ::testing::Test { +protected: + GMainContext *ctx = nullptr; + + void SetUp() override + { + ctx = g_main_context_new(); + g_power_cb_calls = 0; + g_last_power_state.clear(); + g_manufacturer_cb_calls = 0; + g_model_cb_calls = 0; + g_activation_cb_calls = 0; + g_last_activation_state = false; + g_last_activation_friendlyname.clear(); + g_friendlyname_cb_calls = 0; + g_last_friendlyname.clear(); + g_registerapps_cb_calls = 0; + g_last_registerapps_payload = nullptr; + g_nwstandby_cb_calls = 0; + g_last_nwstandby_mode = false; + + unsetenv("SYSTEM_SLEEP_REQUEST_KEY"); + unsetenv("ENABLE_NETFLIX_STOP"); + + gdail_plat_dev_register_powerstate_cb(test_power_cb); + gdail_plat_dev_register_nwstandbymode_cb(test_nwstandby_cb); + + gdial_cpp_test_register_activation_cb(nullptr); + gdial_cpp_test_register_friendlyname_cb(nullptr); + gdial_cpp_test_register_registerapps_cb(nullptr); + gdial_cpp_test_register_manufacturername_cb(nullptr); + gdial_cpp_test_register_modelname_cb(nullptr); + } + + void TearDown() override + { + gdail_plat_dev_register_powerstate_cb(nullptr); + gdail_plat_dev_register_nwstandbymode_cb(nullptr); + + gdial_cpp_test_register_activation_cb(nullptr); + gdial_cpp_test_register_friendlyname_cb(nullptr); + gdial_cpp_test_register_registerapps_cb(nullptr); + gdial_cpp_test_register_manufacturername_cb(nullptr); + gdial_cpp_test_register_modelname_cb(nullptr); + + unsetenv("SYSTEM_SLEEP_REQUEST_KEY"); + unsetenv("ENABLE_NETFLIX_STOP"); + + gdial_cpp_test_term(); + if (ctx) { + g_main_context_unref(ctx); + ctx = nullptr; + } + } +}; + +} // namespace + +TEST_F(GDialCppTest, ParseQuery_NullReturnsEmptyMap) +{ + std::map out = gdial_cpp_test_parse_query(nullptr); + EXPECT_TRUE(out.empty()); +} + +TEST_F(GDialCppTest, ParseQuery_ValidKeyValuePairs) +{ + std::map out = gdial_cpp_test_parse_query("action=sleep&key=abc"); + ASSERT_EQ(out.size(), 2u); + EXPECT_EQ(out["action"], "sleep"); + EXPECT_EQ(out["key"], "abc"); +} + +TEST_F(GDialCppTest, ParseQuery_InvalidEscapeFallsBackToRaw) +{ + std::map out = gdial_cpp_test_parse_query("k=100%"); + ASSERT_EQ(out.size(), 1u); + EXPECT_EQ(out["k"], "100%"); +} + +TEST_F(GDialCppTest, InitAndTerm_Idempotent) +{ + EXPECT_TRUE(gdial_cpp_test_init(ctx)); + EXPECT_TRUE(gdial_cpp_test_init(ctx)); + gdial_cpp_test_term(); + /* Second term after cleanup is also safe. */ + gdial_cpp_test_term(); +} + +TEST_F(GDialCppTest, OsGetProtocolVersion_WhenUninitializedReturnsDefault) +{ + const char *ver = gdial_cpp_test_os_application_get_protocol_version(); + ASSERT_NE(ver, nullptr); + EXPECT_STREQ(ver, GDIAL_PROTOCOL_VERSION_STR); +} + +TEST_F(GDialCppTest, OsServiceNotification_UninitializedReturnsInternal) +{ + DummyNotifier n; + EXPECT_EQ(gdial_cpp_test_os_application_service_notification(TRUE, &n), GDIAL_APP_ERROR_INTERNAL); +} + +TEST_F(GDialCppTest, OsServiceNotification_AfterInitReturnsNone) +{ + DummyNotifier n; + ASSERT_TRUE(gdial_cpp_test_init(ctx)); + EXPECT_EQ(gdial_cpp_test_os_application_service_notification(TRUE, &n), GDIAL_APP_ERROR_NONE); + EXPECT_EQ(gdial_cpp_test_os_application_service_notification(FALSE, nullptr), GDIAL_APP_ERROR_NONE); +} + +TEST_F(GDialCppTest, OsUpdateManufacturerName_NullAndUninitializedReturnInternal) +{ + EXPECT_EQ(gdial_cpp_test_os_application_update_manufacturer_name(nullptr), GDIAL_CAST_ERROR_INTERNAL); + EXPECT_EQ(gdial_cpp_test_os_application_update_manufacturer_name("Acme"), GDIAL_CAST_ERROR_INTERNAL); +} + +TEST_F(GDialCppTest, OsUpdateModelName_NullAndUninitializedReturnInternal) +{ + EXPECT_EQ(gdial_cpp_test_os_application_update_model_name(nullptr), GDIAL_CAST_ERROR_INTERNAL); + EXPECT_EQ(gdial_cpp_test_os_application_update_model_name("ModelX"), GDIAL_CAST_ERROR_INTERNAL); +} + +TEST_F(GDialCppTest, OsUpdateManufacturerAndModel_AfterInitReturnNone) +{ + ASSERT_TRUE(gdial_cpp_test_init(ctx)); + gdial_cpp_test_register_manufacturername_cb(test_manufacturer_cb); + gdial_cpp_test_register_modelname_cb(test_model_cb); + + EXPECT_EQ(gdial_cpp_test_os_application_update_manufacturer_name("Acme"), GDIAL_CAST_ERROR_NONE); + EXPECT_EQ(gdial_cpp_test_os_application_update_model_name("ModelX"), GDIAL_CAST_ERROR_NONE); + EXPECT_EQ(g_manufacturer_cb_calls, 1); + EXPECT_EQ(g_model_cb_calls, 1); +} + +TEST_F(GDialCppTest, OsApplicationStart_SystemSleepTriggersPowerOff) +{ + ASSERT_TRUE(gdial_cpp_test_init(ctx)); + int instance_id = 0; + EXPECT_EQ(gdial_cpp_test_os_application_start("system", "", "action=sleep", "", &instance_id), GDIAL_APP_ERROR_NONE); + EXPECT_EQ(g_power_cb_calls, 1); + EXPECT_EQ(g_last_power_state, "STANDBY"); +} + +TEST_F(GDialCppTest, OsApplicationStart_SystemTogglePowerTriggersToggle) +{ + ASSERT_TRUE(gdial_cpp_test_init(ctx)); + int instance_id = 0; + EXPECT_EQ(gdial_cpp_test_os_application_start("system", "", "action=togglepower", "", &instance_id), GDIAL_APP_ERROR_NONE); + EXPECT_EQ(g_power_cb_calls, 1); + EXPECT_EQ(g_last_power_state, "TOGGLE"); +} + +TEST_F(GDialCppTest, OsApplicationState_SystemReturnsHide) +{ + GDialAppState state = GDIAL_APP_STATE_MAX; + EXPECT_EQ(gdial_cpp_test_os_application_state("system", 1, &state), GDIAL_APP_ERROR_NONE); + EXPECT_EQ(state, GDIAL_APP_STATE_HIDE); +} + +TEST_F(GDialCppTest, OsApplicationStart_SystemSleepWithWrongKeyReturnsInternal) +{ + ASSERT_TRUE(gdial_cpp_test_init(ctx)); + setenv("SYSTEM_SLEEP_REQUEST_KEY", "expected", 1); + int instance_id = 0; + EXPECT_EQ(gdial_cpp_test_os_application_start("system", "", "action=sleep&key=wrong", "", &instance_id), + GDIAL_APP_ERROR_INTERNAL); +} + +TEST_F(GDialCppTest, OsApplicationStart_SystemToggleWithWrongKeyReturnsInternal) +{ + ASSERT_TRUE(gdial_cpp_test_init(ctx)); + setenv("SYSTEM_SLEEP_REQUEST_KEY", "expected", 1); + int instance_id = 0; + EXPECT_EQ(gdial_cpp_test_os_application_start("system", "", "action=togglepower&key=wrong", "", &instance_id), + GDIAL_APP_ERROR_INTERNAL); +} + +TEST_F(GDialCppTest, OsApplicationStart_NonSystemWithNotifierLaunchesWithParams) +{ + DummyNotifier n; + ASSERT_TRUE(gdial_cpp_test_init(ctx)); + ASSERT_EQ(gdial_cpp_test_os_application_service_notification(TRUE, &n), GDIAL_APP_ERROR_NONE); + + int instance_id = 0; + EXPECT_EQ(gdial_cpp_test_os_application_start("Netflix", "payload", "k=v", "url", &instance_id), + GDIAL_APP_ERROR_NONE); + EXPECT_EQ(n.launch_with_params_calls, 1); +} + +TEST_F(GDialCppTest, OsApplicationStateChanged_InitializedReturnsNoneAndUpdatesState) +{ + ASSERT_TRUE(gdial_cpp_test_init(ctx)); + + EXPECT_EQ(gdial_cpp_test_os_application_state_changed("App", "id", "running", "none"), GDIAL_APP_ERROR_NONE); + + GDialAppState state = GDIAL_APP_STATE_MAX; + EXPECT_EQ(gdial_cpp_test_os_application_state("App", 1, &state), GDIAL_APP_ERROR_NONE); + EXPECT_EQ(state, GDIAL_APP_STATE_RUNNING); +} + +TEST_F(GDialCppTest, OsApplicationState_MapsHiddenAndStopped) +{ + ASSERT_TRUE(gdial_cpp_test_init(ctx)); + + EXPECT_EQ(gdial_cpp_test_os_application_state_changed("App", "id", "hidden", "none"), GDIAL_APP_ERROR_NONE); + GDialAppState state = GDIAL_APP_STATE_MAX; + EXPECT_EQ(gdial_cpp_test_os_application_state("App", 1, &state), GDIAL_APP_ERROR_NONE); + EXPECT_EQ(state, GDIAL_APP_STATE_HIDE); + + EXPECT_EQ(gdial_cpp_test_os_application_state_changed("App", "id", "stopped", "none"), GDIAL_APP_ERROR_NONE); + EXPECT_EQ(gdial_cpp_test_os_application_state("App", 1, &state), GDIAL_APP_ERROR_NONE); + EXPECT_EQ(state, GDIAL_APP_STATE_STOPPED); +} + +TEST_F(GDialCppTest, OsApplicationHideResumeStop_NonSystemPaths) +{ + DummyNotifier n; + ASSERT_TRUE(gdial_cpp_test_init(ctx)); + ASSERT_EQ(gdial_cpp_test_os_application_service_notification(TRUE, &n), GDIAL_APP_ERROR_NONE); + + /* Running -> hide succeeds and notifies observer. */ + ASSERT_EQ(gdial_cpp_test_os_application_state_changed("App", "id", "running", "none"), GDIAL_APP_ERROR_NONE); + EXPECT_EQ(gdial_cpp_test_os_application_hide("App", 7), GDIAL_APP_ERROR_NONE); + EXPECT_EQ(n.hide_calls, 1); + + /* Running -> resume returns bad request per implementation. */ + EXPECT_EQ(gdial_cpp_test_os_application_resume("App", 7), GDIAL_APP_ERROR_BAD_REQUEST); + + /* Stop path currently always issues request (failsafe strategy). */ + EXPECT_EQ(gdial_cpp_test_os_application_stop("App", 7), GDIAL_APP_ERROR_NONE); + EXPECT_EQ(n.stop_calls, 1); +} + +TEST_F(GDialCppTest, OsApplicationRegisterApplications_InitializedInvokesCallback) +{ + ASSERT_TRUE(gdial_cpp_test_init(ctx)); + gdial_cpp_test_register_registerapps_cb(test_registerapps_cb); + + auto *list = new RegisterAppEntryList; + auto *entry = new RegisterAppEntry; + entry->Names = "YouTube"; + entry->prefixes = "com.google"; + entry->cors = ".youtube.com"; + entry->allowStop = true; + list->pushBack(entry); + + EXPECT_EQ(gdial_cpp_test_os_application_register_applications(list), GDIAL_APP_ERROR_NONE); + EXPECT_EQ(g_registerapps_cb_calls, 1); + EXPECT_NE(g_last_registerapps_payload, nullptr); +} + +TEST_F(GDialCppTest, OsApplicationActivationAndFriendlyName_InitializedCallbacks) +{ + ASSERT_TRUE(gdial_cpp_test_init(ctx)); + gdial_cpp_test_register_activation_cb(test_activation_cb); + gdial_cpp_test_register_friendlyname_cb(test_friendlyname_cb); + + EXPECT_EQ(gdial_cpp_test_os_application_activation_changed("true", "LivingRoom"), GDIAL_APP_ERROR_NONE); + EXPECT_EQ(g_activation_cb_calls, 1); + EXPECT_TRUE(g_last_activation_state); + EXPECT_EQ(g_last_activation_friendlyname, "LivingRoom"); + + EXPECT_EQ(gdial_cpp_test_os_application_activation_changed("false", "Kitchen"), GDIAL_APP_ERROR_NONE); + EXPECT_EQ(g_activation_cb_calls, 2); + EXPECT_FALSE(g_last_activation_state); + EXPECT_EQ(g_last_activation_friendlyname, "Kitchen"); + + EXPECT_EQ(gdial_cpp_test_os_application_friendlyname_changed("Bedroom"), GDIAL_APP_ERROR_NONE); + EXPECT_EQ(g_friendlyname_cb_calls, 1); + EXPECT_EQ(g_last_friendlyname, "Bedroom"); +} + +TEST_F(GDialCppTest, OsApplicationUpdateNetworkStandbyMode_InitializedInvokesDevCb) +{ + ASSERT_TRUE(gdial_cpp_test_init(ctx)); + + gdial_cpp_test_os_application_update_network_standby_mode(TRUE); + EXPECT_EQ(g_nwstandby_cb_calls, 1); + EXPECT_TRUE(g_last_nwstandby_mode); + + gdial_cpp_test_os_application_update_network_standby_mode(FALSE); + EXPECT_EQ(g_nwstandby_cb_calls, 2); + EXPECT_FALSE(g_last_nwstandby_mode); +} + +TEST_F(GDialCppTest, OsApplicationState_NetflixEnableStopBranchExecutes) +{ + ASSERT_TRUE(gdial_cpp_test_init(ctx)); + ASSERT_EQ(gdial_cpp_test_os_application_state_changed("Netflix", "id", "running", "none"), GDIAL_APP_ERROR_NONE); + setenv("ENABLE_NETFLIX_STOP", "true", 1); + + GDialAppState state = GDIAL_APP_STATE_MAX; + EXPECT_EQ(gdial_cpp_test_os_application_state("Netflix", 1, &state), GDIAL_APP_ERROR_NONE); + /* Stubbed GetCurrentState() returns empty string -> state forced to running. */ + EXPECT_EQ(state, GDIAL_APP_STATE_RUNNING); +} + +TEST_F(GDialCppTest, OsApplicationActivationChanged_UninitializedReturnsInternal) +{ + EXPECT_EQ(gdial_cpp_test_os_application_activation_changed("true", "TV"), GDIAL_APP_ERROR_INTERNAL); +} + +TEST_F(GDialCppTest, OsApplicationFriendlyNameChanged_UninitializedReturnsInternal) +{ + EXPECT_EQ(gdial_cpp_test_os_application_friendlyname_changed("LivingRoom"), GDIAL_APP_ERROR_INTERNAL); +} + +TEST_F(GDialCppTest, OsApplicationStateChanged_UninitializedReturnsInternal) +{ + EXPECT_EQ(gdial_cpp_test_os_application_state_changed("App", "id", "running", "none"), GDIAL_APP_ERROR_INTERNAL); +} + +TEST_F(GDialCppTest, OsApplicationRegisterApplications_UninitializedReturnsInternal) +{ + auto *list = new RegisterAppEntryList; + EXPECT_EQ(gdial_cpp_test_os_application_register_applications(list), GDIAL_APP_ERROR_INTERNAL); + /* Uninitialized path does not consume the list. */ + delete list; +} diff --git a/tests/L1Tests/plat/test_gdialPlat.cpp b/tests/L1Tests/plat/test_gdialPlat.cpp new file mode 100644 index 00000000..66b99318 --- /dev/null +++ b/tests/L1Tests/plat/test_gdialPlat.cpp @@ -0,0 +1,193 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2024 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file test_gdialPlat.cpp + * @brief Unit tests for GDialAppRegistry (server/plat/gdial_app_registry.c) + * + * Functions under test: + * gdial_app_registry_new - object construction + * gdial_app_registry_is_allowed_origin - origin allow-list logic + * gdial_app_regstry_dispose - resource cleanup + */ + +#include + +extern "C" { +#include +#include "gdial_app_registry.h" +} + +/* ================================================================== */ +/* Fixture: auto-disposes the registry created in each test */ +/* ================================================================== */ +class GDialAppRegistryTest : public ::testing::Test { +protected: + GDialAppRegistry *registry = nullptr; + + void TearDown() override { + if (registry) { + gdial_app_regstry_dispose(registry); + registry = nullptr; + } + } +}; + +/* ================================================================== */ +/* gdial_app_registry_new */ +/* ================================================================== */ + +TEST_F(GDialAppRegistryTest, New_ValidNameReturnsNonNull) { + registry = gdial_app_registry_new("Netflix", nullptr, nullptr, + TRUE, FALSE, nullptr); + ASSERT_NE(registry, nullptr); +} + +TEST_F(GDialAppRegistryTest, New_AppNameStoredCorrectly) { + registry = gdial_app_registry_new("YouTube", nullptr, nullptr, + TRUE, FALSE, nullptr); + ASSERT_NE(registry, nullptr); + EXPECT_STREQ(registry->name, "YouTube"); +} + +TEST_F(GDialAppRegistryTest, New_NullNameReturnsNull) { + GDialAppRegistry *r = gdial_app_registry_new(nullptr, nullptr, nullptr, + TRUE, FALSE, nullptr); + EXPECT_EQ(r, nullptr); +} + +TEST_F(GDialAppRegistryTest, New_NonSingletonReturnsNull) { + /* is_singleton=FALSE is rejected by g_return_val_if_fail */ + GDialAppRegistry *r = gdial_app_registry_new("App", nullptr, nullptr, + FALSE, FALSE, nullptr); + EXPECT_EQ(r, nullptr); +} + +TEST_F(GDialAppRegistryTest, New_IsSingletonFlagSet) { + registry = gdial_app_registry_new("App", nullptr, nullptr, + TRUE, FALSE, nullptr); + ASSERT_NE(registry, nullptr); + EXPECT_TRUE(registry->is_singleton); +} + +TEST_F(GDialAppRegistryTest, New_UseAdditionalDataFlagSet) { + registry = gdial_app_registry_new("App", nullptr, nullptr, + TRUE, TRUE, nullptr); + ASSERT_NE(registry, nullptr); + EXPECT_TRUE(registry->use_additional_data); +} + +TEST_F(GDialAppRegistryTest, New_PropertiesCopiedIntoRegistry) { + GHashTable *props = g_hash_table_new_full( + g_str_hash, g_str_equal, g_free, g_free); + g_hash_table_insert(props, g_strdup("version"), g_strdup("2.0")); + + registry = gdial_app_registry_new("App", nullptr, props, + TRUE, FALSE, nullptr); + g_hash_table_destroy(props); + + ASSERT_NE(registry, nullptr); + ASSERT_NE(registry->properties, nullptr); + EXPECT_STREQ( + (gchar *)g_hash_table_lookup(registry->properties, "version"), + "2.0"); +} + +TEST_F(GDialAppRegistryTest, New_NullPropertiesLeavesNullPropertyTable) { + registry = gdial_app_registry_new("App", nullptr, nullptr, + TRUE, FALSE, nullptr); + ASSERT_NE(registry, nullptr); + EXPECT_EQ(registry->properties, nullptr); +} + +/* ================================================================== */ +/* gdial_app_registry_is_allowed_origin */ +/* ================================================================== */ + +TEST_F(GDialAppRegistryTest, IsAllowedOrigin_NullRegistryReturnsFalse) { + EXPECT_FALSE(gdial_app_registry_is_allowed_origin( + nullptr, "http://example.com")); +} + +TEST_F(GDialAppRegistryTest, IsAllowedOrigin_EmptyAllowListPermitsAll) { + /* No allowed_origins → all origins are accepted */ + registry = gdial_app_registry_new("App", nullptr, nullptr, + TRUE, FALSE, nullptr); + ASSERT_NE(registry, nullptr); + EXPECT_TRUE(gdial_app_registry_is_allowed_origin( + registry, "http://any.origin.com")); +} + +TEST_F(GDialAppRegistryTest, IsAllowedOrigin_SuffixMatchPermitsOrigin) { + GList *origins = g_list_append(nullptr, g_strdup("netflix.com")); + registry = gdial_app_registry_new("Netflix", nullptr, nullptr, + TRUE, FALSE, origins); + g_list_free_full(origins, g_free); + ASSERT_NE(registry, nullptr); + EXPECT_TRUE(gdial_app_registry_is_allowed_origin( + registry, "https://www.netflix.com")); +} + +TEST_F(GDialAppRegistryTest, IsAllowedOrigin_NonMatchingOriginDenied) { + GList *origins = g_list_append(nullptr, g_strdup("netflix.com")); + registry = gdial_app_registry_new("Netflix", nullptr, nullptr, + TRUE, FALSE, origins); + g_list_free_full(origins, g_free); + ASSERT_NE(registry, nullptr); + EXPECT_FALSE(gdial_app_registry_is_allowed_origin( + registry, "https://malicious.example.com")); +} + +TEST_F(GDialAppRegistryTest, IsAllowedOrigin_NullOriginDenied) { + GList *origins = g_list_append(nullptr, g_strdup("netflix.com")); + registry = gdial_app_registry_new("Netflix", nullptr, nullptr, + TRUE, FALSE, origins); + g_list_free_full(origins, g_free); + ASSERT_NE(registry, nullptr); + /* NULL header_origin → GDIAL_STR_ENDS_WITH returns FALSE */ + EXPECT_FALSE(gdial_app_registry_is_allowed_origin(registry, nullptr)); +} + +/* ================================================================== */ +/* gdial_app_regstry_dispose */ +/* ================================================================== */ + +TEST(GDialAppRegistryDisposeTest, Dispose_NullDoesNotCrash) { + /* g_return_if_fail(app_registry != NULL) must not segfault */ + gdial_app_regstry_dispose(nullptr); +} + +TEST(GDialAppRegistryDisposeTest, Dispose_ValidRegistryDoesNotCrash) { + GDialAppRegistry *r = gdial_app_registry_new("App", nullptr, nullptr, + TRUE, FALSE, nullptr); + ASSERT_NE(r, nullptr); + gdial_app_regstry_dispose(r); /* must not crash or leak under valgrind */ +} + +TEST(GDialAppRegistryDisposeTest, Dispose_WithPropertiesDoesNotCrash) { + GHashTable *props = g_hash_table_new_full( + g_str_hash, g_str_equal, g_free, g_free); + g_hash_table_insert(props, g_strdup("k"), g_strdup("v")); + GDialAppRegistry *r = gdial_app_registry_new("App", nullptr, props, + TRUE, FALSE, nullptr); + g_hash_table_destroy(props); + ASSERT_NE(r, nullptr); + gdial_app_regstry_dispose(r); +} + diff --git a/tests/L1Tests/plat/test_gdialPlatApp.cpp b/tests/L1Tests/plat/test_gdialPlatApp.cpp new file mode 100644 index 00000000..14e7a5b8 --- /dev/null +++ b/tests/L1Tests/plat/test_gdialPlatApp.cpp @@ -0,0 +1,421 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2024 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file test_gdialPlatApp.cpp + * @brief Unit tests for server/plat/gdial-plat-app.c + * + * Coverage strategy: + * + * GDialPlatAppNullGuardTest — standalone tests (no gdial_plat_init). + * Tests every g_return_val_if_fail guard in the public API. Each guard + * logs a GLib critical to stderr and returns the fail value; the binary + * does NOT abort. + * + * GDialPlatAppTest (fixture) — calls gdial_plat_init(ctx) in SetUp and + * gdial_plat_term() in TearDown. Covers the lifecycle and all + * synchronous delegation paths. + * + * GDialPlatAppAsyncTest (fixture) — extends GDialPlatAppTest, pumps + * g_main_context_default() to fire the 1 ms GLib timeouts used by the + * async dispatch paths. + * + * Note on timer context: g_timeout_add_full() (used inside gdial-plat-app.c) + * attaches to the GLib thread-default context. Since no thread-default is + * pushed in the test thread, that resolves to g_main_context_default(). + * The pump helpers therefore iterate g_main_context_default(), not ctx_. + */ + +#include + +#include + +extern "C" { +#include +#include "gdial-plat-app.h" +#include "gdial-app.h" /* GDIAL_APP_INSTANCE_NONE, GDialAppState, GDialAppError */ +} + +/* ================================================================== */ +/* SECTION 1: Null-guard tests — no gdial_plat_init required */ +/* g_return_val_if_fail returns the fail value and emits a GLib */ +/* critical warning; the process is NOT aborted. */ +/* ================================================================== */ + +TEST(GDialPlatAppNullGuardTest, Init_NullContext_ReturnsInternal) { + EXPECT_EQ(gdial_plat_init(nullptr), (gint)GDIAL_APP_ERROR_INTERNAL); +} + +TEST(GDialPlatAppNullGuardTest, Start_NullName_ReturnsBadRequest) { + gint id = 0; + EXPECT_EQ(gdial_plat_application_start(nullptr, nullptr, nullptr, nullptr, &id), + GDIAL_APP_ERROR_BAD_REQUEST); +} + +TEST(GDialPlatAppNullGuardTest, Start_NullInstanceId_ReturnsBadRequest) { + EXPECT_EQ(gdial_plat_application_start("Netflix", nullptr, nullptr, nullptr, nullptr), + GDIAL_APP_ERROR_BAD_REQUEST); +} + +TEST(GDialPlatAppNullGuardTest, Hide_NullName_ReturnsBadRequest) { + EXPECT_EQ(gdial_plat_application_hide(nullptr, 1), + GDIAL_APP_ERROR_BAD_REQUEST); +} + +TEST(GDialPlatAppNullGuardTest, Hide_InstanceNone_ReturnsBadRequest) { + EXPECT_EQ(gdial_plat_application_hide("App", GDIAL_APP_INSTANCE_NONE), + GDIAL_APP_ERROR_BAD_REQUEST); +} + +TEST(GDialPlatAppNullGuardTest, Resume_NullName_ReturnsBadRequest) { + EXPECT_EQ(gdial_plat_application_resume(nullptr, 1), + GDIAL_APP_ERROR_BAD_REQUEST); +} + +TEST(GDialPlatAppNullGuardTest, Resume_InstanceNone_ReturnsBadRequest) { + EXPECT_EQ(gdial_plat_application_resume("App", GDIAL_APP_INSTANCE_NONE), + GDIAL_APP_ERROR_BAD_REQUEST); +} + +TEST(GDialPlatAppNullGuardTest, Stop_NullName_ReturnsBadRequest) { + EXPECT_EQ(gdial_plat_application_stop(nullptr, 1), + GDIAL_APP_ERROR_BAD_REQUEST); +} + +TEST(GDialPlatAppNullGuardTest, Stop_InstanceNone_ReturnsBadRequest) { + EXPECT_EQ(gdial_plat_application_stop("App", GDIAL_APP_INSTANCE_NONE), + GDIAL_APP_ERROR_BAD_REQUEST); +} + +TEST(GDialPlatAppNullGuardTest, State_NullName_ReturnsBadRequest) { + GDialAppState s = GDIAL_APP_STATE_STOPPED; + EXPECT_EQ(gdial_plat_application_state(nullptr, 1, &s), + GDIAL_APP_ERROR_BAD_REQUEST); +} + +TEST(GDialPlatAppNullGuardTest, State_NullStatePtr_ReturnsBadRequest) { + EXPECT_EQ(gdial_plat_application_state("App", 1, nullptr), + GDIAL_APP_ERROR_BAD_REQUEST); +} + +TEST(GDialPlatAppNullGuardTest, State_InstanceNone_ReturnsBadRequest) { + GDialAppState s = GDIAL_APP_STATE_STOPPED; + EXPECT_EQ(gdial_plat_application_state("App", GDIAL_APP_INSTANCE_NONE, &s), + GDIAL_APP_ERROR_BAD_REQUEST); +} + +TEST(GDialPlatAppNullGuardTest, StateChanged_NullName_ReturnsBadRequest) { + EXPECT_EQ(gdial_plat_application_state_changed(nullptr, "id", "running", "none"), + GDIAL_APP_ERROR_BAD_REQUEST); +} + +TEST(GDialPlatAppNullGuardTest, StateChanged_NullId_ReturnsBadRequest) { + EXPECT_EQ(gdial_plat_application_state_changed("App", nullptr, "running", "none"), + GDIAL_APP_ERROR_BAD_REQUEST); +} + +TEST(GDialPlatAppNullGuardTest, StateChanged_NullState_ReturnsBadRequest) { + EXPECT_EQ(gdial_plat_application_state_changed("App", "id", nullptr, "none"), + GDIAL_APP_ERROR_BAD_REQUEST); +} + +TEST(GDialPlatAppNullGuardTest, StateChanged_NullError_ReturnsBadRequest) { + EXPECT_EQ(gdial_plat_application_state_changed("App", "id", "running", nullptr), + GDIAL_APP_ERROR_BAD_REQUEST); +} + +TEST(GDialPlatAppNullGuardTest, ActivationChanged_NullActivation_ReturnsBadRequest) { + EXPECT_EQ(gdial_plat_application_activation_changed(nullptr, "TV"), + GDIAL_APP_ERROR_BAD_REQUEST); +} + +TEST(GDialPlatAppNullGuardTest, ActivationChanged_NullFriendlyName_ReturnsBadRequest) { + EXPECT_EQ(gdial_plat_application_activation_changed("true", nullptr), + GDIAL_APP_ERROR_BAD_REQUEST); +} + +TEST(GDialPlatAppNullGuardTest, FriendlyNameChanged_Null_ReturnsBadRequest) { + EXPECT_EQ(gdial_plat_application_friendlyname_changed(nullptr), + GDIAL_APP_ERROR_BAD_REQUEST); +} + +TEST(GDialPlatAppNullGuardTest, RegisterApplications_Null_ReturnsBadRequest) { + EXPECT_EQ(gdial_plat_application_register_applications(nullptr), + GDIAL_APP_ERROR_BAD_REQUEST); +} + +TEST(GDialPlatAppNullGuardTest, UpdateManufacturerName_Null_ReturnsBadRequest) { + EXPECT_EQ(gdial_plat_application_update_manufacturer_name(nullptr), + GDIAL_APP_ERROR_BAD_REQUEST); +} + +TEST(GDialPlatAppNullGuardTest, UpdateModelName_Null_ReturnsBadRequest) { + EXPECT_EQ(gdial_plat_application_update_model_name(nullptr), + GDIAL_APP_ERROR_BAD_REQUEST); +} + +TEST(GDialPlatAppNullGuardTest, ServiceNotification_TrueNullNotifier_ReturnsBadRequest) { + /* guard: (notifier != NULL) || (false == isNotifyRequired) */ + EXPECT_EQ(gdial_plat_application_service_notification(TRUE, nullptr), + GDIAL_APP_ERROR_BAD_REQUEST); +} + +/* Before gdial_plat_init, gdial_plat_app_async_contexts is NULL → + * g_return_val_if_fail(gdial_plat_app_async_contexts != NULL, NULL) fires. */ +TEST(GDialPlatAppNullGuardTest, StartAsync_BeforeInit_ReturnsNull) { + void *h = gdial_plat_application_start_async("Netflix", nullptr, nullptr, nullptr, nullptr); + EXPECT_EQ(h, nullptr); +} + +TEST(GDialPlatAppNullGuardTest, StopAsync_BeforeInit_ReturnsNull) { + void *h = gdial_plat_application_stop_async("Netflix", 1, nullptr); + EXPECT_EQ(h, nullptr); +} + +TEST(GDialPlatAppNullGuardTest, StateAsync_BeforeInit_ReturnsNull) { + void *h = gdial_plat_application_state_async("Netflix", 1, nullptr); + EXPECT_EQ(h, nullptr); +} + +/* Empty-name guard: g_return_val_if_fail(app_name != NULL && strlen(app_name), NULL) */ +TEST(GDialPlatAppNullGuardTest, StartAsync_EmptyName_ReturnsNull) { + /* Must call after a gdial_plat_init so async_contexts is non-NULL. + * Use the default context for a minimal init / term pair. */ + GMainContext *ctx = g_main_context_new(); + gdial_plat_init(ctx); + void *h = gdial_plat_application_start_async("", nullptr, nullptr, nullptr, nullptr); + EXPECT_EQ(h, nullptr); + gdial_plat_term(); + g_main_context_unref(ctx); +} + +/* ================================================================== */ +/* SECTION 2: Lifecycle and synchronous delegation */ +/* ================================================================== */ + +class GDialPlatAppTest : public ::testing::Test { +protected: + GMainContext *ctx_ = nullptr; + + void SetUp() override { + ctx_ = g_main_context_new(); + gdial_plat_init(ctx_); + } + + void TearDown() override { + /* Keep base fixture teardown minimal for sync tests. Draining the + * process-global default context here can execute unrelated stale + * timers from other tests and crash before this suite progresses. + * Async fixture teardown performs the explicit drain. */ + gdial_plat_term(); + g_main_context_unref(ctx_); + ctx_ = nullptr; + } +}; + +TEST_F(GDialPlatAppTest, Init_ValidContext_ReturnsNone) { + /* gdial_plat_init was called in SetUp; assert it succeeded */ + SUCCEED(); +} + +TEST_F(GDialPlatAppTest, Init_SameContextAgain_ReturnsNone) { + /* Calling with the same context passes the guard */ + EXPECT_EQ(gdial_plat_init(ctx_), (gint)GDIAL_APP_ERROR_NONE); + /* Balance the extra ref and hash-table ref acquired by the second init */ + gdial_plat_term(); +} + +TEST_F(GDialPlatAppTest, Init_DifferentContext_ReturnsInternal) { + GMainContext *other = g_main_context_new(); + EXPECT_EQ(gdial_plat_init(other), (gint)GDIAL_APP_ERROR_INTERNAL); + g_main_context_unref(other); +} + +TEST_F(GDialPlatAppTest, RegisterCallbacks_NoCrash) { + gdail_plat_register_activation_cb(nullptr); + gdail_plat_register_friendlyname_cb(nullptr); + gdail_plat_register_registerapps_cb(nullptr); + gdail_plat_register_manufacturername_cb(nullptr); + gdail_plat_register_modelname_cb(nullptr); + SUCCEED(); +} + +TEST_F(GDialPlatAppTest, Start_ValidArgs_ReturnsNoneAndSetsId) { + gint id = 0; + EXPECT_EQ(gdial_plat_application_start("Netflix", nullptr, nullptr, nullptr, &id), + GDIAL_APP_ERROR_NONE); + EXPECT_EQ(id, 1); /* OS stub sets instance_id = 1 */ +} + +TEST_F(GDialPlatAppTest, Start_WithPayloadAndQuery_ReturnsNone) { + gint id = 0; + EXPECT_EQ(gdial_plat_application_start("YouTube", "payload", "query=1", nullptr, &id), + GDIAL_APP_ERROR_NONE); +} + +TEST_F(GDialPlatAppTest, Hide_ValidArgs_ReturnsNone) { + EXPECT_EQ(gdial_plat_application_hide("Netflix", 1), GDIAL_APP_ERROR_NONE); +} + +TEST_F(GDialPlatAppTest, Resume_ValidArgs_ReturnsNone) { + EXPECT_EQ(gdial_plat_application_resume("Netflix", 1), GDIAL_APP_ERROR_NONE); +} + +TEST_F(GDialPlatAppTest, Stop_ValidArgs_ReturnsNone) { + EXPECT_EQ(gdial_plat_application_stop("Netflix", 1), GDIAL_APP_ERROR_NONE); +} + +TEST_F(GDialPlatAppTest, State_ValidArgs_ReturnsNoneAndSetsState) { + GDialAppState state = GDIAL_APP_STATE_MAX; + EXPECT_EQ(gdial_plat_application_state("Netflix", 1, &state), GDIAL_APP_ERROR_NONE); + EXPECT_EQ(state, GDIAL_APP_STATE_STOPPED); /* OS stub default */ +} + +TEST_F(GDialPlatAppTest, StateChanged_ValidArgs_ReturnsNone) { + EXPECT_EQ(gdial_plat_application_state_changed("Netflix", "id1", "running", "none"), + GDIAL_APP_ERROR_NONE); +} + +TEST_F(GDialPlatAppTest, ActivationChanged_ValidArgs_ReturnsNone) { + EXPECT_EQ(gdial_plat_application_activation_changed("true", "LivingRoom"), + GDIAL_APP_ERROR_NONE); +} + +TEST_F(GDialPlatAppTest, FriendlyNameChanged_ValidArgs_ReturnsNone) { + EXPECT_EQ(gdial_plat_application_friendlyname_changed("LivingRoom"), + GDIAL_APP_ERROR_NONE); +} + +TEST_F(GDialPlatAppTest, GetProtocolVersion_ReturnsNonEmpty) { + const char *ver = gdial_plat_application_get_protocol_version(); + ASSERT_NE(ver, nullptr); + EXPECT_GT(strlen(ver), 0u); +} + +TEST_F(GDialPlatAppTest, RegisterApplications_ValidPtr_ReturnsNone) { + int dummy = 42; + EXPECT_EQ(gdial_plat_application_register_applications(&dummy), GDIAL_APP_ERROR_NONE); +} + +TEST_F(GDialPlatAppTest, UpdateNetworkStandbyMode_NoCrash) { + gdial_plat_application_update_network_standby_mode(TRUE); + gdial_plat_application_update_network_standby_mode(FALSE); + SUCCEED(); +} + +TEST_F(GDialPlatAppTest, UpdateManufacturerName_ValidArgs_ReturnsNone) { + EXPECT_EQ(gdial_plat_application_update_manufacturer_name("Acme"), + GDIAL_APP_ERROR_NONE); +} + +TEST_F(GDialPlatAppTest, UpdateModelName_ValidArgs_ReturnsNone) { + EXPECT_EQ(gdial_plat_application_update_model_name("BoxV2"), + GDIAL_APP_ERROR_NONE); +} + +TEST_F(GDialPlatAppTest, ServiceNotification_FalseNullNotifier_ReturnsNone) { + /* Guard: (notifier != NULL) || (false == isNotifyRequired) — FALSE+NULL passes */ + EXPECT_EQ(gdial_plat_application_service_notification(FALSE, nullptr), + GDIAL_APP_ERROR_NONE); +} + +TEST_F(GDialPlatAppTest, ServiceNotification_TrueValidNotifier_ReturnsNone) { + int dummy = 1; + EXPECT_EQ(gdial_plat_application_service_notification(TRUE, &dummy), + GDIAL_APP_ERROR_NONE); +} + +/* ================================================================== */ +/* SECTION 3: Async dispatch tests */ +/* Timers fire on g_main_context_default(); pump that context. */ +/* ================================================================== */ + +class GDialPlatAppAsyncTest : public GDialPlatAppTest { +protected: + void SetUp() override { + GDialPlatAppTest::SetUp(); + } + + void TearDown() override { + GDialPlatAppTest::TearDown(); + } +}; + +TEST_F(GDialPlatAppAsyncTest, StartAsync_Netflix_ReturnsNonNullAndCanBeCancelled) { + void *h = gdial_plat_application_start_async("Netflix", nullptr, nullptr, nullptr, nullptr); + ASSERT_NE(h, nullptr); + gdial_plat_application_remove_async_source(h); + SUCCEED(); +} + +TEST_F(GDialPlatAppAsyncTest, StartAsync_Youtube_ReturnsNonNullAndCanBeCancelled) { + void *h = gdial_plat_application_start_async("Youtube", nullptr, nullptr, nullptr, nullptr); + ASSERT_NE(h, nullptr); + gdial_plat_application_remove_async_source(h); + SUCCEED(); +} + +TEST_F(GDialPlatAppAsyncTest, StartAsync_UnknownApp_ReturnsNonNullAndCanBeCancelled) { + void *h = gdial_plat_application_start_async("UnknownApp", nullptr, nullptr, nullptr, nullptr); + ASSERT_NE(h, nullptr); + gdial_plat_application_remove_async_source(h); + SUCCEED(); +} + +TEST_F(GDialPlatAppAsyncTest, StateAsync_ValidArgs_ReturnsNonNullAndCanBeCancelled) { + void *h = gdial_plat_application_state_async("Netflix", 1, nullptr); + ASSERT_NE(h, nullptr); + gdial_plat_application_remove_async_source(h); + SUCCEED(); +} + +TEST_F(GDialPlatAppAsyncTest, StateAsync_EmptyName_ReturnsNull) { + void *h = gdial_plat_application_state_async("", 1, nullptr); + EXPECT_EQ(h, nullptr); +} + +TEST_F(GDialPlatAppAsyncTest, StopAsync_ValidArgs_ReturnsNonNullAndCanBeCancelled) { + void *h = gdial_plat_application_stop_async("Netflix", 1, nullptr); + ASSERT_NE(h, nullptr); + gdial_plat_application_remove_async_source(h); + SUCCEED(); +} + +TEST_F(GDialPlatAppAsyncTest, StopAsync_EmptyName_ReturnsNull) { + void *h = gdial_plat_application_stop_async("", 1, nullptr); + EXPECT_EQ(h, nullptr); +} + +TEST_F(GDialPlatAppAsyncTest, RemoveAsyncSource_BeforeTimerFires_NoCrash) { + /* Schedule a stop_async, then explicitly cancel it before the timer fires */ + void *h = gdial_plat_application_stop_async("Netflix", 1, nullptr); + ASSERT_NE(h, nullptr); + gdial_plat_application_remove_async_source(h); + SUCCEED(); /* handle freed by remove; no need to pump context */ +} + +TEST_F(GDialPlatAppAsyncTest, SetStateCb_AllowsCallbackRegistration_NoCrash) { + gdial_plat_application_set_state_cb( + [](gint, GDialAppState, gpointer) {}, + nullptr); + void *h = gdial_plat_application_state_async("Netflix", 1, nullptr); + ASSERT_NE(h, nullptr); + gdial_plat_application_remove_async_source(h); + SUCCEED(); +} diff --git a/tests/L1Tests/plat/test_gdialPlatDev.cpp b/tests/L1Tests/plat/test_gdialPlatDev.cpp new file mode 100644 index 00000000..f6780449 --- /dev/null +++ b/tests/L1Tests/plat/test_gdialPlatDev.cpp @@ -0,0 +1,164 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2026 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file test_gdialPlatDev.cpp + * @brief Unit tests for server/plat/gdial-plat-dev.c + */ + +#include + +extern "C" { +#include +#include "gdial-plat-dev.h" +} + +namespace { + +static int g_power_cb_calls = 0; +static const char *g_last_power_state = nullptr; +static int g_nw_cb_calls = 0; +static bool g_last_nw_mode = false; + +static void reset_cb_state() +{ + g_power_cb_calls = 0; + g_last_power_state = nullptr; + g_nw_cb_calls = 0; + g_last_nw_mode = false; +} + +static void power_cb(const char *state) +{ + ++g_power_cb_calls; + g_last_power_state = state; +} + +static void nwstandby_cb(const bool mode) +{ + ++g_nw_cb_calls; + g_last_nw_mode = mode; +} + +class GDialPlatDevTest : public ::testing::Test { +protected: + void SetUp() override + { + reset_cb_state(); + gdail_plat_dev_register_powerstate_cb(nullptr); + gdail_plat_dev_register_nwstandbymode_cb(nullptr); + } + + void TearDown() override + { + gdail_plat_dev_register_powerstate_cb(nullptr); + gdail_plat_dev_register_nwstandbymode_cb(nullptr); + } +}; + +} // namespace + +TEST_F(GDialPlatDevTest, SetPowerStateOn_ReturnsTrueWithoutCallback) +{ + EXPECT_TRUE(gdial_plat_dev_set_power_state_on()); + EXPECT_EQ(g_power_cb_calls, 0); +} + +TEST_F(GDialPlatDevTest, SetPowerStateOff_ReturnsTrueWithoutCallback) +{ + EXPECT_TRUE(gdial_plat_dev_set_power_state_off()); + EXPECT_EQ(g_power_cb_calls, 0); +} + +TEST_F(GDialPlatDevTest, TogglePowerState_ReturnsTrueWithoutCallback) +{ + EXPECT_TRUE(gdial_plat_dev_toggle_power_state()); + EXPECT_EQ(g_power_cb_calls, 0); +} + +TEST_F(GDialPlatDevTest, SetPowerStateOn_InvokesCallbackWithOn) +{ + gdail_plat_dev_register_powerstate_cb(power_cb); + + EXPECT_TRUE(gdial_plat_dev_set_power_state_on()); + ASSERT_EQ(g_power_cb_calls, 1); + ASSERT_NE(g_last_power_state, nullptr); + EXPECT_STREQ(g_last_power_state, "ON"); +} + +TEST_F(GDialPlatDevTest, SetPowerStateOff_InvokesCallbackWithStandby) +{ + gdail_plat_dev_register_powerstate_cb(power_cb); + + EXPECT_TRUE(gdial_plat_dev_set_power_state_off()); + ASSERT_EQ(g_power_cb_calls, 1); + ASSERT_NE(g_last_power_state, nullptr); + EXPECT_STREQ(g_last_power_state, "STANDBY"); +} + +TEST_F(GDialPlatDevTest, TogglePowerState_InvokesCallbackWithToggle) +{ + gdail_plat_dev_register_powerstate_cb(power_cb); + + EXPECT_TRUE(gdial_plat_dev_toggle_power_state()); + ASSERT_EQ(g_power_cb_calls, 1); + ASSERT_NE(g_last_power_state, nullptr); + EXPECT_STREQ(g_last_power_state, "TOGGLE"); +} + +TEST_F(GDialPlatDevTest, RegisterPowerCallback_NullClearsCallback) +{ + gdail_plat_dev_register_powerstate_cb(power_cb); + EXPECT_TRUE(gdial_plat_dev_set_power_state_on()); + EXPECT_EQ(g_power_cb_calls, 1); + + gdail_plat_dev_register_powerstate_cb(nullptr); + EXPECT_TRUE(gdial_plat_dev_set_power_state_on()); + EXPECT_EQ(g_power_cb_calls, 1); +} + +TEST_F(GDialPlatDevTest, NwStandbyModeChange_WithoutCallback_NoCrash) +{ + gdial_plat_dev_nwstandby_mode_change(true); + EXPECT_EQ(g_nw_cb_calls, 0); +} + +TEST_F(GDialPlatDevTest, NwStandbyModeChange_InvokesRegisteredCallback) +{ + gdail_plat_dev_register_nwstandbymode_cb(nwstandby_cb); + + gdial_plat_dev_nwstandby_mode_change(true); + ASSERT_EQ(g_nw_cb_calls, 1); + EXPECT_TRUE(g_last_nw_mode); + + gdial_plat_dev_nwstandby_mode_change(false); + ASSERT_EQ(g_nw_cb_calls, 2); + EXPECT_FALSE(g_last_nw_mode); +} + +TEST_F(GDialPlatDevTest, RegisterNwStandbyCallback_NullClearsCallback) +{ + gdail_plat_dev_register_nwstandbymode_cb(nwstandby_cb); + gdial_plat_dev_nwstandby_mode_change(true); + EXPECT_EQ(g_nw_cb_calls, 1); + + gdail_plat_dev_register_nwstandbymode_cb(nullptr); + gdial_plat_dev_nwstandby_mode_change(true); + EXPECT_EQ(g_nw_cb_calls, 1); +} diff --git a/tests/L1Tests/plat/test_gdialPlatUtil.cpp b/tests/L1Tests/plat/test_gdialPlatUtil.cpp new file mode 100644 index 00000000..7bf0ef2c --- /dev/null +++ b/tests/L1Tests/plat/test_gdialPlatUtil.cpp @@ -0,0 +1,115 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2026 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +extern "C" { +#include "gdial-plat-util.h" +#include "gdialservicelogging.h" +} + +TEST(GDialPlatUtilTest, GetIfaceIpv4Addr_InvalidIfaceReturnsNull) { + const char *ip = gdial_plat_util_get_iface_ipv4_addr("iface_does_not_exist_123"); + EXPECT_EQ(ip, nullptr); +} + +TEST(GDialPlatUtilTest, GetIfaceIpv4Addr_LoopbackLooksValid) { + const char *ip = gdial_plat_util_get_iface_ipv4_addr("lo"); + ASSERT_NE(ip, nullptr); + EXPECT_STREQ(ip, "127.0.0.1"); +} + +TEST(GDialPlatUtilTest, GetIfaceIpv4Addr_UsesStableStaticBuffer) { + const char *ip1 = gdial_plat_util_get_iface_ipv4_addr("lo"); + ASSERT_NE(ip1, nullptr); + + const char *ip2 = gdial_plat_util_get_iface_ipv4_addr("lo"); + ASSERT_NE(ip2, nullptr); + + /* Implementation returns a pointer to a static internal buffer. */ + EXPECT_EQ(ip1, ip2); + EXPECT_STREQ(ip2, "127.0.0.1"); +} + +TEST(GDialPlatUtilTest, GetIfaceMacAddr_InvalidIfaceReturnsNull) { + const char *mac = gdial_plat_util_get_iface_mac_addr("iface_does_not_exist_123"); + EXPECT_EQ(mac, nullptr); +} + +TEST(GDialPlatUtilTest, GetIfaceMacAddr_LoopbackLooksLikeMac) { + const char *mac = gdial_plat_util_get_iface_mac_addr("lo"); + ASSERT_NE(mac, nullptr); + EXPECT_EQ(std::strlen(mac), 17u); + for (size_t i = 0; i < 17; ++i) { + if ((i + 1) % 3 == 0) { + EXPECT_EQ(mac[i], ':'); + } else { + EXPECT_TRUE(std::isxdigit(static_cast(mac[i])) != 0); + } + } +} + +TEST(GDialPlatUtilTest, GetIfaceMacAddr_UsesStableStaticBuffer) { + const char *mac1 = gdial_plat_util_get_iface_mac_addr("lo"); + ASSERT_NE(mac1, nullptr); + + const char *mac2 = gdial_plat_util_get_iface_mac_addr("lo"); + ASSERT_NE(mac2, nullptr); + + /* Implementation returns a pointer to a static internal buffer. */ + EXPECT_EQ(mac1, mac2); +} + +TEST(GDialPlatUtilLogTest, LoggerInitAndSetLevelDoNotCrash) { + gdial_plat_util_logger_init(); + gdial_plat_util_set_loglevel(INFO_LEVEL); + SUCCEED(); +} + +TEST(GDialPlatUtilLogTest, LoggerInit_UsesEnvLogLevelAndSyncStdoutDoNotCrash) { + setenv("SYNC_STDOUT", "1", 1); + setenv("GDIAL_LIBRARY_DEFAULT_LOG_LEVEL", "4", 1); + + gdial_plat_util_logger_init(); + gdial_plat_util_log(VERBOSE_LEVEL, "fn", "file.c", 3, 125, "verbose after env"); + + unsetenv("SYNC_STDOUT"); + unsetenv("GDIAL_LIBRARY_DEFAULT_LOG_LEVEL"); + SUCCEED(); +} + +TEST(GDialPlatUtilLogTest, LogAtFatalDoesNotCrash) { + gdial_plat_util_log(FATAL_LEVEL, "fn", "file.c", 1, 123, "fatal %d", 1); + SUCCEED(); +} + +TEST(GDialPlatUtilLogTest, LogAtVerboseDoesNotCrash) { + gdial_plat_util_set_loglevel(TRACE_LEVEL); + gdial_plat_util_log(VERBOSE_LEVEL, "fn", "file.c", 2, 124, "verbose %s", "ok"); + SUCCEED(); +} + +TEST(GDialPlatUtilLogTest, LogFilteredByLevelDoesNotCrash) { + gdial_plat_util_set_loglevel(INFO_LEVEL); + gdial_plat_util_log(TRACE_LEVEL, "fn", "file.c", 4, 126, "trace should be filtered"); + SUCCEED(); +} diff --git a/tests/L1Tests/server/test_gdialRest.cpp b/tests/L1Tests/server/test_gdialRest.cpp new file mode 100644 index 00000000..fb4b4d1a --- /dev/null +++ b/tests/L1Tests/server/test_gdialRest.cpp @@ -0,0 +1,486 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2026 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +extern "C" { +#include +#include +#include "gdial-rest.h" +#include "gdial-rest-builder.h" +#include "gdial-app.h" + +void gdial_plat_stub_reset_behavior(void); +void gdial_plat_stub_set_app_state(GDialAppState state); +void gdial_plat_stub_set_errors( + GDialAppError start_err, + GDialAppError hide_err, + GDialAppError resume_err, + GDialAppError stop_err, + GDialAppError state_err); +} + +class GDialRestServerTest : public ::testing::Test { +protected: + const char *rest_route_id = "apps123"; + SoupServer *rest_server = nullptr; + SoupServer *local_rest_server = nullptr; + GDialRestServer *server = nullptr; + SoupSession *session = nullptr; + GMainLoop *main_loop = nullptr; + GThread *main_loop_thread = nullptr; + std::string rest_base; + std::string local_base; + + void SetUp() override { + gdial_plat_stub_reset_behavior(); + + rest_server = soup_server_new(nullptr, nullptr); + local_rest_server = soup_server_new(nullptr, nullptr); + ASSERT_NE(rest_server, nullptr); + ASSERT_NE(local_rest_server, nullptr); + + GError *error = nullptr; + ASSERT_TRUE(soup_server_listen_local(rest_server, 0, SOUP_SERVER_LISTEN_IPV4_ONLY, &error)); + ASSERT_EQ(error, nullptr); + ASSERT_TRUE(soup_server_listen_local(local_rest_server, 0, SOUP_SERVER_LISTEN_IPV4_ONLY, &error)); + ASSERT_EQ(error, nullptr); + + GSList *rest_uris = soup_server_get_uris(rest_server); + ASSERT_NE(rest_uris, nullptr); + SoupURI *rest_uri = (SoupURI *)rest_uris->data; + guint rest_port = soup_uri_get_port(rest_uri); + rest_base = std::string("http://127.0.0.1:") + std::to_string(rest_port) + "/" + rest_route_id; + g_slist_free_full(rest_uris, (GDestroyNotify)soup_uri_free); + + GSList *local_uris = soup_server_get_uris(local_rest_server); + ASSERT_NE(local_uris, nullptr); + SoupURI *local_uri = (SoupURI *)local_uris->data; + guint local_port = soup_uri_get_port(local_uri); + local_base = std::string("http://127.0.0.1:") + std::to_string(local_port); + g_slist_free_full(local_uris, (GDestroyNotify)soup_uri_free); + + main_loop = g_main_loop_new(nullptr, FALSE); + ASSERT_NE(main_loop, nullptr); + main_loop_thread = g_thread_new( + "gdial-rest-test-loop", + [](gpointer data) -> gpointer { + g_main_loop_run((GMainLoop *)data); + return nullptr; + }, + main_loop); + ASSERT_NE(main_loop_thread, nullptr); + + for (int i = 0; i < 200 && !g_main_loop_is_running(main_loop); ++i) { + g_usleep(1000); + } + ASSERT_TRUE(g_main_loop_is_running(main_loop)); + + session = soup_session_new_with_options( + SOUP_SESSION_TIMEOUT, 5, + SOUP_SESSION_IDLE_TIMEOUT, 5, + NULL); + ASSERT_NE(session, nullptr); + + server = gdial_rest_server_new(rest_server, local_rest_server, (gchar *)rest_route_id); + ASSERT_NE(server, nullptr); + g_object_set(server, "enable", TRUE, NULL); + } + + void TearDown() override { + if (server) { + gdial_rest_server_unregister_all_apps(server); + } + + if (session) { + g_object_unref(session); + session = nullptr; + } + + if (main_loop) { + g_main_loop_quit(main_loop); + } + if (main_loop_thread) { + g_thread_join(main_loop_thread); + main_loop_thread = nullptr; + } + if (main_loop) { + g_main_loop_unref(main_loop); + main_loop = nullptr; + } + + if (server) { + g_object_unref(server); + server = nullptr; + } + + if (rest_server) { + g_object_unref(rest_server); + rest_server = nullptr; + } + if (local_rest_server) { + g_object_unref(local_rest_server); + local_rest_server = nullptr; + } + + gdial_plat_stub_reset_behavior(); + + std::remove("/tmp/.dial_Netflix_uuid.txt"); + std::remove("/tmp/.dial_YouTube_uuid.txt"); + } + + static GList *make_list1(const gchar *v1) { + GList *list = nullptr; + list = g_list_prepend(list, (gpointer)v1); + return list; + } + + SoupMessage *send_rest( + const char *method, + const std::string &suffix, + const char *origin = nullptr, + const char *body = nullptr, + const char *content_type = "application/x-www-form-urlencoded") + { + std::string url = rest_base + suffix; + SoupMessage *msg = soup_message_new(method, url.c_str()); + EXPECT_NE(msg, nullptr); + if (!msg) { + return nullptr; + } + if (origin) { + soup_message_headers_replace(msg->request_headers, "Origin", origin); + } + if (body) { + soup_message_set_request(msg, content_type, SOUP_MEMORY_COPY, body, strlen(body)); + } + soup_session_send_message(session, msg); + return msg; + } + + SoupMessage *send_local( + const char *method, + const std::string &suffix, + const char *body = nullptr, + const char *content_type = "application/x-www-form-urlencoded") + { + std::string url = local_base + suffix; + SoupMessage *msg = soup_message_new(method, url.c_str()); + EXPECT_NE(msg, nullptr); + if (!msg) { + return nullptr; + } + if (body) { + soup_message_set_request(msg, content_type, SOUP_MEMORY_COPY, body, strlen(body)); + } + soup_session_send_message(session, msg); + return msg; + } +}; + +TEST(GDialRestHelperTest, NewAdditionalDataUrl_UnencodedLooksCorrect) { + gchar *url = gdial_rest_server_new_additional_data_url(56890, "Netflix", FALSE, "/abcd"); + ASSERT_NE(url, nullptr); + EXPECT_STREQ(url, "http://localhost:56890/abcd/dial_data"); + g_free(url); +} + +TEST(GDialRestHelperTest, NewAdditionalDataUrl_EncodedLooksUrlEncoded) { + gchar *url = gdial_rest_server_new_additional_data_url(1234, "Netflix", TRUE, "/abcd"); + ASSERT_NE(url, nullptr); + EXPECT_NE(strstr(url, "http%3A%2F%2Flocalhost%3A1234%2Fabcd%2Fdial_data"), nullptr); + g_free(url); +} + +TEST_F(GDialRestServerTest, RegisterFindAndUnregisterAppByName) { + GList *allowed_origins = make_list1(".example.com"); + + ASSERT_TRUE(gdial_rest_server_register_app( + server, "Netflix", nullptr, nullptr, TRUE, FALSE, allowed_origins)); + + EXPECT_TRUE(gdial_rest_server_is_app_registered(server, "Netflix")); + + GDialAppRegistry *registry = gdial_rest_server_find_app_registry(server, "Netflix"); + ASSERT_NE(registry, nullptr); + EXPECT_STREQ(registry->name, "Netflix"); + + EXPECT_TRUE(gdial_rest_server_unregister_app(server, "Netflix")); + EXPECT_FALSE(gdial_rest_server_is_app_registered(server, "Netflix")); + + g_list_free(allowed_origins); +} + +TEST_F(GDialRestServerTest, RegisterDuplicateAppFails) { + ASSERT_TRUE(gdial_rest_server_register_app( + server, "Netflix", nullptr, nullptr, TRUE, FALSE, nullptr)); + EXPECT_FALSE(gdial_rest_server_register_app( + server, "Netflix", nullptr, nullptr, TRUE, FALSE, nullptr)); +} + +TEST_F(GDialRestServerTest, RegisterApp_NonSingletonRejected) { + EXPECT_FALSE(gdial_rest_server_register_app( + server, "Netflix", nullptr, nullptr, FALSE, FALSE, nullptr)); +} + +TEST_F(GDialRestServerTest, FindRegistryByUuidMatchesRegisteredAppUri) { + ASSERT_TRUE(gdial_rest_server_register_app( + server, "Netflix", nullptr, nullptr, TRUE, FALSE, nullptr)); + + GDialAppRegistry *by_name = gdial_rest_server_find_app_registry(server, "Netflix"); + ASSERT_NE(by_name, nullptr); + ASSERT_TRUE(by_name->app_uri[0] == '/'); + + GDialAppRegistry *by_uuid = + gdial_rest_server_find_app_registry_by_uuid(server, &by_name->app_uri[1]); + ASSERT_NE(by_uuid, nullptr); + EXPECT_EQ(by_uuid, by_name); +} + +TEST_F(GDialRestServerTest, FindRegistryByUuid_UnknownUuidReturnsNull) { + ASSERT_TRUE(gdial_rest_server_register_app( + server, "Netflix", nullptr, nullptr, TRUE, FALSE, nullptr)); + + GDialAppRegistry *by_uuid = + gdial_rest_server_find_app_registry_by_uuid(server, "not-a-real-uuid"); + EXPECT_EQ(by_uuid, nullptr); +} + +TEST_F(GDialRestServerTest, FindRegistry_MatchesByPrefix) { + GList *prefixes = make_list1("YouTube"); + ASSERT_TRUE(gdial_rest_server_register_app( + server, "YouTube", prefixes, nullptr, TRUE, FALSE, nullptr)); + + GDialAppRegistry *registry = + gdial_rest_server_find_app_registry(server, "YouTubeTV"); + ASSERT_NE(registry, nullptr); + EXPECT_STREQ(registry->name, "YouTube"); + + g_list_free(prefixes); +} + +TEST_F(GDialRestServerTest, AllowedOrigin_NonYouTubeBehavior) { + GList *allowed_origins = make_list1(".example.com"); + ASSERT_TRUE(gdial_rest_server_register_app( + server, "Netflix", nullptr, nullptr, TRUE, FALSE, allowed_origins)); + + EXPECT_TRUE(gdial_rest_server_is_allowed_origin(server, nullptr, "Netflix")); + EXPECT_TRUE(gdial_rest_server_is_allowed_origin(server, "", "Netflix")); + EXPECT_TRUE(gdial_rest_server_is_allowed_origin(server, "https://www.example.com", "Netflix")); + EXPECT_FALSE(gdial_rest_server_is_allowed_origin(server, "https://evil.org", "Netflix")); + EXPECT_TRUE(gdial_rest_server_is_allowed_origin(server, "http://evil.org", "Netflix")); + + g_list_free(allowed_origins); +} + +TEST_F(GDialRestServerTest, AllowedOrigin_YouTubeRequiresSpecificOriginRules) { + GList *allowed_origins = make_list1(".youtube.com"); + ASSERT_TRUE(gdial_rest_server_register_app( + server, "YouTube", nullptr, nullptr, TRUE, FALSE, allowed_origins)); + + EXPECT_FALSE(gdial_rest_server_is_allowed_origin(server, nullptr, "YouTube")); + EXPECT_TRUE(gdial_rest_server_is_allowed_origin(server, "https://www.youtube.com", "YouTube")); + EXPECT_FALSE(gdial_rest_server_is_allowed_origin(server, "https://www.example.com", "YouTube")); + EXPECT_TRUE(gdial_rest_server_is_allowed_origin(server, "package:youtube", "YouTube")); + + g_list_free(allowed_origins); +} + +TEST_F(GDialRestServerTest, RegisterAppRegistryAndUnregisterAllApps) { + GDialAppRegistry *registry = + gdial_app_registry_new("Netflix", nullptr, nullptr, TRUE, FALSE, nullptr); + ASSERT_NE(registry, nullptr); + + ASSERT_TRUE(gdial_rest_server_register_app_registry(server, registry)); + EXPECT_TRUE(gdial_rest_server_is_app_registered(server, "Netflix")); + + GDialApp *app = gdial_app_new("Netflix"); + ASSERT_NE(app, nullptr); + EXPECT_EQ(gdial_app_start(app, nullptr, nullptr, nullptr, nullptr), GDIAL_APP_ERROR_NONE); + g_object_unref(app); + + EXPECT_TRUE(gdial_rest_server_unregister_all_apps(server)); + EXPECT_FALSE(gdial_rest_server_is_app_registered(server, "Netflix")); +} + +TEST_F(GDialRestServerTest, RegisterAppRegistry_DuplicateRejected) { + GDialAppRegistry *registry1 = + gdial_app_registry_new("Netflix", nullptr, nullptr, TRUE, FALSE, nullptr); + ASSERT_NE(registry1, nullptr); + ASSERT_TRUE(gdial_rest_server_register_app_registry(server, registry1)); + + GDialAppRegistry *registry2 = + gdial_app_registry_new("Netflix", nullptr, nullptr, TRUE, FALSE, nullptr); + ASSERT_NE(registry2, nullptr); + EXPECT_FALSE(gdial_rest_server_register_app_registry(server, registry2)); + + /* registry2 is not owned by server because registration failed. */ + gdial_app_regstry_dispose(registry2); + + EXPECT_TRUE(gdial_rest_server_unregister_app(server, "Netflix")); +} + +TEST_F(GDialRestServerTest, UnregisterUnknownAppReturnsFalse) { + EXPECT_FALSE(gdial_rest_server_unregister_app(server, "MissingApp")); +} + +TEST_F(GDialRestServerTest, EnablePropertyCanBeToggled) { + g_object_set(server, "enable", TRUE, NULL); + g_object_set(server, "enable", FALSE, NULL); + SUCCEED(); +} + +TEST_F(GDialRestServerTest, HttpOptionsOnAppPathReturnsNoContent) { + ASSERT_TRUE(gdial_rest_server_register_app( + server, "Netflix", nullptr, nullptr, TRUE, FALSE, nullptr)); + + SoupMessage *msg = send_rest("OPTIONS", "/Netflix"); + ASSERT_NE(msg, nullptr); + EXPECT_EQ(msg->status_code, SOUP_STATUS_NO_CONTENT); + EXPECT_STREQ( + soup_message_headers_get_one(msg->response_headers, "Access-Control-Allow-Methods"), + "GET, POST, OPTIONS"); + g_object_unref(msg); +} + +TEST_F(GDialRestServerTest, HttpPostOnAppPathCreatesInstance) { + ASSERT_TRUE(gdial_rest_server_register_app( + server, "Netflix", nullptr, nullptr, TRUE, FALSE, nullptr)); + gdial_plat_stub_set_app_state(GDIAL_APP_STATE_RUNNING); + + SoupMessage *msg = send_rest("POST", "/Netflix", nullptr, "k=v"); + ASSERT_NE(msg, nullptr); + EXPECT_EQ(msg->status_code, SOUP_STATUS_CREATED); + const char *location = soup_message_headers_get_one(msg->response_headers, "Location"); + ASSERT_NE(location, nullptr); + EXPECT_NE(strstr(location, "/Netflix/run"), nullptr); + g_object_unref(msg); +} + +TEST_F(GDialRestServerTest, HttpGetOnAppPathReturnsXml) { + ASSERT_TRUE(gdial_rest_server_register_app( + server, "Netflix", nullptr, nullptr, TRUE, FALSE, nullptr)); + + SoupMessage *msg = send_rest("GET", "/Netflix"); + ASSERT_NE(msg, nullptr); + EXPECT_EQ(msg->status_code, SOUP_STATUS_OK); + ASSERT_NE(msg->response_body, nullptr); + ASSERT_NE(msg->response_body->data, nullptr); + EXPECT_NE(strstr(msg->response_body->data, "instance_id = 1; + app->state = GDIAL_APP_STATE_RUNNING; + + SoupMessage *msg = send_rest("POST", "/Netflix/run/hide", "https://www.example.com"); + ASSERT_NE(msg, nullptr); + EXPECT_EQ(msg->status_code, SOUP_STATUS_OK); + EXPECT_STREQ( + soup_message_headers_get_one(msg->response_headers, "Content-Type"), + "text/plain; charset=utf-8"); + EXPECT_STREQ( + soup_message_headers_get_one(msg->response_headers, "Access-Control-Allow-Origin"), + "https://www.example.com"); + + g_object_unref(msg); + g_object_unref(app); + g_list_free(allowed_origins); +} + +TEST_F(GDialRestServerTest, HttpDeleteOnRunPathRunsHandleDelete) { + ASSERT_TRUE(gdial_rest_server_register_app( + server, "Netflix", nullptr, nullptr, TRUE, FALSE, nullptr)); + gdial_plat_stub_set_app_state(GDIAL_APP_STATE_RUNNING); + + GDialApp *app = gdial_app_new("Netflix"); + ASSERT_NE(app, nullptr); + app->instance_id = 1; + app->state = GDIAL_APP_STATE_RUNNING; + + SoupMessage *msg = send_rest("DELETE", "/Netflix/run"); + ASSERT_NE(msg, nullptr); + EXPECT_EQ(msg->status_code, SOUP_STATUS_OK); + g_object_unref(msg); +} + +TEST_F(GDialRestServerTest, LocalPostDialDataPathReturnsOk) { + ASSERT_TRUE(gdial_rest_server_register_app( + server, "Netflix", nullptr, nullptr, TRUE, FALSE, nullptr)); + GDialAppRegistry *registry = gdial_rest_server_find_app_registry(server, "Netflix"); + ASSERT_NE(registry, nullptr); + + std::string path = std::string(registry->app_uri) + "/dial_data"; + SoupMessage *msg = send_local("POST", path, "a=1&b=2"); + ASSERT_NE(msg, nullptr); + EXPECT_EQ(msg->status_code, SOUP_STATUS_OK); + g_object_unref(msg); +} + +TEST(GDialRestBuilderTest, BuildRunningResponseIncludesLinkAndOptions) { + void *builder = GET_APP_response_builder_new("Netflix"); + ASSERT_NE(builder, nullptr); + + GET_APP_response_builder_set_option(builder, "allowStop", "true"); + GET_APP_response_builder_set_state(builder, GDIAL_APP_STATE_RUNNING); + GET_APP_response_builder_set_link_href(builder, "run?q=1"); + GET_APP_response_builder_set_installable(builder, "http://example/install"); + GET_APP_response_builder_set_additionalData(builder, "k=v"); + + gsize len = 0; + gchar *xml = GET_APP_response_builder_build(builder, &len); + ASSERT_NE(xml, nullptr); + EXPECT_GT(len, 0u); + EXPECT_NE(strstr(xml, "Netflix"), nullptr); + EXPECT_NE(strstr(xml, "allowStop=\"true\""), nullptr); + EXPECT_NE(strstr(xml, "running"), nullptr); + EXPECT_NE(strstr(xml, "stopped"), nullptr); + EXPECT_EQ(strstr(xml, " +#include +#include + +extern "C" { +#include +#include +#include "gdial-app.h" +} + +/* ================================================================== */ +/* gdial_app_state_to_string */ +/* ================================================================== */ + +TEST(GDialAppStateToStringTest, Stopped) { + EXPECT_STREQ(gdial_app_state_to_string(GDIAL_APP_STATE_STOPPED), "stopped"); +} + +TEST(GDialAppStateToStringTest, Running) { + EXPECT_STREQ(gdial_app_state_to_string(GDIAL_APP_STATE_RUNNING), "running"); +} + +TEST(GDialAppStateToStringTest, Hide) { + EXPECT_STREQ(gdial_app_state_to_string(GDIAL_APP_STATE_HIDE), "hidden"); +} + +TEST(GDialAppStateToStringTest, MaxReturnsNull) { + EXPECT_EQ(gdial_app_state_to_string(GDIAL_APP_STATE_MAX), nullptr); +} + +/* ================================================================== */ +/* GDialAppState enum contracts */ +/* ================================================================== */ + +TEST(GDialAppStateEnumTest, StoppedIsZero) { + EXPECT_EQ(GDIAL_APP_STATE_STOPPED, 0); +} + +TEST(GDialAppStateEnumTest, MaxIsGreaterThanAllRunningStates) { + EXPECT_GT(GDIAL_APP_STATE_MAX, GDIAL_APP_STATE_STOPPED); + EXPECT_GT(GDIAL_APP_STATE_MAX, GDIAL_APP_STATE_HIDE); + EXPECT_GT(GDIAL_APP_STATE_MAX, GDIAL_APP_STATE_RUNNING); +} + +/* ================================================================== */ +/* GDialAppError enum contracts */ +/* ================================================================== */ + +TEST(GDialAppErrorEnumTest, ErrorNoneIsZero) { + EXPECT_EQ(GDIAL_APP_ERROR_NONE, 0); +} + +TEST(GDialAppErrorEnumTest, ImplErrorsExceedPublicErrors) { + /* GDIAL_APP_ERROR_IMPL_ (0x1000) must be above the state-derived errors */ + EXPECT_GT((int)GDIAL_APP_ERROR_IMPL_, (int)GDIAL_APP_ERROR_UNAUTH); +} + +/* ================================================================== */ +/* GDialApp object lifecycle */ +/* ================================================================== */ + +class GDialAppLifecycleTest : public ::testing::Test { +protected: + GDialApp *app = nullptr; + + void TearDown() override { + if (app) { + g_object_unref(app); + app = nullptr; + } + } +}; + +TEST_F(GDialAppLifecycleTest, New_ValidNameReturnsNonNull) { + app = gdial_app_new("Netflix"); + ASSERT_NE(app, nullptr); +} + +TEST_F(GDialAppLifecycleTest, New_AppNameStoredCorrectly) { + app = gdial_app_new("YouTube"); + ASSERT_NE(app, nullptr); + EXPECT_STREQ(app->name, "YouTube"); +} + +TEST_F(GDialAppLifecycleTest, New_InitialStateIsStopped) { + app = gdial_app_new("Netflix"); + ASSERT_NE(app, nullptr); + EXPECT_EQ(GDIAL_APP_GET_STATE(app), GDIAL_APP_STATE_STOPPED); +} + +TEST_F(GDialAppLifecycleTest, New_InitialInstanceIdIsZero) { + app = gdial_app_new("Netflix"); + ASSERT_NE(app, nullptr); + EXPECT_EQ(app->instance_id, 0); +} + +TEST_F(GDialAppLifecycleTest, State_UnstartedAppReturnsStopped) { + /* instance_id == NONE → gdial_app_state fast-path returns STOPPED */ + app = gdial_app_new("Netflix"); + ASSERT_NE(app, nullptr); + GDialAppError err = gdial_app_state(app); + EXPECT_EQ(err, GDIAL_APP_ERROR_NONE); + EXPECT_EQ(GDIAL_APP_GET_STATE(app), GDIAL_APP_STATE_STOPPED); +} + +TEST_F(GDialAppLifecycleTest, GetStateMacro_MatchesStructField) { + app = gdial_app_new("App"); + ASSERT_NE(app, nullptr); + /* The macro must read the same field the struct exposes */ + EXPECT_EQ(GDIAL_APP_GET_STATE(app), app->state); +} + +/* ================================================================== */ +/* Additional coverage for gdial-app.c helpers */ +/* ================================================================== */ + +class GDialAppExtendedTest : public ::testing::Test { +protected: + GDialApp *app = nullptr; + const gchar *kAppName = "CoverageApp"; + + void SetUp() override { + app = gdial_app_new(kAppName); + ASSERT_NE(app, nullptr); + } + + void TearDown() override { + /* Ensure persistent additional-data test file is removed. */ + gdial_app_remove_additional_dial_data_file(kAppName); + if (app) { + g_object_unref(app); + app = nullptr; + } + } +}; + +TEST_F(GDialAppExtendedTest, StartHideResumeStop_ReturnsNone) { + EXPECT_EQ(gdial_app_start(app, "payload", "query", "url", nullptr), GDIAL_APP_ERROR_NONE); + EXPECT_EQ(gdial_app_hide(app), GDIAL_APP_ERROR_NONE); + EXPECT_EQ(gdial_app_resume(app), GDIAL_APP_ERROR_NONE); + EXPECT_EQ(gdial_app_stop(app), GDIAL_APP_ERROR_NONE); +} + +TEST_F(GDialAppExtendedTest, Start_AssignsInstanceId) { + EXPECT_EQ(app->instance_id, 0); + EXPECT_EQ(gdial_app_start(app, nullptr, nullptr, nullptr, nullptr), GDIAL_APP_ERROR_NONE); + EXPECT_NE(app->instance_id, GDIAL_APP_INSTANCE_NONE); +} + +TEST_F(GDialAppExtendedTest, FindInstanceByNameAndId) { + EXPECT_EQ(gdial_app_start(app, nullptr, nullptr, nullptr, nullptr), GDIAL_APP_ERROR_NONE); + + GDialApp *by_name = gdial_app_find_instance_by_name(kAppName); + ASSERT_NE(by_name, nullptr); + EXPECT_EQ(by_name, app); + + GDialApp *by_id = gdial_app_find_instance_by_instance_id(app->instance_id); + ASSERT_NE(by_id, nullptr); + EXPECT_EQ(by_id, app); +} + +TEST_F(GDialAppExtendedTest, SetAndGetLaunchPayload) { + EXPECT_EQ(gdial_app_get_launch_payload(app), nullptr); + + gdial_app_set_launch_payload(app, "hello"); + ASSERT_NE(gdial_app_get_launch_payload(app), nullptr); + EXPECT_STREQ(gdial_app_get_launch_payload(app), "hello"); + + gdial_app_set_launch_payload(app, nullptr); + EXPECT_EQ(gdial_app_get_launch_payload(app), nullptr); +} + +TEST_F(GDialAppExtendedTest, SetGetAdditionalDialDataByKey) { + GHashTable *ht = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + g_hash_table_insert(ht, g_strdup("k1"), g_strdup("v1")); + g_hash_table_insert(ht, g_strdup("k2"), g_strdup("v2")); + + gdial_app_set_additional_dial_data(app, ht); + EXPECT_STREQ(gdial_app_get_additional_dial_data_by_key(app, "k1"), "v1"); + EXPECT_STREQ(gdial_app_get_additional_dial_data_by_key(app, "k2"), "v2"); + + GHashTable *dup = gdial_app_get_additional_dial_data(app); + ASSERT_NE(dup, nullptr); + EXPECT_EQ(g_hash_table_size(dup), (guint)2); + g_hash_table_unref(dup); + g_hash_table_destroy(ht); +} + +TEST_F(GDialAppExtendedTest, ClearAdditionalDialDataEmptiesTable) { + GHashTable *ht = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + g_hash_table_insert(ht, g_strdup("k"), g_strdup("v")); + gdial_app_set_additional_dial_data(app, ht); + g_hash_table_destroy(ht); + + gdial_app_clear_additional_dial_data(app); + EXPECT_EQ(gdial_app_get_additional_dial_data_by_key(app, "k"), nullptr); +} + +TEST_F(GDialAppExtendedTest, RefreshAdditionalDialDataReadsFromFile) { + const gchar *raw = "alpha one\r\nbeta two\r\n"; + ASSERT_TRUE(gdial_app_write_additional_dial_data(kAppName, raw, strlen(raw))); + + gdial_app_refresh_additional_dial_data(app); + EXPECT_STREQ(gdial_app_get_additional_dial_data_by_key(app, "alpha"), "one"); + EXPECT_STREQ(gdial_app_get_additional_dial_data_by_key(app, "beta"), "two"); +} + +TEST_F(GDialAppExtendedTest, FileHelpers_WriteReadRemoveRoundTrip) { + const gchar *payload = "x y\r\n"; + gchar *out = nullptr; + size_t out_len = 0; + + ASSERT_TRUE(gdial_app_write_additional_dial_data(kAppName, payload, strlen(payload))); + ASSERT_TRUE(gdial_app_read_additional_dial_data(kAppName, &out, &out_len)); + ASSERT_NE(out, nullptr); + EXPECT_EQ(out_len, strlen(payload)); + EXPECT_STREQ(out, payload); + g_free(out); + + EXPECT_TRUE(gdial_app_remove_additional_dial_data_file(kAppName)); +} + +TEST_F(GDialAppExtendedTest, StateResponseNew_GeneratesXml) { + int len = 0; + app->state = GDIAL_APP_STATE_RUNNING; + + gchar *xml = gdial_app_state_response_new( + app, + "2.2.1", + "2.2", + "urn:dial-multiscreen-org:schemas:dial", + &len); + + ASSERT_NE(xml, nullptr); + EXPECT_GT(len, 0); + EXPECT_NE(strstr(xml, "running"), nullptr); + xmlFree(xml); +} + +TEST_F(GDialAppExtendedTest, StateResponseNew_ClientBelow21MapsHiddenToStopped) { + int len = 0; + app->state = GDIAL_APP_STATE_HIDE; + + gchar *xml = gdial_app_state_response_new( + app, + "2.2.1", + "2.0", + "urn:dial-multiscreen-org:schemas:dial", + &len); + + ASSERT_NE(xml, nullptr); + EXPECT_NE(strstr(xml, "stopped"), nullptr); + xmlFree(xml); +} + diff --git a/tests/L1Tests/server/test_gdialService.cpp b/tests/L1Tests/server/test_gdialService.cpp new file mode 100644 index 00000000..b2304c5c --- /dev/null +++ b/tests/L1Tests/server/test_gdialService.cpp @@ -0,0 +1,476 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2026 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file test_gdialService.cpp + * + * Coverage strategy: + * - GDialServiceImplTest: exercises gdialServiceImpl singleton, sendRequest, + * notifyResponse (via on* callbacks), and stop_GDialServer without a live + * server — covers lines 831-848, 1119-1222. + * - GDialServiceTest: calls gdialService::getInstance() which always exercises + * start_GDialServer up to the point it fails in CI (loopback-only environment + * causes rest_http_server and local_rest_http_server to conflict on port 56889) + * — covers lines 850-961 + 222-332. + * - Public API tests are conditional: they GTEST_SKIP when getInstance returns + * null, and provide full coverage when a real non-loopback interface exists. + */ + +#include + +#include +#include + +#include "gdialservice.h" +#include "gdialserviceimpl.h" +#include "gdialservicecommon.h" + +#include "gdial-plat-util.h" + +/* ================================================================== */ +/* Minimal notifier */ +/* ================================================================== */ + +class TestServiceNotifier : public GDialNotifier { +public: + int update_power_calls = 0; + + void onApplicationLaunchRequest(std::string, std::string) override {} + void onApplicationLaunchRequestWithLaunchParam(std::string, std::string, std::string, std::string) override {} + void onApplicationStopRequest(std::string, std::string) override {} + void onApplicationHideRequest(std::string, std::string) override {} + void onApplicationResumeRequest(std::string, std::string) override {} + void onApplicationStateRequest(std::string, std::string) override {} + void updatePowerState(std::string) override { ++update_power_calls; } +}; + +/* ================================================================== */ +/* Helper: find an interface that has a non-loopback IPv4 address so */ +/* that rest_http_server and local_rest_http_server can bind to */ +/* different addresses (127.0.0.1 vs ), avoiding the port */ +/* conflict that prevents start_GDialServer from succeeding. */ +/* ================================================================== */ +static const char *find_usable_iface() { + static const char *candidates[] = { + "eth0", "eth1", "ens3", "ens4", "ens5", "ens33", + "enp0s3", "enp3s0", "enp0s8", "em1", "wlan0", nullptr + }; + for (int i = 0; candidates[i]; ++i) { + const char *ip = gdial_plat_util_get_iface_ipv4_addr(candidates[i]); + if (ip && strcmp(ip, "127.0.0.1") != 0) { + return candidates[i]; + } + } + return nullptr; /* only loopback available; start_GDialServer will fail */ +} + +/* ================================================================== */ +/* SECTION 1: gdialServiceImpl standalone tests */ +/* These always run and cover getInstance, destroyInstance, */ +/* stop_GDialServer (null-thread path), sendRequest, notifyResponse, */ +/* and all on* virtual method bodies in gdialservice.cpp. */ +/* ================================================================== */ + +class GDialServiceImplTest : public ::testing::Test { +protected: + TestServiceNotifier notifier; + + void SetUp() override { + gdialServiceImpl::destroyInstance(); + } + void TearDown() override { + gdialServiceImpl::destroyInstance(); + } +}; + +TEST_F(GDialServiceImplTest, GetInstance_ReturnsNonNull) { + gdialServiceImpl *impl = gdialServiceImpl::getInstance(); + ASSERT_NE(impl, nullptr); +} + +TEST_F(GDialServiceImplTest, GetInstance_ReturnsSingleton) { + gdialServiceImpl *a = gdialServiceImpl::getInstance(); + gdialServiceImpl *b = gdialServiceImpl::getInstance(); + EXPECT_EQ(a, b); +} + +TEST_F(GDialServiceImplTest, DestroyInstance_NoCrash) { + /* Covers destroyInstance + stop_GDialServer when no threads are running */ + gdialServiceImpl::getInstance(); + gdialServiceImpl::destroyInstance(); + SUCCEED(); +} + +TEST_F(GDialServiceImplTest, SendRequest_AppStateChanged_NoCrash) { + /* Covers sendRequest() body (lines ~1119-1127) */ + gdialServiceImpl *impl = gdialServiceImpl::getInstance(); + ASSERT_NE(impl, nullptr); + + RequestHandlerPayload p = {}; + p.event = APP_STATE_CHANGED; + p.appNameOrfriendlyname = "Netflix"; + p.appIdOractivation = "id1"; + p.state = "running"; + p.error = "none"; + impl->sendRequest(p); + SUCCEED(); +} + +TEST_F(GDialServiceImplTest, SendRequest_ActivationChanged_NoCrash) { + gdialServiceImpl *impl = gdialServiceImpl::getInstance(); + RequestHandlerPayload p = {}; + p.event = ACTIVATION_CHANGED; + p.appIdOractivation = "true"; + p.appNameOrfriendlyname = "TV"; + impl->sendRequest(p); + SUCCEED(); +} + +TEST_F(GDialServiceImplTest, SendRequest_FriendlyNameChanged_NoCrash) { + gdialServiceImpl *impl = gdialServiceImpl::getInstance(); + RequestHandlerPayload p = {}; + p.event = FRIENDLYNAME_CHANGED; + p.appNameOrfriendlyname = "LivingRoom"; + impl->sendRequest(p); + SUCCEED(); +} + +TEST_F(GDialServiceImplTest, SendRequest_RegisterApplications_NoCrash) { + gdialServiceImpl *impl = gdialServiceImpl::getInstance(); + RequestHandlerPayload p = {}; + p.event = REGISTER_APPLICATIONS; + p.data_param = nullptr; + impl->sendRequest(p); + SUCCEED(); +} + +TEST_F(GDialServiceImplTest, SendRequest_UpdateNwStandby_NoCrash) { + gdialServiceImpl *impl = gdialServiceImpl::getInstance(); + RequestHandlerPayload p = {}; + p.event = UPDATE_NW_STANDBY; + p.user_param1 = true; + impl->sendRequest(p); + SUCCEED(); +} + +TEST_F(GDialServiceImplTest, SendRequest_UpdateManufacturer_NoCrash) { + gdialServiceImpl *impl = gdialServiceImpl::getInstance(); + RequestHandlerPayload p = {}; + p.event = UPDATE_MANUFACTURER_NAME; + p.manufacturer = "Acme"; + impl->sendRequest(p); + SUCCEED(); +} + +TEST_F(GDialServiceImplTest, SendRequest_UpdateModel_NoCrash) { + gdialServiceImpl *impl = gdialServiceImpl::getInstance(); + RequestHandlerPayload p = {}; + p.event = UPDATE_MODEL_NAME; + p.model = "BoxV2"; + impl->sendRequest(p); + SUCCEED(); +} + +TEST_F(GDialServiceImplTest, OnApplicationLaunchRequest_NoCrash) { + /* Covers onApplicationLaunchRequest + notifyResponse (lines ~1153-1137) */ + gdialServiceImpl *impl = gdialServiceImpl::getInstance(); + impl->onApplicationLaunchRequest("App1", "param1"); + SUCCEED(); +} + +TEST_F(GDialServiceImplTest, OnApplicationLaunchRequestWithParams_NoCrash) { + gdialServiceImpl *impl = gdialServiceImpl::getInstance(); + impl->onApplicationLaunchRequestWithLaunchParam("App", "payload", "query", "url"); + SUCCEED(); +} + +TEST_F(GDialServiceImplTest, OnApplicationStopRequest_NoCrash) { + gdialServiceImpl *impl = gdialServiceImpl::getInstance(); + impl->onApplicationStopRequest("App1", "id1"); + SUCCEED(); +} + +TEST_F(GDialServiceImplTest, OnApplicationHideRequest_NoCrash) { + gdialServiceImpl *impl = gdialServiceImpl::getInstance(); + impl->onApplicationHideRequest("App1", "id1"); + SUCCEED(); +} + +TEST_F(GDialServiceImplTest, OnApplicationResumeRequest_NoCrash) { + gdialServiceImpl *impl = gdialServiceImpl::getInstance(); + impl->onApplicationResumeRequest("App1", "id1"); + SUCCEED(); +} + +TEST_F(GDialServiceImplTest, OnApplicationStateRequest_NoCrash) { + gdialServiceImpl *impl = gdialServiceImpl::getInstance(); + impl->onApplicationStateRequest("App1", "id1"); + SUCCEED(); +} + +TEST_F(GDialServiceImplTest, UpdatePowerState_NullObserver_NoCrash) { + /* m_observer is null (setService never called) - covers null branch */ + gdialServiceImpl *impl = gdialServiceImpl::getInstance(); + impl->updatePowerState("STANDBY"); + SUCCEED(); +} + +TEST_F(GDialServiceImplTest, UpdatePowerState_WithObserver_CallsThrough) { + /* Covers the m_observer branch in updatePowerState (line ~1219) */ + gdialServiceImpl *impl = gdialServiceImpl::getInstance(); + impl->setService(¬ifier); + impl->updatePowerState("ON"); + EXPECT_EQ(notifier.update_power_calls, 1); +} + +/* ================================================================== */ +/* SECTION 2: gdialService lifecycle — covers getInstance failure and */ +/* conditional success paths, plus destroyInstance. */ +/* ================================================================== */ + +class GDialServiceTest : public ::testing::Test { +protected: + TestServiceNotifier notifier; + gdialService *svc = nullptr; + + void SetUp() override { + gdialService::destroyInstance(); + + std::vector args; + const char *iface = find_usable_iface(); + if (iface) { + args.push_back("--network-interface"); + args.push_back(iface); + } + + svc = gdialService::getInstance(¬ifier, args, "L1Test"); + } + + void TearDown() override { + gdialService::destroyInstance(); + svc = nullptr; + } +}; + +TEST_F(GDialServiceTest, GetInstance_NoCrash) { + /* Covers getInstance() call path including start_GDialServer attempt */ + SUCCEED(); +} + +TEST_F(GDialServiceTest, DestroyInstance_NoCrash) { + gdialService::destroyInstance(); + svc = nullptr; + SUCCEED(); +} + +TEST_F(GDialServiceTest, ApplicationStateChanged_ReturnsNone) { + if (!svc) GTEST_SKIP() << "Service start failed (loopback-only CI env)"; + EXPECT_EQ(svc->ApplicationStateChanged("Netflix", "running", "id1", "none"), + GDIAL_SERVICE_ERROR_NONE); +} + +TEST_F(GDialServiceTest, ActivationChanged_TrueAndFalse_ReturnsNone) { + if (!svc) GTEST_SKIP() << "Service start failed (loopback-only CI env)"; + EXPECT_EQ(svc->ActivationChanged("true", "TV"), GDIAL_SERVICE_ERROR_NONE); + EXPECT_EQ(svc->ActivationChanged("false", "TV"), GDIAL_SERVICE_ERROR_NONE); +} + +TEST_F(GDialServiceTest, FriendlyNameChanged_ReturnsNone) { + if (!svc) GTEST_SKIP() << "Service start failed (loopback-only CI env)"; + EXPECT_EQ(svc->FriendlyNameChanged("LivingRoom"), GDIAL_SERVICE_ERROR_NONE); +} + +TEST_F(GDialServiceTest, RegisterApplications_WithList_ReturnsNone) { + if (!svc) GTEST_SKIP() << "Service start failed (loopback-only CI env)"; + RegisterAppEntryList *list = new RegisterAppEntryList; + RegisterAppEntry *e = new RegisterAppEntry; + e->Names = "Netflix"; + e->cors = ".netflix.com"; + list->pushBack(e); + EXPECT_EQ(svc->RegisterApplications(list), GDIAL_SERVICE_ERROR_NONE); +} + +TEST_F(GDialServiceTest, RegisterApplications_NullList_ReturnsNone) { + if (!svc) GTEST_SKIP() << "Service start failed (loopback-only CI env)"; + EXPECT_EQ(svc->RegisterApplications(nullptr), GDIAL_SERVICE_ERROR_NONE); +} + +TEST_F(GDialServiceTest, SetNetworkStandbyMode_NoCrash) { + if (!svc) GTEST_SKIP() << "Service start failed (loopback-only CI env)"; + svc->setNetworkStandbyMode(true); + svc->setNetworkStandbyMode(false); + SUCCEED(); +} + +TEST_F(GDialServiceTest, SetManufacturerName_ReturnsNone) { + if (!svc) GTEST_SKIP() << "Service start failed (loopback-only CI env)"; + EXPECT_EQ(svc->setManufacturerName("Acme"), GDIAL_SERVICE_ERROR_NONE); +} + +TEST_F(GDialServiceTest, SetModelName_ReturnsNone) { + if (!svc) GTEST_SKIP() << "Service start failed (loopback-only CI env)"; + EXPECT_EQ(svc->setModelName("BoxV2"), GDIAL_SERVICE_ERROR_NONE); +} + +TEST_F(GDialServiceTest, GetProtocolVersion_ReturnsNonEmpty) { + if (!svc) GTEST_SKIP() << "Service start failed (loopback-only CI env)"; + EXPECT_FALSE(svc->getProtocolVersion().empty()); +} + +/* ================================================================== */ +/* SECTION 2b: start_GDialServer app_list else branch */ +/* Passes --app-list so the option-parsing else branch executes, */ +/* covering all the g_strstr_len checks for netflix/youtube/etc. */ +/* Only reachable when a non-loopback interface is present. */ +/* ================================================================== */ + +class GDialServiceWithAppListTest : public ::testing::Test { +protected: + TestServiceNotifier notifier; + gdialService *svc = nullptr; + + void SetUp() override { + gdialService::destroyInstance(); + + const char *iface = find_usable_iface(); + if (!iface) return; /* TearDown still safe; svc stays null */ + + std::vector args = { + "--network-interface", iface, + "--app-list", "netflix,youtube,youtubetv,youtubekids,amazoninstantvideo,spotify,pairing,system" + }; + svc = gdialService::getInstance(¬ifier, args, "L1Test"); + } + + void TearDown() override { + gdialService::destroyInstance(); + svc = nullptr; + } +}; + +TEST_F(GDialServiceWithAppListTest, StartWithAppList_ExercisesElseBranch) { + /* Exercises the `else { ... }` block in start_GDialServer that processes + * options_.app_list — covers all g_strstr_len checks at lines ~360-440. */ + if (!svc) GTEST_SKIP() << "Service start failed (loopback-only CI env)"; + SUCCEED(); +} + +/* ================================================================== */ +/* SECTION 3: RegisterAppEntryList and enum helpers */ +/* ================================================================== */ + +TEST(RegisterAppEntryListTest, PushBackAndGetValuesPreservesOrder) { + RegisterAppEntryList list; + + for (int i = 0; i < 3; ++i) { + RegisterAppEntry *e = new RegisterAppEntry; + e->Names = "App" + std::to_string(i); + list.pushBack(e); + } + + const auto &vals = list.getValues(); + ASSERT_EQ(vals.size(), 3u); + EXPECT_EQ(vals[0]->Names, "App0"); + EXPECT_EQ(vals[1]->Names, "App1"); + EXPECT_EQ(vals[2]->Names, "App2"); +} + +TEST(RegisterAppEntryListTest, DestructorFreesEntries) { + { + RegisterAppEntryList list; + RegisterAppEntry *e = new RegisterAppEntry; + e->Names = "YouTube"; + list.pushBack(e); + } + SUCCEED(); +} + +TEST(AppRequestEventsEnumTest, InvalidRequestIsLast) { + EXPECT_GT(INVALID_REQUEST, REGISTER_APPLICATIONS); +} + +TEST(AppResponseEventsEnumTest, InvalidStateIsLast) { + EXPECT_GT(APP_INVALID_STATE, APP_RESUME_REQUEST); +} + +/* ------------------------------------------------------------------ */ +/* SECTION 4: server_register_application coverage */ +/* */ +/* Note: server_register_application is a static callback registered */ +/* during service startup. It's invoked via the callback chain: */ +/* sendRequest(REGISTER_APPLICATIONS) -> */ +/* gdial_plat_application_register_applications() -> */ +/* [stub invokes callback with GList*] -> */ +/* server_register_application() */ +/* */ +/* The actual callback invocation with proper GList* construction is */ +/* tested in test_gdialCpp.cpp via GDialCastObject::registerApps(). */ +/* These tests verify the service layer handles REGISTER_APPLICATIONS */ +/* events without crashing. */ +/* ------------------------------------------------------------------ */ + +TEST_F(GDialServiceImplTest, RegisterApplications_WithNullList_NoCrash) { + gdialServiceImpl *impl = gdialServiceImpl::getInstance(); + RequestHandlerPayload p = {}; + p.event = REGISTER_APPLICATIONS; + p.data_param = nullptr; + // Verify service handles null app list without crash + impl->sendRequest(p); + SUCCEED(); +} + +TEST_F(GDialServiceImplTest, RegisterApplications_WithAppList_NoCrash) { + gdialServiceImpl *impl = gdialServiceImpl::getInstance(); + RegisterAppEntryList *list = new RegisterAppEntryList; + RegisterAppEntry *entry = new RegisterAppEntry; + entry->Names = "TestApp"; + entry->prefixes = "com.test"; + entry->cors = ".test.com"; + entry->allowStop = true; + list->pushBack(entry); + + RequestHandlerPayload p = {}; + p.event = REGISTER_APPLICATIONS; + p.data_param = list; + // Verify service handles populated app list without crash + impl->sendRequest(p); + SUCCEED(); +} + +TEST_F(GDialServiceImplTest, RegisterApplications_MultipleEntries_NoCrash) { + gdialServiceImpl *impl = gdialServiceImpl::getInstance(); + RegisterAppEntryList *list = new RegisterAppEntryList; + + // Add multiple app entries + for (int i = 0; i < 3; ++i) { + RegisterAppEntry *entry = new RegisterAppEntry; + entry->Names = "App" + std::to_string(i); + entry->prefixes = "com.test" + std::to_string(i); + entry->cors = ".test.com"; + entry->allowStop = (i % 2 == 0); + list->pushBack(entry); + } + + RequestHandlerPayload p = {}; + p.event = REGISTER_APPLICATIONS; + p.data_param = list; + // Verify service handles multiple entries without crash + impl->sendRequest(p); + SUCCEED(); +} diff --git a/tests/L1Tests/server/test_gdialShield.cpp b/tests/L1Tests/server/test_gdialShield.cpp new file mode 100644 index 00000000..6a361625 --- /dev/null +++ b/tests/L1Tests/server/test_gdialShield.cpp @@ -0,0 +1,166 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2026 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +extern "C" { +#include +#include +#include "gdial-shield.h" +} + +namespace { + +static void ping_handler( + SoupServer *server, + SoupMessage *msg, + const char *path, + GHashTable *query, + SoupClientContext *client, + gpointer user_data) +{ + (void)server; + (void)path; + (void)query; + (void)client; + (void)user_data; + soup_message_set_status(msg, SOUP_STATUS_OK); + const char *payload = "pong"; + soup_message_set_response(msg, "text/plain", SOUP_MEMORY_COPY, payload, 4); +} + +class GDialShieldTest : public ::testing::Test { +protected: + SoupServer *server = nullptr; + SoupSession *session = nullptr; + GMainLoop *main_loop = nullptr; + GThread *main_loop_thread = nullptr; + bool shield_inited = false; + std::string base_url; + + void SetUp() override { + server = soup_server_new(nullptr, nullptr); + ASSERT_NE(server, nullptr); + + GError *error = nullptr; + ASSERT_TRUE(soup_server_listen_local(server, 0, SOUP_SERVER_LISTEN_IPV4_ONLY, &error)); + ASSERT_EQ(error, nullptr); + + GSList *uris = soup_server_get_uris(server); + ASSERT_NE(uris, nullptr); + SoupURI *uri = (SoupURI *)uris->data; + guint port = soup_uri_get_port(uri); + base_url = std::string("http://127.0.0.1:") + std::to_string(port); + g_slist_free_full(uris, (GDestroyNotify)soup_uri_free); + + soup_server_add_handler(server, "/ping", ping_handler, nullptr, nullptr); + + main_loop = g_main_loop_new(nullptr, FALSE); + ASSERT_NE(main_loop, nullptr); + main_loop_thread = g_thread_new( + "gdial-shield-test-loop", + [](gpointer data) -> gpointer { + g_main_loop_run((GMainLoop *)data); + return nullptr; + }, + main_loop); + ASSERT_NE(main_loop_thread, nullptr); + + for (int i = 0; i < 200 && !g_main_loop_is_running(main_loop); ++i) { + g_usleep(1000); + } + ASSERT_TRUE(g_main_loop_is_running(main_loop)); + + session = soup_session_new_with_options( + SOUP_SESSION_TIMEOUT, 5, + SOUP_SESSION_IDLE_TIMEOUT, 5, + NULL); + ASSERT_NE(session, nullptr); + } + + void TearDown() override { + if (shield_inited) { + gdial_shield_term(); + shield_inited = false; + } + + if (session) { + g_object_unref(session); + session = nullptr; + } + + if (main_loop) { + g_main_loop_quit(main_loop); + } + if (main_loop_thread) { + g_thread_join(main_loop_thread); + main_loop_thread = nullptr; + } + if (main_loop) { + g_main_loop_unref(main_loop); + main_loop = nullptr; + } + + if (server) { + g_object_unref(server); + server = nullptr; + } + } + + SoupMessage *send_get(const std::string &suffix) { + std::string url = base_url + suffix; + SoupMessage *msg = soup_message_new("GET", url.c_str()); + EXPECT_NE(msg, nullptr); + if (!msg) { + return nullptr; + } + soup_session_send_message(session, msg); + return msg; + } +}; + +TEST_F(GDialShieldTest, InitServerTerm_NoCrash) { + gdial_shield_init(); + shield_inited = true; + + gdial_shield_server(server); + + gdial_shield_term(); + shield_inited = false; + + SUCCEED(); +} + +TEST_F(GDialShieldTest, ShieldedServer_ProcessesBasicRequest) { + gdial_shield_init(); + shield_inited = true; + + gdial_shield_server(server); + + SoupMessage *msg = send_get("/ping"); + ASSERT_NE(msg, nullptr); + EXPECT_EQ(msg->status_code, SOUP_STATUS_OK); + ASSERT_NE(msg->response_body, nullptr); + ASSERT_NE(msg->response_body->data, nullptr); + EXPECT_NE(strstr(msg->response_body->data, "pong"), nullptr); + g_object_unref(msg); +} + +} // namespace diff --git a/tests/L1Tests/server/test_gdialSsdp.cpp b/tests/L1Tests/server/test_gdialSsdp.cpp new file mode 100644 index 00000000..01f0655b --- /dev/null +++ b/tests/L1Tests/server/test_gdialSsdp.cpp @@ -0,0 +1,247 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2026 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +extern "C" { +#include +#include +#include "gdial-ssdp.h" +#include "gdial-options.h" +void gdial_ssdp_networkstandbymode_handler(const bool nwstandby); +} + +namespace { + +class GDialSsdpTest : public ::testing::Test { +protected: + SoupServer *server = nullptr; + + static std::string BuildServerBaseUrl(SoupServer *srv) { + GSList *uris = soup_server_get_uris(srv); + if (!uris) return ""; + SoupURI *uri = static_cast(uris->data); + if (!uri) { + g_slist_free(uris); + return ""; + } + + char *uri_str = soup_uri_to_string(uri, FALSE); + std::string base = uri_str ? uri_str : ""; + g_free(uri_str); + soup_uri_free(uri); + g_slist_free(uris); + return base; + } + + void SetUp() override { + server = soup_server_new(nullptr, nullptr); + ASSERT_NE(server, nullptr); + } + + void TearDown() override { + if (server) { + g_object_unref(server); + server = nullptr; + } + } +}; + +TEST_F(GDialSsdpTest, New_NullServerRejected) { + GDialOptions opt = {}; + opt.iface_name = g_strdup("lo"); + EXPECT_EQ(gdial_ssdp_new(nullptr, &opt, "uuid1"), -1); + g_free(opt.iface_name); +} + +TEST_F(GDialSsdpTest, New_NullOptionsRejected) { + EXPECT_EQ(gdial_ssdp_new(server, nullptr, "uuid1"), -1); +} + +TEST_F(GDialSsdpTest, New_NullIfaceRejected) { + GDialOptions opt = {}; + opt.iface_name = nullptr; + EXPECT_EQ(gdial_ssdp_new(server, &opt, "uuid1"), -1); +} + +TEST_F(GDialSsdpTest, Setters_WithValues_NoCrash) { + EXPECT_EQ(gdial_ssdp_set_friendlyname("Living Room"), 0); + EXPECT_EQ(gdial_ssdp_set_manufacturername("Acme"), 0); + EXPECT_EQ(gdial_ssdp_set_modelname("ModelX"), 0); +} + +TEST_F(GDialSsdpTest, Setters_WithNull_NoCrash) { + EXPECT_EQ(gdial_ssdp_set_friendlyname(nullptr), 0); + EXPECT_EQ(gdial_ssdp_set_manufacturername(nullptr), 0); + EXPECT_EQ(gdial_ssdp_set_modelname(nullptr), 0); +} + +TEST_F(GDialSsdpTest, SetAvailable_NoInit_NoCrash) { + EXPECT_EQ(gdial_ssdp_set_available(true, "Kitchen"), 0); + EXPECT_EQ(gdial_ssdp_set_available(false, "Kitchen"), 0); +} + +TEST_F(GDialSsdpTest, NetworkStandbyHandler_NoInit_NoCrash) { + gdial_ssdp_networkstandbymode_handler(true); + gdial_ssdp_networkstandbymode_handler(false); + SUCCEED(); +} + +TEST_F(GDialSsdpTest, SsdpHttpCallback_GetDdXmlReturnsOkAndHeaders) { + GError *error = nullptr; + ASSERT_TRUE(soup_server_listen_local(server, 0, SOUP_SERVER_LISTEN_IPV4_ONLY, &error)); + ASSERT_EQ(error, nullptr); + + GDialOptions opt = {}; + opt.iface_name = g_strdup("lo"); + opt.friendly_name = g_strdup("L1Friendly"); + opt.manufacturer = g_strdup("L1Maker"); + opt.model_name = g_strdup("L1Model"); + opt.uuid = g_strdup("12345678-abcd-abcd-1234-123456789abc"); + + const char *uuid = "uuid_ut"; + ASSERT_EQ(gdial_ssdp_new(server, &opt, uuid), 0); + + /* Override the process-global app_manufacturer_name / app_model_name that + * may have been set by earlier Setters tests. The HTTP callback checks + * these statics first and only falls back to gdial_options_ when NULL. */ + gdial_ssdp_set_manufacturername("L1Maker"); + gdial_ssdp_set_modelname("L1Model"); + + std::string base = BuildServerBaseUrl(server); + ASSERT_FALSE(base.empty()); + std::string url = base + uuid + "/dd.xml"; + + /* + * Use async queue_message + g_main_loop_run so that both the outbound + * client I/O and the SoupServer's inbound dispatch are handled by the + * same g_main_context_default() iteration. Blocking send_message + * deadlocks because the server needs the context iterated while the + * test thread is blocked on the socket waiting for a response. + */ + struct Ctx { + GMainLoop *loop; + guint status = 0; + std::string app_url; + std::string body; + } ctx; + ctx.loop = g_main_loop_new(nullptr, FALSE); + + /* Safety watchdog — quits the loop if no response arrives in 5 s. */ + GSource *watchdog = g_timeout_source_new_seconds(5); + g_source_set_callback(watchdog, + [](gpointer d) -> gboolean { + g_main_loop_quit(static_cast(d)); + return G_SOURCE_REMOVE; + }, ctx.loop, nullptr); + g_source_attach(watchdog, nullptr); + g_source_unref(watchdog); + + SoupSession *session = soup_session_new_with_options( + SOUP_SESSION_TIMEOUT, (guint)5, nullptr); + ASSERT_NE(session, nullptr); + SoupMessage *msg = soup_message_new("GET", url.c_str()); + ASSERT_NE(msg, nullptr); + + /* queue_message transfers ownership of msg to the session. */ + soup_session_queue_message(session, msg, + [](SoupSession *, SoupMessage *m, gpointer d) { + auto *c = static_cast(d); + c->status = m->status_code; + const char *au = soup_message_headers_get_one( + m->response_headers, "Application-URL"); + c->app_url = au ? au : ""; + if (m->response_body && m->response_body->data) + c->body.assign(m->response_body->data, + (std::string::size_type)m->response_body->length); + g_main_loop_quit(c->loop); + }, &ctx); + + g_main_loop_run(ctx.loop); + g_main_loop_unref(ctx.loop); + g_object_unref(session); + + EXPECT_EQ(ctx.status, (guint)SOUP_STATUS_OK); + EXPECT_FALSE(ctx.app_url.empty()); + EXPECT_NE(ctx.body.find("L1Friendly"), std::string::npos); + EXPECT_NE(ctx.body.find("L1Maker"), std::string::npos); + EXPECT_NE(ctx.body.find("L1Model"), std::string::npos); + + gdial_ssdp_destroy(); +} + +TEST_F(GDialSsdpTest, SsdpHttpCallback_NonGetReturnsBadRequest) { + GError *error = nullptr; + ASSERT_TRUE(soup_server_listen_local(server, 0, SOUP_SERVER_LISTEN_IPV4_ONLY, &error)); + ASSERT_EQ(error, nullptr); + + GDialOptions opt = {}; + opt.iface_name = g_strdup("lo"); + opt.friendly_name = g_strdup("L1Friendly"); + opt.manufacturer = g_strdup("L1Maker"); + opt.model_name = g_strdup("L1Model"); + opt.uuid = g_strdup("12345678-abcd-abcd-1234-123456789abc"); + + const char *uuid = "uuid_ut"; + ASSERT_EQ(gdial_ssdp_new(server, &opt, uuid), 0); + + std::string base = BuildServerBaseUrl(server); + ASSERT_FALSE(base.empty()); + std::string url = base + uuid + "/dd.xml"; + + struct Ctx { + GMainLoop *loop; + guint status = 0; + } ctx; + ctx.loop = g_main_loop_new(nullptr, FALSE); + + GSource *watchdog = g_timeout_source_new_seconds(5); + g_source_set_callback(watchdog, + [](gpointer d) -> gboolean { + g_main_loop_quit(static_cast(d)); + return G_SOURCE_REMOVE; + }, ctx.loop, nullptr); + g_source_attach(watchdog, nullptr); + g_source_unref(watchdog); + + SoupSession *session = soup_session_new_with_options( + SOUP_SESSION_TIMEOUT, (guint)5, nullptr); + ASSERT_NE(session, nullptr); + SoupMessage *msg = soup_message_new("POST", url.c_str()); + ASSERT_NE(msg, nullptr); + + soup_session_queue_message(session, msg, + [](SoupSession *, SoupMessage *m, gpointer d) { + auto *c = static_cast(d); + c->status = m->status_code; + g_main_loop_quit(c->loop); + }, &ctx); + + g_main_loop_run(ctx.loop); + g_main_loop_unref(ctx.loop); + g_object_unref(session); + + EXPECT_EQ(ctx.status, (guint)SOUP_STATUS_BAD_REQUEST); + + gdial_ssdp_destroy(); +} + +} // namespace diff --git a/tests/L1Tests/server/test_gdialserver_ut.cpp b/tests/L1Tests/server/test_gdialserver_ut.cpp new file mode 100644 index 00000000..c7c9ffb2 --- /dev/null +++ b/tests/L1Tests/server/test_gdialserver_ut.cpp @@ -0,0 +1,160 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2026 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include + +namespace { +int g_get_instance_calls = 0; +int g_destroy_instance_calls = 0; +int g_activation_calls = 0; +int g_register_calls = 0; + +void reset_stub_state() { + g_get_instance_calls = 0; + g_destroy_instance_calls = 0; + g_activation_calls = 0; + g_register_calls = 0; +} +} // namespace + +#define gdialService gdialServiceFake +#define main gdialserver_ut_main +#include "../../../server/gdialserver_ut.cpp" +#undef main +#undef gdialService + +gdialServiceFake* gdialServiceFake::getInstance( + GDialNotifier* observer, + const std::vector& gdial_args, + const std::string& actualprocessName) +{ + (void)observer; + (void)gdial_args; + (void)actualprocessName; + ++g_get_instance_calls; + return reinterpret_cast(0x1); +} + +void gdialServiceFake::destroyInstance() { + ++g_destroy_instance_calls; +} + +GDIAL_SERVICE_ERROR_CODES gdialServiceFake::ApplicationStateChanged( + std::string applicationName, + std::string appState, + std::string applicationId, + std::string error) +{ + (void)applicationName; + (void)appState; + (void)applicationId; + (void)error; + return GDIAL_SERVICE_ERROR_NONE; +} + +GDIAL_SERVICE_ERROR_CODES gdialServiceFake::ActivationChanged(std::string activation, std::string friendlyname) { + (void)activation; + (void)friendlyname; + ++g_activation_calls; + return GDIAL_SERVICE_ERROR_NONE; +} + +GDIAL_SERVICE_ERROR_CODES gdialServiceFake::FriendlyNameChanged(std::string friendlyname) { + (void)friendlyname; + return GDIAL_SERVICE_ERROR_NONE; +} + +std::string gdialServiceFake::getProtocolVersion(void) { + return "2.2"; +} + +GDIAL_SERVICE_ERROR_CODES gdialServiceFake::RegisterApplications(RegisterAppEntryList* appConfigList) { + ++g_register_calls; + delete appConfigList; + return GDIAL_SERVICE_ERROR_NONE; +} + +void gdialServiceFake::setNetworkStandbyMode(bool nwStandbymode) { + (void)nwStandbymode; +} + +GDIAL_SERVICE_ERROR_CODES gdialServiceFake::setManufacturerName(std::string manufacturer) { + (void)manufacturer; + return GDIAL_SERVICE_ERROR_NONE; +} + +GDIAL_SERVICE_ERROR_CODES gdialServiceFake::setModelName(std::string model) { + (void)model; + return GDIAL_SERVICE_ERROR_NONE; +} + +class GDialServerUTMainTest : public ::testing::Test { +protected: + void SetUp() override { + running = true; + } + + void TearDown() override { + running = true; + } + + int run_main_with_input(const std::string& input, int argc = 1, char** argv = nullptr) { + std::istringstream input_stream(input); + std::streambuf* old_buf = std::cin.rdbuf(input_stream.rdbuf()); + + char default_arg0[] = "gdialserver_ut"; + char* default_argv[] = { default_arg0, nullptr }; + char** use_argv = argv ? argv : default_argv; + + int ret = gdialserver_ut_main(argc, use_argv); + + std::cin.rdbuf(old_buf); + return ret; + } +}; + +TEST_F(GDialServerUTMainTest, SignalHandlerStopsRunningLoop) { + running = true; + signalHandler(SIGINT); + EXPECT_FALSE(running.load()); +} + +TEST_F(GDialServerUTMainTest, MainQuitCommandExitsAndCleansUp) { + int ret = run_main_with_input("q\n"); + + EXPECT_EQ(ret, 0); + EXPECT_EQ(g_get_instance_calls, 1); + EXPECT_EQ(g_destroy_instance_calls, 1); + EXPECT_EQ(g_activation_calls, 0); + EXPECT_EQ(g_register_calls, 0); +} + +TEST_F(GDialServerUTMainTest, MainEnableDisableRegisterRestartFlow) { + int ret = run_main_with_input("enable\ndisable\nregister\nrestart\nq\n"); + + EXPECT_EQ(ret, 0); + EXPECT_EQ(g_get_instance_calls, 3); + EXPECT_EQ(g_destroy_instance_calls, 3); + EXPECT_EQ(g_activation_calls, 2); + EXPECT_EQ(g_register_calls, 1); +} diff --git a/tests/L1Tests/stubs/gdial_cpp_test_stubs.hpp b/tests/L1Tests/stubs/gdial_cpp_test_stubs.hpp new file mode 100644 index 00000000..f35c833d --- /dev/null +++ b/tests/L1Tests/stubs/gdial_cpp_test_stubs.hpp @@ -0,0 +1,130 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2026 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef GDIAL_CPP_TEST_STUBS_HPP_ +#define GDIAL_CPP_TEST_STUBS_HPP_ + +#include +#include +#include + +#ifndef _T +#define _T(x) x +#endif + +namespace WPEFramework { + +class JsonObject { +public: + std::map values; + + std::string &operator[](const std::string &key) + { + return values[key]; + } +}; + +namespace Core { + +static const uint32_t ERROR_NONE = 0; + +class SystemInfo { +public: + static void SetEnvironment(const std::string &, const std::string &) {} +}; + +namespace JSON { + +class IElement {}; + +class String { +public: + String() = default; + explicit String(const std::string &v) : value_(v) {} + + const std::string &Data() const { return value_; } + + String &operator=(const std::string &v) + { + value_ = v; + return *this; + } + +private: + std::string value_; +}; + +template +class ArrayType { +public: + class Iterator { + public: + explicit Iterator(const ArrayType &) {} + bool Next() { return false; } + const T &Current() const + { + static T t; + return t; + } + }; + + const ArrayType &Elements() const { return *this; } +}; + +} // namespace JSON +} // namespace Core + +namespace PluginHost { +namespace MetaData { + +struct Service { + Core::JSON::String JSONState; +}; + +} // namespace MetaData +} // namespace PluginHost + +namespace JSONRPC { + +template +class LinkType { +public: + LinkType(const std::string &, bool, const std::string &) {} + + template + uint32_t Get(uint32_t, const std::string &, R &) + { + return Core::ERROR_NONE; + } + + uint32_t Invoke(const std::string &, const JsonObject &, JsonObject &) + { + return Core::ERROR_NONE; + } +}; + +} // namespace JSONRPC +} // namespace WPEFramework + +inline int GetSecurityToken(int, unsigned char *) +{ + return 0; +} + +#endif diff --git a/tests/L1Tests/test_main.cpp b/tests/L1Tests/test_main.cpp new file mode 100644 index 00000000..5ee18834 --- /dev/null +++ b/tests/L1Tests/test_main.cpp @@ -0,0 +1,39 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2024 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +/** + * @file test_main.cpp + * @brief Main entry point for the xdialserver L1 unit test runner + * + * Initializes Google Test and Google Mock, then runs all registered test suites. + * Individual component tests live under subdirectories (e.g. server/, plat/, utils/) + * and are compiled into this single binary. + */ + +#include +#include + +/** + * Main test entry point + * Initializes the test framework and runs all registered tests + */ +int main(int argc, char **argv) { + ::testing::InitGoogleMock(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/tests/L1Tests/tests/gdialserver_ut.cpp b/tests/L1Tests/tests/gdialserver_ut.cpp new file mode 100644 index 00000000..3a0d1baa --- /dev/null +++ b/tests/L1Tests/tests/gdialserver_ut.cpp @@ -0,0 +1,53 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2024 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +/** + * @brief Example test class for xdialserver components + * + * Add your unit tests here. This is a template structure + * that can be expanded with actual test cases. + */ +class GDialServerTest : public ::testing::Test { +protected: + /** + * @brief SetUp is called before each test + */ + void SetUp() override { + // Initialize any test fixtures here + } + + /** + * @brief TearDown is called after each test + */ + void TearDown() override { + // Clean up any test fixtures here + } +}; + +/** + * @brief Example placeholder test + * + * Replace this with actual unit tests for xdialserver components + */ +TEST_F(GDialServerTest, ExampleTest) { + EXPECT_TRUE(true); +} diff --git a/tests/L1Tests/utils/test_gdialUtil.cpp b/tests/L1Tests/utils/test_gdialUtil.cpp new file mode 100644 index 00000000..fb94c2f5 --- /dev/null +++ b/tests/L1Tests/utils/test_gdialUtil.cpp @@ -0,0 +1,314 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2024 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file test_gdialUtil.cpp + * @brief Unit tests for gdial-util.c (server/gdial-util.h) + * + * Functions under test: + * gdial_util_is_ascii_printable + * gdial_util_str_str_hashtable_to_string + * gdial_util_str_str_hashtable_from_string + * gdial_util_str_str_hashtable_to_xml_string + * gdial_util_str_str_hashtable_dup + * gdial_util_str_str_hashtable_equal + * gdial_util_str_str_hashtable_merge + */ + +#include +#include + +extern "C" { +#include +#include "gdial-util.h" +} + +/* ================================================================== */ +/* Fixture: owns two GHashTable* and destroys them in TearDown */ +/* ================================================================== */ +class GDialUtilHashTableTest : public ::testing::Test { +protected: + GHashTable *ht1 = nullptr; + GHashTable *ht2 = nullptr; + + void SetUp() override { + ht1 = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + ht2 = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + } + + void TearDown() override { + if (ht1) { g_hash_table_destroy(ht1); ht1 = nullptr; } + if (ht2) { g_hash_table_destroy(ht2); ht2 = nullptr; } + } +}; + +/* ================================================================== */ +/* gdial_util_is_ascii_printable */ +/* ================================================================== */ + +TEST(GDialUtilTest, IsPrintable_AllPrintableChars) { + const gchar *s = "Hello World!"; + EXPECT_TRUE(gdial_util_is_ascii_printable(s, strlen(s))); +} + +TEST(GDialUtilTest, IsPrintable_WithWhitespace) { + /* Tabs and newlines count as g_ascii_isspace → should pass */ + const gchar *s = "foo\tbar\n"; + EXPECT_TRUE(gdial_util_is_ascii_printable(s, strlen(s))); +} + +TEST(GDialUtilTest, IsPrintable_NonPrintableControlChar) { + const gchar data[] = { '\x01', '\x02', '\0' }; + EXPECT_FALSE(gdial_util_is_ascii_printable(data, 2)); +} + +TEST(GDialUtilTest, IsPrintable_NullDataReturnsFalse) { + EXPECT_FALSE(gdial_util_is_ascii_printable(nullptr, 4)); +} + +TEST(GDialUtilTest, IsPrintable_ZeroLengthReturnsFalse) { + EXPECT_FALSE(gdial_util_is_ascii_printable("abc", 0)); +} + +/* ================================================================== */ +/* gdial_util_str_str_hashtable_to_string */ +/* ================================================================== */ + +TEST_F(GDialUtilHashTableTest, ToStr_ContainsKeyAndValue) { + g_hash_table_insert(ht1, g_strdup("k"), g_strdup("v")); + gsize len = 0; + gchar *s = gdial_util_str_str_hashtable_to_string(ht1, "=", FALSE, &len); + ASSERT_NE(s, nullptr); + EXPECT_NE(g_strstr_len(s, -1, "k"), nullptr); + EXPECT_NE(g_strstr_len(s, -1, "v"), nullptr); + EXPECT_GT(len, (gsize)0); + g_free(s); +} + +TEST_F(GDialUtilHashTableTest, ToStr_NullDelimiterDefaultsToSpace) { + g_hash_table_insert(ht1, g_strdup("k"), g_strdup("v")); + gsize len = 0; + gchar *s = gdial_util_str_str_hashtable_to_string(ht1, nullptr, FALSE, &len); + ASSERT_NE(s, nullptr); + EXPECT_NE(g_strstr_len(s, -1, " "), nullptr); + g_free(s); +} + +TEST_F(GDialUtilHashTableTest, ToStr_WithNewline) { + g_hash_table_insert(ht1, g_strdup("key"), g_strdup("val")); + gsize len = 0; + gchar *s = gdial_util_str_str_hashtable_to_string(ht1, "=", TRUE, &len); + ASSERT_NE(s, nullptr); + EXPECT_NE(g_strstr_len(s, -1, "\r\n"), nullptr); + g_free(s); +} + +TEST_F(GDialUtilHashTableTest, ToStr_NullTableReturnsNull) { + gsize len = 0; + EXPECT_EQ(gdial_util_str_str_hashtable_to_string(nullptr, "=", FALSE, &len), nullptr); +} + +TEST_F(GDialUtilHashTableTest, ToStr_NullLengthReturnsNull) { + g_hash_table_insert(ht1, g_strdup("k"), g_strdup("v")); + EXPECT_EQ(gdial_util_str_str_hashtable_to_string(ht1, "=", FALSE, nullptr), nullptr); +} + +/* ================================================================== */ +/* gdial_util_str_str_hashtable_from_string */ +/* ================================================================== */ + +TEST_F(GDialUtilHashTableTest, FromStr_ParsesSinglePair) { + /* Format expected by the parser: "key val\r\n" */ + const gchar *str = "mykey myval\r\n"; + EXPECT_TRUE(gdial_util_str_str_hashtable_from_string(str, strlen(str), ht1)); + EXPECT_STREQ((gchar *)g_hash_table_lookup(ht1, "mykey"), "myval"); +} + +TEST_F(GDialUtilHashTableTest, FromStr_NullStringReturnsFalse) { + EXPECT_FALSE(gdial_util_str_str_hashtable_from_string(nullptr, 4, ht1)); +} + +TEST_F(GDialUtilHashTableTest, FromStr_NullTableReturnsFalse) { + EXPECT_FALSE(gdial_util_str_str_hashtable_from_string("k v\r\n", 6, nullptr)); +} + +TEST_F(GDialUtilHashTableTest, FromStr_ParsesMultiplePairs) { + const gchar *str = "k1 v1\r\nk2 v2\r\n"; + EXPECT_TRUE(gdial_util_str_str_hashtable_from_string(str, strlen(str), ht1)); + EXPECT_STREQ((gchar *)g_hash_table_lookup(ht1, "k1"), "v1"); + EXPECT_STREQ((gchar *)g_hash_table_lookup(ht1, "k2"), "v2"); +} + +TEST_F(GDialUtilHashTableTest, FromStr_ZeroLengthNoEntries) { + EXPECT_TRUE(gdial_util_str_str_hashtable_from_string("k v\r\n", 0, ht1)); + EXPECT_EQ(g_hash_table_size(ht1), (guint)0); +} + +/* ================================================================== */ +/* gdial_util_str_str_hashtable_to_xml_string */ +/* ================================================================== */ + +TEST_F(GDialUtilHashTableTest, ToXml_ContainsAttrEqualsValue) { + g_hash_table_insert(ht1, g_strdup("xmlns"), g_strdup("urn:test")); + gsize len = 0; + gchar *s = gdial_util_str_str_hashtable_to_xml_string(ht1, &len); + ASSERT_NE(s, nullptr); + EXPECT_NE(g_strstr_len(s, -1, "xmlns"), nullptr); + EXPECT_NE(g_strstr_len(s, -1, "urn:test"), nullptr); + g_free(s); +} + +TEST_F(GDialUtilHashTableTest, ToXml_NullTableReturnsNull) { + gsize len = 0; + EXPECT_EQ(gdial_util_str_str_hashtable_to_xml_string(nullptr, &len), nullptr); +} + +TEST_F(GDialUtilHashTableTest, ToXml_NullLengthPointerAllowed) { + g_hash_table_insert(ht1, g_strdup("k"), g_strdup("v")); + gchar *s = gdial_util_str_str_hashtable_to_xml_string(ht1, nullptr); + ASSERT_NE(s, nullptr); + EXPECT_NE(g_strstr_len(s, -1, "k=\"v\""), nullptr); + g_free(s); +} + +/* ================================================================== */ +/* gdial_util_str_str_hashtable_dup */ +/* ================================================================== */ + +TEST_F(GDialUtilHashTableTest, Dup_ClonesAllEntries) { + g_hash_table_insert(ht1, g_strdup("a"), g_strdup("1")); + g_hash_table_insert(ht1, g_strdup("b"), g_strdup("2")); + GHashTable *copy = gdial_util_str_str_hashtable_dup(ht1); + ASSERT_NE(copy, nullptr); + EXPECT_EQ(g_hash_table_size(copy), (guint)2); + EXPECT_STREQ((gchar *)g_hash_table_lookup(copy, "a"), "1"); + EXPECT_STREQ((gchar *)g_hash_table_lookup(copy, "b"), "2"); + g_hash_table_destroy(copy); +} + +TEST_F(GDialUtilHashTableTest, Dup_IsDeepCopyNotSamePointer) { + g_hash_table_insert(ht1, g_strdup("key"), g_strdup("val")); + GHashTable *copy = gdial_util_str_str_hashtable_dup(ht1); + ASSERT_NE(copy, nullptr); + EXPECT_NE(copy, ht1); + g_hash_table_destroy(copy); +} + +TEST(GDialUtilTest, Dup_NullReturnsNull) { + EXPECT_EQ(gdial_util_str_str_hashtable_dup(nullptr), nullptr); +} + +/* ================================================================== */ +/* gdial_util_str_str_hashtable_equal */ +/* ================================================================== */ + +TEST_F(GDialUtilHashTableTest, Equal_EmptyTablesAreEqual) { + EXPECT_TRUE(gdial_util_str_str_hashtable_equal(ht1, ht2)); +} + +TEST_F(GDialUtilHashTableTest, Equal_SamePointerIsEqual) { + g_hash_table_insert(ht1, g_strdup("x"), g_strdup("y")); + EXPECT_TRUE(gdial_util_str_str_hashtable_equal(ht1, ht1)); +} + +TEST_F(GDialUtilHashTableTest, Equal_IdenticalContentsAreEqual) { + g_hash_table_insert(ht1, g_strdup("k"), g_strdup("v")); + g_hash_table_insert(ht2, g_strdup("k"), g_strdup("v")); + EXPECT_TRUE(gdial_util_str_str_hashtable_equal(ht1, ht2)); +} + +TEST_F(GDialUtilHashTableTest, Equal_DifferentValuesNotEqual) { + g_hash_table_insert(ht1, g_strdup("k"), g_strdup("v1")); + g_hash_table_insert(ht2, g_strdup("k"), g_strdup("v2")); + EXPECT_FALSE(gdial_util_str_str_hashtable_equal(ht1, ht2)); +} + +TEST_F(GDialUtilHashTableTest, Equal_DifferentSizeNotEqual) { + g_hash_table_insert(ht1, g_strdup("k"), g_strdup("v")); + /* ht2 is empty */ + EXPECT_FALSE(gdial_util_str_str_hashtable_equal(ht1, ht2)); +} + +TEST_F(GDialUtilHashTableTest, Equal_NullValuesAreEqual) { + g_hash_table_insert(ht1, g_strdup("k"), nullptr); + g_hash_table_insert(ht2, g_strdup("k"), nullptr); + EXPECT_TRUE(gdial_util_str_str_hashtable_equal(ht1, ht2)); +} + +TEST_F(GDialUtilHashTableTest, Equal_OneNullValueNotEqual) { + g_hash_table_insert(ht1, g_strdup("k"), nullptr); + g_hash_table_insert(ht2, g_strdup("k"), g_strdup("v")); + EXPECT_FALSE(gdial_util_str_str_hashtable_equal(ht1, ht2)); +} + +TEST_F(GDialUtilHashTableTest, Equal_NullRightNotEqual) { + EXPECT_FALSE(gdial_util_str_str_hashtable_equal(ht1, nullptr)); +} + +TEST_F(GDialUtilHashTableTest, Equal_NullLeftNotEqual) { + EXPECT_FALSE(gdial_util_str_str_hashtable_equal(nullptr, ht2)); +} + +/* ================================================================== */ +/* gdial_util_str_str_hashtable_merge */ +/* ================================================================== */ + +TEST_F(GDialUtilHashTableTest, Merge_AddsSrcKeysToDst) { + /* merge() reuses src key/value pointers inside dst via g_hash_table_replace. + * Use a non-owning src table here to avoid double-free in fixture teardown. */ + GHashTable *src = g_hash_table_new(g_str_hash, g_str_equal); + + g_hash_table_insert(ht1, g_strdup("a"), g_strdup("1")); + g_hash_table_insert(src, g_strdup("b"), g_strdup("2")); + + GHashTable *result = gdial_util_str_str_hashtable_merge(ht1, src); + + EXPECT_EQ(result, ht1); + EXPECT_EQ(g_hash_table_size(ht1), (guint)2); + EXPECT_STREQ((gchar *)g_hash_table_lookup(ht1, "b"), "2"); + + g_hash_table_destroy(src); +} + +TEST_F(GDialUtilHashTableTest, Merge_NullSrcReturnsDstUnchanged) { + g_hash_table_insert(ht1, g_strdup("k"), g_strdup("v")); + GHashTable *result = gdial_util_str_str_hashtable_merge(ht1, nullptr); + EXPECT_EQ(result, ht1); + EXPECT_EQ(g_hash_table_size(ht1), (guint)1); +} + +TEST_F(GDialUtilHashTableTest, Merge_NullDstReturnsNull) { + g_hash_table_insert(ht2, g_strdup("k"), g_strdup("v")); + GHashTable *result = gdial_util_str_str_hashtable_merge(nullptr, ht2); + EXPECT_EQ(result, nullptr); +} + +TEST_F(GDialUtilHashTableTest, Merge_OverwritesExistingKey) { + GHashTable *src = g_hash_table_new(g_str_hash, g_str_equal); + + g_hash_table_insert(ht1, g_strdup("k"), g_strdup("old")); + g_hash_table_insert(src, g_strdup("k"), g_strdup("new")); + + GHashTable *result = gdial_util_str_str_hashtable_merge(ht1, src); + EXPECT_EQ(result, ht1); + EXPECT_STREQ((gchar *)g_hash_table_lookup(ht1, "k"), "new"); + + g_hash_table_destroy(src); +} + diff --git a/tests/Makefile.am b/tests/Makefile.am new file mode 100644 index 00000000..1b70995c --- /dev/null +++ b/tests/Makefile.am @@ -0,0 +1,22 @@ +########################################################################## +# If not stated otherwise in this file or this component's LICENSE +# file the following copyright and licenses apply: +# +# Copyright 2019 RDK Management +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +########################################################################## + +if ENABLE_L1TESTS +SUBDIRS = L1Tests +endif diff --git a/tests/README.md b/tests/README.md index e25fbe47..a6d5e7eb 100644 --- a/tests/README.md +++ b/tests/README.md @@ -1,2 +1,134 @@ # Tests -The test files will be updated to this repo in near future. + +This folder contains the xdialserver test harness and all unit-level (L1) tests. + +## What Is Here + +Current top-level layout: + +- tests/L1Tests: L1 test sources and build recipe +- tests/mocks: shared mock code used by tests +- tests/Makefile.am: autotools entry for test subdirectories +- tests/README.md: this guide + +Inside tests/L1Tests: + +- tests/L1Tests/server: server-layer tests (REST, SSDP, shield, service, app lifecycle) +- tests/L1Tests/plat: platform integration tests for plat APIs and gdial.cpp adapter paths +- tests/L1Tests/utils: utility tests +- tests/L1Tests/stubs: shadow headers and test-specific C/C++ shim headers +- tests/L1Tests/mocks: C mocks compiled into run_L1Tests +- tests/L1Tests/tests: legacy unit file coverage entry points +- tests/L1Tests/test_main.cpp: gtest main +- tests/L1Tests/Makefile.am: source list and compile/link flags + +## How The L1 System Works + +The L1 test binary is a single executable: + +- run_L1Tests + +It links: + +- test files from tests/L1Tests +- selected real implementation files from server and server/plat +- test stubs and mocks that replace external dependencies + +Key behavior: + +1. Real source + selective stubbing +- We compile real modules for behavior coverage, but override dependencies below those modules. +- Example: a real server/plat module can be tested while its OS/backend calls are stubbed. + +2. Include shadowing for external frameworks +- tests/L1Tests/stubs is first on the include path. +- CI generates lightweight wrapper headers there so build-time includes resolve without requiring full external frameworks. + +3. One process, shared globals +- Most L1 tests run in the same process, so static/global state in C modules can leak between tests unless explicitly reset. +- Tests that touch module-level caches must clean up in TearDown. + +## Build And Run Locally + +Typical local flow: + +1. Generate autotools files +- autoreconf -if + +2. Configure with L1 enabled +- ./configure --enable-l1tests + +3. Build +- make -C tests/L1Tests + +4. Run +- ./tests/L1Tests/run_L1Tests + +Optional single-test execution: + +- ./tests/L1Tests/run_L1Tests --gtest_filter=GDialSsdpTest.* +- ./tests/L1Tests/run_L1Tests --gtest_filter=GDialPlatAppTest.* + +## CI Workflow Summary + +The workflow in .github/workflows/L1-tests.yml does the following: + +1. Installs dependencies and builds googletest +2. Generates stub wrapper headers under tests/L1Tests/stubs +3. Builds run_L1Tests using autotools +4. Runs tests normally and under valgrind +5. Publishes test results, valgrind log, and coverage artifacts + +## Adding Or Modifying Tests Safely + +When adding tests: + +1. Place file in the matching area +- server logic: tests/L1Tests/server +- platform logic: tests/L1Tests/plat +- utility helpers: tests/L1Tests/utils + +2. Register file in tests/L1Tests/Makefile.am +- Add it to run_L1Tests_SOURCES or it will not compile in CI. + +3. Isolate global state +- If module under test uses static/global variables, reset via module destroy/reset APIs in TearDown. +- Do not rely on test execution order. + +4. Prefer deterministic async tests +- For GLib/libsoup event paths, avoid timing-sensitive sleeps when possible. +- Use explicit loop-driven completion or cancel/remove handles in tests to avoid race-driven flakes. + +5. Keep assertions aligned with implementation contracts +- Validate against current API behavior and constants from headers. +- If a behavior changed intentionally, update tests and document the expected contract in the test name. + +## Known Pitfalls (Important) + +1. Static cache/state in SSDP tests +- server/gdial-ssdp.c caches dd.xml response and keeps process-global name/model overrides. +- If a test sets manufacturer/model through setter APIs, later tests may observe those values unless reset or overridden. + +2. Async source lifecycle in platform app tests +- server/plat/gdial-plat-app.c async calls use GLib timeout sources. +- Running timer callbacks near teardown can trigger use-after-free races if test cleanup is not deterministic. +- Prefer cancel/remove flow tests over race-prone timer execution unless explicitly testing callback timing. + +3. Header dependency visibility in C++ tests +- Some C headers expose GLib types such as gboolean. +- In C++ test translation units, include glib.h before such headers when needed to avoid type-resolution build breaks. + +## Quick Troubleshooting + +If CI fails but local passes: + +1. Re-run only the failing fixture with gtest_filter. +2. Run under valgrind locally if available. +3. Check for shared static state and missing teardown cleanup. +4. Verify test source is included in tests/L1Tests/Makefile.am. +5. Check .github/workflows/L1-tests.yml for CI-only generated stubs that local build may not have. + +## Maintainer Notes + +- Keep this document updated whenever structure or test harness behavior changes. +- When introducing new shared stubs or wrappers, document where they are generated and why.