From e69890d5fbd26aaf9adade062122dba0057d07a0 Mon Sep 17 00:00:00 2001 From: tabbas651 Date: Tue, 27 Oct 2026 14:59:35 -0400 Subject: [PATCH 1/7] RDKB-67395: Data model and event-based marker with whitespace value are not reflected in the report Reason for change: PR #406 (RDKB-64502) treated any marker value whose first character is a space as empty, and any value starting with "NULL" as empty. Subscribed data model and event markers such as " test log " or " xyz " were therefore dropped when the event was stored and again when the report was built. Now a value is empty only if it is NULL, exactly "NULL", "" or whitespace only. Values with leading or trailing spaces are reported, and trimmed only when trim is true. Test Procedure: Configure a profile with subscribe data model markers and event markers (trim true and false). Send values with leading and trailing spaces and verify they appear in the cJSON report. Verify "", " " and "NULL" are still skipped when reportEmpty is false. Risks: Medium Priority: P1 Signed-off-by: Thamim Razith Abbas Ali --- source/bulkdata/profile.c | 9 +++++++-- source/bulkdata/profilexconf.c | 9 +++++++-- source/reportgen/reportgen.c | 17 ++++++++++++++++- 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/source/bulkdata/profile.c b/source/bulkdata/profile.c index 67a2e554..7e78f48d 100644 --- a/source/bulkdata/profile.c +++ b/source/bulkdata/profile.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "profile.h" #include "reportprofiles.h" @@ -73,11 +74,15 @@ static bool isEmptySubscriberValue(const char *value) { return true; } - if(strlen(value) < 1 || value[0] == ' ' || strncmp(value, "NULL", 4) == 0) + if(strcmp(value, "NULL") == 0) { return true; } - return false; + while(*value != '\0' && isspace((unsigned char)*value)) + { + value++; + } + return (*value == '\0'); } static void freeRequestURIparam(void *data) diff --git a/source/bulkdata/profilexconf.c b/source/bulkdata/profilexconf.c index 41abe597..9a96cae4 100644 --- a/source/bulkdata/profilexconf.c +++ b/source/bulkdata/profilexconf.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include "profilexconf.h" @@ -63,11 +64,15 @@ static bool isEmptySubscriberValue(const char *value) { return true; } - if(strlen(value) < 1 || value[0] == ' ' || strncmp(value, "NULL", 4) == 0) + if(strcmp(value, "NULL") == 0) { return true; } - return false; + while(*value != '\0' && isspace((unsigned char)*value)) + { + value++; + } + return (*value == '\0'); } #ifdef GTEST_ENABLE diff --git a/source/reportgen/reportgen.c b/source/reportgen/reportgen.c index 98834b1f..0a2e0927 100644 --- a/source/reportgen/reportgen.c +++ b/source/reportgen/reportgen.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -55,6 +56,20 @@ static bool checkForEmptyString( char* valueString ) return isEmpty; } +// Unlike checkForEmptyString, a value with leading/trailing spaces but real content is not empty. +static bool isEmptyMarkerValue(const char *value) +{ + if(value == NULL || strcmp(value, "NULL") == 0) + { + return true; + } + while(*value != '\0' && isspace((unsigned char)*value)) + { + value++; + } + return (*value == '\0'); +} + void freeParamValueSt(tr181ValStruct_t **valStructs, int valSize) { int i; @@ -1567,7 +1582,7 @@ T2ERROR encodeEventMarkersInJSON(cJSON *valArray, Vector *eventMarkerList) default: if(eventMarker->u.markerValue != NULL) { - if(!eventMarker->reportEmptyParam && checkForEmptyString(eventMarker->u.markerValue)) + if(!eventMarker->reportEmptyParam && isEmptyMarkerValue(eventMarker->u.markerValue)) { T2Debug("Skipping empty/\"NULL\" marker value for : %s\n", eventMarker->markerName); free(eventMarker->u.markerValue); From 610948a1a8afe39adcc743fd8de8d1ece2eaee9a Mon Sep 17 00:00:00 2001 From: tabbas651 Date: Tue, 3 Nov 2026 12:34:25 -0500 Subject: [PATCH 2/7] Addressed the Code review comments --- source/bulkdata/profile.c | 4 ++-- source/bulkdata/profilexconf.c | 4 ++-- source/reportgen/reportgen.c | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/source/bulkdata/profile.c b/source/bulkdata/profile.c index 7e78f48d..595db4f7 100644 --- a/source/bulkdata/profile.c +++ b/source/bulkdata/profile.c @@ -74,11 +74,11 @@ static bool isEmptySubscriberValue(const char *value) { return true; } - if(strcmp(value, "NULL") == 0) + if(strncmp(value, "NULL", sizeof("NULL")) == 0) { return true; } - while(*value != '\0' && isspace((unsigned char)*value)) + while(*value != '\0' && isspace((unsigned char) * value)) { value++; } diff --git a/source/bulkdata/profilexconf.c b/source/bulkdata/profilexconf.c index 9a96cae4..76d718da 100644 --- a/source/bulkdata/profilexconf.c +++ b/source/bulkdata/profilexconf.c @@ -64,11 +64,11 @@ static bool isEmptySubscriberValue(const char *value) { return true; } - if(strcmp(value, "NULL") == 0) + if(strncmp(value, "NULL", sizeof("NULL")) == 0) { return true; } - while(*value != '\0' && isspace((unsigned char)*value)) + while(*value != '\0' && isspace((unsigned char) * value)) { value++; } diff --git a/source/reportgen/reportgen.c b/source/reportgen/reportgen.c index 0a2e0927..82441383 100644 --- a/source/reportgen/reportgen.c +++ b/source/reportgen/reportgen.c @@ -59,11 +59,11 @@ static bool checkForEmptyString( char* valueString ) // Unlike checkForEmptyString, a value with leading/trailing spaces but real content is not empty. static bool isEmptyMarkerValue(const char *value) { - if(value == NULL || strcmp(value, "NULL") == 0) + if(value == NULL || strncmp(value, "NULL", sizeof("NULL")) == 0) { return true; } - while(*value != '\0' && isspace((unsigned char)*value)) + while(*value != '\0' && isspace((unsigned char) * value)) { value++; } From dedafc5ff776dca7d5ad37f67cb5ef87c99a2287 Mon Sep 17 00:00:00 2001 From: tabbas651 Date: Tue, 3 Nov 2026 15:28:04 -0500 Subject: [PATCH 3/7] Addressed the copilot review comments --- source/test/bulkdata/profileTest.cpp | 81 ++++++++++++++++++++ source/test/bulkdata/profilexconfTest.cpp | 74 ++++++++++++++++++ source/test/reportgen/reportgenTest.cpp | 92 +++++++++++++++++++++++ 3 files changed, 247 insertions(+) diff --git a/source/test/bulkdata/profileTest.cpp b/source/test/bulkdata/profileTest.cpp index f76e377c..3ec06a54 100644 --- a/source/test/bulkdata/profileTest.cpp +++ b/source/test/bulkdata/profileTest.cpp @@ -18,6 +18,7 @@ #include "test/mocks/VectorMock.h" #include "test/bulkdata/SchedulerMock.h" #include "test/bulkdata/profileMock.h" +#include using namespace std; @@ -371,6 +372,86 @@ TEST_F(ProfileTest, TriggerReportOnCondition_SchedulerFailureKeepsActiveMutexOwn test_reset_profile_list(); } +class ProfileMarkerStorageTest : public ProfileTest, + public ::testing::WithParamInterface> { +protected: + Profile profile = {}; + EventMarker marker = {}; + Vector *profiles = nullptr; + + void SetUp() override { + ProfileTest::SetUp(); + delete g_vectorMock; + g_vectorMock = nullptr; + ASSERT_EQ(Vector_Create(&profiles), T2ERROR_SUCCESS); + ASSERT_EQ(Vector_Create(&profile.eMarkerList), T2ERROR_SUCCESS); + profile.name = strdup("StorageProfile"); + profile.enable = true; + ASSERT_EQ(pthread_mutex_init(&profile.eventMutex, nullptr), 0); + marker.markerName = strdup("StorageMarker"); + marker.mType = std::get<0>(GetParam()); + marker.reportEmptyParam = std::get<1>(GetParam()); + if(marker.mType == MTYPE_ACCUMULATE) { + ASSERT_EQ(Vector_Create(&marker.u.accumulatedValues), T2ERROR_SUCCESS); + } + ASSERT_EQ(Vector_PushBack(profile.eMarkerList, &marker), T2ERROR_SUCCESS); + ASSERT_EQ(Vector_PushBack(profiles, &profile), T2ERROR_SUCCESS); + test_set_profile_list(profiles); + } + + void TearDown() override { + test_reset_profile_list(); + if(marker.mType == MTYPE_ACCUMULATE) { + Vector_Destroy(marker.u.accumulatedValues, free); + } else { + free(marker.u.markerValue); + } + free(marker.markerName); + Vector_Destroy(profile.eMarkerList, nullptr); + Vector_Destroy(profiles, nullptr); + pthread_mutex_destroy(&profile.eventMutex); + free(profile.name); + ProfileTest::TearDown(); + } +}; + +TEST_P(ProfileMarkerStorageTest, StoresClassifiedValuesWithoutTrimming) { + const struct { + const char *value; + bool empty; + } cases[] = { + {" test log ", false}, {" xyz ", false}, {"NULLify", false}, + {"", true}, {" ", true}, {"\t\r\n", true}, {"NULL", true} + }; + for(const auto &testCase : cases) { + SCOPED_TRACE(testCase.value); + const bool retained = marker.reportEmptyParam || !testCase.empty; + if(marker.mType == MTYPE_ACCUMULATE) { + Vector_Clear(marker.u.accumulatedValues, free); + ASSERT_EQ(Vector_PushBack(marker.u.accumulatedValues, strdup("previous")), T2ERROR_SUCCESS); + } else { + free(marker.u.markerValue); + marker.u.markerValue = strdup("previous"); + } + T2Event event = {}; + event.name = marker.markerName; + event.value = const_cast(testCase.value); + EXPECT_EQ(Profile_storeMarkerEvent(profile.name, &event), T2ERROR_SUCCESS); + if(marker.mType == MTYPE_ACCUMULATE) { + ASSERT_EQ(Vector_Size(marker.u.accumulatedValues), retained ? 2u : 1u); + EXPECT_STREQ(static_cast(Vector_At(marker.u.accumulatedValues, 0)), "previous"); + if(retained) { + EXPECT_STREQ(static_cast(Vector_At(marker.u.accumulatedValues, 1)), testCase.value); + } + } else { + EXPECT_STREQ(marker.u.markerValue, retained ? testCase.value : "previous"); + } + } +} + +INSTANTIATE_TEST_SUITE_P(SubscriberValues, ProfileMarkerStorageTest, + ::testing::Combine(::testing::Values(MTYPE_ABSOLUTE, MTYPE_ACCUMULATE), ::testing::Bool())); + TEST_F(ProfileTest, getMinThresholdDuration_Failure) { EXPECT_CALL(*g_vectorMock, Vector_Size(_)).Times(::testing::AtMost(1)).WillRepeatedly(Return(0)); EXPECT_EQ(getMinThresholdDuration("profile1"), 0); diff --git a/source/test/bulkdata/profilexconfTest.cpp b/source/test/bulkdata/profilexconfTest.cpp index e2ad1068..d84184f2 100644 --- a/source/test/bulkdata/profilexconfTest.cpp +++ b/source/test/bulkdata/profilexconfTest.cpp @@ -551,6 +551,80 @@ TEST_F(profileXconfTestFixture, ReportProfiles_storeMarkerEvent_success_2) free(eventInfo); } +class XconfMarkerStorageTest : public profileXconfTestFixture, + public ::testing::WithParamInterface> { +protected: + ProfileXConf *profile = nullptr; + EventMarker *marker = nullptr; + + void SetUp() override { + profileXconfTestFixture::SetUp(); + delete g_fileIOMock; + g_fileIOMock = new ::testing::NiceMock(); + EXPECT_CALL(*g_fileIOMock, opendir(_)) + .WillRepeatedly(Return(nullptr)); + ASSERT_EQ(ProfileXConf_init(false), T2ERROR_SUCCESS); + profile = static_cast(calloc(1, sizeof(ProfileXConf))); + ASSERT_NE(profile, nullptr); + profile->name = strdup("StorageProfile"); + ASSERT_EQ(Vector_Create(&profile->eMarkerList), T2ERROR_SUCCESS); + EXPECT_CALL(*g_schedulerMock, registerProfileWithScheduler(_, _, _, _, _, _, _, _)) + .WillOnce(Return(T2ERROR_SUCCESS)); + ASSERT_EQ(ProfileXConf_set(profile), T2ERROR_SUCCESS); + marker = static_cast(calloc(1, sizeof(EventMarker))); + ASSERT_NE(marker, nullptr); + marker->markerName = strdup("StorageMarker"); + marker->mType = std::get<0>(GetParam()); + marker->reportEmptyParam = std::get<1>(GetParam()); + if(marker->mType == MTYPE_XCONF_ACCUMULATE) { + ASSERT_EQ(Vector_Create(&marker->u.accumulatedValues), T2ERROR_SUCCESS); + } + ASSERT_EQ(Vector_PushBack(profile->eMarkerList, marker), T2ERROR_SUCCESS); + } + + void TearDown() override { + EXPECT_EQ(ProfileXConf_uninit(), T2ERROR_SUCCESS); + profileXconfTestFixture::TearDown(); + } +}; + +TEST_P(XconfMarkerStorageTest, StoresClassifiedValuesWithoutTrimming) { + const struct { + const char *value; + bool empty; + } cases[] = { + {" test log ", false}, {" xyz ", false}, {"NULLify", false}, + {"", true}, {" ", true}, {"\t\r\n", true}, {"NULL", true} + }; + for(const auto &testCase : cases) { + SCOPED_TRACE(testCase.value); + const bool retained = marker->reportEmptyParam || !testCase.empty; + if(marker->mType == MTYPE_XCONF_ACCUMULATE) { + Vector_Clear(marker->u.accumulatedValues, free); + ASSERT_EQ(Vector_PushBack(marker->u.accumulatedValues, strdup("previous")), T2ERROR_SUCCESS); + } else { + free(marker->u.markerValue); + marker->u.markerValue = strdup("previous"); + } + T2Event event = {}; + event.name = marker->markerName; + event.value = const_cast(testCase.value); + EXPECT_EQ(ProfileXConf_storeMarkerEvent(&event), T2ERROR_SUCCESS); + if(marker->mType == MTYPE_XCONF_ACCUMULATE) { + ASSERT_EQ(Vector_Size(marker->u.accumulatedValues), retained ? 2u : 1u); + EXPECT_STREQ(static_cast(Vector_At(marker->u.accumulatedValues, 0)), "previous"); + if(retained) { + EXPECT_STREQ(static_cast(Vector_At(marker->u.accumulatedValues, 1)), testCase.value); + } + } else { + EXPECT_STREQ(marker->u.markerValue, retained ? testCase.value : "previous"); + } + } +} + +INSTANTIATE_TEST_SUITE_P(SubscriberValues, XconfMarkerStorageTest, + ::testing::Combine(::testing::Values(MTYPE_XCONF_ABSOLUTE, MTYPE_XCONF_ACCUMULATE), ::testing::Bool())); + TEST_F(profileXconfTestFixture, ProfileXConf_OnDemandStatusSnapshot) { testing::InSequence sequence; diff --git a/source/test/reportgen/reportgenTest.cpp b/source/test/reportgen/reportgenTest.cpp index a8b00bc9..39a0fa9a 100644 --- a/source/test/reportgen/reportgenTest.cpp +++ b/source/test/reportgen/reportgenTest.cpp @@ -39,6 +39,8 @@ sigset_t blocking_signal; #include "../mocks/rdklogMock.h" #include #include +#include +#include using namespace std; using ::testing::_; @@ -1526,6 +1528,96 @@ TEST_F(reportgenTestFixture, encodeEventMarkersInJSON7) Vector_Destroy(eventMarkerList, freeEMarker); } +class MarkerReportFilterTest : public reportgenTestFixture, + public ::testing::WithParamInterface> { +protected: + EventMarker marker = {}; + Vector *markers = nullptr; + cJSON *report = nullptr; + void *jsonLibrary = nullptr; + cJSON *(*createArray)() = nullptr; + void (*deleteJson)(cJSON *) = nullptr; + + void SetUp() override { + jsonLibrary = dlopen("libcjson.so.1", RTLD_NOW | RTLD_LOCAL); + ASSERT_NE(jsonLibrary, nullptr) << dlerror(); + createArray = reinterpret_cast(dlsym(jsonLibrary, "cJSON_CreateArray")); + auto createObject = reinterpret_cast(dlsym(jsonLibrary, "cJSON_CreateObject")); + auto addString = reinterpret_cast( + dlsym(jsonLibrary, "cJSON_AddStringToObject")); + auto addItem = reinterpret_cast( + dlsym(jsonLibrary, "cJSON_AddItemToArray")); + deleteJson = reinterpret_cast(dlsym(jsonLibrary, "cJSON_Delete")); + ASSERT_NE(createArray, nullptr); + ASSERT_NE(createObject, nullptr); + ASSERT_NE(addString, nullptr); + ASSERT_NE(addItem, nullptr); + ASSERT_NE(deleteJson, nullptr); + EXPECT_CALL(mock_IO, cJSON_CreateObject()).Times(::testing::AnyNumber()) + .WillRepeatedly(::testing::Invoke(createObject)); + EXPECT_CALL(mock_IO, cJSON_AddStringToObject(_, _, _)).Times(::testing::AnyNumber()) + .WillRepeatedly(::testing::Invoke(addString)); + EXPECT_CALL(mock_IO, cJSON_AddItemToArray(_, _)).Times(::testing::AnyNumber()) + .WillRepeatedly(::testing::Invoke(addItem)); + EXPECT_CALL(mock_IO, cJSON_Delete(_)).Times(::testing::AnyNumber()) + .WillRepeatedly(::testing::Invoke(deleteJson)); + ASSERT_EQ(Vector_Create(&markers), T2ERROR_SUCCESS); + marker.markerName = strdup("StorageMarker"); + marker.mType = MTYPE_ABSOLUTE; + marker.reportEmptyParam = std::get<0>(GetParam()); + marker.trimParam = std::get<1>(GetParam()); + ASSERT_EQ(Vector_PushBack(markers, &marker), T2ERROR_SUCCESS); + } + + void TearDown() override { + if(deleteJson) { + deleteJson(report); + } + free(marker.u.markerValue); + free(marker.markerName); + Vector_Destroy(markers, nullptr); + if(jsonLibrary) { + dlclose(jsonLibrary); + } + } +}; + +TEST_P(MarkerReportFilterTest, SerializesRetainedValuesWithOptionalTrimming) { + const struct { + const char *value; + const char *trimmed; + bool empty; + } cases[] = { + {" test log ", "test log", false}, {" xyz ", "xyz", false}, + {"NULLsuffix", "NULLsuffix", false}, {"", "", true}, + {" ", "", true}, {"\t\r\n", "\r\n", true}, {"NULL", "NULL", true} + }; + for(const auto &testCase : cases) { + SCOPED_TRACE(testCase.value); + report = createArray(); + ASSERT_NE(report, nullptr); + marker.u.markerValue = strdup(testCase.value); + ASSERT_NE(marker.u.markerValue, nullptr); + EXPECT_EQ(encodeEventMarkersInJSON(report, markers), T2ERROR_SUCCESS); + EXPECT_EQ(marker.u.markerValue, nullptr); + if(marker.reportEmptyParam || !testCase.empty) { + ASSERT_NE(report->child, nullptr); + EXPECT_EQ(report->child->next, nullptr); + ASSERT_NE(report->child->child, nullptr); + EXPECT_STREQ(report->child->child->string, marker.markerName); + EXPECT_STREQ(report->child->child->valuestring, + marker.trimParam ? testCase.trimmed : testCase.value); + } else { + EXPECT_EQ(report->child, nullptr); + } + deleteJson(report); + report = nullptr; + } +} + +INSTANTIATE_TEST_SUITE_P(SubscriberValues, MarkerReportFilterTest, + ::testing::Combine(::testing::Bool(), ::testing::Bool())); + TEST_F(reportgenTestFixture, encodeEventMarkersInJSON8) { cJSON *valArray = (cJSON *)malloc(sizeof(cJSON)); From 4aaf39543952c0c20791695116d80c6d07aed102 Mon Sep 17 00:00:00 2001 From: tabbas651 Date: Tue, 3 Nov 2026 17:29:15 -0500 Subject: [PATCH 4/7] L2 Failure test resolved --- .../tests/test_multiprofile_msgpacket.py | 4 ++-- .../tests/test_xconf_communications.py | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/test/functional-tests/tests/test_multiprofile_msgpacket.py b/test/functional-tests/tests/test_multiprofile_msgpacket.py index 2dd6c24c..5261b896 100644 --- a/test/functional-tests/tests/test_multiprofile_msgpacket.py +++ b/test/functional-tests/tests/test_multiprofile_msgpacket.py @@ -273,8 +273,8 @@ def test_for_invalid_activation_timeout(): assert "MODEL_NAME\":\"NULL" in grep_T2logs("cJSON Report ") # 239 - Support for reportEmpty of profiles assert "TR_AC6919" in grep_T2logs("firstreporting interval is given") # 240 - Support for First Reporting Interval -1 assert "5 sec" in grep_T2logs("firstreporting interval is given") # 240 - Support for First Reporting Interval - 2 - assert "NEW TEST PROFILE" in grep_T2logs(LOG_PROFILE_SET) # 101 - Report fetch and parse via HTTP - assert "60 sec" in grep_T2logs("reporting interval is taken - NEW TEST PROFILE") # 107 - Configurable reporting interval + assert "NEW_TEST_PROFILE" in grep_T2logs(LOG_PROFILE_SET) # 101 - Report fetch and parse via HTTP + assert "60 sec" in grep_T2logs("reporting interval is taken - NEW_TEST_PROFILE") # 107 - Configurable reporting interval # 241 - Support for DCM profile and multiprofile parallel execution assert "AccountId\":\"Platform_Container_Test_DEVICE" in grep_T2logs("cJSON Report ") # 102 - Include data from data source as TR181 Parameter assert "SYS_GREP_TEST" in grep_T2logs("cJSON Report ") # 110, 111 - Data harvesting from previous logs folder for DCA profiles with log file search markers. diff --git a/test/functional-tests/tests/test_xconf_communications.py b/test/functional-tests/tests/test_xconf_communications.py index 99a10522..3b706edd 100644 --- a/test/functional-tests/tests/test_xconf_communications.py +++ b/test/functional-tests/tests/test_xconf_communications.py @@ -96,7 +96,7 @@ def test_change_profile(): kill_telemetry(12) sleep(10) assert os.path.exists(XCONF_PERSISTANT_FILE) - assert "NEW TEST PROFILE" in grep_T2logs("NEW TEST PROFILE") + assert "NEW_TEST_PROFILE" in grep_T2logs("NEW_TEST_PROFILE") @pytest.mark.run(order=6) def test_verify_urlencoding(): @@ -118,7 +118,7 @@ def test_verify_schedule(): command = ["echo 'This is Test Log' > /opt/logs/test.log"] run_shell_command(command) sleep(60) - ERROR_MSG = "Waiting for 60 sec for next TIMEOUT for profile as reporting interval is taken - NEW TEST PROFILE" + ERROR_MSG = "Waiting for 60 sec for next TIMEOUT for profile as reporting interval is taken - NEW_TEST_PROFILE" assert ERROR_MSG in grep_T2logs(ERROR_MSG) @pytest.mark.run(order=8) @@ -131,14 +131,14 @@ def test_verify_markers(): @pytest.mark.run(order=9) def test_verify_report(): - ERROR_MSG1 = "CollectAndReportXconf ++in profileName : NEW TEST PROFILE" + ERROR_MSG1 = "CollectAndReportXconf ++in profileName : NEW_TEST_PROFILE" ERROR_MSG2 = "cJSON Report =" assert ERROR_MSG1 in grep_T2logs(ERROR_MSG1) assert ERROR_MSG2 in grep_T2logs(ERROR_MSG2) @pytest.mark.run(order=10) def test_log_upload(): - LOG_MSG1 = "CollectAndReportXconf ++in profileName : NEW TEST PROFILE" + LOG_MSG1 = "CollectAndReportXconf ++in profileName : NEW_TEST_PROFILE" LOG_MSG2 = "cJSON Report =" clear_T2logs() kill_telemetry(10) @@ -148,7 +148,7 @@ def test_log_upload(): @pytest.mark.run(order=11) def test_log_upload_on_demand(): - LOG_MSG1 = "CollectAndReportXconf ++in profileName : NEW TEST PROFILE" + LOG_MSG1 = "CollectAndReportXconf ++in profileName : NEW_TEST_PROFILE" LOG_MSG2 = "cJSON Report =" clear_T2logs() kill_telemetry(29) From 803b83864cd96952a96485e679e10b1d6a825e02 Mon Sep 17 00:00:00 2001 From: tabbas651 Date: Tue, 3 Nov 2026 17:55:00 -0500 Subject: [PATCH 5/7] L2 testing failures resolved --- source/reportgen/reportgen.c | 1 + source/test/reportgen/reportgenTest.cpp | 5 +++++ test/functional-tests/tests/test_multiprofile_msgpacket.py | 2 +- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/source/reportgen/reportgen.c b/source/reportgen/reportgen.c index 82441383..90525a0c 100644 --- a/source/reportgen/reportgen.c +++ b/source/reportgen/reportgen.c @@ -156,6 +156,7 @@ void trimLeadingAndTrailingws(char* string) string[j++] = string[i]; } string[j] = '\0'; + j = -1; for(i = 0; string[i] != '\0'; i++) { diff --git a/source/test/reportgen/reportgenTest.cpp b/source/test/reportgen/reportgenTest.cpp index 39a0fa9a..15b81389 100644 --- a/source/test/reportgen/reportgenTest.cpp +++ b/source/test/reportgen/reportgenTest.cpp @@ -1543,6 +1543,8 @@ class MarkerReportFilterTest : public reportgenTestFixture, ASSERT_NE(jsonLibrary, nullptr) << dlerror(); createArray = reinterpret_cast(dlsym(jsonLibrary, "cJSON_CreateArray")); auto createObject = reinterpret_cast(dlsym(jsonLibrary, "cJSON_CreateObject")); + auto createString = reinterpret_cast( + dlsym(jsonLibrary, "cJSON_CreateString")); auto addString = reinterpret_cast( dlsym(jsonLibrary, "cJSON_AddStringToObject")); auto addItem = reinterpret_cast( @@ -1550,11 +1552,14 @@ class MarkerReportFilterTest : public reportgenTestFixture, deleteJson = reinterpret_cast(dlsym(jsonLibrary, "cJSON_Delete")); ASSERT_NE(createArray, nullptr); ASSERT_NE(createObject, nullptr); + ASSERT_NE(createString, nullptr); ASSERT_NE(addString, nullptr); ASSERT_NE(addItem, nullptr); ASSERT_NE(deleteJson, nullptr); EXPECT_CALL(mock_IO, cJSON_CreateObject()).Times(::testing::AnyNumber()) .WillRepeatedly(::testing::Invoke(createObject)); + EXPECT_CALL(mock_IO, cJSON_CreateString(_)).Times(::testing::AnyNumber()) + .WillRepeatedly(::testing::Invoke(createString)); EXPECT_CALL(mock_IO, cJSON_AddStringToObject(_, _, _)).Times(::testing::AnyNumber()) .WillRepeatedly(::testing::Invoke(addString)); EXPECT_CALL(mock_IO, cJSON_AddItemToArray(_, _)).Times(::testing::AnyNumber()) diff --git a/test/functional-tests/tests/test_multiprofile_msgpacket.py b/test/functional-tests/tests/test_multiprofile_msgpacket.py index 5261b896..eb1fcf2e 100644 --- a/test/functional-tests/tests/test_multiprofile_msgpacket.py +++ b/test/functional-tests/tests/test_multiprofile_msgpacket.py @@ -272,7 +272,7 @@ def test_for_invalid_activation_timeout(): assert "TR_AC88" in grep_T2logs(ERROR_PROFILE_TIMEOUT) # Verify profile not set if activation timeout is less than reporting interval assert "MODEL_NAME\":\"NULL" in grep_T2logs("cJSON Report ") # 239 - Support for reportEmpty of profiles assert "TR_AC6919" in grep_T2logs("firstreporting interval is given") # 240 - Support for First Reporting Interval -1 - assert "5 sec" in grep_T2logs("firstreporting interval is given") # 240 - Support for First Reporting Interval - 2 + assert "5 sec" in grep_T2logs("firstreporting interval is given") # 240 - Support for First Reporting Interval - 2/ assert "NEW_TEST_PROFILE" in grep_T2logs(LOG_PROFILE_SET) # 101 - Report fetch and parse via HTTP assert "60 sec" in grep_T2logs("reporting interval is taken - NEW_TEST_PROFILE") # 107 - Configurable reporting interval # 241 - Support for DCM profile and multiprofile parallel execution From ba5526368a4f79b736dd4cb5bd05828ec5c84745 Mon Sep 17 00:00:00 2001 From: tabbas651 Date: Thu, 5 Nov 2026 13:49:22 -0500 Subject: [PATCH 6/7] Coverity issue - MISSING_LOCK: Accessing "profile->grepSeekProfile->execCounter" without holding lock "xconfProfileLock". Elsewhere, "_GrepSeekProfile.execCounter" is written to with "xconfProfileLock" --- source/bulkdata/profilexconf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/bulkdata/profilexconf.c b/source/bulkdata/profilexconf.c index 76d718da..5213a35e 100644 --- a/source/bulkdata/profilexconf.c +++ b/source/bulkdata/profilexconf.c @@ -389,10 +389,10 @@ static void* CollectAndReportXconf(void* data) { encodeEventMarkersInJSON(valArray, profile->eMarkerList); } - pthread_mutex_unlock(&xconfProfileLock); profile->grepSeekProfile->execCounter += 1; T2Info("Xconf Profile Execution Count = %d\n", profile->grepSeekProfile->execCounter); + pthread_mutex_unlock(&xconfProfileLock); ret = prepareJSONReport(profile->jsonReportObj, &jsonReport); destroyJSONReport(profile->jsonReportObj); From be3f08933de741e55de9186fc3a9d32abff778b7 Mon Sep 17 00:00:00 2001 From: tabbas651 Date: Thu, 5 Nov 2026 14:27:48 -0500 Subject: [PATCH 7/7] Coverity Fix --- source/bulkdata/profile.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/source/bulkdata/profile.c b/source/bulkdata/profile.c index 595db4f7..2c3d3aae 100644 --- a/source/bulkdata/profile.c +++ b/source/bulkdata/profile.c @@ -429,6 +429,11 @@ static void* CollectAndReport(void* data) pthread_mutex_unlock(&profile->reportInProgressMutex); pthread_mutex_unlock(&profile->reuseThreadMutex); + /* xconfProfileLock (static in profilexconf.c) guards only the XConf singleProfile's + * GrepSeekProfile. This multi-profile's grepSeekProfile is a separate instance that is + * read/replaced exclusively by this report thread and freed only after the thread is + * joined, so no cross-thread access exists here. */ + /* coverity[missing_lock] */ int count = profile->grepSeekProfile->execCounter; Vector *profileParamVals = NULL;