diff --git a/.github/workflows/L2-tests.yml b/.github/workflows/L2-tests.yml index d9650e42..83828884 100644 --- a/.github/workflows/L2-tests.yml +++ b/.github/workflows/L2-tests.yml @@ -51,15 +51,11 @@ jobs: - name: Start mock-xconf service run: | - docker run -d --name mockxconf -p 50050:50050 -p 50051:50051 -p 50052:50052 -v ${{ github.workspace }}:/mnt/L2_CONTAINER_SHARED_VOLUME ghcr.io/rdkcentral/docker-device-mgt-service-test/mockxconf:latest + docker run -d --name mockxconf -p 50050:50050 -p 50051:50051 -p 50052:50052 -e ENABLE_MTLS=true -v ${{ github.workspace }}:/mnt/L2_CONTAINER_SHARED_VOLUME ghcr.io/rdkcentral/docker-device-mgt-service-test/mockxconf:latest - - name: Copy xconf-dcm-response2 json to mockxconf service - run: | - docker cp ${{ github.workspace }}/test/test-artifacts/mockxconf/xconf-dcm-response2.json mockxconf:/etc/xconf/xconf-dcm-response2.json - - name: Start l2-container service run: | - docker run -d --name native-platform --link mockxconf -v ${{ github.workspace }}:/mnt/L2_CONTAINER_SHARED_VOLUME ghcr.io/rdkcentral/docker-device-mgt-service-test/native-platform:latest + docker run -d --name native-platform --link mockxconf -e ENABLE_MTLS=true -v ${{ github.workspace }}:/mnt/L2_CONTAINER_SHARED_VOLUME ghcr.io/rdkcentral/docker-device-mgt-service-test/native-platform:latest - name: Enter Inside Platform native container and run L2 Test run: | diff --git a/CHANGELOG.md b/CHANGELOG.md index 84a90bde..e687f448 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,15 @@ All notable changes to this project will be documented in this file. Dates are d Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). +#### [1.8.1](https://github.com/rdkcentral/telemetry/compare/1.8.0...1.8.1) + +- RDK-60476: Reduce default connection pool size to 1 [`#260`](https://github.com/rdkcentral/telemetry/pull/260) +- RDK-60805: Adding L1 unit test cases for reportprofiles [`#265`](https://github.com/rdkcentral/telemetry/pull/265) + #### [1.8.0](https://github.com/rdkcentral/telemetry/compare/1.7.4...1.8.0) +> 6 February 2026 + - RDK-60312: Remove fork calls for curl transactions [`#242`](https://github.com/rdkcentral/telemetry/pull/242) - RDK-60533: L1 unit test cases - protocol/rbusMethod and http [`#241`](https://github.com/rdkcentral/telemetry/pull/241) - RDKB-63348 : Fix duplicate close calls on file descriptor [`#251`](https://github.com/rdkcentral/telemetry/pull/251) @@ -13,6 +20,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). - RDK-60519: Adding L1 unit test cases for scheduler [`#238`](https://github.com/rdkcentral/telemetry/pull/238) - RDK-60304: Adding L1 unit test cases to improve code coverage [`#237`](https://github.com/rdkcentral/telemetry/pull/237) - RDK-60303: Adding L1 unit test cases to improve code coverage for protocol/http [`#231`](https://github.com/rdkcentral/telemetry/pull/231) +- Changelog updates for 1.8.0 release [`8440d51`](https://github.com/rdkcentral/telemetry/commit/8440d513de7bdfda8331e44099f22b82c8cf669e) #### [1.7.4](https://github.com/rdkcentral/telemetry/compare/1.7.3...1.7.4) diff --git a/build_inside_container.sh b/build_inside_container.sh index 72467960..f962ea15 100755 --- a/build_inside_container.sh +++ b/build_inside_container.sh @@ -27,8 +27,8 @@ autoreconf --install # FLags to print compiler warnings DEBUG_CFLAGS="-Wall -Werror -Wextra" -export CFLAGS=" ${DEBUG_CFLAGS} -I${INSTALL_DIR}/include/rtmessage -I${INSTALL_DIR}/include/msgpack -I${INSTALL_DIR}/include/rbus -I${INSTALL_DIR}/include -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -I/usr/local/include -DFEATURE_SUPPORT_WEBCONFIG -DRDK_LOGGER -DPERSIST_LOG_MON_REF -DDCMAGENT" +export CFLAGS=" ${DEBUG_CFLAGS} -I${INSTALL_DIR}/include/rtmessage -I${INSTALL_DIR}/include/msgpack -I${INSTALL_DIR}/include/rbus -I${INSTALL_DIR}/include -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -I/usr/local/include -DFEATURE_SUPPORT_WEBCONFIG -DRDK_LOGGER -DPERSIST_LOG_MON_REF -DDCMAGENT -DENABLE_MTLS" export LDFLAGS="-L/usr/lib/x86_64-linux-gnu -lglib-2.0" -./configure --prefix=${INSTALL_DIR} && make && make install +./configure --prefix=${INSTALL_DIR} --enable-rdkcertselector=yes && make && make install diff --git a/source/bulkdata/profile.c b/source/bulkdata/profile.c index bafe50c4..5a0055c9 100644 --- a/source/bulkdata/profile.c +++ b/source/bulkdata/profile.c @@ -45,6 +45,11 @@ #define MAX_LEN 256 +#ifdef GTEST_ENABLE +#define sendReportOverHTTP __wrap_sendReportOverHTTP +#define sendCachedReportsOverHTTP __wrap_sendCachedReportsOverHTTP +#endif + static bool initialized = false; static Vector *profileList; static pthread_mutex_t plMutex; diff --git a/source/bulkdata/profilexconf.c b/source/bulkdata/profilexconf.c index cd8aece8..77acf20e 100644 --- a/source/bulkdata/profilexconf.c +++ b/source/bulkdata/profilexconf.c @@ -986,4 +986,9 @@ T2ERROR ProfileXConf_storeMarkerEvent(T2Event *eventInfo) T2Debug("%s --out\n", __FUNCTION__); return T2ERROR_SUCCESS; } - +#ifdef GTEST_ENABLE +void test_set_reportThreadExits(bool value) +{ + reportThreadExits = value; +} +#endif diff --git a/source/bulkdata/reportprofiles.c b/source/bulkdata/reportprofiles.c index f220d0e1..fc7b1205 100644 --- a/source/bulkdata/reportprofiles.c +++ b/source/bulkdata/reportprofiles.c @@ -907,6 +907,7 @@ void ReportProfiles_ProcessReportProfilesBlob(cJSON *profiles_root, bool rprofil { getMarkerCompRbusSub(false); } + // Populate profile hash map for current configuration for( profileIndex = 0; profileIndex < profiles_count; profileIndex++ ) { @@ -976,7 +977,6 @@ void ReportProfiles_ProcessReportProfilesBlob(cJSON *profiles_root, bool rprofil T2Error("Failed to remove previous report profile from the disk\n"); } } - if(isRbusEnabled()) { unregisterDEforCompEventList(); @@ -1064,7 +1064,6 @@ void ReportProfiles_ProcessReportProfilesBlob(cJSON *profiles_root, bool rprofil } } } - if (rm_flag) { removeProfileFromDisk(DirPath, MSGPACK_REPORTPROFILES_PERSISTENT_FILE); @@ -1515,3 +1514,21 @@ bool isMtlsEnabled(void) #endif #endif } + +#ifdef GTEST_ENABLE +typedef void* (*reportOnDemandFunc)(void*); +reportOnDemandFunc reportOnDemandFuncCallback(void) +{ + return reportOnDemand; +} +typedef void (*freeProfilesHashMapFunc)(void *); +freeProfilesHashMapFunc freeProfilesHashMapFuncCallback(void) +{ + return freeProfilesHashMap; +} +typedef void (*freeReportProfileHashMapFunc)(void *); +freeReportProfileHashMapFunc freeReportProfileHashMapFuncCallback(void) +{ + return freeReportProfileHashMap; +} +#endif diff --git a/source/ccspinterface/rbusInterface.c b/source/ccspinterface/rbusInterface.c index a6a4c605..c566005a 100644 --- a/source/ccspinterface/rbusInterface.c +++ b/source/ccspinterface/rbusInterface.c @@ -1954,9 +1954,11 @@ bool rbusCheckMethodExists(const char* rbusMethodName) return true ; } #ifdef GTEST_ENABLE +#ifdef DCMAGENT typedef void (*rbusReloadConfFunc)(rbusHandle_t, rbusEvent_t const *, rbusEventSubscription_t *); rbusReloadConfFunc rbusReloadConfFuncCallback(void) { return rbusReloadConf; } #endif +#endif diff --git a/source/docs/protocol/curl_usage_architecture.md b/source/docs/protocol/curl_usage_architecture.md new file mode 100644 index 00000000..a6b35bea --- /dev/null +++ b/source/docs/protocol/curl_usage_architecture.md @@ -0,0 +1,426 @@ +# CURL Usage Architecture Documentation + +## Overview + +This document describes the architecture of the CURL-based HTTP communication subsystem in the Telemetry component. The system implements a connection pooling mechanism for efficient HTTP operations with thread-safe access and mTLS support. + +## Components + +### 1. **multicurlinterface.c** - Connection Pool Layer +The core component implementing a thread-safe connection pool for CURL handles. + +**Key Features:** +- Connection pooling with configurable pool size (1-5 connections) +- Thread-safe handle acquisition and release +- Support for both GET and POST operations +- mTLS certificate management integration +- Keep-alive and connection reuse optimization + +**Key Data Structures:** +```c +typedef struct { + CURL *easy_handle; // Single CURL handle + bool handle_available; // Availability flag + rdkcertselector_h cert_selector; // Certificate selector (LIBRDKCERTSEL_BUILD) + rdkcertselector_h rcvry_cert_selector; // Recovery cert selector +} http_pool_entry_t; +``` + +**Global State:** +- `pool_entries`: Array of pool entries +- `pool_mutex`: Protects pool state (statically initialized) +- `pool_cond`: Condition variable for handle availability +- `pool_initialized`: Initialization flag +- `active_requests`: Counter for in-use handles +- `pool_size`: Configured pool size + +### 2. **curlinterface.c** - Wrapper Layer +Simple wrapper providing backward compatibility and convenience functions. + +**Key Functions:** +- `sendReportOverHTTP()`: Sends telemetry reports via HTTP POST +- `sendCachedReportsOverHTTP()`: Sends multiple cached reports sequentially + +### 3. **xconfclient.c** - Configuration Client +Manages fetching configuration from XConf server with threading and retry logic. + +**Key Features:** +- Background thread for configuration fetching +- Retry logic with exponential backoff +- Multiple condition variables for flow control +- DCM integration (when DCMAGENT is defined) + +**Synchronization Primitives:** +- `xcMutex` & `xcCond`: Controls retry timing +- `xcThreadMutex` & `xcThreadCond`: Controls thread lifecycle +- `xcrThread`: Configuration fetching thread +- `dcmThread`: DCM notification thread (DCMAGENT) + +## Architecture Diagrams + +### Component Architecture + +### HTTP GET Flow + +```mermaid +sequenceDiagram + participant XC as XConf Client + participant MCI as multicurlinterface + participant POOL as Connection Pool + participant CURL as libcurl + participant CERT as Cert Selector + + XC->>MCI: http_pool_get(url, response_data) + MCI->>MCI: acquire_pool_handle(&easy, &idx) + + alt Handle Available + MCI->>POOL: Lock pool_mutex + POOL-->>MCI: Return handle & index + else All Handles Busy + MCI->>MCI: pthread_cond_wait(pool_cond) + Note over MCI: Wait until handle available + POOL-->>MCI: Return handle & index + end + + MCI->>MCI: Clear POST options + MCI->>MCI: Set GET options (URL, callback) + + alt mTLS Enabled + MCI->>CERT: rdkcertselector_getCertificate() + CERT-->>MCI: Certificate URI + MCI->>CURL: Set CURLOPT_SSLCERT + + loop Retry on Certificate Error + MCI->>CURL: curl_easy_perform() + CURL-->>MCI: CURLcode result + alt Certificate Error + MCI->>CERT: rdkcertselector_setCurlStatus(TRY_ANOTHER) + CERT-->>MCI: Next certificate + else Success or Other Error + MCI->>MCI: Break loop + end + end + else mTLS Disabled + MCI->>CURL: curl_easy_perform() + CURL-->>MCI: CURLcode result + end + + MCI->>MCI: release_pool_handle(idx) + MCI->>POOL: Mark handle available + MCI->>POOL: pthread_cond_signal(pool_cond) + MCI-->>XC: Return T2ERROR status +``` + +### HTTP POST Flow + +```mermaid +sequenceDiagram + participant APP as Application + participant CI as curlinterface + participant MCI as multicurlinterface + participant POOL as Connection Pool + participant CURL as libcurl + + APP->>CI: sendReportOverHTTP(url, payload) + CI->>MCI: http_pool_post(url, payload) + + MCI->>MCI: acquire_pool_handle(&easy, &idx) + + rect rgb(200, 220, 240) + Note over MCI,POOL: Critical Section: pool_mutex locked + POOL-->>MCI: Handle acquired + end + + MCI->>MCI: Set POST options + MCI->>CURL: CURLOPT_POSTFIELDS = payload + MCI->>CURL: CURLOPT_HTTPHEADER = post_headers + + alt mTLS with Certificates + MCI->>MCI: Configure SSL certificates + MCI->>CURL: curl_easy_perform() + else Standard POST + MCI->>CURL: curl_easy_perform() + end + + CURL-->>MCI: HTTP Response Code + + MCI->>MCI: release_pool_handle(idx) + + rect rgb(200, 220, 240) + Note over MCI,POOL: Critical Section: pool_mutex locked + MCI->>POOL: Mark handle available + MCI->>POOL: pthread_cond_signal(pool_cond) + end + + MCI-->>CI: T2ERROR status + CI-->>APP: T2ERROR status +``` + +### XConf Client Thread Flow + +```mermaid +sequenceDiagram + participant MAIN as Main Thread + participant XCT as XConf Thread + participant MCI as multicurlinterface + participant MUTEX as xcThreadMutex + + MAIN->>XCT: initXConfClient() + activate XCT + + XCT->>MUTEX: pthread_mutex_lock(&xcThreadMutex) + + loop While isXconfInit + XCT->>XCT: getRemoteConfigURL(&configURL) + + alt URL Not Ready + XCT->>XCT: Lock xcMutex + XCT->>XCT: pthread_cond_timedwait(xcCond, RFC_RETRY_TIMEOUT) + Note over XCT: Wait 60 seconds + XCT->>XCT: Unlock xcMutex + else URL Ready + XCT->>XCT: Break loop + end + + XCT->>MCI: fetchRemoteConfiguration(url, &data) + MCI-->>XCT: Configuration data + + alt Config Fetch Success + XCT->>XCT: Process configuration + XCT->>XCT: stopFetchRemoteConfiguration = true + else Config Fetch Failed + XCT->>XCT: Increment retry count + XCT->>XCT: pthread_cond_timedwait(xcCond, XCONF_RETRY_TIMEOUT) + Note over XCT: Wait 180 seconds + end + + alt Done Fetching + XCT->>XCT: pthread_cond_wait(xcThreadCond) + Note over XCT: Wait for restart signal + end + end + + MAIN->>XCT: uninitXConfClient() + MAIN->>MUTEX: pthread_cond_signal(xcThreadCond) + XCT->>MUTEX: pthread_mutex_unlock(&xcThreadMutex) + deactivate XCT + MAIN->>XCT: pthread_join(xcrThread) +``` + +### Connection Pool Initialization + +```mermaid +stateDiagram-v2 + [*] --> Uninitialized: pool_initialized = false + + Uninitialized --> Initializing: init_connection_pool() + + Initializing --> CheckInitialized: Lock pool_mutex + CheckInitialized --> AlreadyInitialized: pool_initialized == true + CheckInitialized --> ConfigureSize: pool_initialized == false + + ConfigureSize --> AllocatePool: Read T2_CONNECTION_POOL_SIZE + AllocatePool --> AllocateHandles: calloc(pool_size) + + AllocateHandles --> ConfigureHandle1: Create CURL handles + ConfigureHandle1 --> ConfigureHandle2: Set CURLOPT_* options + ConfigureHandle2 --> InitCertSelector: Configure keepalive, SSL + + InitCertSelector --> SetupHeaders: rdkcertselector_init() + SetupHeaders --> MarkInitialized: curl_slist_append() + + MarkInitialized --> Ready: pool_initialized = true + AlreadyInitialized --> Ready + + Ready --> [*]: Unlock pool_mutex + + Ready --> Cleanup: http_pool_cleanup() + Cleanup --> CleanupWait: Lock pool_mutex + CleanupWait --> WaitForActive: active_requests > 0 + WaitForActive --> BroadcastShutdown: pthread_cond_broadcast() + BroadcastShutdown --> FreeResources: Free handles & cert selectors + FreeResources --> [*]: pool_initialized = false +``` + +### Handle Acquisition & Release Flow + +```mermaid +flowchart TD + Start([acquire_pool_handle]) --> Lock[Lock pool_mutex] + Lock --> CheckInit{pool_initialized?} + + CheckInit -->|No| InitPool[Call init_connection_pool] + InitPool --> CheckInitFail{Init Success?} + CheckInitFail -->|No| ReturnError1[Return T2ERROR_FAILURE] + CheckInitFail -->|Yes| WaitLoop + + CheckInit -->|Yes| WaitLoop[Enter while loop] + + WaitLoop --> CheckAvail{Handle available?} + + CheckAvail -->|Yes| FindHandle[Search for available handle] + FindHandle --> MarkBusy[Mark handle busy] + MarkBusy --> IncrActive[active_requests++] + IncrActive --> Unlock1[Unlock pool_mutex] + Unlock1 --> ReturnSuccess[Return T2ERROR_SUCCESS] + + CheckAvail -->|No| CheckShutdown{pool_initialized?} + CheckShutdown -->|No| Unlock2[Unlock pool_mutex] + Unlock2 --> ReturnError2[Return T2ERROR_FAILURE] + + CheckShutdown -->|Yes| CondWait[pthread_cond_wait] + CondWait --> WaitLoop + + ReturnSuccess --> End1([End]) + ReturnError1 --> End2([End]) + ReturnError2 --> End3([End]) + + Release([release_pool_handle]) --> LockR[Lock pool_mutex] + LockR --> CheckIdx{Valid index?} + CheckIdx -->|Yes| MarkAvail[Mark handle available] + MarkAvail --> DecrActive[active_requests--] + DecrActive --> Signal[pthread_cond_signal] + Signal --> UnlockR[Unlock pool_mutex] + + CheckIdx -->|No| LogError[Log error] + LogError --> UnlockR + + UnlockR --> EndR([End]) +``` + +## Synchronization Mechanisms + +## Critical Race Conditions & Deadlock Risks + +### 🔴 HIGH RISK: Pool Cleanup During Active Requests + +**Location:** `http_pool_cleanup()` in multicurlinterface.c + +**Issue:** +```c +while(active_requests > 0) { + pthread_cond_wait(&pool_cond, &pool_mutex); +} +``` + +**Risk:** If a thread is blocked in `acquire_pool_handle()` waiting for an available handle when cleanup starts: +1. Cleanup sets `pool_initialized = false` +2. Waiting thread wakes up, sees `!pool_initialized`, exits with error +3. If the thread had incremented `active_requests` before waiting, the counter may be incorrect +4. Cleanup may wait forever if signals are missed + +**Mitigation Required:** +- Use `pthread_cond_broadcast()` instead of waiting +- Add shutdown flag checked in acquire path +- Ensure proper signal on all return paths + + + +### 🟡 MEDIUM RISK: Static Initialization Race + +**Location:** `init_connection_pool()` in multicurlinterface.c + +**Issue:** +```c +static pthread_mutex_t pool_mutex = PTHREAD_MUTEX_INITIALIZER; +static bool pool_initialized = false; + +// Later in code: +pthread_mutex_lock(&pool_mutex); +if(pool_initialized) { + pthread_mutex_unlock(&pool_mutex); + return T2ERROR_SUCCESS; +} +``` + +**Risk:** Although `PTHREAD_MUTEX_INITIALIZER` provides static initialization, there's a check-then-act pattern that could theoretically have issues if multiple threads call `init_connection_pool()` simultaneously before `pool_initialized` is set. + +**Current Protection:** The lock is acquired before checking, so this is actually safe. However, the `acquire_pool_handle()` function calls `init_connection_pool()` while holding `pool_mutex`, which is correct but creates nested critical sections. + +**Status:** Currently protected, but recursive pattern is fragile. + +### 🟡 MEDIUM RISK: Condition Variable Missed Signals + +**Location:** Various locations using `pthread_cond_wait()` + + +### 🟢 LOW RISK: Active Request Counter + +**Location:** `acquire_pool_handle()` and `release_pool_handle()` in multicurlinterface.c + +**Issue:** +```c +active_requests++; // In acquire +active_requests--; // In release +``` + +**Risk:** Counter could become inaccurate if: +- Release called without acquire +- Error path doesn't decrement +- Signal handling interrupts + +**Current Protection:** +- All paths protected by `pool_mutex` +- index validation in release prevents double-release + +**Status:** Well-protected but no assertions to detect corruption. + + +## Thread Safety Analysis + +### Thread-Safe Operations +✅ `http_pool_get()` - Protected by pool_mutex +✅ `http_pool_post()` - Protected by pool_mutex +✅ `init_connection_pool()` - Double-check locking with mutex +✅ `http_pool_cleanup()` - Synchronized with broadcast +✅ Handle acquisition/release - Mutex + condition variable + +### Non-Thread-Safe (By Design) +⚠️ Individual CURL handle operations - Assumes single thread per handle (correct) +⚠️ Certificate selector callbacks - Called within handle's execution context (correct) + + +## Recommended Improvements + +### 1. Add Timeout to Acquire - Prevent deadlocks + +```c +T2ERROR acquire_pool_handle_timeout(CURL **easy, int *idx, int timeout_ms); +``` + +### 2. Separate Cert Selectors from Pool (Not needed now for pool sie 1) + +Consider managing cert selectors independently to reduce coupling and simplify state management. + + + +## Testing Recommendations + +### Unit Tests Needed +1. ✅ Pool initialization and cleanup +2. ✅ Concurrent acquire/release from multiple threads +3. ✅ Pool exhaustion behavior (all handles busy) +4. ⚠️ Cleanup during active requests +5. ⚠️ Signal/wait patterns with multiple threads +6. ⚠️ Lock ordering validation +7. ⚠️ Environment variable edge cases + +### Integration Tests Needed +1. ✅ Full GET/POST operations +2. ⚠️ Certificate rotation scenarios +3. ⚠️ Network failures and retries +4. ⚠️ Graceful shutdown with pending requests +5. ⚠️ Pool size scaling under load + +### Stress Tests Needed +1. High-frequency requests (pool saturation) +2. Long-running requests (blocking pool) simulated by long responding connection +3. Rapid init/cleanup cycles +4. Mixed GET/POST workloads + +--- + +**Document Version:** 1.0 +**Last Updated:** February 24, 2026 +**Author:** Architecture Analysis +**Status:** Initial Analysis diff --git a/source/protocol/http/Makefile.am b/source/protocol/http/Makefile.am index 5590a1dc..460bfa2a 100644 --- a/source/protocol/http/Makefile.am +++ b/source/protocol/http/Makefile.am @@ -24,7 +24,7 @@ libhttp_la_SOURCES = curlinterface.c multicurlinterface.c libhttp_la_LDFLAGS = -shared -fPIC -lcurl if IS_LIBRDKCERTSEL_ENABLED libhttp_la_CFLAGS = $(LIBRDKCERTSEL_FLAG) -libhttp_la_LDFLAGS += -lRdkCertSelector +libhttp_la_LDFLAGS += -lRdkCertSelector -lrdkconfig endif libhttp_la_CPPFLAGS = -fPIC -I${PKG_CONFIG_SYSROOT_DIR}$(includedir)/dbus-1.0 \ -I${PKG_CONFIG_SYSROOT_DIR}$(libdir)/dbus-1.0/include \ diff --git a/source/protocol/http/curlinterface.c b/source/protocol/http/curlinterface.c index 718132a8..695fa828 100644 --- a/source/protocol/http/curlinterface.c +++ b/source/protocol/http/curlinterface.c @@ -42,9 +42,11 @@ #include "rdkcertselector.h" #define FILESCHEME "file://" #endif +#ifndef LIBRDKCERTSEL_BUILD #ifdef LIBRDKCONFIG_BUILD #include "rdkconfig.h" #endif +#endif #ifdef GTEST_ENABLE #define curl_easy_setopt curl_easy_setopt_mock #define curl_easy_getinfo curl_easy_getinfo_mock @@ -82,11 +84,11 @@ T2ERROR sendReportOverHTTP(char *httpUrl, char *payload) if(ret == T2ERROR_SUCCESS) { - T2Info("Report Sent Successfully over HTTP using connection pool\n"); + T2Debug("Report Sent Successfully over HTTP\n"); } else { - T2Error("Failed to send report using connection pool\n"); + T2Error("Failed to send report\n"); } T2Debug("%s --out\n", __FUNCTION__); diff --git a/source/protocol/http/multicurlinterface.c b/source/protocol/http/multicurlinterface.c index 1d7d80e1..2d0113dc 100644 --- a/source/protocol/http/multicurlinterface.c +++ b/source/protocol/http/multicurlinterface.c @@ -32,6 +32,7 @@ #include #include #include +#include #include "multicurlinterface.h" #include "busInterface.h" #include "t2log_wrapper.h" @@ -63,15 +64,17 @@ //Global variables #define IFINTERFACE "erouter0" -#define DEFAULT_POOL_SIZE 2 +#define DEFAULT_POOL_SIZE 1 #define MAX_ALLOWED_POOL_SIZE 5 // Maximum allowed pool size #define MIN_ALLOWED_POOL_SIZE 1 // Minimum allowed pool size #define HTTP_RESPONSE_FILE "/tmp/httpOutput.txt" +#define POOL_ACQUIRE_TIMEOUT_SEC 35 +#define POOL_ACQUIRE_RETRY_MS 100 static bool pool_initialized = false; +static bool pool_shutting_down = false; -// Static initialization of mutex and condition variable to avoid race conditions +// pool_mutex protects pool state and synchronizes access to pool entries static pthread_mutex_t pool_mutex = PTHREAD_MUTEX_INITIALIZER; -static pthread_cond_t pool_cond = PTHREAD_COND_INITIALIZER; // Single pool entry structure typedef struct @@ -88,6 +91,7 @@ typedef struct static http_pool_entry_t *pool_entries = NULL; // Dynamic array of pool entries static struct curl_slist *post_headers = NULL; // Shared POST headers static int pool_size = 0; // Number of pool entries +static unsigned int active_requests = 0; // Number of in-flight curl_easy_perform() calls #ifdef LIBRDKCERTSEL_BUILD #if defined(ENABLE_RED_RECOVERY_SUPPORT) @@ -103,7 +107,8 @@ static int get_configured_pool_size(void) { int configured_size = DEFAULT_POOL_SIZE; - // Check environment variable T2_CONNECTION_POOL_SIZE + // Check environment variable T2_CONNECTION_POOL_SIZE to have the size + // of the connection pool based on the device type const char *env_size = getenv("T2_CONNECTION_POOL_SIZE"); if (env_size != NULL) { @@ -111,7 +116,7 @@ static int get_configured_pool_size(void) if (env_value >= MIN_ALLOWED_POOL_SIZE && env_value <= MAX_ALLOWED_POOL_SIZE) { configured_size = env_value; - T2Info("Using connection pool size from environment: %d\n", configured_size); + T2Info("Curl connection pool size set from environment: %d\n", configured_size); } else { @@ -134,7 +139,6 @@ static size_t httpGetCallBack(void *responseBuffer, size_t len, size_t nmemb, size_t realsize = len * nmemb; curlResponseData* response = (curlResponseData*) stream; - // FIX: Check for NULL stream to prevent crashes if (!response) { T2Error("httpGetCallBack: NULL stream parameter\n"); @@ -146,8 +150,6 @@ static size_t httpGetCallBack(void *responseBuffer, size_t len, size_t nmemb, if (!ptr) { T2Error("%s:%u , T2:memory realloc failed\n", __func__, __LINE__); - // On realloc failure, abort the transfer to avoid silently dropping data - // Return 0 to signal an error to curl return 0; } response->data = ptr; @@ -216,7 +218,6 @@ T2ERROR init_connection_pool() pthread_mutex_lock(&pool_mutex); - // Check if already initialized if(pool_initialized) { T2Info("Connection pool already initialized with size %d\n", pool_size); @@ -224,15 +225,11 @@ T2ERROR init_connection_pool() return T2ERROR_SUCCESS; } - // Get configured pool size from environment variable + //Get configured pool size from environment variable for low end devices pool_size = get_configured_pool_size(); - - T2Info("Initializing connection pool with size: %d\n", pool_size); - - // Allocate dynamic array of pool entries based on configured pool size + T2Debug("Initializing connection pool with size: %d\n", pool_size); pool_entries = (http_pool_entry_t *)calloc(pool_size, sizeof(http_pool_entry_t)); - // Check if allocation succeeded if (!pool_entries) { T2Error("Failed to allocate memory for connection pool of size %d\n", pool_size); @@ -241,14 +238,14 @@ T2ERROR init_connection_pool() return T2ERROR_FAILURE; } - // Pre-allocate easy handles and initialize pool entries + // Allocating easy handles and initialize pool entries for(int i = 0; i < pool_size; i++) { pool_entries[i].easy_handle = curl_easy_init(); if(pool_entries[i].easy_handle == NULL) { T2Error("%s : Failed to initialize curl handle %d\n", __FUNCTION__, i); - // Cleanup previously initialized handles using helper function + // Cleanup previously initialized handles to prevent memory leak cleanup_curl_handles(); pthread_mutex_unlock(&pool_mutex); return T2ERROR_FAILURE; @@ -283,7 +280,7 @@ T2ERROR init_connection_pool() CURL_SETOPT_CHECK(pool_entries[i].easy_handle, CURLOPT_SSL_VERIFYPEER, 1L); #ifdef LIBRDKCERTSEL_BUILD - // Initialize per-entry certificate selectors + // Initialize certificate selectors for each easy handle bool state_red_enable = false; #if defined(ENABLE_RED_RECOVERY_SUPPORT) state_red_enable = isStateRedEnabled(); @@ -291,7 +288,7 @@ T2ERROR init_connection_pool() if (state_red_enable) { - // Initialize recovery certificate selector for this entry + // Initialize recovery certificate selector while in state red pool_entries[i].rcvry_cert_selector = rdkcertselector_new(NULL, NULL, "RCVRY"); if (pool_entries[i].rcvry_cert_selector == NULL) { @@ -304,11 +301,11 @@ T2ERROR init_connection_pool() { T2Info("%s: Initialized recovery cert selector for entry %d\n", __func__, i); } - pool_entries[i].cert_selector = NULL; // Not used in recovery mode + pool_entries[i].cert_selector = NULL; } else { - // Initialize normal certificate selector for this entry + // Initialize normal certificate selector while not in red state pool_entries[i].cert_selector = rdkcertselector_new(NULL, NULL, "MTLS"); if (pool_entries[i].cert_selector == NULL) { @@ -321,7 +318,7 @@ T2ERROR init_connection_pool() { T2Info("%s: Initialized cert selector for entry %d\n", __func__, i); } - pool_entries[i].rcvry_cert_selector = NULL; // Not used in normal mode + pool_entries[i].rcvry_cert_selector = NULL; } #endif } @@ -352,75 +349,103 @@ T2ERROR init_connection_pool() return T2ERROR_SUCCESS; } -// Helper function to acquire any available handle with waiting + static T2ERROR acquire_pool_handle(CURL **easy, int *idx) { + struct timespec start_time, current_time; + if (clock_gettime(CLOCK_MONOTONIC, &start_time) != 0) + { + T2Error("clock_gettime failed for start_time: %s\n", strerror(errno)); + return T2ERROR_FAILURE; + } + pthread_mutex_lock(&pool_mutex); - if (!pool_initialized) + bool need_init = !pool_initialized && !pool_shutting_down; + pthread_mutex_unlock(&pool_mutex); + + if (need_init) { - pthread_mutex_unlock(&pool_mutex); T2ERROR ret = init_connection_pool(); if(ret != T2ERROR_SUCCESS) { T2Error("Failed to initialize connection pool\n"); return ret; } - pthread_mutex_lock(&pool_mutex); } - // Waits until a handle becomes available or pool is being cleaned up + // Poll for an available handle with a bounded timeout. + // If no handle is available within POOL_ACQUIRE_TIMEOUT_SEC, treat as upload failure. while(1) { - // Check if pool is being shut down - if (!pool_initialized) + pthread_mutex_lock(&pool_mutex); + + if (pool_shutting_down || !pool_initialized) { - T2Info("Pool is being cleaned up, aborting handle acquisition\n"); pthread_mutex_unlock(&pool_mutex); + T2Info("Pool is shutting down or not initialized, aborting handle acquisition\n"); return T2ERROR_FAILURE; } - // Find an available handle + // Try to find an available handle for(int i = 0; i < pool_size; i++) { if(pool_entries[i].handle_available) { - T2Info("acquire_pool_handle ; Available handle = %d (pool size: %d)\n", i, pool_size); + T2Info("Acquired handle = %d (pool size: %d)\n", i, pool_size); *idx = i; pool_entries[i].handle_available = false; *easy = pool_entries[i].easy_handle; + // Guard against overflow + if (active_requests >= (unsigned int)pool_size) + { + T2Error("Warning: active_requests (%u) >= pool_size (%d) before increment\n", active_requests, pool_size); + } + active_requests++; pthread_mutex_unlock(&pool_mutex); return T2ERROR_SUCCESS; } } - // No handle available, wait for one to become free - T2Info("No curl handle available (pool size: %d), waiting for one to become free...\n", pool_size); - pthread_cond_wait(&pool_cond, &pool_mutex); - // After waking up, loop back to check pool_initialized status - // This handles both spurious wakeups and shutdown signals + pthread_mutex_unlock(&pool_mutex); + + // Check elapsed time against timeout + if (clock_gettime(CLOCK_MONOTONIC, ¤t_time) != 0) + { + T2Error("clock_gettime failed for current_time: %s\n", strerror(errno)); + return T2ERROR_FAILURE; + } + long elapsed_sec = current_time.tv_sec - start_time.tv_sec; + + if (elapsed_sec >= POOL_ACQUIRE_TIMEOUT_SEC) + { + T2Error("Timeout waiting for available curl handle after %ld seconds, treating as upload failure\n", elapsed_sec); + return T2ERROR_FAILURE; + } + + // Sleep briefly before retrying + T2Debug("No curl handle available (pool size: %d), retrying in %dms...\n", pool_size, POOL_ACQUIRE_RETRY_MS); + usleep(POOL_ACQUIRE_RETRY_MS * 1000); } } -// Helper function to release handle back to pool static void release_pool_handle(int idx) { pthread_mutex_lock(&pool_mutex); if (idx >= 0 && idx < pool_size) { pool_entries[idx].handle_available = true; - pthread_cond_signal(&pool_cond); // Signal waiting threads - T2Info("release_pool_handle ; Released handle = %d (pool size: %d)\n", idx, pool_size); + active_requests--; + T2Info("Released curl handle = %d, active_requests = %d\n", idx, active_requests); } else { - T2Error("release_pool_handle ; Invalid handle index = %d (pool size: %d)\n", idx, pool_size); + T2Error("Invalid curl handle index = %d (pool size: %d)\n", idx, pool_size); } pthread_mutex_unlock(&pool_mutex); } #if defined(ENABLE_RDKB_SUPPORT) && !defined(RDKB_EXTENDER) -// Function to configure WAN interface for CURL handle static void configure_wan_interface(CURL *easy) { T2Debug("%s ++in\n", __FUNCTION__); @@ -453,7 +478,7 @@ static void configure_wan_interface(CURL *easy) } #endif -// GET API - Updated to use shared pool + T2ERROR http_pool_get(const char *url, char **response_data, bool enable_file_output) { T2Debug("%s ++in\n", __FUNCTION__); @@ -464,7 +489,7 @@ T2ERROR http_pool_get(const char *url, char **response_data, bool enable_file_ou return T2ERROR_FAILURE; } - T2Info("%s ; GET url = %s\n", __FUNCTION__, url); + T2Info("GET url = %s\n", url); CURL *easy; int idx = -1; @@ -477,13 +502,11 @@ T2ERROR http_pool_get(const char *url, char **response_data, bool enable_file_ou } // Clear any POST-specific settings so that the handle can be used for GET operations - CURL_SETOPT_CHECK(easy, CURLOPT_CUSTOMREQUEST, NULL); + // curl_easy_perform ends up in crash when POST specific options are not cleared CURL_SETOPT_CHECK(easy, CURLOPT_POSTFIELDS, NULL); CURL_SETOPT_CHECK(easy, CURLOPT_POSTFIELDSIZE, 0L); CURL_SETOPT_CHECK(easy, CURLOPT_HTTPHEADER, NULL); - T2Debug("http_pool_get using handle %d\n", idx); - // Allocate response buffer locally for GET requests only curlResponseData* response = (curlResponseData *) malloc(sizeof(curlResponseData)); if (!response) @@ -565,7 +588,7 @@ T2ERROR http_pool_get(const char *url, char **response_data, bool enable_file_ou CURL_SETOPT_CHECK_STR(easy, CURLOPT_KEYPASSWD, pCertPC); CURL_SETOPT_CHECK(easy, CURLOPT_SSL_VERIFYPEER, 1L); - // Execute the request directly + // Execute the request and retry incase of certificate related error curl_code = curl_easy_perform(easy); long http_code; @@ -573,11 +596,11 @@ T2ERROR http_pool_get(const char *url, char **response_data, bool enable_file_ou if(curl_code != CURLE_OK || http_code != 200) { - T2Error("%s: Failed to establish connection using xPKI certificate: %s, Curl failed : %d (entry %d)\n", __func__, pCertFile, curl_code, idx); + T2Error("%s: Failed to establish connection using xPKI certificate: %s, Curl failed : %d\n", __func__, pCertFile, curl_code); } else { - T2Info("%s: Using xpki Certs connection certname : %s (entry %d)\n", __FUNCTION__, pCertFile, idx); + T2Info("%s: Using xpki Certs connection certname : %s \n", __FUNCTION__, pCertFile); } } } @@ -586,13 +609,11 @@ T2ERROR http_pool_get(const char *url, char **response_data, bool enable_file_ou // Fallback to getMtlsCerts if certificate selector not available if(T2ERROR_SUCCESS == getMtlsCerts(&pCertFile, &pCertPC)) { - // Configure mTLS certificates CURL_SETOPT_CHECK_STR(easy, CURLOPT_SSLCERTTYPE, "P12"); CURL_SETOPT_CHECK_STR(easy, CURLOPT_SSLCERT, pCertFile); CURL_SETOPT_CHECK_STR(easy, CURLOPT_KEYPASSWD, pCertPC); CURL_SETOPT_CHECK(easy, CURLOPT_SSL_VERIFYPEER, 1L); - // Execute the request curl_code = curl_easy_perform(easy); } else @@ -610,13 +631,13 @@ T2ERROR http_pool_get(const char *url, char **response_data, bool enable_file_ou return T2ERROR_FAILURE; } #endif + //reset the security options set to curl handle CURL_SETOPT_CHECK(easy, CURLOPT_SSLCERT, NULL); CURL_SETOPT_CHECK(easy, CURLOPT_KEYPASSWD, NULL); } else { T2Info("Attempting curl communication without mtls\n"); - // Execute without mTLS curl_code = curl_easy_perform(easy); } @@ -647,7 +668,6 @@ T2ERROR http_pool_get(const char *url, char **response_data, bool enable_file_ou long http_code; curl_easy_getinfo(easy, CURLINFO_RESPONSE_CODE, &http_code); - T2Info("%s ; HTTP response code: %ld\n", __FUNCTION__, http_code); T2ERROR result = T2ERROR_FAILURE; if (http_code == 200) @@ -665,7 +685,7 @@ T2ERROR http_pool_get(const char *url, char **response_data, bool enable_file_ou int fd = open(HTTP_RESPONSE_FILE, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); if (fd >= 0) { - FILE *httpOutput = fdopen(fd, "w+"); + FILE *httpOutput = fdopen(fd, "w"); if (httpOutput) { T2Debug("Update config data in response file %s \n", HTTP_RESPONSE_FILE); @@ -684,7 +704,7 @@ T2ERROR http_pool_get(const char *url, char **response_data, bool enable_file_ou } } - // Copy response data + // Copy response data to return to the caller function if (response_data) { *response_data = NULL; @@ -739,6 +759,7 @@ T2ERROR http_pool_get(const char *url, char **response_data, bool enable_file_ou } // Clean up certificates - only for non-LIBRDKCERTSEL_BUILD path + // In case of CertSelector, CertSelector will take care of freeing the certicates #ifndef LIBRDKCERTSEL_BUILD if(pCertFile != NULL) { @@ -759,6 +780,7 @@ T2ERROR http_pool_get(const char *url, char **response_data, bool enable_file_ou pCertPC = NULL; } #endif + // Important Note: When using LIBRDKCERTSEL_BUILD, pCertURI and pCertPC are owned by the // cert selector object and are freed when rdkcertselector_free() is called @@ -768,7 +790,6 @@ T2ERROR http_pool_get(const char *url, char **response_data, bool enable_file_ou return result; } -// POST API - Updated to use shared pool T2ERROR http_pool_post(const char *url, const char *payload) { T2Debug("%s ++in\n", __FUNCTION__); @@ -778,7 +799,7 @@ T2ERROR http_pool_post(const char *url, const char *payload) return T2ERROR_FAILURE; } - T2Info("%s ; POST url = %s\n", __FUNCTION__, url); + T2Info("POST url = %s\n", url); // Acquire any available handle (with waiting) CURL *easy; @@ -791,16 +812,13 @@ T2ERROR http_pool_post(const char *url, const char *payload) return ret; } - T2Debug("http_pool_post using handle %d\n", idx); - // Clear any GET-specific settings from previous use - CURL_SETOPT_CHECK(easy, CURLOPT_HTTPGET, 0L); CURL_SETOPT_CHECK(easy, CURLOPT_WRITEFUNCTION, NULL); - CURL_SETOPT_CHECK(easy, CURLOPT_WRITEDATA, NULL); + CURL_SETOPT_CHECK(easy, CURLOPT_WRITEDATA, (void *)stdout); // Configure request-specific options for POST CURL_SETOPT_CHECK_STR(easy, CURLOPT_URL, url); - CURL_SETOPT_CHECK_STR(easy, CURLOPT_CUSTOMREQUEST, "POST"); + CURL_SETOPT_CHECK(easy, CURLOPT_POST, 1L); CURL_SETOPT_CHECK(easy, CURLOPT_HTTPHEADER, post_headers); CURL_SETOPT_CHECK_STR(easy, CURLOPT_POSTFIELDS, payload); CURL_SETOPT_CHECK(easy, CURLOPT_POSTFIELDSIZE, strlen(payload)); @@ -812,6 +830,7 @@ T2ERROR http_pool_post(const char *url, const char *payload) // curl_easy_perform crashes without file output configuration. This can be removed once the root cause of the crash is identified and fixed. // For now, we will set up file output for POST requests to ensure stability. // Set up file output for POST requests + // If file opens successfully, override the default stdout with the file pointer int curl_output_fd = open("/tmp/curlOutput.txt", O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); FILE *fp = NULL; if (curl_output_fd >= 0) @@ -824,13 +843,13 @@ T2ERROR http_pool_post(const char *url, const char *payload) } else { - T2Error("fdopen failed for /tmp/curlOutput.txt\n"); + T2Error("fdopen failed for /tmp/curlOutput.txt, will use stdout as fallback\n"); close(curl_output_fd); } } else { - T2Error("Unable to open /tmp/curlOutput.txt for writing\n"); + T2Error("Unable to open /tmp/curlOutput.txt for writing, will use stdout as fallback\n"); } // Certificate handling - check if mTLS is enabled @@ -860,11 +879,11 @@ T2ERROR http_pool_post(const char *url, const char *payload) // RED recovery mode - use recovery cert selector if (pool_entries[idx].rcvry_cert_selector == NULL) { - T2Info("%s: Initializing recovery cert selector for entry %d\n", __func__, idx); + T2Info("Initializing recovery cert selector for this easy handle\n"); pool_entries[idx].rcvry_cert_selector = rdkcertselector_new(NULL, NULL, "RCVRY"); if (pool_entries[idx].rcvry_cert_selector == NULL) { - T2Error("%s: Failed to initialize recovery cert selector for entry %d\n", __func__, idx); + T2Error("Failed to initialize recovery cert selector for this easy handle\n"); release_pool_handle(idx); if(fp) { @@ -945,7 +964,6 @@ T2ERROR http_pool_post(const char *url, const char *payload) CURL_SETOPT_CHECK_STR(easy, CURLOPT_KEYPASSWD, pCertPC); CURL_SETOPT_CHECK(easy, CURLOPT_SSL_VERIFYPEER, 1L); - // Execute the request directly curl_code = curl_easy_perform(easy); long http_code; @@ -972,7 +990,6 @@ T2ERROR http_pool_post(const char *url, const char *payload) CURL_SETOPT_CHECK_STR(easy, CURLOPT_KEYPASSWD, pCertPC); CURL_SETOPT_CHECK(easy, CURLOPT_SSL_VERIFYPEER, 1L); - // Execute the request curl_code = curl_easy_perform(easy); } else @@ -991,7 +1008,6 @@ T2ERROR http_pool_post(const char *url, const char *payload) } else { - // Execute without mTLS T2Info("Attempting curl communication without mtls\n"); curl_code = curl_easy_perform(easy); } @@ -1005,16 +1021,16 @@ T2ERROR http_pool_post(const char *url, const char *payload) { long http_code; curl_easy_getinfo(easy, CURLINFO_RESPONSE_CODE, &http_code); - T2Debug("%s ; HTTP response code: %ld\n", __FUNCTION__, http_code); + T2Debug("HTTP response code: %ld\n", http_code); if (http_code == 200) { result = T2ERROR_SUCCESS; - T2Info("%s ; HTTP Request successful\n", __FUNCTION__); + T2Info("Report Sent Successfully over HTTP : %ld\n", http_code); } else { - T2Error("%s ; HTTP request failed with code: %ld\n", __FUNCTION__, http_code); + T2Error("HTTP request failed with code: %ld\n", http_code); } } else @@ -1064,11 +1080,54 @@ T2ERROR http_pool_cleanup(void) T2Info("Cleaning up http pool resources\n"); - // Reset initialization flag - pool_initialized = false; + // Signal all new acquire attempts to bail out immediately + pool_shutting_down = true; + pthread_mutex_unlock(&pool_mutex); + + // Poll until all in-flight curl_easy_perform() calls complete. + // Max wait = POOL_ACQUIRE_TIMEOUT_SEC (curl timeout 30s + 5s buffer). + // Polling avoids the deadlock where cleanup holds mutex while waiting, + // and release_pool_handle also needs the mutex to decrement active_requests. + struct timespec start_time, current_time; + if (clock_gettime(CLOCK_MONOTONIC, &start_time) != 0) + { + T2Error("clock_gettime failed for start_time: %s\n", strerror(errno)); + return T2ERROR_FAILURE; + } + + while(1) + { + pthread_mutex_lock(&pool_mutex); + unsigned int pending = active_requests; + pthread_mutex_unlock(&pool_mutex); - // Signal any waiting threads to wake up - pthread_cond_broadcast(&pool_cond); + if (pending == 0) + { + T2Info("All active requests completed, proceeding with cleanup\n"); + break; + } + + if (clock_gettime(CLOCK_MONOTONIC, ¤t_time) != 0) + { + T2Error("clock_gettime failed for current_time: %s\n", strerror(errno)); + return T2ERROR_FAILURE; + } + long elapsed_sec = current_time.tv_sec - start_time.tv_sec; + + if (elapsed_sec >= POOL_ACQUIRE_TIMEOUT_SEC) + { + T2Error("Cleanup timeout after %ld seconds with %u request(s) still active, forcing cleanup\n", + elapsed_sec, pending); + break; + } + + T2Info("Waiting for %u active request(s) to complete before pool cleanup\n", pending); + usleep(POOL_ACQUIRE_RETRY_MS * 1000); + } + + pthread_mutex_lock(&pool_mutex); + pool_initialized = false; + pool_shutting_down = false; pthread_mutex_unlock(&pool_mutex); // Cleanup all curl handles and per-entry certificate selectors diff --git a/source/test/bulkdata/Makefile.am b/source/test/bulkdata/Makefile.am index d0fbbad3..b4413819 100644 --- a/source/test/bulkdata/Makefile.am +++ b/source/test/bulkdata/Makefile.am @@ -18,16 +18,16 @@ # limitations under the License. ########################################################################## -AM_CXXFLAGS = -fno-permissive -std=c++11 -DPERSIST_LOG_MON_REF -fPIC -DPRIVACYMODES_CONTROL -DBULKDATA +AM_CXXFLAGS = -fno-permissive -std=c++11 -DGTEST_ENABLE -DPERSIST_LOG_MON_REF -fPIC -DPRIVACYMODES_CONTROL -DBULKDATA #-DFEATURE_SUPPORT_WEBCONFIG -DDROP_ROOT_PRIV -AM_CFLAGS = -DPERSIST_LOG_MON_REF -fPIC +AM_CFLAGS = -DPERSIST_LOG_MON_REF -DGTEST_ENABLE -fPIC AUTOMAKE_OPTIONS = subdir-objects ACLOCAL_AMFLAGS = -I m4 -bin_PROGRAMS = profile_gtest.bin datamodel_gtest.bin t2markers_gtest.bin profilexconf_gtest.bin +bin_PROGRAMS = profile_gtest.bin datamodel_gtest.bin t2markers_gtest.bin profilexconf_gtest.bin reportprofiles_gtest.bin datamodel_gtest_bin_CPPFLAGS = -I$(PKG_CONFIG_SYSROOT_DIR)/usr/include/gtest -I$(PKG_CONFIG_SYSROOT_DIR)/usr/include/glib-2.0 -I$(PKG_CONFIG_SYSROOT_DIR)/usr/lib/x86_64-linux-gnu/glib-2.0/include -I$(PKG_CONFIG_SYSROOT_DIR)/usr/local/lib -I$(PKG_CONFIG_SYSROOT_DIR)$(includedir)/gtest -I${top_srcdir}/gtest/include -I${top_srcdir}/source/include -I${top_srcdir}/source -I${top_srcdir}/source/test/mocks -I${top_srcdir}/source/test/rbus -I${top_srcdir}/source/test/rdk_logger -I${top_srcdir}/include -I${top_srcdir}/source/utils -I${top_srcdir}/source/privacycontrol -I${PKG_CONFIG_SYSROOT_DIR}$(includedir)/rbus -I${top_srcdir}/source/dcautil -I${top_srcdir}/source/ccspinterface -I${top_srcdir}/source/reportgen -I${top_srcdir}/source/xconf-client -I${top_srcdir}/source/protocol/http -I${top_srcdir}/source/protocol/rbusMethod -I${top_srcdir}/source/t2parser -I${top_srcdir}/source/bulkdata -I${top_srcdir}/source/scheduler -I${top_srcdir}/source/ccspinterface -I${PKG_CONFIG_SYSROOT_DIR}$(includedir) -I${PKG_CONFIG_SYSROOT_DIR}/usr/src/googletest/googlemock/include -I${RDK_PROJECT_ROOT_PATH}/$(GLIB_CFLAGS) -I${PKG_CONFIG_SYSROOT_DIR}$(includedir)/glib-2.0 -I${PKG_CONFIG_SYSROOT_DIR}$(libdir)/glib-2.0/include @@ -51,11 +51,25 @@ profilexconf_gtest_bin_LDFLAGS = -L/usr/src/googletest/googletest/lib/.libs -l profilexconf_gtest_bin_LDFLAGS += -Wl,--wrap=sendReportOverHTTP -Wl,--wrap=sendCachedReportsOverHTTP -profilexconf_gtest_bin_SOURCES = = gtest_main.cpp profilexconfTest.cpp profilexconfMock.cpp SchedulerMock.cpp ../mocks/SystemMock.cpp ../mocks/FileioMock.cpp ../mocks/rdklogMock.cpp ../mocks/rbusMock.cpp ../../bulkdata/profilexconf.c ../../utils/vector.c ../../utils/t2log_wrapper.c ../../utils/t2common.c ../../utils/t2collection.c ../../utils/persistence.c ../../bulkdata/t2markers.c ../../reportgen/reportgen.c ../../dcautil/legacyutils.c +profilexconf_gtest_bin_SOURCES = gtest_main.cpp profilexconfTest.cpp profilexconfMock.cpp SchedulerMock.cpp ../mocks/SystemMock.cpp ../mocks/FileioMock.cpp ../mocks/rdklogMock.cpp ../mocks/rbusMock.cpp ../../bulkdata/profilexconf.c ../../utils/vector.c ../../utils/t2log_wrapper.c ../../utils/t2common.c ../../utils/t2collection.c ../../utils/persistence.c ../../bulkdata/t2markers.c ../../reportgen/reportgen.c ../../dcautil/legacyutils.c + +profile_gtest_bin_CFLAGS = -DGTEST_ENABLE profile_gtest_bin_CPPFLAGS = -I$(PKG_CONFIG_SYSROOT_DIR)/usr/include/gtest -I$(PKG_CONFIG_SYSROOT_DIR)/usr/include/glib-2.0 -I$(PKG_CONFIG_SYSROOT_DIR)/usr/lib/x86_64-linux-gnu/glib-2.0/include -I$(PKG_CONFIG_SYSROOT_DIR)/usr/local/lib -I$(PKG_CONFIG_SYSROOT_DIR)$(includedir)/gtest -I${top_srcdir}/gtest/include -I${top_srcdir}/source/include -I${top_srcdir}/source -I${top_srcdir}/source/test/mocks -I${top_srcdir}/source/test/rbus -I${top_srcdir}/source/test/rdk_logger -I${top_srcdir}/include -I${top_srcdir}/source/utils -I${top_srcdir}/source/privacycontrol -I${PKG_CONFIG_SYSROOT_DIR}$(includedir)/rbus -I${top_srcdir}/source/dcautil -I${top_srcdir}/source/ccspinterface -I${top_srcdir}/source/reportgen -I${top_srcdir}/source/xconf-client -I${top_srcdir}/source/protocol/http -I${top_srcdir}/source/protocol/rbusMethod -I${top_srcdir}/source/t2parser -I${top_srcdir}/source/bulkdata -I${top_srcdir}/source/scheduler -I${top_srcdir}/source/ccspinterface -I${PKG_CONFIG_SYSROOT_DIR}$(includedir) -I${PKG_CONFIG_SYSROOT_DIR}/usr/src/googletest/googlemock/include -I${RDK_PROJECT_ROOT_PATH}/$(GLIB_CFLAGS) -I${PKG_CONFIG_SYSROOT_DIR}$(includedir)/glib-2.0 -I${PKG_CONFIG_SYSROOT_DIR}$(libdir)/glib-2.0/include -profile_gtest_bin_SOURCES = gtest_main.cpp ../mocks/SystemMock.cpp ../mocks/FileioMock.cpp ../mocks/rdklogMock.cpp ../mocks/rbusMock.cpp ../mocks/rdkconfigMock.cpp ../mocks/VectorMock.cpp SchedulerMock.cpp ../../bulkdata/profile.c profileTest.cpp ../../utils/persistence.c ../../utils/t2common.c ../../utils/t2collection.c ../../utils/t2MtlsUtils.c ../../utils/t2log_wrapper.c ../../dcautil/dcautil.c ../../dcautil/dca.c ../../dcautil/legacyutils.c ../../dcautil/dcaproc.c ../../xconf-client/xconfclient.c ../../protocol/rbusMethod/rbusmethodinterface.c ../../privacycontrol/rdkservices_privacyutils.c ../../reportgen/reportgen.c ../../bulkdata/t2eventreceiver.c ../../bulkdata/t2markers.c ../../t2parser/t2parser.c ../../bulkdata/datamodel.c ../../t2parser/t2parserxconf.c ../../bulkdata/reportprofiles.c ../../bulkdata/profilexconf.c ../../ccspinterface/rbusInterface.c ../../ccspinterface/busInterface.c ../../protocol/http/curlinterface.c ../../protocol/http/multicurlinterface.c +profile_gtest_bin_SOURCES = gtest_main.cpp ../mocks/SystemMock.cpp ../mocks/FileioMock.cpp ../mocks/rdklogMock.cpp ../mocks/rbusMock.cpp ../mocks/rdkconfigMock.cpp ../mocks/VectorMock.cpp SchedulerMock.cpp profileMock.cpp ../../bulkdata/profile.c profileTest.cpp ../../utils/persistence.c ../../utils/t2common.c ../../utils/t2collection.c ../../utils/t2MtlsUtils.c ../../utils/t2log_wrapper.c ../../dcautil/dcautil.c ../../dcautil/dca.c ../../dcautil/legacyutils.c ../../dcautil/dcaproc.c ../../xconf-client/xconfclient.c ../../protocol/rbusMethod/rbusmethodinterface.c ../../privacycontrol/rdkservices_privacyutils.c ../../reportgen/reportgen.c ../../bulkdata/t2eventreceiver.c ../../bulkdata/t2markers.c ../../t2parser/t2parser.c ../../bulkdata/datamodel.c ../../t2parser/t2parserxconf.c ../../bulkdata/reportprofiles.c ../../bulkdata/profilexconf.c ../../ccspinterface/rbusInterface.c ../../ccspinterface/busInterface.c ../../protocol/http/curlinterface.c ../../protocol/http/multicurlinterface.c profile_gtest_bin_LDFLAGS = -L/usr/src/googletest/googletest/lib/.libs -lgcov -L/src/googletest/googlemock/lib -L/usr/src/googletest/googlemock/lib/.libs -L/usr/include/glib-2.0 -lgmock -lcjson -lcurl -lmsgpackc -lgtest -lgtest_main -lglib-2.0 +profile_gtest_bin_LDFLAGS += -Wl,--wrap=sendReportOverHTTP -Wl,--wrap=sendCachedReportsOverHTTP + +reportprofiles_gtest_bin_CFLAGS = -DGTEST_ENABLE + +reportprofiles_gtest_bin_CPPFLAGS = -I$(PKG_CONFIG_SYSROOT_DIR)/usr/include/gtest -I$(PKG_CONFIG_SYSROOT_DIR)/usr/include/glib-2.0 -I$(PKG_CONFIG_SYSROOT_DIR)/usr/lib/x86_64-linux-gnu/glib-2.0/include -I$(PKG_CONFIG_SYSROOT_DIR)/usr/local/lib -I$(PKG_CONFIG_SYSROOT_DIR)$(includedir)/gtest -I${top_srcdir}/gtest/include -I${top_srcdir}/source/include -I${top_srcdir}/source -I${top_srcdir}/source/test/mocks -I${top_srcdir}/source/test/rbus -I${top_srcdir}/source/test/rdk_logger -I${top_srcdir}/include -I${top_srcdir}/source/utils -I${top_srcdir}/source/privacycontrol -I${PKG_CONFIG_SYSROOT_DIR}$(includedir)/rbus -I${top_srcdir}/source/dcautil -I${top_srcdir}/source/ccspinterface -I${top_srcdir}/source/reportgen -I${top_srcdir}/source/xconf-client -I${top_srcdir}/source/protocol/http -I${top_srcdir}/source/protocol/rbusMethod -I${top_srcdir}/source/t2parser -I${top_srcdir}/source/bulkdata -I${top_srcdir}/source/scheduler -I${top_srcdir}/source/ccspinterface -I${PKG_CONFIG_SYSROOT_DIR}$(includedir) -I${PKG_CONFIG_SYSROOT_DIR}/usr/src/googletest/googlemock/include -I${RDK_PROJECT_ROOT_PATH}/$(GLIB_CFLAGS) -I${PKG_CONFIG_SYSROOT_DIR}$(includedir)/glib-2.0 -I${PKG_CONFIG_SYSROOT_DIR}$(libdir)/glib-2.0/include + +reportprofiles_gtest_bin_SOURCES = gtest_main.cpp ../mocks/SystemMock.cpp ../mocks/FileioMock.cpp ../mocks/rdklogMock.cpp ../mocks/rbusMock.cpp ../mocks/rdkconfigMock.cpp ../mocks/VectorMock.cpp SchedulerMock.cpp reportprofileMock.cpp ../../bulkdata/reportprofiles.c reportprofilesTest.cpp ../../utils/persistence.c ../../utils/t2common.c ../../utils/t2collection.c ../../utils/t2MtlsUtils.c ../../utils/t2log_wrapper.c ../../dcautil/dcautil.c ../../dcautil/dca.c ../../dcautil/legacyutils.c ../../dcautil/dcaproc.c ../../xconf-client/xconfclient.c ../../protocol/rbusMethod/rbusmethodinterface.c ../../privacycontrol/rdkservices_privacyutils.c ../../reportgen/reportgen.c ../../bulkdata/t2eventreceiver.c ../../bulkdata/t2markers.c ../../t2parser/t2parser.c ../../bulkdata/datamodel.c ../../t2parser/t2parserxconf.c ../../bulkdata/profile.c ../../bulkdata/profilexconf.c ../../ccspinterface/rbusInterface.c ../../ccspinterface/busInterface.c ../../protocol/http/curlinterface.c ../../protocol/http/multicurlinterface.c + +reportprofiles_gtest_bin_LDFLAGS = -L/usr/src/googletest/googletest/lib/.libs -L/usr/src/googletest/googlemock/lib/.libs -lgmock -lgtest -lpthread -lcjson -lmsgpackc -lglib-2.0 -lrt -lcurl + +reportprofiles_gtest_bin_LDFLAGS += -Wl,--wrap=isRbusEnabled -Wl,--wrap=sendReportOverHTTP -Wl,--wrap=sendCachedReportsOverHTTP + diff --git a/source/test/bulkdata/profileMock.cpp b/source/test/bulkdata/profileMock.cpp new file mode 100644 index 00000000..0a295e19 --- /dev/null +++ b/source/test/bulkdata/profileMock.cpp @@ -0,0 +1,45 @@ +/* +* Copyright 2020 Comcast Cable Communications Management, LLC +* +* 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. +* +* SPDX-License-Identifier: Apache-2.0 +*/ +#include +#include +#include "vector.h" +#include "msgpack.h" +#include "test/bulkdata/profileMock.h" + + +//protocol mock functions +extern "C" T2ERROR __wrap_sendReportOverHTTP(char *httpUrl, char *payload) +{ + if(!g_profileMock) + { + return T2ERROR_FAILURE; + } + // No mock function needed, just a stub + return g_profileMock->sendReportOverHTTP(httpUrl, payload); +} + +extern "C" T2ERROR __wrap_sendCachedReportsOverHTTP(char *httpUrl, Vector *reportList) +{ + if(!g_profileMock) + { + return T2ERROR_FAILURE; + } + // No mock function needed, just a stub + return g_profileMock->sendCachedReportsOverHTTP(httpUrl, reportList); +} + diff --git a/source/test/bulkdata/profileMock.h b/source/test/bulkdata/profileMock.h new file mode 100644 index 00000000..f6f7c6ef --- /dev/null +++ b/source/test/bulkdata/profileMock.h @@ -0,0 +1,35 @@ +/* +* Copyright 2020 Comcast Cable Communications Management, LLC +* +* 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. +* +* SPDX-License-Identifier: Apache-2.0 +*/ +//#ifndef SOURCE_TEST_MOCKS_SYSTEMMOCK_H_ +//#define SOURCE_TEST_MOCKS_SYSTEMMOCK_H_ + +#include +#include +#include "telemetry2_0.h" + + +class profileMock +{ +public: + + MOCK_METHOD(T2ERROR, sendReportOverHTTP, (char *httpUrl, char *payload), ()); + MOCK_METHOD(T2ERROR, sendCachedReportsOverHTTP, (char *httpUrl, Vector *reportList), ()); +}; + +extern profileMock *g_profileMock; + diff --git a/source/test/bulkdata/profileTest.cpp b/source/test/bulkdata/profileTest.cpp index 7626f48d..09108f6d 100644 --- a/source/test/bulkdata/profileTest.cpp +++ b/source/test/bulkdata/profileTest.cpp @@ -17,6 +17,7 @@ #include "test/mocks/rdkconfigMock.h" #include "test/mocks/VectorMock.h" #include "test/bulkdata/SchedulerMock.h" +#include "test/bulkdata/profileMock.h" using namespace std; @@ -34,6 +35,8 @@ extern "C" { #include "profilexconf.h" #include "t2eventreceiver.h" #include "msgpack.h" +#include +#include extern bool initialized; @@ -46,6 +49,7 @@ SystemMock * g_systemMock = NULL; rdklogMock *m_rdklogMock = NULL; rbusMock *g_rbusMock = NULL; rdkconfigMock *g_rdkconfigMock = nullptr; +profileMock *g_profileMock = nullptr; extern VectorMock *g_vectorMock; extern SchedulerMock *g_schedulerMock; @@ -59,6 +63,7 @@ class ProfileTest : public ::testing::Test { g_rdkconfigMock = new rdkconfigMock(); g_vectorMock = new VectorMock(); g_schedulerMock = new SchedulerMock(); + g_profileMock = new profileMock(); } void TearDown() override { @@ -68,6 +73,7 @@ class ProfileTest : public ::testing::Test { delete g_rdkconfigMock; delete g_vectorMock; delete g_schedulerMock; + delete g_profileMock; g_fileIOMock = nullptr; g_systemMock = nullptr; @@ -75,6 +81,7 @@ class ProfileTest : public ::testing::Test { g_rdkconfigMock = nullptr; g_vectorMock = nullptr; g_schedulerMock = nullptr; + g_profileMock = nullptr; } }; @@ -225,6 +232,28 @@ TEST_F(ProfileTest, UninitProfileList_Success) { EXPECT_EQ(uninitProfileList(), T2ERROR_SUCCESS); } +TEST_F(ProfileTest, ReportProfiles_uninit) { + EXPECT_CALL(*g_vectorMock, Vector_Create(_)) + .Times(::testing::AtMost(3)) + .WillRepeatedly(Return(T2ERROR_SUCCESS)); + EXPECT_CALL(*g_vectorMock, Vector_PushBack(_, _)) + .Times(::testing::AtMost(1)) + .WillRepeatedly(Return(T2ERROR_SUCCESS)); + EXPECT_CALL(*g_vectorMock, Vector_Size(_)) + .Times(::testing::AtMost(3)) + .WillRepeatedly(Return(0)); + EXPECT_CALL(*g_vectorMock, Vector_At(_, _)) + .Times(::testing::AtMost(2)) + .WillRepeatedly(Return(nullptr)); + EXPECT_CALL(*g_schedulerMock, uninitScheduler()) + .Times(::testing::AtMost(1)); + EXPECT_CALL(*g_schedulerMock, unregisterProfileFromScheduler(_)) + .Times(::testing::AtMost(5)) + .WillRepeatedly(Return(T2ERROR_SUCCESS)); + + EXPECT_EQ(ReportProfiles_uninit(), T2ERROR_FAILURE); +} + // Test getProfileCount TEST_F(ProfileTest, GetProfileCount_NotInitialized) { EXPECT_CALL(*g_vectorMock, Vector_Size(_)) @@ -759,6 +788,14 @@ TEST_F(ProfileTest, ReportProfiles_deleteProfileXConf) { } #endif +TEST_F(ProfileTest, ReportProfiles_deleteProfileXConf_EmptyList) { + ProfileXConf profile; + EXPECT_CALL(*g_vectorMock, Vector_Size(_)) + .Times(::testing::AtMost(1)) + .WillRepeatedly(Return(0)); // Return 1 to indicate only one profile (no duplicates) + EXPECT_EQ(ReportProfiles_deleteProfileXConf(&profile), T2ERROR_SUCCESS); +} + TEST_F(ProfileTest, ReportProfiles_deleteProfile) { EXPECT_CALL(*g_vectorMock, Vector_Size(_)) .Times(::testing::AtMost(2)) @@ -810,7 +847,16 @@ TEST_F(ProfileTest, RemovePreRPfromDisk) { EXPECT_EQ(RemovePreRPfromDisk("/tmp", &dummy), T2ERROR_SUCCESS); } -#if 0 +TEST_F(ProfileTest, RemovePreRPfromDisk_FailsIfDirNull) { + hash_map_t dummy; + // Mock opendir to return NULL to simulate failure + EXPECT_CALL(*g_fileIOMock, opendir(_)) + .Times(1) + .WillOnce(Return(nullptr)); + // readdir and closedir should NOT be called in this branch + EXPECT_EQ(RemovePreRPfromDisk("/tmp", &dummy), T2ERROR_FAILURE); +} + TEST_F(ProfileTest, deleteAllReportProfiles) { EXPECT_CALL(*g_vectorMock, Vector_Size(_)) .Times(::testing::AtMost(1)) @@ -819,56 +865,67 @@ TEST_F(ProfileTest, deleteAllReportProfiles) { .WillRepeatedly(Return(T2ERROR_SUCCESS)); EXPECT_EQ(deleteAllReportProfiles(), T2ERROR_SUCCESS); } -#endif -#if 0 -TEST_F(ProfileTest, isMtlsEnabled) { - char status[8] = "true"; - EXPECT_CALL(*g_rbusMock, rbus_get(_,_,_)) - .Times(::testing::AtMost(2)) - .WillRepeatedly(Return(RBUS_ERROR_SUCCESS)); - EXPECT_CALL(*g_rbusMock, rbusValue_GetType(_)) - .Times(::testing::AtMost(2)) - .WillRepeatedly(Return(RBUS_BOOLEAN)); - EXPECT_CALL(*g_rbusMock, rbusValue_GetBoolean(_)) - .Times(::testing::AtMost(2)) - .WillRepeatedly(Return(RBUS_ERROR_SUCCESS)); - EXPECT_CALL(*g_rbusMock, rbusValue_Release(_)) - .Times(::testing::AtMost(2)) - .WillRepeatedly(Return()); - EXPECT_CALL(*g_rbusMock, rbusValue_ToString(_,_,_)) - .Times(::testing::AtMost(1)) - .WillRepeatedly(Return(status)); - EXPECT_TRUE(isMtlsEnabled()); -} -#endif +#ifdef GTEST_ENABLE +extern "C" { +typedef void* (*reportOnDemandFunc)(void*); +reportOnDemandFunc reportOnDemandFuncCallback(void); +typedef void (*freeProfilesHashMapFunc)(void *); +freeProfilesHashMapFunc freeProfilesHashMapFuncCallback(void); +typedef void (*freeReportProfileHashMapFunc)(void *); +freeReportProfileHashMapFunc freeReportProfileHashMapFuncCallback(void); +typedef void (*__msgpack_free_blobFunc)(void*); +__msgpack_free_blobFunc __msgpack_free_blobFuncCallback(void); +} + +TEST(ReportProfilesCallbacks, FreeProfilesHashMap) { + auto cb = freeProfilesHashMapFuncCallback(); + ASSERT_NE(cb, nullptr); + + // Test with an actual element + hash_element_t* item = (hash_element_t*) std::malloc(sizeof(hash_element_t)); + item->key = (char*) std::malloc(12); + std::strcpy(item->key, "testkey"); + item->data = std::malloc(8); + cb(item); + + // Test with nullptr + cb(nullptr); +} +TEST_F(ProfileTest, reportOnDemandTest) +{ + reportOnDemandFunc func = reportOnDemandFuncCallback(); + ASSERT_NE(func,nullptr); + func((void*)"ABORT"); + func((void*)"FOO"); + func(nullptr); +} + +TEST(ReportProfilesCallbacks, FreeReportProfileHashMap) { + auto cb = freeReportProfileHashMapFuncCallback(); + ASSERT_NE(cb, nullptr); + + // Make an item with ReportProfile-like .data + hash_element_t* item = (hash_element_t*) std::malloc(sizeof(hash_element_t)); + item->key = (char*) std::malloc(12); + std::strcpy(item->key, "profkey"); + struct ReportProfile { + char* hash; + char* config; + void* hash_map_pad; // just to align with how your system might fill it, can be omitted + }; + ReportProfile* rp = (ReportProfile*) std::malloc(sizeof(ReportProfile)); + rp->hash = (char*) std::malloc(6); + std::strcpy(rp->hash, "hashV"); + rp->config = (char*) std::malloc(8); + std::strcpy(rp->config, "cfgVal"); + item->data = rp; -#if 0 -TEST_F(ProfileTest, ReportProfiles_uninit) { - EXPECT_CALL(*g_vectorMock, Vector_Create(_)) - .Times(::testing::AtMost(3)) // 1 for local test configlist, 1 for global profileList, 1 for configList in loadReportProfilesFromDisk - .WillRepeatedly(Return(T2ERROR_SUCCESS)); - EXPECT_CALL(*g_vectorMock, Vector_PushBack(_, _)) - .Times(::testing::AtMost(1)) - .WillRepeatedly(Return(T2ERROR_SUCCESS)); - EXPECT_CALL(*g_vectorMock, Vector_Size(_)) - .Times(::testing::AtMost(3)) // May be called multiple times - in deleteAllProfiles, etc. - .WillRepeatedly(Return(0)); // Return 0 to indicate no profiles (avoid unregister calls) - EXPECT_CALL(*g_vectorMock, Vector_At(_, _)) - .Times(::testing::AtMost(2)) // May be called if profiles exist - .WillRepeatedly(Return(nullptr)); - - // Scheduler mock expectations - uninitScheduler is definitely called - EXPECT_CALL(*g_schedulerMock, uninitScheduler()) - .Times(::testing::AtMost(1)); - - // unregisterProfileFromScheduler may be called for each profile during deleteAllProfiles - // Using AtMost to handle cases where profiles exist - EXPECT_CALL(*g_schedulerMock, unregisterProfileFromScheduler(_)) - .Times(::testing::AtMost(5)) // Allow up to 5 calls in case profiles exist - .WillRepeatedly(Return(T2ERROR_SUCCESS)); - - EXPECT_EQ(ReportProfiles_uninit(), T2ERROR_SUCCESS); + cb(item); + + // Safe to call with nullptr + cb(nullptr); + SUCCEED(); } #endif #endif @@ -877,6 +934,13 @@ TEST_F(ProfileTest, ReportProfiles_uninit) { //comment //=================================== profilexconf.c ================================ + +#ifdef GTEST_ENABLE +extern "C" { + void test_set_reportThreadExits(bool value); + } +#endif + TEST_F(ProfileTest, InitAndUninit) { // Covers ProfileXConf_init and ProfileXConf_uninit #if 1 @@ -921,11 +985,21 @@ TEST_F(ProfileTest, SetAndIsSet) { profile->cachedReportList = nullptr; profile->protocol = strdup("HTTP"); profile->encodingType = strdup("JSON"); - profile->t2HTTPDest = nullptr; - profile->grepSeekProfile = nullptr; + profile->jsonReportObj = nullptr; // types now match + profile->checkPreviousSeek = true; + + profile->t2HTTPDest = (T2HTTP *)malloc(sizeof(T2HTTP)); + profile->t2HTTPDest->URL = strdup("https://mock1xconf:50051/dataLakeMockXconf"); + profile->isUpdated = true; + GrepSeekProfile *gsProfile = (GrepSeekProfile *)malloc(sizeof(GrepSeekProfile)); + if (gsProfile) + { + gsProfile->logFileSeekMap = hash_map_create(); + gsProfile->execCounter = 0; + } + profile->grepSeekProfile = gsProfile; + //profile->grepSeekProfile = nullptr; profile->reportInProgress = false; - profile->isUpdated = false; - EXPECT_CALL(*g_vectorMock, Vector_Size(_)) .Times(::testing::AtMost(3)) .WillRepeatedly(Return(0)); // Return 1 to indicate one profile in the list @@ -938,26 +1012,29 @@ TEST_F(ProfileTest, SetAndIsSet) { EXPECT_CALL(*g_vectorMock, Vector_PushBack(_, _)) .Times(::testing::AtMost(3)) .WillRepeatedly(Return(T2ERROR_SUCCESS)); - + // Scheduler mock expectations - ProfileXConf_set calls registerProfileWithScheduler EXPECT_CALL(*g_schedulerMock, registerProfileWithScheduler(_, _, _, _, _, _, _, _)) .Times(::testing::AtMost(1)) .WillRepeatedly(Return(T2ERROR_SUCCESS)); - + EXPECT_EQ(ProfileXConf_set(profile), T2ERROR_SUCCESS); EXPECT_TRUE(ProfileXConf_isSet()); + // Get name char* name = ProfileXconf_getName(); ASSERT_NE(name, nullptr); EXPECT_STREQ(name, "TestProfile"); free(name); - // Clean up - ProfileXConf_uninit calls unregisterProfileFromScheduler - EXPECT_CALL(*g_schedulerMock, unregisterProfileFromScheduler(_)) - .Times(::testing::AtMost(1)) - .WillRepeatedly(Return(T2ERROR_SUCCESS)); - + test_set_reportThreadExits(true); + generateDcaReport(false,true); + EXPECT_CALL(*g_schedulerMock, SendInterruptToTimeoutThread(_)) + .Times(::testing::AtMost(1)); + + ReportProfiles_Interrupt(); + EXPECT_EQ(ProfileXConf_uninit(), T2ERROR_SUCCESS); } @@ -1097,6 +1174,7 @@ TEST_F(ProfileTest, DeleteProfile) { profile->isUpdated = false; ProfileXConf_set(profile); + EXPECT_EQ(ReportProfiles_setProfileXConf(profile),T2ERROR_FAILURE); EXPECT_EQ(ProfileXConf_delete(profile), T2ERROR_FAILURE); ProfileXConf_uninit(); } diff --git a/source/test/bulkdata/reportprofileMock.cpp b/source/test/bulkdata/reportprofileMock.cpp new file mode 100644 index 00000000..50cd748b --- /dev/null +++ b/source/test/bulkdata/reportprofileMock.cpp @@ -0,0 +1,54 @@ +/* +* Copyright 2020 Comcast Cable Communications Management, LLC +* +* 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. +* +* SPDX-License-Identifier: Apache-2.0 +*/ + +#include +#include +#include "vector.h" +#include "test/bulkdata/reportprofileMock.h" + + + +// Mock Method + +extern "C" bool __wrap_isRbusEnabled() +{ + if (!g_reportprofileMock) + { + return false; + } + return g_reportprofileMock->isRbusEnabled(); +} + +extern "C" { +T2ERROR __wrap_sendReportOverHTTP(char *httpUrl, char *payload) +{ + if (g_reportprofileMock) { + return g_reportprofileMock->sendReportOverHTTP(httpUrl, payload); + } + return T2ERROR_FAILURE; +} + +T2ERROR __wrap_sendCachedReportsOverHTTP(char *httpUrl, Vector *reportList) +{ + if (g_reportprofileMock) { + return g_reportprofileMock->sendCachedReportsOverHTTP(httpUrl, reportList); + } + return T2ERROR_FAILURE; +} +} + diff --git a/source/test/bulkdata/reportprofileMock.h b/source/test/bulkdata/reportprofileMock.h new file mode 100644 index 00000000..24e19294 --- /dev/null +++ b/source/test/bulkdata/reportprofileMock.h @@ -0,0 +1,36 @@ +/* +* Copyright 2020 Comcast Cable Communications Management, LLC +* +* 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. +* +* SPDX-License-Identifier: Apache-2.0 +*/ + +#pragma once + +#include +#include + + +#include "telemetry2_0.h" + +class reportprofileMock +{ +public: + + MOCK_METHOD(bool, isRbusEnabled, (), ()); + MOCK_METHOD(T2ERROR, sendReportOverHTTP, (char *httpUrl, char *payload), ()); + MOCK_METHOD(T2ERROR, sendCachedReportsOverHTTP, (char *httpUrl, Vector *reportList), ()); +}; + +extern reportprofileMock *g_reportprofileMock; diff --git a/source/test/bulkdata/reportprofilesTest.cpp b/source/test/bulkdata/reportprofilesTest.cpp new file mode 100644 index 00000000..cea0c380 --- /dev/null +++ b/source/test/bulkdata/reportprofilesTest.cpp @@ -0,0 +1,163 @@ +/* + * 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 "reportprofiles.h" +#include "profile.h" +#include "t2eventreceiver.h" +#include "t2collection.h" +#include "t2log_wrapper.h" +#include "msgpack.h" +#include +#include +} + +#include "test/mocks/SystemMock.h" +#include "test/mocks/FileioMock.h" +#include "test/mocks/rdklogMock.h" +#include "test/mocks/rbusMock.h" +#include "test/mocks/rdkconfigMock.h" +#include "test/mocks/VectorMock.h" +#include "test/bulkdata/SchedulerMock.h" +#include "reportprofileMock.h" + +using namespace std; +using ::testing::_; +using ::testing::Return; +using ::testing::StrEq; +using ::testing::Invoke; +// Create global mocks if you need them just like t2markersTest.cpp +FileMock *g_fileIOMock = NULL; +SystemMock * g_systemMock = NULL; +rdklogMock *m_rdklogMock = NULL; +rbusMock *g_rbusMock = NULL; +rdkconfigMock *g_rdkconfigMock = nullptr; +extern VectorMock *g_vectorMock; +extern SchedulerMock *g_schedulerMock; +reportprofileMock* g_reportprofileMock = nullptr; +// Test fixture for reportprofiles +class reportprofilesTestFixture : public ::testing::Test { +protected: + void SetUp() override { + g_reportprofileMock = new reportprofileMock(); + g_systemMock = new SystemMock(); + g_fileIOMock = new FileMock(); + m_rdklogMock = new rdklogMock(); + g_rbusMock = new rbusMock(); + g_vectorMock = new VectorMock(); + } + void TearDown() override { + delete g_reportprofileMock; + delete g_systemMock; + delete g_fileIOMock; + delete m_rdklogMock; + delete g_rbusMock; + delete g_vectorMock; + g_reportprofileMock = nullptr; + g_systemMock = nullptr; + g_fileIOMock = nullptr; + m_rdklogMock = nullptr; + g_rbusMock = nullptr; + g_vectorMock = nullptr; + } +}; + + +TEST_F(reportprofilesTestFixture, ProcessMsgPackBlob_InvalidFormat) { + struct __msgpack__ msg; + msg.msgpack_blob = nullptr; + msg.msgpack_blob_size = 0; + int ret = __ReportProfiles_ProcessReportProfilesMsgPackBlob(&msg, false); + EXPECT_EQ(ret, T2ERROR_INVALID_ARGS); +} + +TEST_F(reportprofilesTestFixture, ProcessMsgPackBlob_Test1) { + const char *data = "AQ=="; + gsize decodedDataLen = 0; + guchar *webConfigString = g_base64_decode(data, &decodedDataLen); + + struct __msgpack__ *msg = (struct __msgpack__*)malloc(sizeof(struct __msgpack__)); + msg->msgpack_blob = (char*)webConfigString; + msg->msgpack_blob_size = (int)decodedDataLen; + + EXPECT_CALL(*g_vectorMock, Vector_Size(_)) + .Times(::testing::AtMost(1)) + .WillRepeatedly(Return(0)); + EXPECT_CALL(*g_vectorMock, Vector_Destroy(_, _)).Times(::testing::AtMost(1)) + .WillRepeatedly(Return(T2ERROR_SUCCESS)); + + ReportProfiles_ProcessReportProfilesMsgPackBlob(msg->msgpack_blob, msg->msgpack_blob_size); +} + +TEST_F(reportprofilesTestFixture, ProcessReportProfilesBlob_EmptyProfile_T2_TEMP_RP) { + cJSON *root = cJSON_CreateObject(); + cJSON *profiles = cJSON_CreateArray(); + cJSON_AddItemToObject(root, "profiles", profiles); + ReportProfiles_ProcessReportProfilesBlob(root, T2_TEMP_RP); + cJSON_Delete(root); +} + +TEST_F(reportprofilesTestFixture, ProcessReportProfilesBlob_EmptyProfile_Normal) { + cJSON *root = cJSON_CreateObject(); + cJSON *profiles = cJSON_CreateArray(); + cJSON_AddItemToObject(root, "profiles", profiles); + EXPECT_CALL(*g_vectorMock, Vector_Size(_)).WillRepeatedly(Return(0)); + EXPECT_CALL(*g_vectorMock, Vector_Destroy(_, _)).WillRepeatedly(Return(T2ERROR_SUCCESS)); + ReportProfiles_ProcessReportProfilesBlob(root, T2_RP); // normal, triggers deleteAllReportProfiles + cJSON_Delete(root); +} + +TEST_F(reportprofilesTestFixture, ProcessReportProfilesBlob_AddNewProfile) { + cJSON *root = cJSON_CreateObject(); + cJSON *profiles = cJSON_CreateArray(); + cJSON_AddItemToObject(root, "profiles", profiles); + cJSON *profile = cJSON_CreateObject(); + cJSON_AddStringToObject(profile, "name", "newprofile"); + cJSON_AddStringToObject(profile, "hash", "newhash"); + cJSON *value = cJSON_CreateObject(); + cJSON_AddStringToObject(value, "param", "value"); + cJSON_AddItemToObject(profile, "value", value); + cJSON_AddItemToArray(profiles, profile); + + EXPECT_CALL(*g_reportprofileMock, isRbusEnabled()) + .WillRepeatedly(Return(false)); + EXPECT_CALL(*g_vectorMock, Vector_Size(_)).WillRepeatedly(Return(0)); + EXPECT_CALL(*g_vectorMock, Vector_Destroy(_, _)).WillRepeatedly(Return(T2ERROR_SUCCESS)); + + DIR *dir = reinterpret_cast(1); + + EXPECT_CALL(*g_fileIOMock, opendir(_)) + .Times(::testing::AtMost(1)) + .WillRepeatedly(Return(dir)); + EXPECT_CALL(*g_fileIOMock, readdir(_)) + .Times(::testing::AtMost(1)) + .WillRepeatedly(Return((struct dirent *)NULL)); + EXPECT_CALL(*g_fileIOMock, closedir(_)) + .Times(::testing::AtMost(1)) + .WillRepeatedly(Return(0)); + ReportProfiles_ProcessReportProfilesBlob(root, T2_RP); + cJSON_Delete(root); +} +TEST_F(reportprofilesTestFixture, ProcessReportProfilesMsgPackBlob_NullBlob) { + ReportProfiles_ProcessReportProfilesMsgPackBlob(NULL,0); +} + 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..14252f26 100644 --- a/source/test/protocol/ProtocolTest.cpp +++ b/source/test/protocol/ProtocolTest.cpp @@ -80,7 +80,6 @@ using ::testing::_; using ::testing::Return; using ::testing::StrEq; - class protocolTestFixture : public ::testing::Test { protected: void SetUp() override @@ -91,6 +90,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,8 +148,6 @@ class protocolTestFixture : public ::testing::Test { } }; - -#if 0 TEST(SENDREPORTOVERHTTP, 1_NULL_CHECK) { char *payload = "This is a payload string"; @@ -123,6 +160,7 @@ TEST(SENDREPORTOVERHTTP, 2_NULL_CHECK) EXPECT_EQ(T2ERROR_FAILURE, sendReportOverHTTP(url, NULL)); } + TEST(SENDCACREPOVERHTTP, 1_NULL_CHECK) { Vector* reportlist; @@ -138,7 +176,6 @@ TEST(SENDCACREPOVERHTTP, 2_NULL_CHECK) char *url = "https://test.com"; EXPECT_EQ(T2ERROR_FAILURE, sendCachedReportsOverHTTP(url, NULL)); } -#endif TEST(SENDRBUDREPORTOVERRBUS, 1_NULL_CHECK) { @@ -188,15 +225,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 +279,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 +306,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 +331,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 +358,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 +370,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 +396,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) { @@ -432,69 +530,6 @@ TEST_F(protocolTestFixture, sendCachedReportsOverRBUSMethod) Vector_Destroy(reportlist,free); } -#if 0 -//sendReportOverHTTP -TEST_F(protocolTestFixture, sendReportOverHTTP_6) -{ - char* httpURL = "https://mockxconf:50051/dataLakeMock"; - char* payload = strdup("This is a payload string"); - Vector* reportlist = NULL; - Vector_Create(&reportlist); - Vector_PushBack(reportlist, payload); - char *cm = (char*)0xFFFFFFFF; - EXPECT_CALL(*g_fileIOMock, pipe(_)) - .Times(1) - .WillOnce(Return(0)); - #if defined(ENABLE_RDKB_SUPPORT) && !defined(RDKB_EXTENDER) - #if defined(WAN_FAILOVER_SUPPORTED) || defined(FEATURE_RDKB_CONFIGURABLE_WAN_INTERFACE) - EXPECT_CALL(*m_xconfclientMock, getParameterValue(_,_)) - .WillOnce([](const char* paramName, char** paramValue) { - if (strcmp(paramName, "Device.X_RDK_WanManager.CurrentActiveInterface") == 0) - *paramValue = strdup("erouter0"); - else - *paramValue = strdup("unknown"); - return T2ERROR_SUCCESS; - }); - #endif - #endif - EXPECT_CALL(*m_xconfclientMock, isMtlsEnabled()) - .Times(1) - .WillOnce(Return(true)); - EXPECT_CALL(*g_systemMock, access(_,_)) - .Times(1) - .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_EQ(T2ERROR_SUCCESS, sendReportOverHTTP(httpURL, payload)); - Vector_Destroy(reportlist, free); -} - TEST_F(protocolTestFixture, sendCachedReportsOverHTTP_FailureCase) { char *httpURL = "https://mockxconf:50051/dataLakeMock"; @@ -507,26 +542,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) @@ -557,470 +582,4 @@ TEST_F(protocolTestFixture, sendCachedReportsOverHTTP_FailureCase) remove(testFile); } -#if 0 -TEST(CURLINTERFACE_STATIC, SetHeader) -{ - childResponse resp; - memset(&resp, 0, sizeof(resp)); - SetHeaderFunc setHeaderCb = getSetHeaderCallback(); - ASSERT_NE(setHeaderCb, nullptr); - CURL *curl = nullptr; // purposely NULL - const char *destURL = "http://localhost"; - struct curl_slist *headerList = nullptr; - T2ERROR result = setHeaderCb(curl, destURL, &headerList, &resp); - // According to implementation, curl==NULL returns T2ERROR_FAILURE - EXPECT_EQ(result, T2ERROR_FAILURE); -} -TEST(CURLINTERFACE_STATIC, SetMtlsHeaders_NULL) -{ - childResponse resp; - memset(&resp, 0, sizeof(resp)); - SetMtlsHeadersFunc cb = getSetMtlsHeadersCallback(); - ASSERT_NE(cb, nullptr); - // NULL for CURL - EXPECT_EQ(cb(nullptr, "cert", "pwd", &resp), T2ERROR_FAILURE); - // NULL for certFile - EXPECT_EQ(cb((CURL*)0x1, nullptr, "pwd", &resp), T2ERROR_FAILURE); - // NULL for passwd - EXPECT_EQ(cb((CURL*)0x1, "cert", nullptr, &resp), T2ERROR_FAILURE); -} - -TEST(CURLINTERFACE_STATIC, SetPayload_NULL) -{ - childResponse resp; - memset(&resp, 0, sizeof(resp)); - SetPayloadFunc cb = getSetPayloadCallback(); - ASSERT_NE(cb, nullptr); - // NULL for CURL - EXPECT_EQ(cb(nullptr, "payload", &resp), T2ERROR_FAILURE); - // NULL for payload - EXPECT_EQ(cb((CURL*)0x1, nullptr, &resp), T2ERROR_FAILURE); -} - -TEST_F(protocolTestFixture, CURLINTERFACE_STATIC_SetHeader_NULL_DESTURL) -{ - childResponse resp; - memset(&resp, 0, sizeof(resp)); - SetHeaderFunc setHeaderCb = getSetHeaderCallback(); - ASSERT_NE(setHeaderCb, nullptr); - - CURL *curl = (CURL*)0x1; - struct curl_slist *headerList = nullptr; - - // destURL NULL should immediately fail - T2ERROR result = setHeaderCb(curl, NULL, &headerList, &resp); - EXPECT_EQ(result, T2ERROR_FAILURE); -} - -TEST_F(protocolTestFixture, CURLINTERFACE_STATIC_SetHeader_setopt_failure) -{ - childResponse resp; - memset(&resp, 0, sizeof(resp)); - SetHeaderFunc setHeaderCb = getSetHeaderCallback(); - ASSERT_NE(setHeaderCb, nullptr); - - CURL *curl = (CURL*)0x1; - const char *destURL = "http://localhost"; - struct curl_slist *headerList = nullptr; - - // Make the first curl_easy_setopt call fail to exercise early failure path. - EXPECT_CALL(*g_fileIOMock, curl_easy_setopt_mock(_,_,_)) - .Times(1) - .WillOnce(Return(CURLE_FAILED_INIT)); - - T2ERROR result = setHeaderCb(curl, destURL, &headerList, &resp); - EXPECT_EQ(result, T2ERROR_FAILURE); - // curlSetopCode should be set to the failing CURLE code - EXPECT_EQ(resp.curlSetopCode, CURLE_FAILED_INIT); -} - -TEST_F(protocolTestFixture, CURLINTERFACE_STATIC_SetHeader_FirstSetopt_failure_lineNumber) -{ - SetHeaderFunc setHeaderCb = getSetHeaderCallback(); - ASSERT_NE(setHeaderCb, nullptr); - - CURL *curl = (CURL*)0x1; - const char *destURL = "http://localhost"; - struct curl_slist *headerList = nullptr; - childResponse resp; - memset(&resp, 0, sizeof(resp)); - - // The very first curl_easy_setopt_mock call returns CURLE_FAILED_INIT. - EXPECT_CALL(*g_fileIOMock, curl_easy_setopt_mock(_,_,_)) - .Times(1) - .WillOnce(Return(CURLE_FAILED_INIT)); - - T2ERROR code = setHeaderCb(curl, destURL, &headerList, &resp); - EXPECT_EQ(code, T2ERROR_FAILURE); - EXPECT_EQ(resp.curlSetopCode, CURLE_FAILED_INIT); - // Assert that lineNumber field gets set. (Should match the __LINE__ where the macro is expanded; we test it's nonzero.) - EXPECT_NE(resp.lineNumber, 0); -} - -TEST_F(protocolTestFixture, CURLINTERFACE_STATIC_SetMtlsHeaders_setopt_failure) -{ - SetMtlsHeadersFunc cb = getSetMtlsHeadersCallback(); - ASSERT_NE(cb, nullptr); - - childResponse resp; - memset(&resp, 0, sizeof(resp)); - - // Make the first curl_easy_setopt call fail and ensure function returns failure - EXPECT_CALL(*g_fileIOMock, curl_easy_setopt_mock(_,_,_)) - .Times(1) - .WillOnce(Return(CURLE_UNKNOWN_OPTION)); - - T2ERROR result = cb((CURL*)0x1, "dummyCert", "dummyPwd", &resp); - EXPECT_EQ(result, T2ERROR_FAILURE); - EXPECT_EQ(resp.curlSetopCode, CURLE_UNKNOWN_OPTION); -} - -TEST_F(protocolTestFixture, CURLINTERFACE_STATIC_SetPayload_setopt_failure) -{ - SetPayloadFunc cb = getSetPayloadCallback(); - ASSERT_NE(cb, nullptr); - - childResponse resp; - memset(&resp, 0, sizeof(resp)); - - // Make CURLOPT_POSTFIELDS setopt call fail - EXPECT_CALL(*g_fileIOMock, curl_easy_setopt_mock(_,_,_)) - .Times(1) - .WillOnce(Return(CURLE_OUT_OF_MEMORY)); - - T2ERROR result = cb((CURL*)0x1, "payload", &resp); - EXPECT_EQ(result, T2ERROR_FAILURE); - EXPECT_EQ(resp.curlSetopCode, CURLE_OUT_OF_MEMORY); -} - -TEST_F(protocolTestFixture, CURLINTERFACE_STATIC_SetHeader_SSLVERSION_failure) -{ - SetHeaderFunc setHeaderCb = getSetHeaderCallback(); - ASSERT_NE(setHeaderCb, nullptr); - - CURL *curl = (CURL*)0x1; - const char *destURL = "http://localhost"; - struct curl_slist *headerList = nullptr; - childResponse resp; - memset(&resp, 0, sizeof(resp)); - - // First setopt (CURLOPT_URL) succeeds, second (CURLOPT_SSLVERSION) fails. - EXPECT_CALL(*g_fileIOMock, curl_easy_setopt_mock(_,_,_)) - .Times(2) - .WillOnce(Return(CURLE_OK)) - .WillOnce(Return(CURLE_SSL_CONNECT_ERROR)); - - T2ERROR result = setHeaderCb(curl, destURL, &headerList, &resp); - EXPECT_EQ(result, T2ERROR_FAILURE); - EXPECT_EQ(resp.curlSetopCode, CURLE_SSL_CONNECT_ERROR); -} - -TEST_F(protocolTestFixture, CURLINTERFACE_STATIC_SetHeader_CUSTOMREQUEST_failure) -{ - SetHeaderFunc setHeaderCb = getSetHeaderCallback(); - ASSERT_NE(setHeaderCb, nullptr); - - CURL *curl = (CURL*)0x1; - const char *destURL = "http://localhost"; - struct curl_slist *headerList = nullptr; - childResponse resp; - memset(&resp, 0, sizeof(resp)); - - // Sequence: CURLOPT_URL -> OK, CURLOPT_SSLVERSION -> OK, CURLOPT_CUSTOMREQUEST -> fail - EXPECT_CALL(*g_fileIOMock, curl_easy_setopt_mock(_,_,_)) - .Times(3) - .WillOnce(Return(CURLE_OK)) // CURLOPT_URL - .WillOnce(Return(CURLE_OK)) // CURLOPT_SSLVERSION - .WillOnce(Return(CURLE_UNKNOWN_OPTION)); // CURLOPT_CUSTOMREQUEST fails - - T2ERROR result = setHeaderCb(curl, destURL, &headerList, &resp); - EXPECT_EQ(result, T2ERROR_FAILURE); - EXPECT_EQ(resp.curlSetopCode, CURLE_UNKNOWN_OPTION); -} - -TEST_F(protocolTestFixture, CURLINTERFACE_STATIC_SetHeader_TIMEOUT_failure) -{ - SetHeaderFunc setHeaderCb = getSetHeaderCallback(); - ASSERT_NE(setHeaderCb, nullptr); - - CURL *curl = (CURL*)0x1; - const char *destURL = "http://localhost"; - struct curl_slist *headerList = nullptr; - childResponse resp; - memset(&resp, 0, sizeof(resp)); - - // Sequence: CURLOPT_URL -> OK, CURLOPT_SSLVERSION -> OK, CURLOPT_CUSTOMREQUEST -> OK, CURLOPT_TIMEOUT -> fail - EXPECT_CALL(*g_fileIOMock, curl_easy_setopt_mock(_,_,_)) - .Times(4) - .WillOnce(Return(CURLE_OK)) // CURLOPT_URL - .WillOnce(Return(CURLE_OK)) // CURLOPT_SSLVERSION - .WillOnce(Return(CURLE_OK)) // CURLOPT_CUSTOMREQUEST - .WillOnce(Return(CURLE_OUT_OF_MEMORY)); // CURLOPT_TIMEOUT fails - - T2ERROR result = setHeaderCb(curl, destURL, &headerList, &resp); - EXPECT_EQ(result, T2ERROR_FAILURE); - EXPECT_EQ(resp.curlSetopCode, CURLE_OUT_OF_MEMORY); -} - -TEST_F(protocolTestFixture, CURLINTERFACE_STATIC_SetHeader_HTTPHEADER_failure) -{ - SetHeaderFunc setHeaderCb = getSetHeaderCallback(); - ASSERT_NE(setHeaderCb, nullptr); - - CURL *curl = (CURL*)0x1; - const char *destURL = "http://localhost"; - struct curl_slist *headerList = nullptr; - childResponse resp; - memset(&resp, 0, sizeof(resp)); - - // Ensure curl_slist_append calls return a valid non-null list pointer - EXPECT_CALL(*g_fileIOMock, curl_slist_append(_, _)) - .Times(2) - .WillRepeatedly(Return((struct curl_slist*)0x1)); - - // Sequence of curl_easy_setopt_mock returns: - // URL, SSLVERSION, CUSTOMREQUEST, TIMEOUT, (optional INTERFACE), HTTPHEADER -> fail - // Use 6 returns: first 5 OK, 6th is the failing HTTPHEADER setopt. - EXPECT_CALL(*g_fileIOMock, curl_easy_setopt_mock(_,_,_)) - .Times(6) - .WillOnce(Return(CURLE_OK)) // CURLOPT_URL - .WillOnce(Return(CURLE_OK)) // CURLOPT_SSLVERSION - .WillOnce(Return(CURLE_OK)) // CURLOPT_CUSTOMREQUEST - .WillOnce(Return(CURLE_OK)) // CURLOPT_TIMEOUT - .WillOnce(Return(CURLE_OK)) // CURLOPT_INTERFACE or extra opt (if present) - .WillOnce(Return(CURLE_COULDNT_CONNECT)); // CURLOPT_HTTPHEADER fails - - T2ERROR result = setHeaderCb(curl, destURL, &headerList, &resp); - EXPECT_EQ(result, T2ERROR_FAILURE); - EXPECT_EQ(resp.curlSetopCode, CURLE_COULDNT_CONNECT); -} - -TEST_F(protocolTestFixture, CURLINTERFACE_STATIC_SetHeader_HTTPHEADER_failure_block) -{ - SetHeaderFunc setHeaderCb = getSetHeaderCallback(); - ASSERT_NE(setHeaderCb, nullptr); - - CURL *curl = (CURL*)0x1; - const char *destURL = "http://localhost"; - struct curl_slist *headerList = nullptr; - childResponse resp; - memset(&resp, 0, sizeof(resp)); - - // Simulate curl_slist_append returns - EXPECT_CALL(*g_fileIOMock, curl_slist_append(_, _)) - .Times(2) - .WillRepeatedly(Return((struct curl_slist*)0x1)); - - // Mock all prior curl_easy_setopt calls to return OK, only HTTPHEADER fails - ::testing::Sequence s; - EXPECT_CALL(*g_fileIOMock, curl_easy_setopt_mock(_, ::testing::Ne(CURLOPT_HTTPHEADER), _)) - .Times(::testing::AtLeast(1)) - .InSequence(s) - .WillRepeatedly(Return(CURLE_OK)); - EXPECT_CALL(*g_fileIOMock, curl_easy_setopt_mock(_, CURLOPT_HTTPHEADER, _)) - .InSequence(s) - .WillOnce(Return(CURLE_COULDNT_CONNECT)); // Simulate failure at HTTPHEADER - - T2ERROR result = setHeaderCb(curl, destURL, &headerList, &resp); - - EXPECT_EQ(result, T2ERROR_FAILURE); - EXPECT_EQ(resp.curlSetopCode, CURLE_COULDNT_CONNECT); - // Should be set to a non-zero line number corresponding to line 166 in your source - EXPECT_NE(resp.lineNumber, 0); -} - -TEST_F(protocolTestFixture, CURLINTERFACE_STATIC_SetHeader_WRITEFUNCTION_failure) -{ - SetHeaderFunc setHeaderCb = getSetHeaderCallback(); - ASSERT_NE(setHeaderCb, nullptr); - - CURL *curl = (CURL*)0x1; - const char *destURL = "http://localhost"; - struct curl_slist *headerList = nullptr; - childResponse resp; - memset(&resp, 0, sizeof(resp)); - - // Ensure curl_slist_append calls return a valid non-null list pointer - EXPECT_CALL(*g_fileIOMock, curl_slist_append(_, _)) - .Times(2) - .WillRepeatedly(Return((struct curl_slist*)0x1)); - - // Make CURLOPT_WRITEFUNCTION fail specifically (robust to optional extra setopt calls). - EXPECT_CALL(*g_fileIOMock, curl_easy_setopt_mock(_, CURLOPT_WRITEFUNCTION, _)) - .WillOnce(Return(CURLE_SEND_ERROR)); - - // All other curl_easy_setopt_mock invocations should succeed. - EXPECT_CALL(*g_fileIOMock, curl_easy_setopt_mock(_, ::testing::Ne(CURLOPT_WRITEFUNCTION), _)) - .WillRepeatedly(Return(CURLE_OK)); - - T2ERROR result = setHeaderCb(curl, destURL, &headerList, &resp); - EXPECT_EQ(result, T2ERROR_FAILURE); - EXPECT_EQ(resp.curlSetopCode, CURLE_SEND_ERROR); -} -/* New tests to cover success paths for static helpers in curlinterface.c */ -TEST_F(protocolTestFixture, CURLINTERFACE_STATIC_SetPayload_SUCCESS) -{ - SetPayloadFunc cb = getSetPayloadCallback(); - ASSERT_NE(cb, nullptr); - childResponse resp; - // Allow any curl_easy_setopt_mock calls and return CURLE_OK - EXPECT_CALL(*g_fileIOMock, curl_easy_setopt_mock(_,_,_)) - .Times(::testing::AtLeast(2)) - .WillRepeatedly(Return(CURLE_OK)); - T2ERROR res = cb((CURL*)0x1, "dummy-payload", &resp); - EXPECT_EQ(res, T2ERROR_SUCCESS); -} - -TEST_F(protocolTestFixture, CURLINTERFACE_STATIC_SetMtlsHeaders_SSLCERT_failure) -{ - SetMtlsHeadersFunc cb = getSetMtlsHeadersCallback(); - ASSERT_NE(cb, nullptr); - - childResponse resp; - memset(&resp, 0, sizeof(resp)); - - CURL *curl = (CURL*)0x1; - const char* certFile = "/tmp/mycert.p12"; - const char* passwd = "dummyPwd"; - - // We need to return CURLE_OK for CURLOPT_SSLCERTTYPE, - // And return error for CURLOPT_SSLCERT only, then do not expect calls for subsequent steps - { - ::testing::InSequence seq; - EXPECT_CALL(*g_fileIOMock, curl_easy_setopt_mock(curl, CURLOPT_SSLCERTTYPE, ::testing::_)) - .WillOnce(Return(CURLE_OK)); - EXPECT_CALL(*g_fileIOMock, curl_easy_setopt_mock(curl, CURLOPT_SSLCERT, ::testing::_)) - .WillOnce(Return(CURLE_SSL_CERTPROBLEM)); - // No further setopt calls expected after error - } - - T2ERROR result = cb(curl, certFile, passwd, &resp); - EXPECT_EQ(result, T2ERROR_FAILURE); - EXPECT_EQ(resp.curlSetopCode, CURLE_SSL_CERTPROBLEM); - // Optionally: check that lineNumber is set to a value after the failing line -} - -TEST_F(protocolTestFixture, CURLINTERFACE_STATIC_SetMtlsHeaders_KEYPASSWD_failure) -{ - SetMtlsHeadersFunc cb = getSetMtlsHeadersCallback(); - ASSERT_NE(cb, nullptr); - - childResponse resp; - memset(&resp, 0, sizeof(resp)); - - CURL *curl = (CURL*)0x1; - const char* certFile = "/tmp/mycert.p12"; - const char* passwd = "dummyPwd"; - - { - ::testing::InSequence seq; - EXPECT_CALL(*g_fileIOMock, curl_easy_setopt_mock(curl, CURLOPT_SSLCERTTYPE, ::testing::_)) - .WillOnce(Return(CURLE_OK)); - EXPECT_CALL(*g_fileIOMock, curl_easy_setopt_mock(curl, CURLOPT_SSLCERT, ::testing::_)) - .WillOnce(Return(CURLE_OK)); - EXPECT_CALL(*g_fileIOMock, curl_easy_setopt_mock(curl, CURLOPT_KEYPASSWD, ::testing::_)) - .WillOnce(Return(CURLE_LOGIN_DENIED)); - // No further setopt calls expected - } - - T2ERROR result = cb(curl, certFile, passwd, &resp); - EXPECT_EQ(result, T2ERROR_FAILURE); - EXPECT_EQ(resp.curlSetopCode, CURLE_LOGIN_DENIED); -} - -TEST_F(protocolTestFixture, CURLINTERFACE_STATIC_SetMtlsHeaders_SSLVERIFYPEER_failure) -{ - SetMtlsHeadersFunc cb = getSetMtlsHeadersCallback(); - ASSERT_NE(cb, nullptr); - - childResponse resp; - memset(&resp, 0, sizeof(resp)); - - CURL *curl = (CURL*)0x1; - const char* certFile = "/tmp/mycert.p12"; - const char* passwd = "dummyPwd"; - - { - ::testing::InSequence seq; - EXPECT_CALL(*g_fileIOMock, curl_easy_setopt_mock(curl, CURLOPT_SSLCERTTYPE, ::testing::_)) - .WillOnce(Return(CURLE_OK)); - EXPECT_CALL(*g_fileIOMock, curl_easy_setopt_mock(curl, CURLOPT_SSLCERT, ::testing::_)) - .WillOnce(Return(CURLE_OK)); - EXPECT_CALL(*g_fileIOMock, curl_easy_setopt_mock(curl, CURLOPT_KEYPASSWD, ::testing::_)) - .WillOnce(Return(CURLE_OK)); - EXPECT_CALL(*g_fileIOMock, curl_easy_setopt_mock(curl, CURLOPT_SSL_VERIFYPEER, ::testing::_)) - .WillOnce(Return(CURLE_PEER_FAILED_VERIFICATION)); - } - - T2ERROR result = cb(curl, certFile, passwd, &resp); - EXPECT_EQ(result, T2ERROR_FAILURE); - EXPECT_EQ(resp.curlSetopCode, CURLE_PEER_FAILED_VERIFICATION); -} - -TEST_F(protocolTestFixture, CURLINTERFACE_STATIC_SetPayload_POSTFIELDSIZE_failure) -{ - SetPayloadFunc cb = getSetPayloadCallback(); - ASSERT_NE(cb, nullptr); - - childResponse resp; - memset(&resp, 0, sizeof(resp)); - CURL *curl = (CURL*)0x1; - const char *payload = "payload-for-testing"; - - { - ::testing::InSequence seq; - // First call(s) for setting postfields may succeed - EXPECT_CALL(*g_fileIOMock, curl_easy_setopt_mock(curl, CURLOPT_POSTFIELDS, ::testing::_)) - .WillOnce(Return(CURLE_OK)); - // We force failure on POSTFIELDSIZE - EXPECT_CALL(*g_fileIOMock, curl_easy_setopt_mock(curl, CURLOPT_POSTFIELDSIZE, ::testing::_)) - .WillOnce(Return(CURLE_WRITE_ERROR)); - } - - T2ERROR result = cb(curl, payload, &resp); - EXPECT_EQ(result, T2ERROR_FAILURE); - EXPECT_EQ(resp.curlSetopCode, CURLE_WRITE_ERROR); -} - -TEST_F(protocolTestFixture, CURLINTERFACE_STATIC_SetMtlsHeaders_SUCCESS) -{ - SetMtlsHeadersFunc cb = getSetMtlsHeadersCallback(); - ASSERT_NE(cb, nullptr); - childResponse resp; - // Expect at least 4 curl_easy_setopt calls for the mtls settings - EXPECT_CALL(*g_fileIOMock, curl_easy_setopt_mock(_,_,_)) - .Times(::testing::AtLeast(4)) - .WillRepeatedly(Return(CURLE_OK)); - T2ERROR res = cb((CURL*)0x1, "/tmp/cert.p12", "passwd", &resp); - EXPECT_EQ(res, T2ERROR_SUCCESS); -} - -TEST_F(protocolTestFixture, CURLINTERFACE_STATIC_SetHeader_SUCCESS) -{ - SetHeaderFunc setHeaderCb = getSetHeaderCallback(); - ASSERT_NE(setHeaderCb, nullptr); - - CURL *curl = (CURL*)0x1; - const char *destURL = "http://localhost"; - struct curl_slist *headerList = nullptr; - childResponse resp; - memset(&resp, 0, sizeof(resp)); - - // Expect curl_slist_append to be called twice for the two headers and return a non-null list - EXPECT_CALL(*g_fileIOMock, curl_slist_append(_, _)) - .Times(2) - .WillRepeatedly(Return((struct curl_slist*)0x1)); - - // Allow the curl_easy_setopt_mock calls that setHeader performs and return CURLE_OK. - // Use AtLeast to be resilient to small differences in option counts due to build flags. - EXPECT_CALL(*g_fileIOMock, curl_easy_setopt_mock(_,_,_)) - .Times(::testing::AtLeast(6)) - .WillRepeatedly(Return(CURLE_OK)); - - T2ERROR result = setHeaderCb(curl, destURL, &headerList, &resp); - EXPECT_EQ(result, T2ERROR_SUCCESS); - // headerList should have been set by curl_slist_append to a non-null pointer - EXPECT_NE(headerList, nullptr); -} -#endif #endif diff --git a/source/test/xconf-client/xconfclientTest.cpp b/source/test/xconf-client/xconfclientTest.cpp index 1e46ffe3..5738c9a2 100644 --- a/source/test/xconf-client/xconfclientTest.cpp +++ b/source/test/xconf-client/xconfclientTest.cpp @@ -20,16 +20,13 @@ #include "gtest/gtest.h" #include #include -#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 #include "test/mocks/rdklogMock.h" #include "test/mocks/rbusMock.h" #include "xconfclientMock.h" + extern "C" { #include #include @@ -57,22 +54,32 @@ using ::testing::StrEq; using ::testing::SetArgPointee; using ::testing::DoAll; +extern FileMock *g_fileIOMock; XconfclientMock *m_xconfclientMock = NULL; rbusMock *g_rbusMock = NULL; +// 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 TEST(DOHTTPGET, HTTPURL_CHECK) { char* data = NULL; + FileMock fileMock; + g_fileIOMock = &fileMock; + PREVENT_GTEST_LOGGING_DEADLOCK(); EXPECT_EQ(T2ERROR_FAILURE, doHttpGet(NULL, &data)); } -#endif TEST(FETCHREMOTECONFIG, CONFIGUURL_NULL) { @@ -87,6 +94,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,16 +325,12 @@ TEST_F(xconfclientTestFixture, fetchRemoteConfiguration) EXPECT_EQ(T2ERROR_FAILURE, fetchRemoteConfiguration(configURL, &configData)); } -#if 0 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 TEST_F(xconfclientTestFixture, getRemoteConfigURL) { @@ -382,24 +413,37 @@ TEST_F(xconfclientTestFixture, getRemoteConfigURL3) EXPECT_EQ(T2ERROR_SUCCESS, getRemoteConfigURL(&configURL)); } -#if 0 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 +456,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 +503,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,97 +549,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) -#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 T2ERROR_SUCCESS; - }); -#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(4) - .WillOnce(Return(0)) - .WillOnce(Return(0)) - .WillOnce(Return(0)) - .WillOnce(Return(0)); - - 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)); -} + 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++); + })); -TEST_F(xconfclientTestFixture, doHttpGet6) -{ - char* data = NULL; - EXPECT_CALL(*g_fileIOMock, pipe(_)) - .Times(2) - .WillOnce(Return(0)) - .WillOnce(Return(0)); - #if defined(ENABLE_RDKB_SUPPORT) && !defined(RDKB_EXTENDER) +#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"); @@ -576,54 +595,49 @@ TEST_F(xconfclientTestFixture, doHttpGet6) }); #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(0)); - EXPECT_CALL(*g_fileIOMock, fclose(_)) - .Times(1) - .WillOnce(Return(0)); - - EXPECT_EQ(T2ERROR_SUCCESS, 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)); } -//child process 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(_,_)) @@ -639,21 +653,11 @@ TEST_F(xconfclientTestFixture, doHttpGet7) #endif 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()) + .WillOnce(Return(1)); + EXPECT_CALL(*g_systemMock, access(_,_)) .Times(1) - .WillOnce(Return((CURL*)0XFFFFFFFF)); - EXPECT_CALL(*g_fileIOMock, curl_easy_setopt_mock(_,_,_)) - .Times(6) - .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)); @@ -666,31 +670,21 @@ TEST_F(xconfclientTestFixture, doHttpGet7) } 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)) - .WillOnce(Return(0)); - EXPECT_CALL(*g_fileIOMock, fopen(_,_)) - .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); + + EXPECT_EQ(doHttpGet("https://test.com", &data), T2ERROR_SUCCESS); } TEST_F(xconfclientTestFixture, doHttpGet8) { 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(_,_)) @@ -710,24 +704,7 @@ TEST_F(xconfclientTestFixture, doHttpGet8) 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)); + EXPECT_CALL(*g_fileIOMock, curl_easy_perform(_)) .Times(1) .WillOnce(Return(CURLE_OK)); @@ -740,25 +717,9 @@ 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)) - .WillOnce(Return(0)); - EXPECT_CALL(*g_fileIOMock, fopen(_,_)) - .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); -} -#endif + EXPECT_EQ(doHttpGet("https://test.com", &data), T2ERROR_SUCCESS); +} TEST_F(xconfclientTestFixture, initXConfClient_failure) { diff --git a/source/utils/t2MtlsUtils.c b/source/utils/t2MtlsUtils.c index b52933b5..a8a8e5b6 100644 --- a/source/utils/t2MtlsUtils.c +++ b/source/utils/t2MtlsUtils.c @@ -30,9 +30,11 @@ #include "t2log_wrapper.h" #include "t2common.h" +#ifndef LIBRDKCERTSEL_BUILD #ifdef LIBRDKCONFIG_BUILD #include "rdkconfig.h" #endif +#endif #if !defined(ENABLE_RDKC_SUPPORT) diff --git a/source/xconf-client/Makefile.am b/source/xconf-client/Makefile.am index f7be2830..201526b7 100644 --- a/source/xconf-client/Makefile.am +++ b/source/xconf-client/Makefile.am @@ -24,7 +24,7 @@ libxconfclient_la_SOURCES = xconfclient.c libxconfclient_la_LDFLAGS = -shared -fPIC -lcjson -lcurl if IS_LIBRDKCERTSEL_ENABLED libxconfclient_la_CFLAGS = $(LIBRDKCERTSEL_FLAG) -libxconfclient_la_LDFLAGS += -lRdkCertSelector +libxconfclient_la_LDFLAGS += -lRdkCertSelector -lrdkconfig endif libxconfclient_la_LIBADD = ${top_builddir}/source/t2parser/libt2parser.la ${top_builddir}/source/ccspinterface/libccspinterface.la libxconfclient_la_CPPFLAGS = -fPIC -I${PKG_CONFIG_SYSROOT_DIR}$(includedir)/dbus-1.0 \ diff --git a/source/xconf-client/xconfclient.c b/source/xconf-client/xconfclient.c index c1871637..b2cc656c 100644 --- a/source/xconf-client/xconfclient.c +++ b/source/xconf-client/xconfclient.c @@ -50,9 +50,11 @@ #include "rdkcertselector.h" #define FILESCHEME "file://" #endif +#ifndef LIBRDKCERTSEL_BUILD #ifdef LIBRDKCONFIG_BUILD #include "rdkconfig.h" #endif +#endif #define RFC_RETRY_TIMEOUT 60 #define XCONF_RETRY_TIMEOUT 180 #define MAX_XCONF_RETRY_COUNT 5 @@ -274,7 +276,15 @@ static char *getTimezone () free(zoneValue); zoneValue = NULL ; } - zoneValue = strdup(zone); + if (zone != NULL && strlen(zone) > 0) + { + zoneValue = strdup(zone); + } + else + { + zoneValue = NULL; + T2Warning("Warning: zone is NULL or empty, skipping\n"); + } } fclose(file); free(zone); @@ -534,11 +544,11 @@ T2ERROR doHttpGet(char* httpsUrl, char **data) ret = http_pool_get(httpsUrl, data, true); if(ret == T2ERROR_SUCCESS) { - T2Info("HTTP GET request completed successfully using connection pool\n"); + T2Debug("HTTP GET request completed successfully\n"); } else { - T2Error("Failed to perform HTTP GET request using connection pool\n"); + T2Error("Failed to perform HTTP GET request\n"); } T2Debug("%s --out\n", __FUNCTION__); return ret; diff --git a/test/functional-tests/tests/helper_functions.py b/test/functional-tests/tests/helper_functions.py index a287d426..214fb619 100644 --- a/test/functional-tests/tests/helper_functions.py +++ b/test/functional-tests/tests/helper_functions.py @@ -26,6 +26,42 @@ import signal from urllib.parse import unquote, quote, urlparse, parse_qsl +# Certificate configuration for mTLS +# Check for combined PEM files (cert+key in one file) first, then separate files +CERT_COMBINED_PATHS = [ + '/opt/certs/client.pem', + '/etc/pki/Test-RDK-root/Test-RDK-client-ICA/certs/rdkclient.pem', +] + +CERT_SEPARATE_PATHS = [ + ('/opt/certs/client.pem', '/opt/certs/client.key'), + ('/etc/pki/Test-RDK-root/Test-RDK-client-ICA/certs/rdkclient.pem', '/etc/pki/Test-RDK-root/Test-RDK-client-ICA/private/rdkclient.key'), +] + +# Find the first valid certificate +CERT_PATH = None + +# Try combined PEM files first (cert+key in single file) +for cert_file in CERT_COMBINED_PATHS: + if os.path.exists(cert_file): + CERT_PATH = cert_file + print(f"Using combined client certificate+key: {cert_file}") + break + +# If no combined file found, try separate cert and key files +if CERT_PATH is None: + for cert_file, key_file in CERT_SEPARATE_PATHS: + if os.path.exists(cert_file) and os.path.exists(key_file): + CERT_PATH = (cert_file, key_file) + print(f"Using client certificate: {cert_file} with key: {key_file}") + break + +if CERT_PATH is None: + print("WARNING: No valid client certificate found!") + +# CA certificate for server verification +CA_BUNDLE = '/etc/ssl/certs/ca-certificates.crt' # System CA bundle + def run_telemetry(): return subprocess.run("/usr/local/bin/telemetry2_0", shell=True) @@ -75,12 +111,12 @@ def sigterm_telemetry(pid): def adminSupport_cache(save: bool = True): - if(requests.get(ADMIN_SUPPORT_SET, verify=False,params={ADMIN_CACHE_ARG: str(save).lower()})): + if(requests.get(ADMIN_SUPPORT_SET, verify=CA_BUNDLE, cert=CERT_PATH, params={ADMIN_CACHE_ARG: str(save).lower()})): return True return False def adminSupport_requestData(): - return_data = requests.get(ADMIN_SUPPORT_GET, verify=False,params={ADMIN_RQUEST_DATA: "true"}) + return_data = requests.get(ADMIN_SUPPORT_GET, verify=CA_BUNDLE, cert=CERT_PATH, params={ADMIN_RQUEST_DATA: "true"}) try: return_json = return_data.json() except: @@ -146,34 +182,18 @@ def check_Rbus_data(): result = subprocess.run(RBUSCLI_GET_CMD+T2_TEMP_REPORT_PROFILE_PARAM, shell=True, capture_output=True, text=True) assert RBUS_EXCEPTION_STRING not in result.stdout -def adminSupport_DLXconf_requestData(): - return_data = requests.get(ADMIN_SUPPORT_URL, verify=False,params={ADMIN_RQUEST_DATA: "true"}) - try: - return_json = return_data.json() - except: - return_json = [] - return return_json - -def adminSupport_DLReport_requestData(): - return_data = requests.get(ADMIN_SUPPORT_URL, verify=False,params={ADMIN_RQUEST_DATA: "true"}) - try: - return_json = return_data.json() - except: - return_json = [] - return return_json - def adminSupport_DLXconf_cache(save:bool = True): - if(requests.get(DL_ADMIN_URL +"Xconf", verify=False,params={ADMIN_CACHE_ARG: str(save).lower()})): + if(requests.get(DL_ADMIN_URL +"Xconf", verify=CA_BUNDLE, cert=CERT_PATH, params={ADMIN_CACHE_ARG: str(save).lower()})): return True return False def adminSupport_DLReport_cache(save:bool = True): - if(requests.get(DL_ADMIN_URL +"Report", verify=False,params={ADMIN_CACHE_ARG: str(save).lower()})): + if(requests.get(DL_ADMIN_URL +"Report", verify=CA_BUNDLE, cert=CERT_PATH, params={ADMIN_CACHE_ARG: str(save).lower()})): return True return False def adminSupport_DLXconf_requestData(): - return_data = requests.get(DL_ADMIN_URL+"Xconf", verify=False,params={ADMIN_RQUEST_DATA: "true"}) + return_data = requests.get(DL_ADMIN_URL+"Xconf", verify=CA_BUNDLE, cert=CERT_PATH, params={ADMIN_RQUEST_DATA: "true"}) try: return_json = return_data.json() except: @@ -181,7 +201,7 @@ def adminSupport_DLXconf_requestData(): return return_json def adminSupport_DLReport_requestData(): - return_data = requests.get(DL_ADMIN_URL+"Report", verify=False,params={ADMIN_RQUEST_DATA: "true"}) + return_data = requests.get(DL_ADMIN_URL+"Report", verify=CA_BUNDLE, cert=CERT_PATH, params={ADMIN_RQUEST_DATA: "true"}) try: return_json = return_data.json() except: diff --git a/test/functional-tests/tests/report_profiles.py b/test/functional-tests/tests/report_profiles.py index 5e8836ee..54359306 100644 --- a/test/functional-tests/tests/report_profiles.py +++ b/test/functional-tests/tests/report_profiles.py @@ -1234,7 +1234,7 @@ "MaxUploadLatency": 10 }, "HTTP": { - "URL": "https://mockxconf:50051/dataLookeMock/", + "URL": "https://mockxconf:50051/dataLakeMockXconf/", "Compression": "None", "Method": "POST", "RequestURIParameter": [ diff --git a/test/functional-tests/tests/test_multiprofile_msgpacket.py b/test/functional-tests/tests/test_multiprofile_msgpacket.py index 7ff35191..c455085f 100644 --- a/test/functional-tests/tests/test_multiprofile_msgpacket.py +++ b/test/functional-tests/tests/test_multiprofile_msgpacket.py @@ -71,7 +71,6 @@ def test_without_namefield(): assert LOG_MSG not in grep_T2logs(LOG_MSG) #Empty string in namefield assert HASH_ERROR_MSG in grep_T2logs(HASH_ERROR_MSG) #without hash field - #negative case without hashvalue, without version field & without Protocol field @pytest.mark.run(order=2) def test_without_hashvalue(): diff --git a/test/run_l2.sh b/test/run_l2.sh index 7cfe5165..00bcdc97 100755 --- a/test/run_l2.sh +++ b/test/run_l2.sh @@ -33,8 +33,17 @@ fi gcc test/functional-tests/tests/app.c -o test/functional-tests/tests/t2_app -ltelemetry_msgsender -lt2utils +final_result=0 # removing --exitfirst flag as it is causing the test to exit after first failure -pytest -v --json-report --json-report-summary --json-report-file $RESULT_DIR/runs_as_daemon.json test/functional-tests/tests/test_runs_as_daemon.py -pytest -v --json-report --json-report-summary --json-report-file $RESULT_DIR/bootup_sequence.json test/functional-tests/tests/test_bootup_sequence.py -pytest -v --json-report --json-report-summary --json-report-file $RESULT_DIR/xconf_communications.json test/functional-tests/tests/test_xconf_communications.py --exitfirst -pytest -v --json-report --json-report-summary --json-report-file $RESULT_DIR/msg_packet.json test/functional-tests/tests/test_multiprofile_msgpacket.py --exitfirst +pytest -v --json-report --json-report-summary --json-report-file $RESULT_DIR/runs_as_daemon.json test/functional-tests/tests/test_runs_as_daemon.py || final_result=1 +pytest -v --json-report --json-report-summary --json-report-file $RESULT_DIR/bootup_sequence.json test/functional-tests/tests/test_bootup_sequence.py || final_result=1 +pytest -v --json-report --json-report-summary --json-report-file $RESULT_DIR/xconf_communications.json test/functional-tests/tests/test_xconf_communications.py || final_result=1 +pytest -v --json-report --json-report-summary --json-report-file $RESULT_DIR/msg_packet.json test/functional-tests/tests/test_multiprofile_msgpacket.py || final_result=1 + +if [ $final_result -ne 0 ]; then + echo "Some tests failed. Please check the JSON reports in $RESULT_DIR for details." +else + echo "All tests passed successfully." +fi + +exit $final_result diff --git a/test/run_ut.sh b/test/run_ut.sh index 7c5b942a..90f897f5 100755 --- a/test/run_ut.sh +++ b/test/run_ut.sh @@ -49,6 +49,7 @@ tests=" ./source/test/bulkdata/datamodel_gtest.bin ./source/test/bulkdata/profilexconf_gtest.bin ./source/test/bulkdata/t2markers_gtest.bin +./source/test/bulkdata/reportprofiles_gtest.bin ./source/test/reportgen/reportgen_gtest.bin ./source/test/scheduler/scheduler_gtest.bin ./source/test/t2parser/t2parser_gtest.bin diff --git a/test/test-artifacts/mockxconf/xconf-dcm-response2.json b/test/test-artifacts/mockxconf/xconf-dcm-response2.json deleted file mode 100644 index e2deb481..00000000 --- a/test/test-artifacts/mockxconf/xconf-dcm-response2.json +++ /dev/null @@ -1,72 +0,0 @@ -{ - "urn:settings:GroupName": "Generic", - "urn:settings:CheckOnReboot": false, - "urn:settings:TimeZoneMode": "UTC", - "urn:settings:CheckSchedule:cron": "37 5 * * *", - "urn:settings:CheckSchedule:DurationMinutes": 300, - "urn:settings:LogUploadSettings:Message": null, - "urn:settings:LogUploadSettings:Name": "Generic", - "urn:settings:LogUploadSettings:NumberOfDays": 0, - "urn:settings:LogUploadSettings:UploadRepositoryName": "S3", - "urn:settings:LogUploadSettings:UploadRepository:URL": "https://secure.s3.bucket.test.url", - "urn:settings:LogUploadSettings:UploadRepository:uploadProtocol": "HTTP", - "urn:settings:LogUploadSettings:UploadOnReboot": false, - "urn:settings:LogUploadSettings:UploadImmediately": false, - "urn:settings:LogUploadSettings:upload": true, - "urn:settings:LogUploadSettings:UploadSchedule:cron": "36 6 * * *", - "urn:settings:LogUploadSettings:UploadSchedule:levelone:cron": null, - "urn:settings:LogUploadSettings:UploadSchedule:leveltwo:cron": null, - "urn:settings:LogUploadSettings:UploadSchedule:levelthree:cron": null, - "urn:settings:LogUploadSettings:UploadSchedule:TimeZoneMode": "UTC", - "urn:settings:LogUploadSettings:UploadSchedule:DurationMinutes": 240, - "urn:settings:VODSettings:Name": null, - "urn:settings:VODSettings:LocationsURL": null, - "urn:settings:VODSettings:SRMIPList": null, - "urn:settings:TelemetryProfile": { - "@type": "PermanentTelemetryProfile", - "id": "9e9fce57-8053-423e-a647-a537ca8b7899", - "telemetryProfile": [ - { - "header":"IUI_accum", - "content":"Device.DeviceInfo.X_RDKCENTRAL-COM.IUI.Version", - "type":"", - "pollingFrequency":"0" - }, - { - "header":"Test_datamodel_1", - "content":"Device.X_RDK_WebConfig.webcfgSubdocForceReset", - "type":"", - "pollingFrequency":"3" - }, - { - "header": "SYS_GREP_TEST", - "content": "This log file is for previous logs", - "type": "session0.txt", - "pollingFrequency": "0" - }, - { - "header": "SYS_GREP_TEST_2", - "content": "This log file is for previous logs", - "type": "session0.txt", - "pollingFrequency": "0" - }, - { - "header": "SYS_TEST_ReportUpload", - "content": "uploading report", - "type": "core_log.txt", - "pollingFrequency": "0" - }, - { - "header": "SYS_EVENT_TEST_accum", - "content": "generic", - "type": "", - "pollingFrequency": "0" - } - ], - "schedule": "*/1 * * * *", - "expires": 0, - "telemetryProfile:name": "NEW TEST PROFILE", - "uploadRepository:URL": "https://mock1xconf:50051/dataLakeMockXconf", - "uploadRepository:uploadProtocol": "HTTP" - } -}