Skip to content

Feature/rrd prof updated#183

Open
Abhinavpv28 wants to merge 27 commits intodevelopfrom
feature/rrd_prof_updated
Open

Feature/rrd prof updated#183
Abhinavpv28 wants to merge 27 commits intodevelopfrom
feature/rrd_prof_updated

Conversation

@Abhinavpv28
Copy link
Copy Markdown
Contributor

No description provided.

@Abhinavpv28 Abhinavpv28 requested a review from a team as a code owner April 10, 2026 09:13
Copilot AI review requested due to automatic review settings April 10, 2026 09:13
Copy link
Copy Markdown
Contributor

Copilot AI left a 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 PR adds RBUS support for setting and retrieving “profile data” for the RDK Remote Debugger, backed by parsing /etc/rrd/remote_debugger.json and a persisted selected category.

Changes:

  • Introduces new TR-181/RBUS parameter strings for setProfileData and getProfileData, plus a persisted “profile category” selection.
  • Registers/unregisters RBUS data elements and implements RBUS set/get handlers.
  • Adds cJSON-based helper functions to read and transform the profile JSON into response payloads.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 8 comments.

File Description
src/rrdInterface.h Adds new profile RBUS parameter constants, includes cJSON, and declares new handlers/helpers.
src/rrdInterface.c Implements category persistence, registers RBUS data elements, and adds RBUS handlers + JSON processing helpers.

Comment on lines +676 to +684
RDK_LOG(RDK_LOG_DEBUG, LOG_REMDEBUG, "[%s:%d]: Category %s not found\n",
__FUNCTION__, __LINE__, category_name);
return cJSON_Print(cJSON_CreateArray());
}

if (!has_direct_commands(category)) {
RDK_LOG(RDK_LOG_DEBUG, LOG_REMDEBUG, "[%s:%d]: Category %s has nested structure, returning empty\n",
__FUNCTION__, __LINE__, category_name);
return cJSON_Print(cJSON_CreateArray());
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

get_specific_category_json returns cJSON_Print(cJSON_CreateArray()) in the early-exit paths. This leaks the newly created cJSON array object because it is never deleted. Create the empty array in a variable, print it, then cJSON_Delete it before returning the printed string.

Suggested change
RDK_LOG(RDK_LOG_DEBUG, LOG_REMDEBUG, "[%s:%d]: Category %s not found\n",
__FUNCTION__, __LINE__, category_name);
return cJSON_Print(cJSON_CreateArray());
}
if (!has_direct_commands(category)) {
RDK_LOG(RDK_LOG_DEBUG, LOG_REMDEBUG, "[%s:%d]: Category %s has nested structure, returning empty\n",
__FUNCTION__, __LINE__, category_name);
return cJSON_Print(cJSON_CreateArray());
cJSON *emptyArray = cJSON_CreateArray();
char *result_str = NULL;
RDK_LOG(RDK_LOG_DEBUG, LOG_REMDEBUG, "[%s:%d]: Category %s not found\n",
__FUNCTION__, __LINE__, category_name);
result_str = cJSON_Print(emptyArray);
cJSON_Delete(emptyArray);
return result_str;
}
if (!has_direct_commands(category)) {
cJSON *emptyArray = cJSON_CreateArray();
char *result_str = NULL;
RDK_LOG(RDK_LOG_DEBUG, LOG_REMDEBUG, "[%s:%d]: Category %s has nested structure, returning empty\n",
__FUNCTION__, __LINE__, category_name);
result_str = cJSON_Print(emptyArray);
cJSON_Delete(emptyArray);
return result_str;

Copilot uses AI. Check for mistakes.
Comment on lines +627 to +630
jsonBuffer[bytesRead] = '\0';
fclose(fp);

*file_size = fileSz;
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

read_profile_json_file doesn’t verify that fread read the full file (it may return short reads), but still returns the buffer and reports *file_size = fileSz. This can lead to truncated/invalid JSON being parsed with no clear error. Check bytesRead == (size_t)fileSz (or handle partial reads) and set *file_size to the actual bytes read.

Suggested change
jsonBuffer[bytesRead] = '\0';
fclose(fp);
*file_size = fileSz;
if (bytesRead != (size_t)fileSz) {
jsonBuffer[bytesRead] = '\0';
RDK_LOG(RDK_LOG_ERROR, LOG_REMDEBUG, "[%s:%d]: Failed to read complete profile file from %s (expected %ld bytes, read %zu bytes)\n", __FUNCTION__, __LINE__, filename, fileSz, bytesRead);
fclose(fp);
free(jsonBuffer);
return NULL;
}
jsonBuffer[bytesRead] = '\0';
fclose(fp);
*file_size = (long)bytesRead;

Copilot uses AI. Check for mistakes.
return RBUS_ERROR_INVALID_INPUT;
}

const char *filename = "/etc/rrd/remote_debugger.json";
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This hard-codes the profile JSON path as "/etc/rrd/remote_debugger.json", but the project already defines RRD_JSON_FILE in rrdCommon.h. Using the shared constant avoids duplication and keeps future path changes consistent.

Suggested change
const char *filename = "/etc/rrd/remote_debugger.json";
const char *filename = RRD_JSON_FILE;

