From 0c905b2ba1e502d96f895276f2d769240bab08f3 Mon Sep 17 00:00:00 2001 From: Yogeswaran K Date: Wed, 11 Feb 2026 13:13:17 +0000 Subject: [PATCH] RDK-60476: Update L1 and L2 testcases for fork elimination Signed-off-by: Yogeswaran K --- source/test/mocks/FileioMock.cpp | 48 ++- source/test/mocks/FileioMock.h | 4 +- source/test/protocol/ProtocolTest.cpp | 247 ++++++++---- source/test/xconf-client/xconfclientTest.cpp | 383 ++++++++++++------- 4 files changed, 460 insertions(+), 222 deletions(-) diff --git a/source/test/mocks/FileioMock.cpp b/source/test/mocks/FileioMock.cpp index 81cf54d1..60e19aaf 100644 --- a/source/test/mocks/FileioMock.cpp +++ b/source/test/mocks/FileioMock.cpp @@ -49,13 +49,14 @@ typedef struct curl_slist* (*curl_slist_append_ptr) (struct curl_slist *list, co typedef CURL* (*curl_easy_init_ptr)(); typedef CURLcode (*curl_easy_setopt_mock_ptr) (CURL *curl, CURLoption option, void* parameter); typedef CURLcode (*curl_easy_getinfo_mock_ptr) (CURL *curl, CURLINFO info, void* arg); +typedef CURLcode (*curl_easy_getinfo_ptr) (CURL *curl, CURLINFO info, ...); typedef long (*ftell_ptr) (FILE *stream); typedef int (*fscanf_ptr) (FILE *, const char *, va_list); typedef pid_t (*fork_ptr) (); typedef ssize_t (*write_ptr) (int fd, const void *buf, size_t count); //typedef int (*stat_ptr) (const char *pathname, struct stat *statbuf); typedef int (*fprintf_ptr) (FILE* stream, const char* format, va_list args); -//pedef CURLcode (*curl_easy_setopt_ptr) (CURL *curl, CURLoption option, parameter); +typedef CURLcode (*curl_easy_setopt_ptr) (CURL *curl, CURLoption option, ...); typedef void (*curl_easy_cleanup_ptr) (CURL *handle); typedef void (*curl_slist_free_all_ptr) (struct curl_slist *list); typedef int (*munmap_ptr) (void *addr, size_t len); @@ -102,7 +103,9 @@ curl_easy_setopt_mock_ptr curl_easy_setopt_mock_func = (curl_easy_setopt_mock_pt curl_easy_getinfo_mock_ptr curl_easy_getinfo_mock_func = (curl_easy_getinfo_mock_ptr) dlsym(RTLD_NEXT, "curl_easy_getinfo_mock"); curl_easy_cleanup_ptr curl_easy_cleanup_func = (curl_easy_cleanup_ptr) dlsym(RTLD_NEXT, "curl_easy_cleanup"); curl_slist_free_all_ptr curl_slist_free_all_func = (curl_slist_free_all_ptr) dlsym(RTLD_NEXT, "curl_slist_free_all"); -//curl_easy_setopt_ptr curl_easy_setopt_func = (curl_easy_setopt_ptr) dlsym(RTLD_NEXT, "curl_easy_setopt"); +curl_easy_setopt_ptr curl_easy_setopt_func = (curl_easy_setopt_ptr) dlsym(RTLD_NEXT, "curl_easy_setopt"); +curl_easy_getinfo_ptr curl_easy_getinfo_func = (curl_easy_getinfo_ptr) dlsym(RTLD_NEXT, "curl_easy_getinfo"); + munmap_ptr munmap_func = (munmap_ptr) dlsym(RTLD_NEXT, "munmap"); mmap_ptr mmap_func = (mmap_ptr) dlsym(RTLD_NEXT, "mmap"); mkstemp_ptr mkstemp_func = (mkstemp_ptr) dlsym(RTLD_NEXT, "mkstemp"); @@ -386,6 +389,34 @@ extern "C" CURLcode curl_easy_getinfo_mock(CURL *curl, CURLINFO info, void* arg) return g_fileIOMock->curl_easy_getinfo_mock(curl, info, arg); } +extern "C" CURLcode curl_easy_getinfo(CURL *curl, CURLINFO info, ...) +{ + if (g_fileIOMock != nullptr) { + va_list args; + va_start(args, info); + void* arg = va_arg(args, void*); + va_end(args); + return g_fileIOMock->curl_easy_getinfo_mock(curl, info, arg); + } + + // Fallback to real function + if (curl_easy_getinfo_func) { + va_list args; + va_start(args, info); + void* arg = va_arg(args, void*); + va_end(args); + + // Call real function with extracted argument + if (info == CURLINFO_RESPONSE_CODE) { + return curl_easy_getinfo_func(curl, info, (long*)arg); + } else { + return curl_easy_getinfo_func(curl, info, arg); + } + } + + return CURLE_FAILED_INIT; +} + extern "C" struct curl_slist *curl_slist_append(struct curl_slist *list, const char * string) { if (g_fileIOMock == nullptr){ @@ -459,6 +490,19 @@ extern "C" CURLcode curl_easy_setopt(CURL *curl, CURLoption option, ...) return result; } */ + +extern "C" CURLcode curl_easy_setopt(CURL* handle, CURLoption option, ...) { + if (g_fileIOMock != nullptr) { + va_list args; + va_start(args, option); + void* param = va_arg(args, void*); + va_end(args); + return g_fileIOMock->curl_easy_setopt(handle, option, param); + } + return CURLE_FAILED_INIT; +} + + extern "C" void curl_easy_cleanup(CURL *handle) { if (g_fileIOMock == nullptr){ diff --git a/source/test/mocks/FileioMock.h b/source/test/mocks/FileioMock.h index f97cb717..9551ac7b 100644 --- a/source/test/mocks/FileioMock.h +++ b/source/test/mocks/FileioMock.h @@ -86,9 +86,9 @@ class FileMock MOCK_METHOD(ssize_t, getline, (char** lineptr, size_t* n, FILE* stream), ()); MOCK_METHOD(pid_t, fork, (), ()); MOCK_METHOD(ssize_t, write, (int fd, const void* buf, size_t count), ()); - //MOCK_METHOD(CURLcode, curl_easy_setopt, (CURL *handle, CURLoption option, parameter), ()); + MOCK_METHOD(CURLcode, curl_easy_setopt, (CURL *handle, CURLoption option, void *parameter), ()); MOCK_METHOD(void, curl_easy_cleanup, (CURL *handle), ()); - MOCK_METHOD(CURLcode, curl_easy_setopt_mock, (CURL *handle, CURLoption option, void* parameter), ()); + MOCK_METHOD(CURLcode, curl_easy_setopt_mock, (CURL *handle, CURLoption option, void *parameter), ()); MOCK_METHOD(CURLcode, curl_easy_getinfo_mock, (CURL *curl, CURLINFO info, void* arg), ()); MOCK_METHOD(void, curl_slist_free_all, (struct curl_slist *list), ()); //MOCK_METHOD(int, stat, (const char *pathname, struct stat *statbuf), ()); diff --git a/source/test/protocol/ProtocolTest.cpp b/source/test/protocol/ProtocolTest.cpp index 25a24f76..5911e496 100644 --- a/source/test/protocol/ProtocolTest.cpp +++ b/source/test/protocol/ProtocolTest.cpp @@ -80,6 +80,15 @@ using ::testing::_; using ::testing::Return; using ::testing::StrEq; +// Helper macro to prevent deadlock from mocked fwrite and fputs calls in the protocol code when GTest tries to log output, which can cause a deadlock if the logging functions are mocked without allowing real calls to them. +// Helper macro to prevent deadlock from mocked fwrite and fputs calls in the protocol code when GTest tries to log output, which can cause a deadlock if the logging functions are mocked without allowing real calls to them. +#define PREVENT_GTEST_LOGGING_DEADLOCK() \ + EXPECT_CALL(*g_fileIOMock, fwrite(::testing::_, ::testing::_, ::testing::_, ::testing::_)) \ + .Times(::testing::AnyNumber()) \ + .WillRepeatedly(::testing::Invoke( \ + [](const void* ptr, size_t size, size_t nitems, FILE* stream) { \ + return ::fwrite(ptr, size, nitems, stream); \ + })) class protocolTestFixture : public ::testing::Test { protected: @@ -91,6 +100,46 @@ class protocolTestFixture : public ::testing::Test { g_rbusMock = new rbusMock(); g_rdkconfigMock = new rdkconfigMock(); + // Set default behaviors for curl functions to prevent them from being called + EXPECT_CALL(*g_fileIOMock, curl_easy_init()) + .Times(::testing::AnyNumber()) + .WillRepeatedly(::testing::Return(nullptr)); + + EXPECT_CALL(*g_fileIOMock, curl_easy_perform(::testing::_)) + .Times(::testing::AnyNumber()) + .WillRepeatedly(::testing::Return(CURLE_FAILED_INIT)); + + EXPECT_CALL(*g_fileIOMock, curl_easy_cleanup(::testing::_)) + .Times(::testing::AnyNumber()); + + EXPECT_CALL(*g_fileIOMock, curl_easy_setopt(::testing::_, ::testing::_, ::testing::_)) + .Times(::testing::AnyNumber()) + .WillRepeatedly(::testing::Return(CURLE_OK)); + + // Add curl_slist functions to prevent deadlock during pool initialization + EXPECT_CALL(*g_fileIOMock, curl_slist_append(::testing::_, ::testing::_)) + .Times(::testing::AnyNumber()) + .WillRepeatedly(::testing::Invoke([](struct curl_slist* list, const char* str) { + // Return a fake non-null pointer to simulate successful append + static int counter = 1; + return (struct curl_slist*)(uintptr_t)(0x2000 + counter++); + })); + + EXPECT_CALL(*g_fileIOMock, curl_slist_free_all(::testing::_)) + .Times(::testing::AnyNumber()); + + EXPECT_CALL(*g_fileIOMock, fopen(::testing::_, ::testing::_)) + .Times(::testing::AnyNumber()) + .WillRepeatedly(::testing::Return((FILE*)nullptr)) + .RetiresOnSaturation(); + + EXPECT_CALL(*g_fileIOMock, fwrite(::testing::_, ::testing::_, ::testing::_, ::testing::_)) + .Times(::testing::AnyNumber()) + .WillRepeatedly(::testing::Invoke( + [](const void* ptr, size_t size, size_t nitems, FILE* stream) { + return ::fwrite(ptr, size, nitems, stream); + })) + .RetiresOnSaturation(); } void TearDown() override @@ -109,20 +158,22 @@ class protocolTestFixture : public ::testing::Test { } }; - -#if 0 TEST(SENDREPORTOVERHTTP, 1_NULL_CHECK) { char *payload = "This is a payload string"; + //PREVENT_GTEST_LOGGING_DEADLOCK(); EXPECT_EQ(T2ERROR_FAILURE, sendReportOverHTTP(NULL, payload)); } TEST(SENDREPORTOVERHTTP, 2_NULL_CHECK) { char *url = "https://test.com"; + //PREVENT_GTEST_LOGGING_DEADLOCK(); EXPECT_EQ(T2ERROR_FAILURE, sendReportOverHTTP(url, NULL)); } + +#if 1 TEST(SENDCACREPOVERHTTP, 1_NULL_CHECK) { Vector* reportlist; @@ -188,15 +239,53 @@ TEST(SENDRBUSCACHEREPORTOVERRBUS, NULL_CHECK) Vector_Destroy(reportList, free); } -#if 0 +TEST_F(protocolTestFixture, SENDREPORTOVERHTTP0) +{ + char* httpURL = "https://mockxconf:50051/dataLakeMock"; + char* payload = strdup("This is a payload string"); + //http_pool_cleanup(); // Ensure pool is cleaned up before test to prevent interference from previous tests + EXPECT_CALL(*g_fileIOMock, curl_easy_init()) + .Times(::testing::AnyNumber()) + .WillRepeatedly(::testing::Return(nullptr)); + EXPECT_CALL(*m_xconfclientMock, isMtlsEnabled()) + .Times(1) + .WillOnce(Return(0)); + EXPECT_EQ(T2ERROR_FAILURE, sendReportOverHTTP(httpURL, payload)); + free(payload); +} + TEST_F(protocolTestFixture, SENDREPORTOVERHTTP1) { - char* httpURL = "https://mockxconf:50051/dataLakeMock"; - char* payload = strdup("This is a payload string"); - EXPECT_CALL(*g_fileIOMock, pipe(_)) - .Times(1) - .WillOnce(Return(-1)); - EXPECT_EQ(T2ERROR_FAILURE, sendReportOverHTTP(httpURL, payload)); + char* httpURL = "https://mockxconf:50051/dataLakeMock"; + char* payload = strdup("This is a payload string"); + + EXPECT_CALL(*g_fileIOMock, curl_easy_init()) + .Times(::testing::AnyNumber()) + .WillRepeatedly(::testing::Invoke([]() { + // Return unique fake handles for each call + static int handle_counter = 1; + return (CURL*)(uintptr_t)(0x1000 + handle_counter++); + })); + + EXPECT_CALL(*m_xconfclientMock, isMtlsEnabled()) + .Times(1) + .WillOnce(Return(0)); + EXPECT_CALL(*g_fileIOMock, curl_easy_perform(_)) + .Times(1) + .WillOnce(Return(CURLE_OK)); + EXPECT_CALL(*g_fileIOMock, curl_easy_getinfo_mock(_,_,_)) + .Times(::testing::AtLeast(1)) + .WillRepeatedly([](CURL* curl, CURLINFO info, void* response_code) { + if (info == CURLINFO_RESPONSE_CODE) { + *(long*)response_code = 200; + } + return CURLE_OK; + }); + + //Add curl_easy_cleanup expectation + EXPECT_CALL(*g_fileIOMock, curl_easy_cleanup(_)) + .Times(::testing::AnyNumber()); + EXPECT_EQ(T2ERROR_SUCCESS, sendReportOverHTTP(httpURL, payload)); free(payload); } @@ -204,10 +293,15 @@ TEST_F(protocolTestFixture, SENDREPORTOVERHTTP2) { char* httpURL = "https://mockxconf:50051/dataLakeMock"; char* payload = strdup("This is a payload string"); - char *cm = (char*)0xFFFFFFFF; - EXPECT_CALL(*g_fileIOMock, pipe(_)) - .Times(1) - .WillOnce(Return(0)); + + EXPECT_CALL(*g_fileIOMock, curl_easy_init()) + .Times(::testing::AnyNumber()) + .WillRepeatedly(::testing::Invoke([]() { + // Return unique fake handles for each call + static int handle_counter = 1; + return (CURL*)(uintptr_t)(0x1000 + handle_counter++); + })); + #if defined(ENABLE_RDKB_SUPPORT) && !defined(RDKB_EXTENDER) #if defined(WAN_FAILOVER_SUPPORTED) || defined(FEATURE_RDKB_CONFIGURABLE_WAN_INTERFACE) EXPECT_CALL(*m_xconfclientMock, getParameterValue(_,_)) @@ -226,10 +320,22 @@ TEST_F(protocolTestFixture, SENDREPORTOVERHTTP2) .Times(1) .WillOnce(Return(RDKCONFIG_OK)); #endif - EXPECT_CALL(*g_fileIOMock, fork()) - .Times(1) - .WillOnce(Return(-1)); - EXPECT_EQ(T2ERROR_FAILURE, sendReportOverHTTP(httpURL, payload)); + EXPECT_CALL(*g_fileIOMock, curl_easy_perform(_)) + .Times(1) + .WillOnce(Return(CURLE_OK)); + EXPECT_CALL(*g_fileIOMock, curl_easy_getinfo_mock(_,_,_)) + .Times(::testing::AtLeast(1)) + .WillRepeatedly([](CURL* curl, CURLINFO info, void* response_code) { + if (info == CURLINFO_RESPONSE_CODE) { + *(long*)response_code = 200; + } + return CURLE_OK; + }); + + //Add curl_easy_cleanup expectation + EXPECT_CALL(*g_fileIOMock, curl_easy_cleanup(_)) + .Times(::testing::AnyNumber()); + EXPECT_EQ(T2ERROR_SUCCESS, sendReportOverHTTP(httpURL, payload)); free(payload); } @@ -239,9 +345,15 @@ TEST_F(protocolTestFixture, SENDREPORTOVERHTTP3) char* httpURL = "https://mockxconf:50051/dataLakeMock"; char* payload = strdup("This is a payload string"); char *cm = (char*)0xFFFFFFFF; - EXPECT_CALL(*g_fileIOMock, pipe(_)) - .Times(1) - .WillOnce(Return(0)); + + EXPECT_CALL(*g_fileIOMock, curl_easy_init()) + .Times(::testing::AnyNumber()) + .WillRepeatedly(::testing::Invoke([]() { + // Return unique fake handles for each call + static int handle_counter = 1; + return (CURL*)(uintptr_t)(0x1000 + handle_counter++); + })); + #if defined(ENABLE_RDKB_SUPPORT) && !defined(RDKB_EXTENDER) #if defined(WAN_FAILOVER_SUPPORTED) || defined(FEATURE_RDKB_CONFIGURABLE_WAN_INTERFACE) EXPECT_CALL(*m_xconfclientMock, getParameterValue(_,_)) @@ -260,17 +372,7 @@ TEST_F(protocolTestFixture, SENDREPORTOVERHTTP3) .Times(1) .WillOnce(Return(RDKCONFIG_OK)); #endif - EXPECT_CALL(*g_fileIOMock, fork()) - .Times(1) - .WillOnce(Return(1)); - EXPECT_CALL(*g_fileIOMock, close(_)) - .Times(2) - .WillOnce(Return(-1)) - .WillOnce(Return(-1)); - EXPECT_CALL(*g_fileIOMock, read(_,_,_)) - .Times(1) - .WillOnce(Return(-1)); - EXPECT_EQ(T2ERROR_SUCCESS, sendReportOverHTTP(httpURL, payload)); + EXPECT_EQ(T2ERROR_FAILURE, sendReportOverHTTP(httpURL, payload)); free(payload); } @@ -282,9 +384,14 @@ TEST_F(protocolTestFixture, SENDCACHEDREPORTOVERHTTP) Vector_Create(&reportlist); Vector_PushBack(reportlist, payload); char *cm = (char*)0xFFFFFFFF; - EXPECT_CALL(*g_fileIOMock, pipe(_)) - .Times(1) - .WillOnce(Return(0)); + + EXPECT_CALL(*g_fileIOMock, curl_easy_init()) + .Times(::testing::AnyNumber()) + .WillRepeatedly(::testing::Invoke([]() { + // Return unique fake handles for each call + static int handle_counter = 1; + return (CURL*)(uintptr_t)(0x1000 + handle_counter++); + })); #if defined(ENABLE_RDKB_SUPPORT) && !defined(RDKB_EXTENDER) #if defined(WAN_FAILOVER_SUPPORTED) || defined(FEATURE_RDKB_CONFIGURABLE_WAN_INTERFACE) EXPECT_CALL(*m_xconfclientMock, getParameterValue(_,_)) @@ -303,20 +410,25 @@ TEST_F(protocolTestFixture, SENDCACHEDREPORTOVERHTTP) .Times(1) .WillOnce(Return(RDKCONFIG_OK)); #endif - EXPECT_CALL(*g_fileIOMock, fork()) - .Times(1) - .WillOnce(Return(1)); - EXPECT_CALL(*g_fileIOMock, close(_)) - .Times(2) - .WillOnce(Return(-1)) - .WillOnce(Return(-1)); - EXPECT_CALL(*g_fileIOMock, read(_,_,_)) - .Times(1) - .WillOnce(Return(-1)); + EXPECT_CALL(*g_fileIOMock, curl_easy_perform(_)) + .Times(1) + .WillOnce(Return(CURLE_OK)); + EXPECT_CALL(*g_fileIOMock, curl_easy_getinfo_mock(_,_,_)) + .Times(::testing::AtLeast(1)) + .WillRepeatedly([](CURL* curl, CURLINFO info, void* response_code) { + if (info == CURLINFO_RESPONSE_CODE) { + *(long*)response_code = 200; + } + return CURLE_OK; + }); + + //Add curl_easy_cleanup expectation + EXPECT_CALL(*g_fileIOMock, curl_easy_cleanup(_)) + .Times(::testing::AnyNumber()); + EXPECT_EQ(T2ERROR_SUCCESS, sendCachedReportsOverHTTP(httpURL, reportlist)); Vector_Destroy(reportlist, free); } -#endif TEST_F(protocolTestFixture, SENDREPORTSOVERRBUSMETHOD1) { @@ -442,9 +554,11 @@ TEST_F(protocolTestFixture, sendReportOverHTTP_6) Vector_Create(&reportlist); Vector_PushBack(reportlist, payload); char *cm = (char*)0xFFFFFFFF; - EXPECT_CALL(*g_fileIOMock, pipe(_)) - .Times(1) - .WillOnce(Return(0)); + + EXPECT_CALL(*g_fileIOMock, curl_easy_init()) + .Times(::testing::AnyNumber()) + .WillRepeatedly(::testing::Return(nullptr)); + #if defined(ENABLE_RDKB_SUPPORT) && !defined(RDKB_EXTENDER) #if defined(WAN_FAILOVER_SUPPORTED) || defined(FEATURE_RDKB_CONFIGURABLE_WAN_INTERFACE) EXPECT_CALL(*m_xconfclientMock, getParameterValue(_,_)) @@ -458,42 +572,29 @@ TEST_F(protocolTestFixture, sendReportOverHTTP_6) #endif #endif EXPECT_CALL(*m_xconfclientMock, isMtlsEnabled()) - .Times(1) + .Times(::testing::AnyNumber()) .WillOnce(Return(true)); EXPECT_CALL(*g_systemMock, access(_,_)) - .Times(1) + .Times(::testing::AnyNumber()) .WillOnce(Return(0)); #ifdef LIBRDKCONFIG_BUILD EXPECT_CALL(*g_rdkconfigMock, rdkconfig_get(_,_,_)) .Times(1) .WillOnce(Return(RDKCONFIG_OK)); #endif - EXPECT_CALL(*g_fileIOMock, fork()) - .Times(1) - .WillOnce(Return(1)); - EXPECT_CALL(*g_fileIOMock, close(_)) - .Times(2) - .WillOnce(Return(-1)) - .WillOnce(Return(-1)); - EXPECT_CALL(*g_fileIOMock, read(_,_,_)) - .Times(1) - .WillOnce([](int fd, void *buf, size_t count) { - childResponse* resp = (childResponse*)buf; - resp->curlStatus = true; - resp->curlResponse = CURLE_OK; - resp->curlSetopCode = CURLE_OK; - resp->http_code = 200; - resp->lineNumber = 123; // Set to test value - return sizeof(childResponse); - }); + #ifdef LIBRDKCONFIG_BUILD EXPECT_CALL(*g_rdkconfigMock, rdkconfig_free(_, _)) .Times(1) .WillOnce(Return(RDKCONFIG_OK)); #endif + EXPECT_CALL(*g_fileIOMock, curl_easy_perform(_)) + .Times(1) + .WillOnce(Return(CURLE_OK)); EXPECT_EQ(T2ERROR_SUCCESS, sendReportOverHTTP(httpURL, payload)); Vector_Destroy(reportlist, free); } +#endif TEST_F(protocolTestFixture, sendCachedReportsOverHTTP_FailureCase) { @@ -507,26 +608,16 @@ TEST_F(protocolTestFixture, sendCachedReportsOverHTTP_FailureCase) Vector_PushBack(reportList, payload1); Vector_PushBack(reportList, payload2); - // Mock failure for sendReportOverHTTP on the first payload - EXPECT_CALL(*g_fileIOMock, pipe(_)) - .Times(1) - .WillOnce(Return(0)); - EXPECT_CALL(*m_xconfclientMock, isMtlsEnabled()) .Times(1) .WillOnce(Return(false)); - EXPECT_CALL(*g_fileIOMock, fork()) - .Times(1) - .WillOnce(Return(-1)); - // Ensure that the function returns a failure due to the mocked failure EXPECT_EQ(T2ERROR_FAILURE, sendCachedReportsOverHTTP(httpURL, reportList)); // Clean up Vector_Destroy(reportList, free); } -#endif #ifdef GTEST_ENABLE // Unit test for static writeToFile via its function pointer TEST(CURLINTERFACE_STATIC, WriteToFile) diff --git a/source/test/xconf-client/xconfclientTest.cpp b/source/test/xconf-client/xconfclientTest.cpp index 1e46ffe3..d3124df9 100644 --- a/source/test/xconf-client/xconfclientTest.cpp +++ b/source/test/xconf-client/xconfclientTest.cpp @@ -20,16 +20,17 @@ #include "gtest/gtest.h" #include #include -#define curl_easy_setopt curl_easy_setopt_mock -#define curl_easy_getinfo curl_easy_getinfo_mock +//#define curl_easy_setopt curl_easy_setopt_mock +//#define curl_easy_getinfo curl_easy_getinfo_mock #include "test/mocks/SystemMock.h" #include "test/mocks/FileioMock.h" -#undef curl_easy_setopt -#undef curl_easy_getinfo +//#undef curl_easy_setopt +//#undef curl_easy_getinfo #include "test/mocks/rdklogMock.h" #include "test/mocks/rbusMock.h" #include "xconfclientMock.h" + extern "C" { #include #include @@ -57,19 +58,32 @@ using ::testing::StrEq; using ::testing::SetArgPointee; using ::testing::DoAll; +extern FileMock *g_fileIOMock; XconfclientMock *m_xconfclientMock = NULL; rbusMock *g_rbusMock = NULL; +//#define curl_easy_setopt curl_easy_setopt_mock +// Helper macro to prevent deadlock from mocked fwrite and fputs calls in the protocol code when GTest tries to log output, which can cause a deadlock if the logging functions are mocked without allowing real calls to them. +#define PREVENT_GTEST_LOGGING_DEADLOCK() \ + EXPECT_CALL(*g_fileIOMock, fwrite(::testing::_, ::testing::_, ::testing::_, ::testing::_)) \ + .Times(::testing::AnyNumber()) \ + .WillRepeatedly(::testing::Invoke( \ + [](const void* ptr, size_t size, size_t nitems, FILE* stream) { \ + return ::fwrite(ptr, size, nitems, stream); \ + })) TEST(GETBUILDTYPE, NULL_CHECK) { EXPECT_EQ(T2ERROR_FAILURE, getBuildType(NULL)); } -#if 0 +#if 1 TEST(DOHTTPGET, HTTPURL_CHECK) { char* data = NULL; + FileMock fileMock; + g_fileIOMock = &fileMock; + PREVENT_GTEST_LOGGING_DEADLOCK(); EXPECT_EQ(T2ERROR_FAILURE, doHttpGet(NULL, &data)); } #endif @@ -87,6 +101,34 @@ class xconfclientTestFixture : public ::testing::Test { g_fileIOMock = new FileMock(); g_systemMock = new SystemMock(); m_xconfclientMock = new XconfclientMock(); + + // Set default behaviors for curl functions to prevent them from being called + EXPECT_CALL(*g_fileIOMock, curl_easy_init()) + .Times(::testing::AnyNumber()) + .WillRepeatedly(::testing::Return(nullptr)); + + EXPECT_CALL(*g_fileIOMock, curl_easy_perform(::testing::_)) + .Times(::testing::AnyNumber()) + .WillRepeatedly(::testing::Return(CURLE_FAILED_INIT)); + + EXPECT_CALL(*g_fileIOMock, curl_easy_cleanup(::testing::_)) + .Times(::testing::AnyNumber()); + + EXPECT_CALL(*g_fileIOMock, curl_easy_setopt(::testing::_, ::testing::_, ::testing::_)) + .Times(::testing::AnyNumber()) + .WillRepeatedly(::testing::Return(CURLE_OK)); + + // Add curl_slist functions to prevent deadlock during pool initialization + EXPECT_CALL(*g_fileIOMock, curl_slist_append(::testing::_, ::testing::_)) + .Times(::testing::AnyNumber()) + .WillRepeatedly(::testing::Invoke([](struct curl_slist* list, const char* str) { + // Return a fake non-null pointer to simulate successful append + static int counter = 1; + return (struct curl_slist*)(uintptr_t)(0x2000 + counter++); + })); + + EXPECT_CALL(*g_fileIOMock, curl_slist_free_all(::testing::_)) + .Times(::testing::AnyNumber()); } void TearDown() override @@ -290,13 +332,11 @@ TEST_F(xconfclientTestFixture, fetchRemoteConfiguration) EXPECT_EQ(T2ERROR_FAILURE, fetchRemoteConfiguration(configURL, &configData)); } -#if 0 +#if 1 TEST_F(xconfclientTestFixture, doHttpGet) { char* data = NULL; - EXPECT_CALL(*g_fileIOMock, pipe(_)) - .Times(1) - .WillOnce(Return(-1)); + EXPECT_EQ(T2ERROR_FAILURE, doHttpGet("https://test.com", &data)); } #endif @@ -382,24 +422,38 @@ TEST_F(xconfclientTestFixture, getRemoteConfigURL3) EXPECT_EQ(T2ERROR_SUCCESS, getRemoteConfigURL(&configURL)); } -#if 0 +#if 1 TEST_F(xconfclientTestFixture, doHttpGet1) { char* data = NULL; - EXPECT_CALL(*g_fileIOMock, pipe(_)) - .Times(2) - .WillOnce(Return(0)) - .WillOnce(Return(-1)); + + // Set up curl_easy_init to return valid handles for pool initialization + // The pool will call curl_easy_init() based on pool size (default 2) + EXPECT_CALL(*g_fileIOMock, curl_easy_init()) + .Times(::testing::AnyNumber()) + .WillRepeatedly(::testing::Invoke([]() { + // Return unique fake handles for each call + static int handle_counter = 1; + return (CURL*)(uintptr_t)(0x1000 + handle_counter++); + })); + EXPECT_CALL(*m_xconfclientMock, isMtlsEnabled()) + .Times(1) + .WillOnce(Return(0)); EXPECT_EQ(T2ERROR_FAILURE, doHttpGet("https://test.com", &data)); } TEST_F(xconfclientTestFixture, doHttpGet2) { char* data = NULL; - EXPECT_CALL(*g_fileIOMock, pipe(_)) - .Times(2) - .WillOnce(Return(0)) - .WillOnce(Return(0)); + + EXPECT_CALL(*g_fileIOMock, curl_easy_init()) + .Times(::testing::AnyNumber()) + .WillRepeatedly(::testing::Invoke([]() { + // Return unique fake handles for each call + static int handle_counter = 1; + return (CURL*)(uintptr_t)(0x1000 + handle_counter++); + })); + #if defined(ENABLE_RDKB_SUPPORT) && !defined(RDKB_EXTENDER) #if defined(WAN_FAILOVER_SUPPORTED) || defined(FEATURE_RDKB_CONFIGURABLE_WAN_INTERFACE) EXPECT_CALL(*m_xconfclientMock, getParameterValue(_,_)) @@ -412,26 +466,44 @@ TEST_F(xconfclientTestFixture, doHttpGet2) }); #endif #endif + EXPECT_CALL(*m_xconfclientMock, isMtlsEnabled()) .Times(1) .WillOnce(Return(0)); - EXPECT_CALL(*g_fileIOMock, fork()) + EXPECT_CALL(*g_fileIOMock, curl_easy_perform(_)) .Times(1) - .WillOnce(Return(-1)); - EXPECT_EQ(T2ERROR_FAILURE, doHttpGet("https://test.com", &data)); + .WillOnce(Return(CURLE_OK)); + EXPECT_CALL(*g_fileIOMock, curl_easy_getinfo_mock(_,_,_)) + .Times(::testing::AtLeast(1)) + .WillRepeatedly([](CURL* curl, CURLINFO info, void* response_code) { + if (info == CURLINFO_RESPONSE_CODE) { + *(long*)response_code = 200; + } + return CURLE_OK; + }); + + //Add curl_easy_cleanup expectation + EXPECT_CALL(*g_fileIOMock, curl_easy_cleanup(_)) + .Times(::testing::AnyNumber()); + EXPECT_EQ(T2ERROR_SUCCESS, doHttpGet("https://test.com", &data)); } -//parent + TEST_F(xconfclientTestFixture, doHttpGet3) { char* data = NULL; - EXPECT_CALL(*g_fileIOMock, pipe(_)) - .Times(2) - .WillOnce(Return(0)) - .WillOnce(Return(0)); + + EXPECT_CALL(*g_fileIOMock, curl_easy_init()) + .Times(::testing::AnyNumber()) + .WillRepeatedly(::testing::Invoke([]() { + // Return unique fake handles for each call + static int handle_counter = 1; + return (CURL*)(uintptr_t)(0x1000 + handle_counter++); + })); + #if defined(ENABLE_RDKB_SUPPORT) && !defined(RDKB_EXTENDER) #if defined(WAN_FAILOVER_SUPPORTED) || defined(FEATURE_RDKB_CONFIGURABLE_WAN_INTERFACE) - EXPECT_CALL(*m_xconfclientMock, getParameterValue(_,_)) + EXPECT_CALL(*m_xconfclientMock, getParameterValue(_,_)) .WillOnce([](const char* paramName, char** paramValue) { if (strcmp(paramName, "Device.X_RDK_WanManager.CurrentActiveInterface") == 0) *paramValue = strdup("erouter0"); @@ -441,32 +513,43 @@ TEST_F(xconfclientTestFixture, doHttpGet3) }); #endif #endif + EXPECT_CALL(*m_xconfclientMock, isMtlsEnabled()) .Times(1) .WillOnce(Return(0)); - EXPECT_CALL(*g_fileIOMock, fork()) - .Times(1) - .WillOnce(Return(1)); - EXPECT_CALL(*g_fileIOMock, close(_)) - .Times(1) - .WillOnce(Return(0)); - EXPECT_CALL(*g_fileIOMock, read(_,_,_)) + EXPECT_CALL(*g_fileIOMock, curl_easy_perform(_)) .Times(1) - .WillOnce(Return(-1)); - EXPECT_EQ(T2ERROR_FAILURE, doHttpGet("https://test.com", &data)); + .WillOnce(Return(CURLE_OK)); + EXPECT_CALL(*g_fileIOMock, curl_easy_getinfo_mock(_,_,_)) + .Times(::testing::AtLeast(1)) + .WillRepeatedly([](CURL* curl, CURLINFO info, void* response_code) { + if (info == CURLINFO_RESPONSE_CODE) { + *(long*)response_code = 404; + } + return CURLE_OK; + }); + + //Add curl_easy_cleanup expectation + EXPECT_CALL(*g_fileIOMock, curl_easy_cleanup(_)) + .Times(::testing::AnyNumber()); + EXPECT_EQ(T2ERROR_PROFILE_NOT_SET, doHttpGet("https://test.com", &data)); } TEST_F(xconfclientTestFixture, doHttpGet4) { char* data = NULL; - EXPECT_CALL(*g_fileIOMock, pipe(_)) - .Times(2) - .WillOnce(Return(0)) - .WillOnce(Return(0)); - #if defined(ENABLE_RDKB_SUPPORT) && !defined(RDKB_EXTENDER) + + EXPECT_CALL(*g_fileIOMock, curl_easy_init()) + .Times(::testing::AnyNumber()) + .WillRepeatedly(::testing::Invoke([]() { + // Return unique fake handles for each call + static int handle_counter = 1; + return (CURL*)(uintptr_t)(0x1000 + handle_counter++); + })); + +#if defined(ENABLE_RDKB_SUPPORT) && !defined(RDKB_EXTENDER) #if defined(WAN_FAILOVER_SUPPORTED) || defined(FEATURE_RDKB_CONFIGURABLE_WAN_INTERFACE) EXPECT_CALL(*m_xconfclientMock, getParameterValue(_,_)) - .Times(1) .WillOnce([](const char* paramName, char** paramValue) { if (strcmp(paramName, "Device.X_RDK_WanManager.CurrentActiveInterface") == 0) *paramValue = strdup("erouter0"); @@ -476,40 +559,43 @@ TEST_F(xconfclientTestFixture, doHttpGet4) }); #endif #endif + EXPECT_CALL(*m_xconfclientMock, isMtlsEnabled()) .Times(1) .WillOnce(Return(0)); - EXPECT_CALL(*g_fileIOMock, fork()) + EXPECT_CALL(*g_fileIOMock, curl_easy_perform(_)) .Times(1) - .WillOnce(Return(1)); - EXPECT_CALL(*g_fileIOMock, close(_)) - .Times(3) - .WillOnce(Return(0)) - .WillOnce(Return(0)) - .WillOnce(Return(0)); + .WillOnce(Return(CURLE_OK)); + EXPECT_CALL(*g_fileIOMock, curl_easy_getinfo_mock(_,_,_)) + .Times(::testing::AtLeast(1)) + .WillRepeatedly([](CURL* curl, CURLINFO info, void* response_code) { + if (info == CURLINFO_RESPONSE_CODE) { + *(long*)response_code = 302; + } + return CURLE_OK; + }); - EXPECT_CALL(*g_fileIOMock, read(_, _, _)) - .WillOnce([](int fd, void* buf, size_t count) { - if (count == sizeof(T2ERROR)) { - *static_cast(buf) = T2ERROR_SUCCESS; - } - return static_cast(sizeof(T2ERROR)); // Simulate full read - }) - .WillOnce(Return(-1)); - EXPECT_EQ(T2ERROR_FAILURE, doHttpGet("https://test.com", &data)); + //Add curl_easy_cleanup expectation + EXPECT_CALL(*g_fileIOMock, curl_easy_cleanup(_)) + .Times(::testing::AnyNumber()); + EXPECT_EQ(T2ERROR_FAILURE, doHttpGet("https://test.com", &data)); } TEST_F(xconfclientTestFixture, doHttpGet5) { char* data = NULL; - EXPECT_CALL(*g_fileIOMock, pipe(_)) - .Times(2) - .WillOnce(Return(0)) - .WillOnce(Return(0)); - #if defined(ENABLE_RDKB_SUPPORT) && !defined(RDKB_EXTENDER) + + EXPECT_CALL(*g_fileIOMock, curl_easy_init()) + .Times(::testing::AnyNumber()) + .WillRepeatedly(::testing::Invoke([]() { + // Return unique fake handles for each call + static int handle_counter = 1; + return (CURL*)(uintptr_t)(0x1000 + handle_counter++); + })); + +#if defined(ENABLE_RDKB_SUPPORT) && !defined(RDKB_EXTENDER) #if defined(WAN_FAILOVER_SUPPORTED) || defined(FEATURE_RDKB_CONFIGURABLE_WAN_INTERFACE) EXPECT_CALL(*m_xconfclientMock, getParameterValue(_,_)) - .Times(1) .WillOnce([](const char* paramName, char** paramValue) { if (strcmp(paramName, "Device.X_RDK_WanManager.CurrentActiveInterface") == 0) *paramValue = strdup("erouter0"); @@ -519,46 +605,46 @@ TEST_F(xconfclientTestFixture, doHttpGet5) }); #endif #endif + EXPECT_CALL(*m_xconfclientMock, isMtlsEnabled()) .Times(1) .WillOnce(Return(0)); - EXPECT_CALL(*g_fileIOMock, fork()) + // Mock curl_easy_perform to simulate receiving data + EXPECT_CALL(*g_fileIOMock, curl_easy_perform(_)) .Times(1) - .WillOnce(Return(1)); - EXPECT_CALL(*g_fileIOMock, close(_)) - .Times(4) - .WillOnce(Return(0)) - .WillOnce(Return(0)) - .WillOnce(Return(0)) - .WillOnce(Return(0)); + .WillOnce(::testing::Invoke([](CURL* handle) { + // Simulate curl calling the write callback with response data + const char* test_response = "{\"status\":\"success\",\"data\":\"test response data\"}"; + + // In a real scenario, curl would call the callback set via CURLOPT_WRITEFUNCTION + // For testing, we need to directly populate the response structure + // This is tricky because we need access to the response pointer + + return CURLE_OK; + })); + EXPECT_CALL(*g_fileIOMock, curl_easy_getinfo_mock(_,_,_)) + .Times(::testing::AtLeast(1)) + .WillRepeatedly([](CURL* curl, CURLINFO info, void* response_code) { + if (info == CURLINFO_RESPONSE_CODE) { + *(long*)response_code = 200; + } + return CURLE_OK; + }); - EXPECT_CALL(*g_fileIOMock, read(_, _, _)) - .WillOnce([](int fd, void* buf, size_t count) { - if (count == sizeof(T2ERROR)) { - *static_cast(buf) = T2ERROR_SUCCESS; - } - return static_cast(sizeof(T2ERROR)); // Simulate full read - }) - .WillOnce([](int fd, void* buf, size_t count) { - if (count == sizeof(size_t)) { - *static_cast(buf) = 12345; - } - return static_cast(sizeof(size_t)); // Simulate full read - }); - - EXPECT_CALL(*g_fileIOMock, fopen(_,_)) - .Times(1) - .WillOnce(Return((FILE*)0XFFFFFFFF)); - EXPECT_CALL(*g_fileIOMock, fread(_,_,_,_)) - .Times(1) - .WillOnce(Return(-1)); - - EXPECT_EQ(T2ERROR_FAILURE, doHttpGet("https://test.com", &data)); + //Add curl_easy_cleanup expectation + EXPECT_CALL(*g_fileIOMock, curl_easy_cleanup(_)) + .Times(::testing::AnyNumber()); + EXPECT_EQ(T2ERROR_SUCCESS, doHttpGet("https://test.com", &data)); } +#if 0 + TEST_F(xconfclientTestFixture, doHttpGet6) { char* data = NULL; + //FileMock fileMock; + //g_fileIOMock = &fileMock; + EXPECT_CALL(*g_fileIOMock, pipe(_)) .Times(2) .WillOnce(Return(0)) @@ -579,9 +665,6 @@ TEST_F(xconfclientTestFixture, doHttpGet6) EXPECT_CALL(*m_xconfclientMock, isMtlsEnabled()) .Times(1) .WillOnce(Return(0)); - EXPECT_CALL(*g_fileIOMock, fork()) - .Times(1) - .WillOnce(Return(1)); EXPECT_CALL(*g_fileIOMock, close(_)) .Times(4) .WillOnce(Return(0)) @@ -620,6 +703,9 @@ TEST_F(xconfclientTestFixture, doHttpGet6) TEST_F(xconfclientTestFixture, doHttpGet7) { char* data = NULL; + //FileMock fileMock; + //g_fileIOMock = &fileMock; + EXPECT_CALL(*g_fileIOMock, pipe(_)) .Times(2) .WillOnce(Return(0)) @@ -640,13 +726,10 @@ TEST_F(xconfclientTestFixture, doHttpGet7) EXPECT_CALL(*m_xconfclientMock, isMtlsEnabled()) .Times(1) .WillOnce(Return(0)); - EXPECT_CALL(*g_fileIOMock, fork()) - .Times(1) - .WillOnce(Return(0)); EXPECT_CALL(*g_fileIOMock, curl_easy_init()) .Times(1) .WillOnce(Return((CURL*)0XFFFFFFFF)); - EXPECT_CALL(*g_fileIOMock, curl_easy_setopt_mock(_,_,_)) + EXPECT_CALL(*g_fileIOMock, curl_easy_setopt(_,_,_)) .Times(6) .WillOnce(Return(CURLE_OK)) .WillOnce(Return(CURLE_OK)) @@ -683,14 +766,19 @@ TEST_F(xconfclientTestFixture, doHttpGet7) .Times(1); EXPECT_THROW(doHttpGet("https://test.com", &data), std::runtime_error); } +#endif -TEST_F(xconfclientTestFixture, doHttpGet8) +TEST_F(xconfclientTestFixture, doHttpGet7) { char* data = NULL; - EXPECT_CALL(*g_fileIOMock, pipe(_)) - .Times(2) - .WillOnce(Return(0)) - .WillOnce(Return(0)); + + EXPECT_CALL(*g_fileIOMock, curl_easy_init()) + .Times(::testing::AnyNumber()) + .WillRepeatedly(::testing::Invoke([]() { + // Return unique fake handles for each call + static int handle_counter = 1; + return (CURL*)(uintptr_t)(0x1000 + handle_counter++); + })); #if defined(ENABLE_RDKB_SUPPORT) && !defined(RDKB_EXTENDER) #if defined(WAN_FAILOVER_SUPPORTED) || defined(FEATURE_RDKB_CONFIGURABLE_WAN_INTERFACE) EXPECT_CALL(*m_xconfclientMock, getParameterValue(_,_)) @@ -709,25 +797,8 @@ TEST_F(xconfclientTestFixture, doHttpGet8) .WillOnce(Return(1)); EXPECT_CALL(*g_systemMock, access(_,_)) .Times(1) - .WillOnce(Return(0)); - EXPECT_CALL(*g_fileIOMock, fork()) - .Times(1) - .WillOnce(Return(0)); - EXPECT_CALL(*g_fileIOMock, curl_easy_init()) - .Times(1) - .WillOnce(Return((CURL*)0XFFFFFFFF)); - EXPECT_CALL(*g_fileIOMock, curl_easy_setopt_mock(_,_,_)) - .Times(10) - .WillOnce(Return(CURLE_OK)) - .WillOnce(Return(CURLE_OK)) - .WillOnce(Return(CURLE_OK)) - .WillOnce(Return(CURLE_OK)) - .WillOnce(Return(CURLE_OK)) - .WillOnce(Return(CURLE_OK)) - .WillOnce(Return(CURLE_OK)) - .WillOnce(Return(CURLE_OK)) - .WillOnce(Return(CURLE_OK)) - .WillOnce(Return(CURLE_OK)); + .WillOnce(Return(1)); + EXPECT_CALL(*g_fileIOMock, curl_easy_perform(_)) .Times(1) .WillOnce(Return(CURLE_OK)); @@ -740,26 +811,58 @@ TEST_F(xconfclientTestFixture, doHttpGet8) } return CURLE_OK; }); - EXPECT_CALL(*g_fileIOMock, close(_)) - .Times(4) - .WillOnce(Return(0)) - .WillOnce(Return(0)) - .WillOnce(Return(0)) - .WillOnce(Return(0)); - EXPECT_CALL(*g_fileIOMock, write(_,_,_)) - .Times(2) - .WillOnce(Return(0)) + + EXPECT_EQ(doHttpGet("https://test.com", &data), T2ERROR_SUCCESS); +} + +TEST_F(xconfclientTestFixture, doHttpGet8) +{ + char* data = NULL; + + EXPECT_CALL(*g_fileIOMock, curl_easy_init()) + .Times(::testing::AnyNumber()) + .WillRepeatedly(::testing::Invoke([]() { + // Return unique fake handles for each call + static int handle_counter = 1; + return (CURL*)(uintptr_t)(0x1000 + handle_counter++); + })); + #if defined(ENABLE_RDKB_SUPPORT) && !defined(RDKB_EXTENDER) +#if defined(WAN_FAILOVER_SUPPORTED) || defined(FEATURE_RDKB_CONFIGURABLE_WAN_INTERFACE) + EXPECT_CALL(*m_xconfclientMock, getParameterValue(_,_)) + .Times(1) + .WillOnce([](const char* paramName, char** paramValue) { + if (strcmp(paramName, "Device.X_RDK_WanManager.CurrentActiveInterface") == 0) + *paramValue = strdup("erouter0"); + else + *paramValue = strdup("unknown"); + return ; + }); +#endif +#endif + EXPECT_CALL(*m_xconfclientMock, isMtlsEnabled()) + .Times(1) + .WillOnce(Return(1)); + EXPECT_CALL(*g_systemMock, access(_,_)) + .Times(1) .WillOnce(Return(0)); - EXPECT_CALL(*g_fileIOMock, fopen(_,_)) + + EXPECT_CALL(*g_fileIOMock, curl_easy_perform(_)) .Times(1) - .WillOnce(Return((FILE*)NULL)); - EXPECT_CALL(*g_fileIOMock, curl_easy_cleanup(_)) - .Times(1); - EXPECT_THROW(doHttpGet("https://test.com", &data), std::runtime_error); + .WillOnce(Return(CURLE_OK)); + EXPECT_CALL(*g_fileIOMock, curl_easy_getinfo_mock(_,_,_)) + .Times(1) + .WillOnce([](CURL* curl, CURLINFO info, void* response_code) { + if (info == CURLINFO_RESPONSE_CODE) { + *(long*)response_code = 200; + return CURLE_OK; + } + return CURLE_OK; + }); + + EXPECT_EQ(doHttpGet("https://test.com", &data), T2ERROR_SUCCESS); } #endif - TEST_F(xconfclientTestFixture, initXConfClient_failure) { #if defined(ENABLE_RDKB_SUPPORT)