diff --git a/source/test/t2parser/Makefile.am b/source/test/t2parser/Makefile.am index 45142676..1ebd1cf5 100644 --- a/source/test/t2parser/Makefile.am +++ b/source/test/t2parser/Makefile.am @@ -31,7 +31,7 @@ bin_PROGRAMS = t2parser_gtest.bin t2parser_gtest_bin_CPPFLAGS = -I$(PKG_CONFIG_SYSROOT_DIR)$(includedir)/gtest -I$(PKG_CONFIG_SYSROOT_DIR)$(includedir)/glib-2.0 -I$(PKG_CONFIG_SYSROOT_DIR)/usr/local/lib -I${top_srcdir}/source/include -I${top_srcdir}/source/xconf-client -I${top_srcdir}/source/dcautil -I${top_srcdir}/source/t2parser -I${top_srcdir}/source/bulkdata -I${top_srcdir}/source/utils -I${top_srcdir}/source/ccspinterface -I${top_srcdir}/source -I${top_srcdir}/include -I${top_srcdir}/source/reportgen -I$(PKG_CONFIG_SYSROOT_DIR)/usr/lib -I$(PKG_CONFIG_SYSROOT_DIR)/usr/include -I$(PKG_CONFIG_SYSROOT_DIR)/usr/include/cjson -I$(PKG_CONFIG_SYSROOT_DIR)/usr/include/gmock -I$(PKG_CONFIG_SYSROOT_DIR)/usr/src/googletest/googlemock/include -I$(PKG_CONFIG_SYSROOT_DIR)/usr/lib/glib-2.0/include -I$(PKG_CONFIG_SYSROOT_DIR)/usr/include/glib-2.0 -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 -t2parser_gtest_bin_SOURCES = gtest_main.cpp t2parserMock.cpp ../mocks/rdklogMock.cpp ../mocks/rbusMock.cpp t2parserTest.cpp ../../utils/vector.c ../../utils/t2common.c ../../utils/t2log_wrapper.c ../../t2parser/t2parserxconf.c ../../t2parser/t2parser.c ../../dcautil/legacyutils.c ../../utils/t2collection.c +t2parser_gtest_bin_SOURCES = gtest_main.cpp t2parserMock.cpp ../mocks/rdklogMock.cpp ../mocks/rbusMock.cpp t2parserTest.cpp t2parserxconfTest.cpp ../../utils/vector.c ../../utils/t2common.c ../../utils/t2log_wrapper.c ../../t2parser/t2parserxconf.c ../../t2parser/t2parser.c ../../dcautil/legacyutils.c ../../utils/t2collection.c t2parser_gtest_bin_LDFLAGS = -lgtest -lgcov -L/src/googletest/googlemock/lib -L/usr/src/googletest/googlemock/lib/.libs -lgmock -lcjson -lcurl -lmsgpackc -L/usr/include/glib-2.0 -lglib-2.0 diff --git a/source/test/t2parser/t2parserTest.cpp b/source/test/t2parser/t2parserTest.cpp index aab8c27f..1ddce3dc 100644 --- a/source/test/t2parser/t2parserTest.cpp +++ b/source/test/t2parser/t2parserTest.cpp @@ -63,6 +63,13 @@ sigset_t blocking_signal; #include "test/mocks/rdklogMock.h" #include "test/mocks/rbusMock.h" +extern "C" { + // tell C++ about the C function defined in t2parser.c + T2ERROR verifyTriggerCondition(cJSON *jprofileTriggerCondition); + T2ERROR addTriggerCondition(Profile *profile, cJSON *jprofileTriggerCondition); + T2ERROR encodingSet(Profile* profile, cJSON *jprofileEncodingType, cJSON *jprofileJSONReportFormat, cJSON *jprofileJSONReportTimestamp); + T2ERROR protocolSet (Profile *profile, cJSON *jprofileProtocol, cJSON *jprofileHTTPURL, cJSON *jprofileHTTPRequestURIParameter, int ThisprofileHTTPRequestURIParameter_count, cJSON *jprofileRBUSMethodName, cJSON *jprofileRBUSMethodParamArr, int rbusMethodParamArrCount); +} T2parserMock *m_t2parserMock = NULL; rdklogMock *m_rdklogMock = NULL; rbusMock *g_rbusMock = NULL; @@ -72,7 +79,7 @@ TEST(PROCESSXCONFCONFIGURATION, TEST_NULL_INVALID) ProfileXConf *profile = 0; fstream new_file; char* data = NULL; - new_file.open("xconfInputfile.txt", ios::in); + new_file.open("source/test/t2parser/xconfInputfile.txt", ios::in); if (new_file.is_open()) { string sa; @@ -136,7 +143,7 @@ TEST(PROCESSCONFIGURATION_CJSON, TEST_NULL_INVALID_PARAM) fstream new_file; string sa; int len = 0; - new_file.open("rpInputfile.txt", ios::in); + new_file.open("source/test/t2parser/rpInputfile.txt", ios::in); char* data = NULL; if (new_file.is_open()) { getline(new_file, sa); @@ -154,7 +161,6 @@ TEST(PROCESSCONFIGURATION_CJSON, TEST_NULL_INVALID_PARAM) //Protocol NULL EXPECT_EQ(T2ERROR_FAILURE, processConfiguration(&data, "RDKB_Profile2", "hash2", &profile)); delete[] data; - getline(new_file, sa); len = sa.length(); data = new char[len + 1]; @@ -336,7 +342,6 @@ TEST(PROCESSCONFIGURATION_CJSON, TEST_NULL_INVALID_PARAM) strcpy(data, sa.c_str()); EXPECT_EQ(T2ERROR_SUCCESS, processConfiguration(&data, "RDKB_Profile25", "hash25", &profile)); delete[] data; - } new_file.close(); } @@ -780,4 +785,255 @@ TEST(PROCESSCONFIGURATION_MSGPACK, WORKING_CASE) } +/* Helper to create a TriggerCondition JSON object */ +static cJSON* create_tc_obj(const char* type, const char* op, const char* ref, int threshold, int report) +{ + cJSON* obj = cJSON_CreateObject(); + if (type) cJSON_AddStringToObject(obj, "type", type); + if (op) cJSON_AddStringToObject(obj, "operator", op); + if (ref) cJSON_AddStringToObject(obj, "reference", ref); + if (threshold >= 0) cJSON_AddNumberToObject(obj, "threshold", threshold); + if (report >= 0) cJSON_AddBoolToObject(obj, "report", report); + return obj; +} +/* verifyTriggerCondition failure: missing type */ +TEST(T2ParserVerifyTC, MissingTypeShouldFail) +{ + cJSON* arr = cJSON_CreateArray(); + cJSON* tc = cJSON_CreateObject(); + // intentionally no "type" + cJSON_AddStringToObject(tc, "operator", "any"); + cJSON_AddStringToObject(tc, "reference", "Device.Param"); + cJSON_AddItemToArray(arr, tc); + + EXPECT_EQ(T2ERROR_FAILURE, verifyTriggerCondition(arr)); + cJSON_Delete(arr); +} +/* verifyTriggerCondition failure: wrong type string */ +TEST(T2ParserVerifyTC, WrongTypeShouldFail) +{ + cJSON* arr = cJSON_CreateArray(); + cJSON* tc = create_tc_obj("notDataModel", "any", "Device.Param", -1, -1); + cJSON_AddItemToArray(arr, tc); + EXPECT_EQ(T2ERROR_FAILURE, verifyTriggerCondition(arr)); + cJSON_Delete(arr); +} + +/* verifyTriggerCondition failure: missing operator */ +TEST(T2ParserVerifyTC, MissingOperatorShouldFail) +{ + cJSON* arr = cJSON_CreateArray(); + cJSON* tc = create_tc_obj("dataModel", NULL, "Device.Param", -1, -1); + cJSON_AddItemToArray(arr, tc); + EXPECT_EQ(T2ERROR_FAILURE, verifyTriggerCondition(arr)); + cJSON_Delete(arr); +} + +/* verifyTriggerCondition failure: invalid operator */ +TEST(T2ParserVerifyTC, InvalidOperatorShouldFail) +{ + cJSON* arr = cJSON_CreateArray(); + cJSON* tc = create_tc_obj("dataModel", "badop", "Device.Param", -1, -1); + cJSON_AddItemToArray(arr, tc); + EXPECT_EQ(T2ERROR_FAILURE, verifyTriggerCondition(arr)); + cJSON_Delete(arr); +} + +/* verifyTriggerCondition failure: missing threshold for lt operator */ +TEST(T2ParserVerifyTC, ThresholdMissingForLtShouldFail) +{ + cJSON* arr = cJSON_CreateArray(); + cJSON* tc = create_tc_obj("dataModel", "lt", "Device.Param", -1, -1); // no threshold + cJSON_AddItemToArray(arr, tc); + EXPECT_EQ(T2ERROR_FAILURE, verifyTriggerCondition(arr)); + cJSON_Delete(arr); +} + +/* verifyTriggerCondition failure: missing reference */ +TEST(T2ParserVerifyTC, MissingReferenceShouldFail) +{ + cJSON* arr = cJSON_CreateArray(); + cJSON* tc = cJSON_CreateObject(); + cJSON_AddStringToObject(tc, "type", "dataModel"); + cJSON_AddStringToObject(tc, "operator", "any"); + // no reference + cJSON_AddItemToArray(arr, tc); + EXPECT_EQ(T2ERROR_FAILURE, verifyTriggerCondition(arr)); + cJSON_Delete(arr); +} + +/* verifyTriggerCondition failure: empty reference */ +TEST(T2ParserVerifyTC, EmptyReferenceShouldFail) +{ + cJSON* arr = cJSON_CreateArray(); + cJSON* tc = create_tc_obj("dataModel", "any", "", -1, -1); + cJSON_AddItemToArray(arr, tc); + EXPECT_EQ(T2ERROR_FAILURE, verifyTriggerCondition(arr)); + cJSON_Delete(arr); +} + +/* verifyTriggerCondition success: valid tc */ +TEST(T2ParserVerifyTC, ValidShouldSucceed) +{ + cJSON* arr = cJSON_CreateArray(); + cJSON* tc = create_tc_obj("dataModel", "any", "Device.Param", -1, -1); + cJSON_AddItemToArray(arr, tc); + EXPECT_EQ(T2ERROR_SUCCESS, verifyTriggerCondition(arr)); + cJSON_Delete(arr); +} +/* addTriggerCondition should accept a valid TC array and return success */ +TEST(T2ParserAddTC, AddTriggerConditionSuccess) +{ + Profile p; + memset(&p, 0, sizeof(p)); + // Initialize the vector pointer (function will Vector_Create) + p.triggerConditionList = NULL; + + cJSON* arr = cJSON_CreateArray(); + cJSON* tc = create_tc_obj("dataModel", "eq", "Device.Param", 5, 1); + cJSON_AddItemToArray(arr, tc); + + EXPECT_EQ(T2ERROR_SUCCESS, addTriggerCondition(&p, arr)); + // function should have created the triggerConditionList (non-NULL) + EXPECT_NE((void*)NULL, (void*)p.triggerConditionList); + + // clean up: free internal structures created by addTriggerCondition + // We don't have a public destructor here; best-effort free for the test's allocations: + // iterate vector if available (Vector API may provide size/get functions in the project), + // but to stay robust, simply free profile memory fields that addTriggerCondition could set. + // For safety, reset pointer to avoid double-free during test teardown. + // (The CI process reclaims process memory after tests.) + cJSON_Delete(arr); +} +/* encodingSet should set jsonEncoding fields based on JSON nodes */ +TEST(T2ParserEncodingSet, JSONEncodingMapping) +{ + Profile p; + memset(&p, 0, sizeof(p)); + p.jsonEncoding = (JSONEncoding*)malloc(sizeof(JSONEncoding)); + memset(p.jsonEncoding, 0, sizeof(JSONEncoding)); + + cJSON* jEncodingType = cJSON_CreateString("JSON"); + cJSON* jJSONReportFormat = cJSON_CreateString("ObjectHierarchy"); + cJSON* jJSONReportTimestamp = cJSON_CreateString("Unix-Epoch"); + + EXPECT_EQ(T2ERROR_SUCCESS, encodingSet(&p, jEncodingType, jJSONReportFormat, jJSONReportTimestamp)); + EXPECT_EQ(JSONRF_OBJHIERARCHY, p.jsonEncoding->reportFormat); + EXPECT_EQ(TIMESTAMP_UNIXEPOCH, p.jsonEncoding->tsFormat); + + if (p.jsonEncoding) + { + free(p.jsonEncoding); + p.jsonEncoding = NULL; + } + + if (p.name) + { + free(p.name); + p.name = NULL; + } + + cJSON_Delete(jEncodingType); + cJSON_Delete(jJSONReportFormat); + cJSON_Delete(jJSONReportTimestamp); +} +/* protocolSet tests: HTTP branch */ +TEST(T2ParserProtocolSet, HTTPBranchSetsURLAndParams) +{ + Profile p; + memset(&p, 0, sizeof(p)); + p.t2HTTPDest = (T2HTTP*)malloc(sizeof(T2HTTP)); + memset(p.t2HTTPDest, 0, sizeof(T2HTTP)); + p.name = strdup("TestProfile"); + + cJSON* jProtocol = cJSON_CreateString("HTTP"); + // create HTTP nested object + cJSON* jHTTP = cJSON_CreateObject(); + cJSON_AddStringToObject(jHTTP, "URL", "http://upload.test"); + cJSON_AddStringToObject(jHTTP, "Compression", "None"); + cJSON_AddStringToObject(jHTTP, "Method", "POST"); + + // RequestURIParameter array with one valid entry + cJSON* arr = cJSON_CreateArray(); + cJSON* entry = cJSON_CreateObject(); + cJSON_AddStringToObject(entry, "Reference", "someRef"); + cJSON_AddStringToObject(entry, "Name", "someName"); + cJSON_AddItemToArray(arr, entry); + cJSON_AddItemToObject(jHTTP, "RequestURIParameter", arr); + + // Call protocolSet (note: jprofileHTTPRequestURIParameter_count must be set properly by caller) + EXPECT_EQ(T2ERROR_SUCCESS, protocolSet(&p, jProtocol, cJSON_GetObjectItem(jHTTP, "URL"), cJSON_GetObjectItem(jHTTP, "RequestURIParameter"), 1, NULL, NULL, 0)); + + EXPECT_STREQ("http://upload.test", p.t2HTTPDest->URL); + // Cleanup to avoid memory leaks + if (p.t2HTTPDest) + { + if (p.t2HTTPDest->URL) + { + free(p.t2HTTPDest->URL); + p.t2HTTPDest->URL = NULL; + } + // If protocolSet allocated other heap members inside T2HTTP, free them here as needed. + + free(p.t2HTTPDest); + p.t2HTTPDest = NULL; + } + + if (p.name) + { + free(p.name); + p.name = NULL; + } + + cJSON_Delete(jProtocol); + cJSON_Delete(jHTTP); +} + +/* protocolSet tests: RBUS_METHOD branch */ +TEST(T2ParserProtocolSet, RBUSMethodBranchSetsMethodAndParams) +{ + Profile p; + memset(&p, 0, sizeof(p)); + p.t2RBUSDest = (T2RBUS*)malloc(sizeof(T2RBUS)); + memset(p.t2RBUSDest, 0, sizeof(T2RBUS)); + p.name = strdup("RbusProfile"); + + cJSON* jProtocol = cJSON_CreateString("RBUS_METHOD"); + cJSON* jRBUS = cJSON_CreateObject(); + cJSON_AddStringToObject(jRBUS, "Method", "TestMethod"); + + cJSON* params = cJSON_CreateArray(); + cJSON* param = cJSON_CreateObject(); + cJSON_AddStringToObject(param, "name", "param1"); + cJSON_AddStringToObject(param, "value", "val1"); + cJSON_AddItemToArray(params, param); + cJSON_AddItemToObject(jRBUS, "Parameters", params); + + // protocolSet expects jprofileRBUSMethodName and jprofileRBUSMethodParamArr separately; pass appropriate items + EXPECT_EQ(T2ERROR_SUCCESS, protocolSet(&p, jProtocol, NULL, NULL, 0, cJSON_GetObjectItem(jRBUS, "Method"), cJSON_GetObjectItem(jRBUS, "Parameters"), 1)); + + EXPECT_STREQ("TestMethod", p.t2RBUSDest->rbusMethodName); + // Cleanup to avoid memory leaks + if (p.t2RBUSDest) + { + // free any strings allocated by protocolSet if present + if (p.t2RBUSDest->rbusMethodName) + { + free(p.t2RBUSDest->rbusMethodName); + p.t2RBUSDest->rbusMethodName = NULL; + } + // If protocolSet allocated other heap members inside T2RBUS, free them here as needed. + free(p.t2RBUSDest); + p.t2RBUSDest = NULL; + } + + if (p.name) + { + free(p.name); + p.name = NULL; + } + + cJSON_Delete(jProtocol); + cJSON_Delete(jRBUS); +} diff --git a/source/test/t2parser/t2parserxconfTest.cpp b/source/test/t2parser/t2parserxconfTest.cpp new file mode 100644 index 00000000..9f6b2864 --- /dev/null +++ b/source/test/t2parser/t2parserxconfTest.cpp @@ -0,0 +1,91 @@ +#include +#include + +extern "C" { +#include "t2parserxconf.h" +#include "profilexconf.h" +#include "telemetry2_0.h" +} + +/* + * When required fields are missing, processConfigurationXConf should fail. + */ +TEST(T2ParserXConf, MissingRequiredFields) +{ + ProfileXConf *profile = NULL; + const char *json_missing = "{\"urn:settings:TelemetryProfile\": { \"telemetryProfile:name\": \"TestProfile\", \"telemetryProfile\": [] } }"; + // Missing uploadRepository:URL and schedule and no telemetryProfile entries -> failure + EXPECT_EQ(T2ERROR_FAILURE, processConfigurationXConf((char*)json_missing, &profile)); + EXPECT_EQ((ProfileXConf*)NULL, profile); +} + +/* + * When schedule doesn't contain a '/' pattern, default schedule is used (15 minutes -> 900 sec). + */ +TEST(T2ParserXConf, DefaultScheduleUsed) +{ + ProfileXConf *profile = NULL; + const char *json_default_schedule = + "{" + " \"urn:settings:TelemetryProfile\": {" + " \"telemetryProfile:name\": \"DefaultSched\"," + " \"uploadRepository:URL\": \"http://example.com/upload\"," + " \"schedule\": \"11 4 * * *\"," + " \"telemetryProfile\": [ {" + " \"header\": \"param1\"," + " \"content\": \"alias1\"," + " \"type\": \"/var/log/some.log\"," + " \"pollingFrequency\": \"0\"" + " } ]" + " }" + "}"; + EXPECT_EQ(T2ERROR_SUCCESS, processConfigurationXConf((char*)json_default_schedule, &profile)); + ASSERT_NE(profile, nullptr); + // default should be 15 minutes -> 900 seconds + EXPECT_EQ(900, profile->reportingInterval); + ASSERT_NE(profile->t2HTTPDest, nullptr); + EXPECT_STREQ("http://example.com/upload", profile->t2HTTPDest->URL ? profile->t2HTTPDest->URL : ""); + // minimal cleanup (full deep-free is not attempted here) + // tests rely on process teardown to reclaim memory in CI runs +} + +/* + * When schedule contains a slash pattern like "0/5 * * * *", getScheduleInSeconds should + * extract the number after the slash and use it (5 minutes -> 300 seconds). + * This test also includes a top_log.txt entry to exercise topMarker handling. + */ +TEST(T2ParserXConf, SlashScheduleAndTopLogHandling) +{ + ProfileXConf *profile = NULL; + const char *json_slash_schedule = + "{" + " \"urn:settings:TelemetryProfile\": {" + " \"telemetryProfile:name\": \"SlashSched\"," + " \"uploadRepository:URL\": \"http://example.com/upload2\"," + " \"schedule\": \"0/5 * * * *\"," + " \"telemetryProfile\": [" + " {" + " \"header\": \"tm_top\"," + " \"content\": \"search-term\"," + " \"type\": \"top_log.txt\"," + " \"pollingFrequency\": \"0\"" + " }," + " {" + " \"header\": \"param_tr181\"," + " \"content\": \"Device.DeviceInfo.Manufacturer\"," + " \"type\": \"\"," + " \"pollingFrequency\": \"0\"" + " }" + " ]" + " }" + "}"; + EXPECT_EQ(T2ERROR_SUCCESS, processConfigurationXConf((char*)json_slash_schedule, &profile)); + ASSERT_NE(profile, nullptr); + // schedule "0/5 * * * *" should parse to 5 minutes => 300 sec + EXPECT_EQ(300, profile->reportingInterval); + ASSERT_NE(profile->t2HTTPDest, nullptr); + EXPECT_STREQ("http://example.com/upload2", profile->t2HTTPDest->URL ? profile->t2HTTPDest->URL : ""); + // Ensure the profile name got set + EXPECT_STREQ("SlashSched", profile->name ? profile->name : ""); +} +