Copilot uses AI. Check for mistakes.
Comment on lines +539 to +543
rbusError_t rrd_SetHandler(rbusHandle_t handle, rbusProperty_t prop, rbusSetHandlerOptions_t* opts)
{
(void)handle;
(void)opts;

Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New behavior is introduced via rrd_SetHandler/rrd_GetHandler (persisting the selected category and generating RBUS responses based on /etc/rrd/remote_debugger.json), but there are currently no unit tests covering these paths in src/unittest/rrdUnitTestRunner.cpp (which already tests other rrdInterface.c helpers like checkAppendRequest). Adding tests for category persistence, JSON parsing, and the "all" vs specific-category responses would help prevent regressions.

Copilot uses AI. Check for mistakes.
Copilot AI review requested due to automatic review settings April 10, 2026 09:30
Copy link
Copy Markdown
Contributor

Copilot AI left a 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 2 out of 2 changed files in this pull request and generated 5 comments.

Comment on lines +626 to +631
size_t bytesRead = fread(jsonBuffer, 1U, (size_t)fileSz, fp);
jsonBuffer[bytesRead] = '\0';
fclose(fp);

*file_size = fileSz;
return jsonBuffer;
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

read_profile_json_file doesn’t validate that fread read the expected number of bytes. If bytesRead != fileSz (short read / I/O error), the caller may parse truncated JSON and *file_size will be inaccurate. Check for short reads (bytesRead == (size_t)fileSz), handle ferror(fp), and fail/cleanup on mismatch.

Copilot uses AI. Check for mistakes.
Comment on lines +515 to +516
if (ret != 0) {
RDK_LOG(RDK_LOG_ERROR, LOG_REMDEBUG, "[%s:%d]: RBUS unregDataElements failed with error: %d\n", __FUNCTION__, __LINE__, ret);
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rbus_unregDataElements is checked against ret != 0 here, while rbus_regDataElements above uses ret != RBUS_ERROR_SUCCESS. For consistency (and in case success isn’t exactly 0), compare against RBUS_ERROR_SUCCESS and log rbusError_ToString((rbusError_t)ret) like other RBUS paths do.

Suggested change
if (ret != 0) {
RDK_LOG(RDK_LOG_ERROR, LOG_REMDEBUG, "[%s:%d]: RBUS unregDataElements failed with error: %d\n", __FUNCTION__, __LINE__, ret);
if (ret != RBUS_ERROR_SUCCESS) {
RDK_LOG(RDK_LOG_ERROR, LOG_REMDEBUG, "[%s:%d]: RBUS unregDataElements failed with error: %d(%s)\n", __FUNCTION__, __LINE__, ret, rbusError_ToString((rbusError_t)ret));

Copilot uses AI. Check for mistakes.
Comment on lines +539 to +546
rbusError_t rrd_SetHandler(rbusHandle_t handle, rbusProperty_t prop, rbusSetHandlerOptions_t* opts)
{
(void)handle;
(void)opts;

char const* propertyName = rbusProperty_GetName(prop);
rbusValue_t value = rbusProperty_GetValue(prop);
rbusValueType_t type = rbusValue_GetType(value);
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rrd_SetHandler/rrd_GetHandler (and the helper functions below) introduce new RBUS handler APIs/types (rbusProperty_t, rbusGetHandlerOptions_t, etc.). The unit test build compiles rrdInterface.c with -DGTEST_ENABLE and does not define these RBUS types in its mocks, so this will fail to compile there. Wrap these handler implementations in #if !defined(GTEST_ENABLE) (matching the header) or extend the gtest RBUS mocks to include the required types/APIs.

Copilot uses AI. Check for mistakes.
Comment on lines +722 to +759
rbusError_t rrd_GetHandler(rbusHandle_t handle, rbusProperty_t prop, rbusGetHandlerOptions_t* opts)
{
(void)handle;
(void)opts;

char const* propertyName = rbusProperty_GetName(prop);
RDK_LOG(RDK_LOG_DEBUG, LOG_REMDEBUG, "[%s:%d]: Get handler called for [%s]\n", __FUNCTION__, __LINE__, propertyName);

if(strcmp(propertyName, RRD_GET_PROFILE_EVENT) != 0) {
return RBUS_ERROR_INVALID_INPUT;
}

const char *filename = "/etc/rrd/remote_debugger.json";
long file_size;

// Read JSON file
char *jsonBuffer = read_profile_json_file(filename, &file_size);
if (!jsonBuffer) {
return RBUS_ERROR_BUS_ERROR;
}

// Parse JSON
cJSON *json = cJSON_Parse(jsonBuffer);
if (!json) {
RDK_LOG(RDK_LOG_ERROR, LOG_REMDEBUG, "[%s:%d]: Failed to parse JSON from %s\n", __FUNCTION__, __LINE__, filename);
free(jsonBuffer);
return RBUS_ERROR_BUS_ERROR;
}

RDK_LOG(RDK_LOG_DEBUG, LOG_REMDEBUG, "[%s:%d]: JSON parsed successfully, processing categories\n", __FUNCTION__, __LINE__);

// Generate appropriate JSON response
char *result_str = NULL;
if (strlen(RRDProfileCategory) == 0 || strcmp(RRDProfileCategory, "all") == 0) {
result_str = get_all_categories_json(json);
} else {
result_str = get_specific_category_json(json, RRDProfileCategory);
}
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New profile-data behavior (category persistence + JSON category listing + rrd_GetHandler response formatting) is introduced here but there are no corresponding unit tests in src/unittest/rrdUnitTestRunner.cpp (that file already tests other rrdInterface.c helpers like checkAppendRequest). Adding focused tests for: (1) “all” vs specific category output, (2) nested-category returning empty, and (3) missing/invalid JSON file handling would help prevent regressions.

Copilot uses AI. Check for mistakes.
Comment on lines +734 to +736
const char *filename = "/etc/rrd/remote_debugger.json";
long file_size;

Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

file_size is set via read_profile_json_file but never used afterwards, which can trigger an unused-variable warning depending on compiler flags. Either remove file_size (and the out-parameter) if not needed, or use it (e.g., for logging/validation).

Copilot uses AI. Check for mistakes.
Copilot AI review requested due to automatic review settings April 10, 2026 09:47
Removed mock implementations for profile handler functions when GTEST_ENABLE is defined.
Copy link
Copy Markdown
Contributor

Copilot AI left a 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 4 comments.

Comments suppressed due to low confidence (2)

src/unittest/mocks/Client_Mock.cpp:231

  • rbusValue_Release function-pointer variable is defined twice in this file (once around line 211 and again at line 229). This will fail to compile due to a redefinition error; remove the duplicate definition and keep a single initialization.
rbusValueType_t (*rbusValue_GetType)(rbusValue_t) = &RBusApiWrapper::rbusValue_GetType;
char const* (*rbusValue_GetString)(rbusValue_t, int*) = &RBusApiWrapper::rbusValue_GetString;
void (*rbusProperty_SetValue)(rbusProperty_t, rbusValue_t) = &RBusApiWrapper::rbusProperty_SetValue;
void (*rbusValue_Release)(rbusValue_t) = &RBusApiWrapper::rbusValue_Release;

void (*rbusValue_Release)(rbusValue_t) = &RBusApiWrapper::rbusValue_Release;

/* -------- RFC ---------------*/
SetParamInterface *SetParamWrapper::impl = nullptr;

SetParamWrapper::SetParamWrapper() {}

void SetParamWrapper::setImpl(SetParamInterface *newImpl)
{
    EXPECT_TRUE((nullptr == impl) || (nullptr == newImpl));
    impl = newImpl;
}

void SetParamWrapper::clearImpl()
{
    impl = nullptr;
}

tr181ErrorCode_t SetParamWrapper::setParam(char *arg1, const char *arg2, const char *arg3)

src/unittest/mocks/Client_Mock.cpp:228

  • rrd_SetHandler / rrd_GetHandler are defined here under GTEST_ENABLE, but they are also implemented in src/rrdInterface.c in this PR. With the unit-test runner including both Client_Mock.cpp and rrdInterface.c into the same translation unit, this causes duplicate symbol/ODR redefinition build failures. Remove these mock definitions (or compile-guard the real ones) so only one definition exists in tests.
void (*rbusValue_Release)(rbusValue_t) = &RBusApiWrapper::rbusValue_Release;

/* -------- RFC ---------------*/
SetParamInterface *SetParamWrapper::impl = nullptr;

SetParamWrapper::SetParamWrapper() {}

void SetParamWrapper::setImpl(SetParamInterface *newImpl)
{
    EXPECT_TRUE((nullptr == impl) || (nullptr == newImpl));
    impl = newImpl;
}

void SetParamWrapper::clearImpl()
{
    impl = nullptr;

#include "rrdCommon.h"
#if !defined(GTEST_ENABLE)
#include "rbus.h"
#include <cjson/cJSON.h>
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#include <cjson/cJSON.h> is inconsistent with the rest of the codebase, which includes cJSON as "cJSON.h" (e.g., src/rrdJsonParser.h:29, src/rrdCommandSanity.h:28). Using a different include path risks breaking builds depending on include search paths; align this include with the existing convention or ensure the build explicitly provides the cjson/cJSON.h path.

Suggested change
#include <cjson/cJSON.h>
#include "cJSON.h"

Copilot uses AI. Check for mistakes.
// RDK Remote Debugger profile data parameter definitions
#define RRD_SET_PROFILE_EVENT "Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.RDKRemoteDebugger.setProfileData"
#define RRD_GET_PROFILE_EVENT "Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.RDKRemoteDebugger.getProfileData"
#define RRD_PROFILE_CATEGORY_FILE "/tmp/rrd_profile_category"
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The profile category is persisted under /tmp (RRD_PROFILE_CATEGORY_FILE). If this process runs with elevated privileges, writing to a fixed path in a world-writable directory via fopen("w") is vulnerable to symlink/hardlink attacks (clobbering arbitrary files). Consider storing this state under a protected directory (e.g., /var/run//opt) or use a secure open() pattern (O_NOFOLLOW, O_CREAT|O_TRUNC, 0600) and then fdopen().

Suggested change
#define RRD_PROFILE_CATEGORY_FILE "/tmp/rrd_profile_category"
#define RRD_PROFILE_CATEGORY_FILE "/var/run/rrd_profile_category"

Copilot uses AI. Check for mistakes.
Comment on lines +36 to +37
// Global storage for profile category
char RRDProfileCategory[256] = "all";
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RRDProfileCategory is a non-static global with external linkage, but it is only used within this translation unit. Leaving it non-static needlessly exports a global symbol and increases the risk of name collisions at link time. Make it static (and consider keeping it near the handlers that use it).

Suggested change
// Global storage for profile category
char RRDProfileCategory[256] = "all";
// File-local storage for profile category
static char RRDProfileCategory[256] = "all";

Copilot uses AI. Check for mistakes.
Comment on lines +539 to +563
rbusError_t rrd_SetHandler(rbusHandle_t handle, rbusProperty_t prop, rbusSetHandlerOptions_t* opts)
{
(void)handle;
(void)opts;

char const* propertyName = rbusProperty_GetName(prop);
rbusValue_t value = rbusProperty_GetValue(prop);
rbusValueType_t type = rbusValue_GetType(value);

RDK_LOG(RDK_LOG_DEBUG, LOG_REMDEBUG, "[%s:%d]: Set handler called for [%s]\n", __FUNCTION__, __LINE__, propertyName);

if(strcmp(propertyName, RRD_SET_PROFILE_EVENT) == 0) {
if (type == RBUS_STRING) {
const char* str = rbusValue_GetString(value, NULL);
if(strlen(str) > 255) {
RDK_LOG(RDK_LOG_ERROR, LOG_REMDEBUG, "[%s:%d]: String too long for setProfileData\n", __FUNCTION__, __LINE__);
return RBUS_ERROR_INVALID_INPUT;
}

strncpy(RRDProfileCategory, str, sizeof(RRDProfileCategory)-1);
RRDProfileCategory[sizeof(RRDProfileCategory)-1] = '\0';
RDK_LOG(RDK_LOG_INFO, LOG_REMDEBUG, "[%s:%d]: setProfileData value: %s\n", __FUNCTION__, __LINE__, RRDProfileCategory);

// Store the category selection to file
if(save_profile_category() != 0) {
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New RBUS set/get handlers and JSON profile processing logic are introduced here, but there are currently no unit tests covering: (1) setting the category (including file persistence), (2) retrieving "all" vs a specific category, and (3) error paths (missing/invalid JSON, oversized category). Since src/unittest/rrdUnitTestRunner.cpp already provides extensive coverage for other rrdInterface.c behavior, add focused tests for these new handlers to prevent regressions.

Copilot uses AI. Check for mistakes.
Copilot AI review requested due to automatic review settings April 10, 2026 10:55
Copy link
Copy Markdown
Contributor

Copilot AI left a 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 5 comments.

Comment on lines +674 to +679
cJSON *category = cJSON_GetObjectItem(json, category_name);
if (!category || !cJSON_IsObject(category)) {
RDK_LOG(RDK_LOG_DEBUG, LOG_REMDEBUG, "[%s:%d]: Category %s not found\n",
__FUNCTION__, __LINE__, category_name);
return cJSON_Print(cJSON_CreateArray());
}
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

get_specific_category_json returns cJSON_Print(cJSON_CreateArray()) on error paths. This leaks the temporary array object because it is never deleted. Create the array, print it, then cJSON_Delete it before returning (or reuse a shared helper for empty-array responses).

Copilot uses AI. Check for mistakes.
Comment on lines +705 to +714
if (!json_str) {
return RBUS_ERROR_BUS_ERROR;
}

rbusValue_t rbusValue;
rbusValue_Init(&rbusValue);
rbusValue_SetString(rbusValue, json_str);
rbusProperty_SetValue(prop, rbusValue);
rbusValue_Release(rbusValue);

Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

set_rbus_response ignores the return codes from rbusValue_Init / rbusValue_SetString. If init fails, rbusValue may be uninitialized and subsequent calls will misbehave. Capture and check these results and return an appropriate rbusError_t on failure.

Suggested change
if (!json_str) {
return RBUS_ERROR_BUS_ERROR;
}
rbusValue_t rbusValue;
rbusValue_Init(&rbusValue);
rbusValue_SetString(rbusValue, json_str);
rbusProperty_SetValue(prop, rbusValue);
rbusValue_Release(rbusValue);
rbusError_t error;
rbusValue_t rbusValue = NULL;
if (!json_str) {
return RBUS_ERROR_BUS_ERROR;
}
error = rbusValue_Init(&rbusValue);
if (error != RBUS_ERROR_SUCCESS) {
RDK_LOG(RDK_LOG_ERROR, LOG_REMDEBUG, "[%s:%d]: rbusValue_Init failed with error %d\n", __FUNCTION__, __LINE__, error);
return error;
}
error = rbusValue_SetString(rbusValue, json_str);
if (error != RBUS_ERROR_SUCCESS) {
RDK_LOG(RDK_LOG_ERROR, LOG_REMDEBUG, "[%s:%d]: rbusValue_SetString failed with error %d\n", __FUNCTION__, __LINE__, error);
rbusValue_Release(rbusValue);
return error;
}
rbusProperty_SetValue(prop, rbusValue);
rbusValue_Release(rbusValue);

Copilot uses AI. Check for mistakes.
Comment on lines 505 to 512
#if !defined(GTEST_ENABLE)
ret = rbusEvent_UnsubscribeEx(rrdRbusHandle, subscriptions, 3);
if (ret != 0)
{
RDK_LOG(RDK_LOG_ERROR, LOG_REMDEBUG, "[%s:%d]: RBUS Unsubscribe EventHandler for RRD failed!!! \n", __FUNCTION__, __LINE__);
return ret;
}

Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On rbusEvent_UnsubscribeEx failure, this function returns early and skips unregistering the newly-added profile data elements (and also skips rbus_close). Consider doing best-effort cleanup (attempt rbus_unregDataElements and rbus_close) even when event unsubscription fails, and return the first error encountered.

Copilot uses AI. Check for mistakes.
Comment on lines 88 to +90
int RRD_unsubscribe(void);
rbusError_t rrd_SetHandler(rbusHandle_t handle, rbusProperty_t property, rbusSetHandlerOptions_t* opts);
rbusError_t rrd_GetHandler(rbusHandle_t handle, rbusProperty_t prop, rbusGetHandlerOptions_t* opts);
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rrd_SetHandler / rrd_GetHandler are declared unconditionally but their parameter types come from RBUS headers, which are excluded when GTEST_ENABLE is defined. This makes rrdInterface.h non-self-contained for GTest builds unless callers include the mocks first. Wrap these declarations in #if !defined(GTEST_ENABLE) (like the other RBUS-only declarations) or provide forward declarations/typedefs so the header compiles on its own.

Suggested change
int RRD_unsubscribe(void);
rbusError_t rrd_SetHandler(rbusHandle_t handle, rbusProperty_t property, rbusSetHandlerOptions_t* opts);
rbusError_t rrd_GetHandler(rbusHandle_t handle, rbusProperty_t prop, rbusGetHandlerOptions_t* opts);
int RRD_unsubscribe(void);
#if !defined(GTEST_ENABLE)
rbusError_t rrd_SetHandler(rbusHandle_t handle, rbusProperty_t property, rbusSetHandlerOptions_t* opts);
rbusError_t rrd_GetHandler(rbusHandle_t handle, rbusProperty_t prop, rbusGetHandlerOptions_t* opts);
#endif

Copilot uses AI. Check for mistakes.
Comment on lines +541 to +552
(void)handle;
(void)opts;

char const* propertyName = rbusProperty_GetName(prop);
rbusValue_t value = rbusProperty_GetValue(prop);
rbusValueType_t type = rbusValue_GetType(value);

RDK_LOG(RDK_LOG_DEBUG, LOG_REMDEBUG, "[%s:%d]: Set handler called for [%s]\n", __FUNCTION__, __LINE__, propertyName);

if(strcmp(propertyName, RRD_SET_PROFILE_EVENT) == 0) {
if (type == RBUS_STRING) {
const char* str = rbusValue_GetString(value, NULL);
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New RBUS provider handlers (rrd_SetHandler/rrd_GetHandler) add non-trivial behavior (persisting category selection, reading/parsing /etc/rrd/remote_debugger.json, building JSON responses) but there are no corresponding unit tests in the existing GTest suite. Add tests that cover: valid/invalid set input, missing/invalid JSON file, "all" vs specific category selection, and RBUS value init/set failures.

Suggested change
(void)handle;
(void)opts;
char const* propertyName = rbusProperty_GetName(prop);
rbusValue_t value = rbusProperty_GetValue(prop);
rbusValueType_t type = rbusValue_GetType(value);
RDK_LOG(RDK_LOG_DEBUG, LOG_REMDEBUG, "[%s:%d]: Set handler called for [%s]\n", __FUNCTION__, __LINE__, propertyName);
if(strcmp(propertyName, RRD_SET_PROFILE_EVENT) == 0) {
if (type == RBUS_STRING) {
const char* str = rbusValue_GetString(value, NULL);
rbusValue_t value;
rbusValueType_t type;
char const* propertyName;
(void)handle;
(void)opts;
if(prop == NULL) {
RDK_LOG(RDK_LOG_ERROR, LOG_REMDEBUG, "[%s:%d]: NULL property passed to set handler\n", __FUNCTION__, __LINE__);
return RBUS_ERROR_INVALID_INPUT;
}
propertyName = rbusProperty_GetName(prop);
if(propertyName == NULL) {
RDK_LOG(RDK_LOG_ERROR, LOG_REMDEBUG, "[%s:%d]: NULL property name in set handler\n", __FUNCTION__, __LINE__);
return RBUS_ERROR_INVALID_INPUT;
}
value = rbusProperty_GetValue(prop);
if(value == NULL) {
RDK_LOG(RDK_LOG_ERROR, LOG_REMDEBUG, "[%s:%d]: NULL property value for [%s]\n", __FUNCTION__, __LINE__, propertyName);
return RBUS_ERROR_INVALID_INPUT;
}
type = rbusValue_GetType(value);
RDK_LOG(RDK_LOG_DEBUG, LOG_REMDEBUG, "[%s:%d]: Set handler called for [%s]\n", __FUNCTION__, __LINE__, propertyName);
if(strcmp(propertyName, RRD_SET_PROFILE_EVENT) == 0) {
if (type == RBUS_STRING) {
const char* str = rbusValue_GetString(value, NULL);
if(str == NULL) {
RDK_LOG(RDK_LOG_ERROR, LOG_REMDEBUG, "[%s:%d]: NULL string for setProfileData\n", __FUNCTION__, __LINE__);
return RBUS_ERROR_INVALID_INPUT;
}

Copilot uses AI. Check for mistakes.
@github-actions
Copy link
Copy Markdown

Code Coverage Summary

                               Total:|80.2%   5327|95.9%  1326|    -      0

Copilot AI review requested due to automatic review settings April 10, 2026 11:32
@github-actions
Copy link
Copy Markdown

Code Coverage Summary

                               Total:|80.2%   5327|95.9%  1326|    -      0

Copy link
Copy Markdown
Contributor

Copilot AI left a 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 5 out of 5 changed files in this pull request and generated 4 comments.

Comment on lines +180 to 190
// Register RBUS data elements for profile data provider
ret = rbus_regDataElements(rrdRbusHandle, 2, profileDataElements);
if (ret != RBUS_ERROR_SUCCESS) {
RDK_LOG(RDK_LOG_ERROR, LOG_REMDEBUG, "[%s:%d]: RBUS regDataElements failed with error: %d\n", __FUNCTION__, __LINE__, ret);
} else {
RDK_LOG(RDK_LOG_DEBUG, LOG_REMDEBUG, "[%s:%d]: SUCCESS: RBUS profile data elements registered\n", __FUNCTION__, __LINE__);
}

webconfigFrameworkInit();
RDK_LOG(RDK_LOG_DEBUG, LOG_REMDEBUG, "[%s:%d]: ...Exiting.. \n", __FUNCTION__, __LINE__);
return ret;
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RRD_subscribe() overwrites ret with the result of rbus_regDataElements() and then returns it. This can mask an earlier RBUS event subscription failure (e.g., rbusEvent_SubscribeEx fails but rbus_regDataElements succeeds, returning success). Consider preserving the first failure (or combining errors) and avoid reusing ret for unrelated operations.

Copilot uses AI. Check for mistakes.
Comment on lines +36 to +67
// Global storage for profile category
char RRDProfileCategory[256] = "all";
#define MAX_PROFILE_JSON_SIZE 32768

// Helper functions for profile category file-based storage
int load_profile_category(void) {
FILE *fp = fopen(RRD_PROFILE_CATEGORY_FILE, "r");
if (fp) {
if (fgets(RRDProfileCategory, sizeof(RRDProfileCategory), fp)) {
// Remove trailing newline
char *newline = strchr(RRDProfileCategory, '\n');
if (newline) *newline = '\0';
fclose(fp);
return 0;
}
fclose(fp);
}
// Default to "all" if file doesn't exist or read fails
strncpy(RRDProfileCategory, "all", sizeof(RRDProfileCategory) - 1);
RRDProfileCategory[sizeof(RRDProfileCategory) - 1] = '\0';
return -1;
}

int save_profile_category(void) {
FILE *fp = fopen(RRD_PROFILE_CATEGORY_FILE, "w");
if (fp) {
fprintf(fp, "%s\n", RRDProfileCategory);
fclose(fp);
return 0;
}
return -1;
}
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These helper functions and globals (RRDProfileCategory, load_profile_category, save_profile_category) are not static and appear to be used only within rrdInterface.c (no other references in src/). Making them static avoids exporting internal symbols and reduces the chance of name collisions at link time.

Copilot uses AI. Check for mistakes.
Comment on lines +539 to +577
rbusError_t rrd_SetHandler(rbusHandle_t handle, rbusProperty_t prop, rbusSetHandlerOptions_t* opts)
{
(void)handle;
(void)opts;

char const* propertyName = rbusProperty_GetName(prop);
rbusValue_t value = rbusProperty_GetValue(prop);
rbusValueType_t type = rbusValue_GetType(value);

RDK_LOG(RDK_LOG_DEBUG, LOG_REMDEBUG, "[%s:%d]: Set handler called for [%s]\n", __FUNCTION__, __LINE__, propertyName);

if(strcmp(propertyName, RRD_SET_PROFILE_EVENT) == 0) {
if (type == RBUS_STRING) {
const char* str = rbusValue_GetString(value, NULL);
if(strlen(str) > 255) {
RDK_LOG(RDK_LOG_ERROR, LOG_REMDEBUG, "[%s:%d]: String too long for setProfileData\n", __FUNCTION__, __LINE__);
return RBUS_ERROR_INVALID_INPUT;
}

strncpy(RRDProfileCategory, str, sizeof(RRDProfileCategory)-1);
RRDProfileCategory[sizeof(RRDProfileCategory)-1] = '\0';
RDK_LOG(RDK_LOG_INFO, LOG_REMDEBUG, "[%s:%d]: setProfileData value: %s\n", __FUNCTION__, __LINE__, RRDProfileCategory);

// Store the category selection to file
if(save_profile_category() != 0) {
RDK_LOG(RDK_LOG_ERROR, LOG_REMDEBUG, "[%s:%d]: Failed to store profile category\n", __FUNCTION__, __LINE__);
return RBUS_ERROR_BUS_ERROR;
}

RDK_LOG(RDK_LOG_INFO, LOG_REMDEBUG, "[%s:%d]: Successfully set profile category to: %s\n", __FUNCTION__, __LINE__, RRDProfileCategory);
return RBUS_ERROR_SUCCESS;
} else {
RDK_LOG(RDK_LOG_ERROR, LOG_REMDEBUG, "[%s:%d]: Invalid type for setProfileData\n", __FUNCTION__, __LINE__);
return RBUS_ERROR_INVALID_INPUT;
}
}

return RBUS_ERROR_INVALID_INPUT;
}
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unit tests appear to exist for other rrdInterface.c functions (e.g., checkAppendRequest in src/unittest/rrdUnitTestRunner.cpp), but there are no tests covering the newly added RBUS profile handlers (rrd_SetHandler/rrd_GetHandler) and JSON response shaping. Adding tests for valid/invalid category set, missing/invalid JSON file, and category filtering would help prevent regressions.

Copilot uses AI. Check for mistakes.

# Define the compiler flags
COMMON_CXXFLAGS = -frtti -fprofile-arcs -ftest-coverage
COMMON_CXXFLAGS = -frtti -fprofile-arcs -ftest-coverage -fpermissive
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding -fpermissive relaxes C++ type checking and can hide real errors in the unit test build (e.g., invalid conversions that should fail). It would be better to address the underlying issues (or compile C sources as C rather than including .c files in a C++ TU) and keep the test build strict.

Suggested change
COMMON_CXXFLAGS = -frtti -fprofile-arcs -ftest-coverage -fpermissive
COMMON_CXXFLAGS = -frtti -fprofile-arcs -ftest-coverage

Copilot uses AI. Check for mistakes.
Copilot AI review requested due to automatic review settings April 10, 2026 12:23
Copy link
Copy Markdown
Contributor

Copilot AI left a 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 6 out of 6 changed files in this pull request and generated 9 comments.

Comment on lines +582 to +589
bool has_direct_commands(cJSON *category)
{
cJSON *item = NULL;
cJSON_ArrayForEach(item, category) {
if (cJSON_IsObject(item)) {
cJSON *commands = cJSON_GetObjectItem(item, "Commands");
if (commands && cJSON_IsString(commands)) {
return true;
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

has_direct_commands() is called with nullptr in the unit tests, but the implementation iterates unconditionally; cJSON_ArrayForEach on a null pointer will crash. Add an explicit if(!category) return false; guard before iterating.

Copilot uses AI. Check for mistakes.
Comment on lines +601 to +627
FILE *fp = fopen(filename, "rb");
if (!fp) {
RDK_LOG(RDK_LOG_ERROR, LOG_REMDEBUG, "[%s:%d]: Unable to read profile file from %s\n", __FUNCTION__, __LINE__, filename);
return NULL;
}

fseek(fp, 0L, SEEK_END);
long fileSz = ftell(fp);
rewind(fp);

if (fileSz <= 0 || fileSz >= MAX_PROFILE_JSON_SIZE) {
RDK_LOG(RDK_LOG_DEBUG, LOG_REMDEBUG, "[%s:%d]: Invalid file size: %ld\n", __FUNCTION__, __LINE__, fileSz);
fclose(fp);
return NULL;
}

char *jsonBuffer = malloc(fileSz + 1);
if (!jsonBuffer) {
RDK_LOG(RDK_LOG_ERROR, LOG_REMDEBUG, "[%s:%d]: Memory allocation failed for JSON buffer\n", __FUNCTION__, __LINE__);
fclose(fp);
return NULL;
}

size_t bytesRead = fread(jsonBuffer, 1U, (size_t)fileSz, fp);
jsonBuffer[bytesRead] = '\0';
fclose(fp);

Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

read_profile_json_file() does not validate filename or file_size before using them (fopen on nullptr / logging %s with nullptr / dereferencing file_size). Add early validation, ensure *file_size is set to 0 on all failure paths, and consider checking fseek/ftell/fread results (e.g., bytesRead == fileSz) before returning a buffer.

Suggested change
FILE *fp = fopen(filename, "rb");
if (!fp) {
RDK_LOG(RDK_LOG_ERROR, LOG_REMDEBUG, "[%s:%d]: Unable to read profile file from %s\n", __FUNCTION__, __LINE__, filename);
return NULL;
}
fseek(fp, 0L, SEEK_END);
long fileSz = ftell(fp);
rewind(fp);
if (fileSz <= 0 || fileSz >= MAX_PROFILE_JSON_SIZE) {
RDK_LOG(RDK_LOG_DEBUG, LOG_REMDEBUG, "[%s:%d]: Invalid file size: %ld\n", __FUNCTION__, __LINE__, fileSz);
fclose(fp);
return NULL;
}
char *jsonBuffer = malloc(fileSz + 1);
if (!jsonBuffer) {
RDK_LOG(RDK_LOG_ERROR, LOG_REMDEBUG, "[%s:%d]: Memory allocation failed for JSON buffer\n", __FUNCTION__, __LINE__);
fclose(fp);
return NULL;
}
size_t bytesRead = fread(jsonBuffer, 1U, (size_t)fileSz, fp);
jsonBuffer[bytesRead] = '\0';
fclose(fp);
if (!file_size) {
RDK_LOG(RDK_LOG_ERROR, LOG_REMDEBUG, "[%s:%d]: file_size is NULL\n", __FUNCTION__, __LINE__);
return NULL;
}
*file_size = 0;
if (!filename) {
RDK_LOG(RDK_LOG_ERROR, LOG_REMDEBUG, "[%s:%d]: filename is NULL\n", __FUNCTION__, __LINE__);
return NULL;
}
FILE *fp = fopen(filename, "rb");
if (!fp) {
RDK_LOG(RDK_LOG_ERROR, LOG_REMDEBUG, "[%s:%d]: Unable to read profile file from %s\n", __FUNCTION__, __LINE__, filename);
return NULL;
}
if (fseek(fp, 0L, SEEK_END) != 0) {
RDK_LOG(RDK_LOG_ERROR, LOG_REMDEBUG, "[%s:%d]: Failed to seek to end of profile file %s\n", __FUNCTION__, __LINE__, filename);
fclose(fp);
return NULL;
}
long fileSz = ftell(fp);
if (fileSz < 0) {
RDK_LOG(RDK_LOG_ERROR, LOG_REMDEBUG, "[%s:%d]: Failed to get profile file size for %s\n", __FUNCTION__, __LINE__, filename);
fclose(fp);
return NULL;
}
if (fseek(fp, 0L, SEEK_SET) != 0) {
RDK_LOG(RDK_LOG_ERROR, LOG_REMDEBUG, "[%s:%d]: Failed to rewind profile file %s\n", __FUNCTION__, __LINE__, filename);
fclose(fp);
return NULL;
}
if (fileSz <= 0 || fileSz >= MAX_PROFILE_JSON_SIZE) {
RDK_LOG(RDK_LOG_DEBUG, LOG_REMDEBUG, "[%s:%d]: Invalid file size: %ld\n", __FUNCTION__, __LINE__, fileSz);
fclose(fp);
return NULL;
}
char *jsonBuffer = malloc(fileSz + 1);
if (!jsonBuffer) {
RDK_LOG(RDK_LOG_ERROR, LOG_REMDEBUG, "[%s:%d]: Memory allocation failed for JSON buffer\n", __FUNCTION__, __LINE__);
fclose(fp);
return NULL;
}
size_t bytesRead = fread(jsonBuffer, 1U, (size_t)fileSz, fp);
if (bytesRead != (size_t)fileSz) {
RDK_LOG(RDK_LOG_ERROR, LOG_REMDEBUG, "[%s:%d]: Failed to read complete profile file %s\n", __FUNCTION__, __LINE__, filename);
free(jsonBuffer);
fclose(fp);
return NULL;
}
jsonBuffer[fileSz] = '\0';
fclose(fp);

Copilot uses AI. Check for mistakes.
Comment on lines +635 to +642
char* get_all_categories_json(cJSON* json)
{
cJSON *response = cJSON_CreateObject();

cJSON *category = NULL;
cJSON_ArrayForEach(category, json) {
if (cJSON_IsObject(category) && category->string) {
if (has_direct_commands(category)) {
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

get_all_categories_json() assumes json is non-null; calling it with nullptr will crash in cJSON_ArrayForEach. Either return an empty JSON object string for null input or return nullptr consistently and update callers/tests accordingly.

Copilot uses AI. Check for mistakes.
Comment on lines +701 to +712
rbusError_t set_rbus_response(rbusProperty_t prop, const char* json_str)
{
if (!json_str) {
return RBUS_ERROR_BUS_ERROR;
}

rbusValue_t rbusValue;
rbusValue_Init(&rbusValue);
rbusValue_SetString(rbusValue, json_str);
rbusProperty_SetValue(prop, rbusValue);
rbusValue_Release(rbusValue);

Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

set_rbus_response() only checks json_str; if prop is nullptr (as in the unit test), rbusProperty_SetValue(prop, ...) will crash. Add a prop null check and consider propagating/handling failures from rbusValue_Init / rbusValue_SetString before setting the property value.

Copilot uses AI. Check for mistakes.
Comment on lines +539 to +553
rbusError_t rrd_SetHandler(rbusHandle_t handle, rbusProperty_t prop, rbusSetHandlerOptions_t* opts)
{
(void)handle;
(void)opts;

char const* propertyName = rbusProperty_GetName(prop);
rbusValue_t value = rbusProperty_GetValue(prop);
rbusValueType_t type = rbusValue_GetType(value);

RDK_LOG(RDK_LOG_DEBUG, LOG_REMDEBUG, "[%s:%d]: Set handler called for [%s]\n", __FUNCTION__, __LINE__, propertyName);

if(strcmp(propertyName, RRD_SET_PROFILE_EVENT) == 0) {
if (type == RBUS_STRING) {
const char* str = rbusValue_GetString(value, NULL);
if(strlen(str) > 255) {
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rrd_SetHandler() assumes prop, propertyName, value, and the string returned by rbusValue_GetString are non-null; otherwise strcmp/strlen will crash. Add explicit null checks and return RBUS_ERROR_INVALID_INPUT (or similar) when required inputs are missing.

Copilot uses AI. Check for mistakes.
Comment on lines +513 to +518
// Unregister RBUS data elements for profile data provider
ret = rbus_unregDataElements(rrdRbusHandle, 2, profileDataElements);
if (ret != 0) {
RDK_LOG(RDK_LOG_ERROR, LOG_REMDEBUG, "[%s:%d]: RBUS unregDataElements failed with error: %d\n", __FUNCTION__, __LINE__, ret);
} else {
RDK_LOG(RDK_LOG_DEBUG, LOG_REMDEBUG, "[%s:%d]: SUCCESS: RBUS profile data elements unregistered\n", __FUNCTION__, __LINE__);
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In RRD_unsubscribe(), the success check uses ret != 0 while RRD_subscribe() compares against RBUS_ERROR_SUCCESS. For consistency and to avoid relying on numeric values, compare against RBUS_ERROR_SUCCESS here too.

Copilot uses AI. Check for mistakes.
Comment on lines +4060 to +4062
// Function should handle null input gracefully
// Based on implementation, this might crash or return null
// The test documents the current behavior
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test calls get_all_categories_json(nullptr) but does not assert any expected behavior (and the current implementation will crash on null). Once the function is fixed to handle null input, please assert the returned JSON (e.g., empty object) or assert it returns nullptr.

Suggested change
// Function should handle null input gracefully
// Based on implementation, this might crash or return null
// The test documents the current behavior
ASSERT_EQ(result, nullptr);

Copilot uses AI. Check for mistakes.
Comment on lines +4279 to +4288
TEST_F(SetRbusResponseTest, HandlesValidJsonString)
{
// This is difficult to test without full RBUS mock setup
// The function should succeed with valid inputs in a real environment
const char *test_json = R"({"test": "data"})";

// Without full RBUS setup, we can't fully test this
// But we can verify it handles the null case properly
rbusError_t result = set_rbus_response(nullptr, test_json);
// Expected behavior depends on RBUS implementation details
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HandlesValidJsonString calls set_rbus_response(nullptr, test_json) but makes no assertion; with the current implementation this will crash due to a null prop. Either pass a valid mocked rbusProperty_t and assert RBUS_ERROR_SUCCESS, or explicitly assert an error code for nullptr prop after adding input validation.

Suggested change
TEST_F(SetRbusResponseTest, HandlesValidJsonString)
{
// This is difficult to test without full RBUS mock setup
// The function should succeed with valid inputs in a real environment
const char *test_json = R"({"test": "data"})";
// Without full RBUS setup, we can't fully test this
// But we can verify it handles the null case properly
rbusError_t result = set_rbus_response(nullptr, test_json);
// Expected behavior depends on RBUS implementation details
TEST_F(SetRbusResponseTest, HandlesValidJsonStringWithNullProperty)
{
const char *test_json = R"({"test": "data"})";
// The current implementation dereferences the property pointer, so when
// a null RBUS property is passed the call is expected to terminate.
// Make that behavior explicit instead of invoking it without an assertion.
EXPECT_DEATH(
{
(void)set_rbus_response(nullptr, test_json);
},
"");

Copilot uses AI. Check for mistakes.
Comment on lines +3840 to +3853
TEST_F(SaveProfileCategoryTest, SaveToReadOnlyDirectory)
{
// This test checks behavior when file cannot be written
// Create a scenario where the directory might not be writable
// The function should return -1 in error cases

// We can't easily test read-only scenarios in unit tests,
// but we can verify the function handles file creation properly
int result = save_profile_category();

// Should succeed in normal test environment
EXPECT_GE(result, -1); // Either success (0) or expected failure (-1)
}

Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SaveToReadOnlyDirectory doesn’t actually simulate a read-only path and the assertion EXPECT_GE(result, -1) will always pass for most return values, so it won’t catch regressions. Consider removing this test or rewriting it to deterministically force fopen() failure (e.g., by injecting a path or mocking fopen) and assert the exact error return.

Suggested change
TEST_F(SaveProfileCategoryTest, SaveToReadOnlyDirectory)
{
// This test checks behavior when file cannot be written
// Create a scenario where the directory might not be writable
// The function should return -1 in error cases
// We can't easily test read-only scenarios in unit tests,
// but we can verify the function handles file creation properly
int result = save_profile_category();
// Should succeed in normal test environment
EXPECT_GE(result, -1); // Either success (0) or expected failure (-1)
}

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants