-
Notifications
You must be signed in to change notification settings - Fork 20
RDK-60304: Adding L1 unit test cases for reportgen #237
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This pull request adds comprehensive L1 unit test cases for the reportgen component to improve code coverage. The changes focus on testing various error handling paths and edge cases that weren't previously covered.
Changes:
- Added unit tests for
convertVectorToJson,prepareHttpUrl,encodeGrepResultInJSON,encodeEventMarkersInJSON,encodeTopResultInJSON, andtagReportAsCachedfunctions - Extended mock interfaces to support testing of cJSON and regex operations (
cJSON_IsArray,cJSON_InsertItemInArray,cJSON_GetObjectItemCaseSensitive,applyRegexToValue) - Added callback functions in reportgen.c to expose static functions for testing under
GTEST_ENABLEflag
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 6 comments.
| File | Description |
|---|---|
| source/test/reportgen/reportgenTest.cpp | Added approximately 30+ new test cases covering error paths, null argument handling, and edge cases for various reportgen functions |
| source/test/reportgen/reportgenMock.h | Extended mock interface with cJSON and regex-related methods needed for new test cases |
| source/test/reportgen/reportgenMock.cpp | Implemented mock wrappers for newly added interface methods |
| source/reportgen/reportgen.c | Added callback functions under GTEST_ENABLE to expose static functions for testing |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
840160d to
a7e473f
Compare
a7e473f to
07e7d6b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated 7 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| Vector_Destroy(topMarkerList, nullptr); | ||
| if(valArray) free(valArray); | ||
| } | ||
| TEST_F(reportgenTestFixture, encodeTopResultInJSON_cpuMem_cjsonCreateObjectFail) |
Copilot
AI
Jan 14, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similarly, this test name has inconsistent naming. It should be 'encodeTopResultInJSON_cpuMem_CreateObjectFails' to match the naming pattern used elsewhere in the file (e.g., lines 633, 660, 1729). The inconsistency in casing and abbreviation ('cjsonCreateObjectFail' vs 'CreateObjectFails') could lead to confusion.
| TEST_F(reportgenTestFixture, encodeTopResultInJSON_cpuMem_cjsonCreateObjectFail) | |
| TEST_F(reportgenTestFixture, encodeTopResultInJSON_cpuMem_CreateObjectFails) |
| char url[] = "http://rdk.central"; | ||
| httpStruct.URL = url; | ||
| httpStruct.RequestURIparamList = paramList; |
Copilot
AI
Jan 14, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test uses a stack-allocated array with mutable char literals for paramRefFail, paramRefEmpty, paramName, and url, which is good practice. However, the httpStruct.URL assignment (line 293) assigns a stack-allocated array to the URL pointer. While the test only reads from this pointer and doesn't free it, this could be fragile. It would be safer to use strdup for URL as well, or add a comment explaining why a stack-allocated array is acceptable here (since prepareHttpUrl internally calls strdup on http->URL at line 1205).
| ::testing::SetArgPointee<1>(nullptr), | ||
| ::testing::Return(T2ERROR_FAILURE) | ||
| )); | ||
| char* emptyValue = strdup(""); // allocate so code under test can free() it |
Copilot
AI
Jan 14, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comment says 'allocate so code under test can free() it' but the production code at line 1235 in reportgen.c checks 'if (paramValue[0] == '\0')' and then frees paramValue. However, when an empty string "" is passed, paramValue[0] is '\0', so the free will happen. This is correct behavior, but the comment could be more explicit about explaining that the production code will free this empty string when it detects it's empty.
| char* emptyValue = strdup(""); // allocate so code under test can free() it | |
| char* emptyValue = strdup(""); // allocate an empty string so the code under test can detect paramValue[0] == '\0' and free() it |
| TEST_F(reportgenTestFixture, tagReportAsCached_NoArrayErrorBranch) { | ||
| char* fakeJson = strdup("{\"foo\":42}"); | ||
|
|
||
| cJSON* mockRoot = (cJSON*)0x1000; | ||
| cJSON* mockReportTypeObj = (cJSON*)0x3000; | ||
|
|
||
| // Parse | ||
| EXPECT_CALL(*m_reportgenMock, cJSON_Parse(_)) | ||
| .WillOnce(::testing::Return(mockRoot)); | ||
| EXPECT_CALL(*m_reportgenMock, cJSON_CreateObject()) | ||
| .WillOnce(::testing::Return(mockReportTypeObj)); | ||
| EXPECT_CALL(*m_reportgenMock, cJSON_AddStringToObject( | ||
| mockReportTypeObj, | ||
| ::testing::StrEq("REPORT_TYPE"), | ||
| ::testing::StrEq("CACHED")) | ||
| ) | ||
| .WillOnce(::testing::Return(mockReportTypeObj)); | ||
| // Try "searchResult" (not found) | ||
| EXPECT_CALL(*m_reportgenMock, cJSON_GetObjectItemCaseSensitive(mockRoot, ::testing::StrEq("searchResult"))) | ||
| .WillOnce(::testing::Return(nullptr)); | ||
| // Try "Report" (not found) | ||
| EXPECT_CALL(*m_reportgenMock, cJSON_GetObjectItemCaseSensitive(mockRoot, ::testing::StrEq("Report"))) | ||
| .WillOnce(::testing::Return(nullptr)); | ||
| // Act | ||
| tagReportAsCached(&fakeJson); | ||
|
|
||
| // Assert: unchanged input | ||
| EXPECT_STREQ("{\"foo\":42}", fakeJson); | ||
| free(fakeJson); | ||
| } |
Copilot
AI
Jan 14, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test has a bug: it doesn't expect the cleanup calls that should happen when neither 'searchResult' nor 'Report' arrays are found. Looking at the production code (lines 1326-1330 in reportgen.c), when no valid array is found, the code calls destroyJSONReport on both reportTypeEntry and jsonReportObj before returning. This test should expect cJSON_Delete to be called for both mockReportTypeObj and mockRoot, but currently it doesn't mock the cleanup of mockReportTypeObj.
| char* emptyValue = strdup(""); // allocate so code under test can free() it | ||
| EXPECT_CALL(*m_reportgenMock, getParameterValue(::testing::StrEq(paramRefEmpty), ::testing::_)) | ||
| .WillOnce(::testing::DoAll( | ||
| ::testing::SetArgPointee<1>(emptyValue), | ||
| ::testing::Return(T2ERROR_SUCCESS) | ||
| )); |
Copilot
AI
Jan 14, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this test case, when getParameterValue fails or returns an empty value, prepareHttpUrl continues to process more parameters and then returns a strdup'd copy of the URL. The test correctly frees this result, but there's a potential memory leak: the test calls strdup("") for emptyValue which is freed by the production code when paramValue[0] equals '\0' (line 1235 in reportgen.c), but the test doesn't verify this cleanup is actually happening. While the test will work, it would be clearer to add a comment explaining that the production code will free emptyValue.
| .WillOnce(::testing::Return(nullptr)); | ||
| // Try "Report" (not found) | ||
| EXPECT_CALL(*m_reportgenMock, cJSON_GetObjectItemCaseSensitive(mockRoot, ::testing::StrEq("Report"))) | ||
| .WillOnce(::testing::Return(nullptr)); |
Copilot
AI
Jan 14, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test is missing an EXPECT_CALL for cJSON_Delete to cleanup mockReportTypeObj. Looking at the production code path (line 1328 in reportgen.c), when neither array is found, destroyJSONReport is called on reportTypeEntry, which should trigger cJSON_Delete on mockReportTypeObj. Without this expectation, if the production code correctly calls the cleanup, the test will fail due to unexpected mock calls.
| .WillOnce(::testing::Return(nullptr)); | |
| .WillOnce(::testing::Return(nullptr)); | |
| // Expect cleanup of the created report type object | |
| EXPECT_CALL(*m_reportgenMock, cJSON_Delete(mockReportTypeObj)); |
| Vector_Destroy(topMarkerList, nullptr); | ||
| if(valArray) free(valArray); | ||
| } | ||
| TEST_F(reportgenTestFixture, encodeTopResultInJSON_loadAverage_cjsonCreateObjectFail) |
Copilot
AI
Jan 14, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The naming of this test is inconsistent. The test function is named 'encodeTopResultInJSON_loadAverage_cjsonCreateObjectFail' but it should follow the naming pattern more consistently as 'encodeTopResultInJSON_loadAverage_CreateObjectFails' to match the pattern used in other similar tests (e.g., line 633: encodeGrepResultInJSON_CreateObjectFails, line 660: encodeGrepResultInJSON_CreateArrayFails). The inconsistency in casing ('cjsonCreateObjectFail' vs 'CreateObjectFails') and the full spelling could lead to confusion.
| TEST_F(reportgenTestFixture, encodeTopResultInJSON_loadAverage_cjsonCreateObjectFail) | |
| TEST_F(reportgenTestFixture, encodeTopResultInJSON_loadAverage_CreateObjectFails) |
Reason for change: Adding L1 unit test cases for reportgen Test Procedure: Tested and verified Risks: Medium Priority: P1 Signed-off-by: Rose Mary Benny <RoseMary_Benny@comcast.com>
07e7d6b to
8ee327f
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| TEST_F(reportgenTestFixture, tagReportAsCached_NoArrayErrorBranch) { | ||
| char* fakeJson = strdup("{\"foo\":42}"); | ||
|
|
||
| cJSON* mockRoot = (cJSON*)0x1000; | ||
| cJSON* mockReportTypeObj = (cJSON*)0x3000; | ||
|
|
||
| // Parse | ||
| EXPECT_CALL(*m_reportgenMock, cJSON_Parse(_)) | ||
| .WillOnce(::testing::Return(mockRoot)); | ||
| EXPECT_CALL(*m_reportgenMock, cJSON_CreateObject()) | ||
| .WillOnce(::testing::Return(mockReportTypeObj)); | ||
| EXPECT_CALL(*m_reportgenMock, cJSON_AddStringToObject( | ||
| mockReportTypeObj, | ||
| ::testing::StrEq("REPORT_TYPE"), | ||
| ::testing::StrEq("CACHED")) | ||
| ) | ||
| .WillOnce(::testing::Return(mockReportTypeObj)); | ||
| // Try "searchResult" (not found) | ||
| EXPECT_CALL(*m_reportgenMock, cJSON_GetObjectItemCaseSensitive(mockRoot, ::testing::StrEq("searchResult"))) | ||
| .WillOnce(::testing::Return(nullptr)); | ||
| // Try "Report" (not found) | ||
| EXPECT_CALL(*m_reportgenMock, cJSON_GetObjectItemCaseSensitive(mockRoot, ::testing::StrEq("Report"))) | ||
| .WillOnce(::testing::Return(nullptr)); | ||
| // Act | ||
| tagReportAsCached(&fakeJson); | ||
|
|
||
| // Assert: unchanged input | ||
| EXPECT_STREQ("{\"foo\":42}", fakeJson); | ||
| free(fakeJson); | ||
| } |
Copilot
AI
Jan 19, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing mock expectation for cJSON_Delete on mockReportTypeObj. When the production code at reportgen.c:1328 calls destroyJSONReport(reportTypeEntry), it will invoke cJSON_Delete(reportTypeEntry). The test should include an expectation for cJSON_Delete(mockReportTypeObj) before the cJSON_Delete(mockRoot) expectation to properly verify this error path.
| TEST_F(reportgenTestFixture, prepareHttpUrl_CurlInitFails) { | ||
| T2HTTP httpStruct; | ||
| T2HTTP* http = &httpStruct; | ||
|
|
||
| // Simulate curl_easy_init() failing | ||
| EXPECT_CALL(*m_reportgenMock, curl_easy_init()) | ||
| .WillOnce(::testing::Return(nullptr)); | ||
|
|
||
| // Optionally: you could check for error logging if T2Error is mockable | ||
|
|
||
| // Call function under test | ||
| char* result = prepareHttpUrl(http); | ||
|
|
||
| // Expect NULL return | ||
| EXPECT_EQ(result, nullptr); | ||
| } | ||
|
|
||
| TEST_F(reportgenTestFixture, prepareHttpUrl_getParameterValueFailsAndEmptyValue) { | ||
| T2HTTP httpStruct = {0}; | ||
| HTTPReqParam httpParam1 = {0}; | ||
| HTTPReqParam httpParam2 = {0}; | ||
| Vector* paramList = NULL; | ||
| Vector_Create(¶mList); | ||
|
|
||
| // Use mutable char arrays or cast string literals to char* | ||
| char paramRefFail[] = "Test.Fail"; | ||
| char paramRefEmpty[] = "Test.Empty"; | ||
| char paramName[] = "testParam"; | ||
| char url[] = "http://rdk.central"; | ||
| httpStruct.URL = url; | ||
| httpStruct.RequestURIparamList = paramList; | ||
|
|
||
| httpParam1.HttpName = paramName; | ||
| httpParam1.HttpValue = nullptr; | ||
| httpParam1.HttpRef = paramRefFail; | ||
| httpParam2.HttpName = paramName; | ||
| httpParam2.HttpValue = nullptr; | ||
| httpParam2.HttpRef = paramRefEmpty; | ||
|
|
||
| Vector_PushBack(paramList, &httpParam1); | ||
| Vector_PushBack(paramList, &httpParam2); | ||
|
|
||
| EXPECT_CALL(*m_reportgenMock, curl_easy_init()) | ||
| .WillOnce(::testing::Return(reinterpret_cast<CURL*>(0x1))); | ||
| EXPECT_CALL(*m_reportgenMock, getParameterValue(::testing::StrEq(paramRefFail), ::testing::_)) | ||
| .WillOnce(::testing::DoAll( | ||
| ::testing::SetArgPointee<1>(nullptr), | ||
| ::testing::Return(T2ERROR_FAILURE) | ||
| )); | ||
| char* emptyValue = strdup(""); // allocate so code under test can free() it | ||
| EXPECT_CALL(*m_reportgenMock, getParameterValue(::testing::StrEq(paramRefEmpty), ::testing::_)) | ||
| .WillOnce(::testing::DoAll( | ||
| ::testing::SetArgPointee<1>(emptyValue), | ||
| ::testing::Return(T2ERROR_SUCCESS) | ||
| )); | ||
| EXPECT_CALL(*m_reportgenMock, curl_easy_cleanup(reinterpret_cast<CURL*>(0x1))).Times(1); | ||
|
|
||
| char* result = prepareHttpUrl(&httpStruct); | ||
|
|
||
| ASSERT_NE(result, nullptr); | ||
| EXPECT_STREQ(result, url); // No new params on fail/empty | ||
| free(result); | ||
|
|
||
| Vector_Destroy(paramList,nullptr); | ||
| } | ||
|
|
||
| // This tests the branch: getParameterValue returns success, paramValue[0] != '\0', curl_easy_escape is called, paramValue is freed | ||
|
|
||
| TEST_F(reportgenTestFixture, prepareHttpUrl_ParamValueIsEscapedAndFreed) { |
Copilot
AI
Jan 19, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inconsistent naming convention for test case names. While existing tests use PascalCase (e.g., PrepareHttpUrl, PrepareJSONReport1), the newly added tests use snake_case with descriptive suffixes (e.g., prepareHttpUrl_CurlInitFails, encodeGrepResultInJSON_CreateObjectFails). Consider using a consistent naming convention throughout the test file. Either align new tests with the existing PascalCase pattern or update all tests to use the more descriptive snake_case pattern.
Reason for change: Adding L1 unit test cases for reportgen
Test Procedure: Tested and verified
Risks: Medium
Priority: P1
Signed-off-by: Rose Mary Benny RoseMary_Benny@comcast.com