Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/L1-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: L1 Unit Tests

on:
pull_request:
branches: [ develop ]
branches: [ develop, support/1.9 ]

env:
AUTOMATICS_UNAME: ${{ secrets.AUTOMATICS_UNAME }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/L2-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: L2 Integration Tests

on:
pull_request:
branches: [ develop ]
branches: [ develop, support/1.9 ]

env:
AUTOMATICS_UNAME: ${{ secrets.AUTOMATICS_UNAME }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/native_full_build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: Build Component in Native Environment

on:
pull_request:
branches: [ develop ]
branches: [ develop, support/1.9 ]
paths: ['**/*.c', '**/*.cpp', '**/*.cc', '**/*.cxx', '**/*.h', '**/*.hpp']

jobs:
Expand Down
192 changes: 192 additions & 0 deletions source/test/xconf-client/xconfclientTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -762,3 +762,195 @@ TEST(STOPXCONFCLIENT, success_check)
{
EXPECT_EQ(T2ERROR_SUCCESS, stopXConfClient());
}

/*
* Tests for the getTimezone() timeZoneDST fallback path.
*
* getTimezone() is a static function exercised indirectly via appendRequestParams().
* The fallback to /opt/persistent/timeZoneDST is reached after the JSON-based
* primary path exhausts its 10 retries without finding a timezone.
*
* Preconditions for each test:
* - All getParameterValue calls succeed (6 calls with dummy values).
* - getBuildType succeeds: fopen(DEVICE_PROPERTIES) returns a fake handle, fscanf
* fills "BUILD_TYPE=PROD" and then returns EOF.
Comment thread
yogeswaransky marked this conversation as resolved.
Comment thread
yogeswaransky marked this conversation as resolved.
* - getTimezone CPU_ARCH read: fopen(DEVICE_PROPERTIES) returns NULL (no CPU_ARCH).
* - getTimezone JSON loop: fopen("/opt/output.json") returns NULL (10 retries fail).
* After these preconditions the timeZoneDST fallback is exercised.
*/
#if !defined(ENABLE_RDKB_SUPPORT) && !defined(ENABLE_RDKC_SUPPORT)

// Helper to set up the mocks needed to reach the timeZoneDST fallback in getTimezone().
// Requires g_fileIOMock and m_xconfclientMock to be set.
static void SetupReachTimeZoneDSTFallback(FILE* devicePropsHandle)
{
using namespace testing;

// All 6 getParameterValue calls succeed with dummy values.
EXPECT_CALL(*m_xconfclientMock, getParameterValue(_, _))
.Times(AtLeast(6))
.WillRepeatedly(Invoke([](const char*, char** val) -> T2ERROR {
*val = strdup("dummy");
return T2ERROR_SUCCESS;
}));

// getBuildType: fopen(DEVICE_PROPERTIES) -> devicePropsHandle, fscanf fills
// "BUILD_TYPE=PROD" and breaks (no EOF call), fclose returns 0.
EXPECT_CALL(*g_fileIOMock, fopen(StrEq("/etc/device.properties"), _))
.WillOnce(Return(devicePropsHandle)) // getBuildType
.WillOnce(Return(nullptr)); // getTimezone CPU_ARCH read

// getBuildType breaks out of its fscanf loop as soon as it finds "BUILD_TYPE",
// so fscanf is called exactly once.
EXPECT_CALL(*g_fileIOMock, fscanf(devicePropsHandle, _, _))
.WillOnce(Invoke([](FILE*, const char*, va_list args) -> int {
char* buf = va_arg(args, char*);
strncpy(buf, "BUILD_TYPE=PROD", 254);
buf[254] = '\0';
return 1;
}));

EXPECT_CALL(*g_fileIOMock, fclose(devicePropsHandle))
.WillOnce(Return(0));

// getTimezone JSON loop: fopen("/opt/output.json") returns NULL all 10 retries.
EXPECT_CALL(*g_fileIOMock, fopen(StrEq("/opt/output.json"), _))
.Times(10)
.WillRepeatedly(Return(nullptr));
}

// Test 1: fopen("/opt/persistent/timeZoneDST") returns NULL.
// The fallback cannot open the file; timezone stays NULL; appendRequestParams returns FAILURE.
TEST_F(xconfclientTestFixture, getTimezone_timeZoneDST_fopen_null)
{
FILE* devicePropsHandle = reinterpret_cast<FILE*>(0x1234);
PREVENT_GTEST_LOGGING_DEADLOCK();
SetupReachTimeZoneDSTFallback(devicePropsHandle);

EXPECT_CALL(*g_fileIOMock, fopen(StrEq("/opt/persistent/timeZoneDST"), _))
.WillOnce(Return(nullptr));

CURLU* requestURL = curl_url();
curl_url_set(requestURL, CURLUPART_URL,
"https://mockxconf:50050/loguploader/getT2DCMSettings", 0);
EXPECT_EQ(T2ERROR_FAILURE, appendRequestParams(requestURL));
curl_free(requestURL);
requestURL = NULL;
Comment thread
yogeswaransky marked this conversation as resolved.
Comment thread
yogeswaransky marked this conversation as resolved.
}

// Test 2: ftell returns -1 (unreadable/error).
// The fallback opens the file but ftell fails; file is closed; timezone stays NULL.
TEST_F(xconfclientTestFixture, getTimezone_timeZoneDST_ftell_negative)
{
FILE* devicePropsHandle = reinterpret_cast<FILE*>(0x1234);
FILE* tzFile = reinterpret_cast<FILE*>(0x5678);
PREVENT_GTEST_LOGGING_DEADLOCK();
SetupReachTimeZoneDSTFallback(devicePropsHandle);

EXPECT_CALL(*g_fileIOMock, fopen(StrEq("/opt/persistent/timeZoneDST"), _))
.WillOnce(Return(tzFile));
EXPECT_CALL(*g_fileIOMock, fseek(tzFile, 0, SEEK_END))
.WillOnce(Return(0));
EXPECT_CALL(*g_fileIOMock, ftell(tzFile))
.WillOnce(Return(-1L));
EXPECT_CALL(*g_fileIOMock, fclose(tzFile))
.WillOnce(Return(0));

CURLU* requestURL = curl_url();
curl_url_set(requestURL, CURLUPART_URL,
"https://mockxconf:50050/loguploader/getT2DCMSettings", 0);
EXPECT_EQ(T2ERROR_FAILURE, appendRequestParams(requestURL));
curl_free(requestURL);
requestURL = NULL;
Comment thread
yogeswaransky marked this conversation as resolved.
Comment thread
yogeswaransky marked this conversation as resolved.
}

// Test 3: ftell returns 0 (empty file).
// The fallback opens the file but it is empty; file is closed; timezone stays NULL.
TEST_F(xconfclientTestFixture, getTimezone_timeZoneDST_empty_file)
{
FILE* devicePropsHandle = reinterpret_cast<FILE*>(0x1234);
FILE* tzFile = reinterpret_cast<FILE*>(0x5678);
PREVENT_GTEST_LOGGING_DEADLOCK();
SetupReachTimeZoneDSTFallback(devicePropsHandle);

EXPECT_CALL(*g_fileIOMock, fopen(StrEq("/opt/persistent/timeZoneDST"), _))
.WillOnce(Return(tzFile));
EXPECT_CALL(*g_fileIOMock, fseek(tzFile, 0, SEEK_END))
.WillOnce(Return(0));
EXPECT_CALL(*g_fileIOMock, ftell(tzFile))
.WillOnce(Return(0L));
EXPECT_CALL(*g_fileIOMock, fclose(tzFile))
.WillOnce(Return(0));

CURLU* requestURL = curl_url();
curl_url_set(requestURL, CURLUPART_URL,
"https://mockxconf:50050/loguploader/getT2DCMSettings", 0);
EXPECT_EQ(T2ERROR_FAILURE, appendRequestParams(requestURL));
curl_free(requestURL);
requestURL = NULL;
Comment thread
yogeswaransky marked this conversation as resolved.
Comment thread
yogeswaransky marked this conversation as resolved.
}

// Test 4: ftell returns a value exceeding the 256-byte limit.
// The fallback rejects oversized files; file is closed; timezone stays NULL.
TEST_F(xconfclientTestFixture, getTimezone_timeZoneDST_file_too_large)
{
FILE* devicePropsHandle = reinterpret_cast<FILE*>(0x1234);
FILE* tzFile = reinterpret_cast<FILE*>(0x5678);
PREVENT_GTEST_LOGGING_DEADLOCK();
SetupReachTimeZoneDSTFallback(devicePropsHandle);

EXPECT_CALL(*g_fileIOMock, fopen(StrEq("/opt/persistent/timeZoneDST"), _))
.WillOnce(Return(tzFile));
EXPECT_CALL(*g_fileIOMock, fseek(tzFile, 0, SEEK_END))
.WillOnce(Return(0));
EXPECT_CALL(*g_fileIOMock, ftell(tzFile))
.WillOnce(Return(512L));
EXPECT_CALL(*g_fileIOMock, fclose(tzFile))
.WillOnce(Return(0));

CURLU* requestURL = curl_url();
curl_url_set(requestURL, CURLUPART_URL,
"https://mockxconf:50050/loguploader/getT2DCMSettings", 0);
EXPECT_EQ(T2ERROR_FAILURE, appendRequestParams(requestURL));
curl_free(requestURL);
requestURL = NULL;
Comment thread
yogeswaransky marked this conversation as resolved.
Comment thread
yogeswaransky marked this conversation as resolved.
}

// Test 5: Valid timezone string read from /opt/persistent/timeZoneDST.
// The fallback reads a valid timezone; appendRequestParams returns SUCCESS.
TEST_F(xconfclientTestFixture, getTimezone_timeZoneDST_valid_timezone)
{
FILE* devicePropsHandle = reinterpret_cast<FILE*>(0x1234);
FILE* tzFile = reinterpret_cast<FILE*>(0x5678);
PREVENT_GTEST_LOGGING_DEADLOCK();
SetupReachTimeZoneDSTFallback(devicePropsHandle);

EXPECT_CALL(*g_fileIOMock, fopen(StrEq("/opt/persistent/timeZoneDST"), _))
.WillOnce(Return(tzFile));
EXPECT_CALL(*g_fileIOMock, fseek(tzFile, 0, SEEK_END))
.WillOnce(Return(0));
EXPECT_CALL(*g_fileIOMock, ftell(tzFile))
.WillOnce(Return(10L));
EXPECT_CALL(*g_fileIOMock, fseek(tzFile, 0, SEEK_SET))
.WillOnce(Return(0));
// fscanf fills the zone buffer with "US/Eastern" and then signals end-of-file.
EXPECT_CALL(*g_fileIOMock, fscanf(tzFile, _, _))
.WillOnce(::testing::Invoke([](FILE*, const char*, va_list args) -> int {
char* buf = va_arg(args, char*);
strncpy(buf, "US/Eastern", 10);
buf[10] = '\0';
return 1;
}))
.WillOnce(Return(0));
EXPECT_CALL(*g_fileIOMock, fclose(tzFile))
Comment thread
yogeswaransky marked this conversation as resolved.
.WillOnce(Return(0));

CURLU* requestURL = curl_url();
curl_url_set(requestURL, CURLUPART_URL,
"https://mockxconf:50050/loguploader/getT2DCMSettings", 0);
EXPECT_EQ(T2ERROR_SUCCESS, appendRequestParams(requestURL));
curl_free(requestURL);
requestURL = NULL;
Comment thread
yogeswaransky marked this conversation as resolved.
}
Comment thread
yogeswaransky marked this conversation as resolved.

#endif /* !defined(ENABLE_RDKB_SUPPORT) && !defined(ENABLE_RDKC_SUPPORT) */
51 changes: 34 additions & 17 deletions source/xconf-client/xconfclient.c
Original file line number Diff line number Diff line change
Expand Up @@ -307,30 +307,47 @@ static char *getTimezone ()
{
fseek(file, 0, SEEK_END);
long numbytes = ftell(file);
char *zone = (char*)malloc(sizeof(char) * (numbytes + 1));
fseek(file, 0, SEEK_SET);

char fmt[32];
snprintf(fmt, sizeof(fmt), "%%%lds", numbytes); //using numbytes as the length for reading the file

while (fscanf(file, fmt, zone) != EOF)
if (numbytes < 0)
{
if(zoneValue)
{
free(zoneValue);
}
if (zone != NULL && strlen(zone) > 0)
T2Warning("timeZoneDST file is unreadable (ftell returned %ld)\n", numbytes);
fclose(file);
}
else if (numbytes == 0 || numbytes > 256)
{
T2Warning("Warning: timeZoneDST file has unexpected size %ld, skipping\n", numbytes);
fclose(file);
}
else
{
char *zone = (char*)malloc(sizeof(char) * (numbytes + 1));
if (zone == NULL)
{
zoneValue = strdup(zone);
T2Error("Failed to allocate %ld bytes for timezone\n", numbytes + 1);
fclose(file);
}
else
{
zoneValue = NULL;
T2Warning("Warning: zone is NULL or empty, skipping\n");
zone[0] = '\0';
fseek(file, 0, SEEK_SET);

char fmt[32];
snprintf(fmt, sizeof(fmt), "%%%lds", numbytes);

while (fscanf(file, fmt, zone) == 1)
{
if (strlen(zone) > 0)
{
if(zoneValue)
{
free(zoneValue);
}
zoneValue = strdup(zone);
}
}
fclose(file);
free(zone);
}
}
fclose(file);
free(zone);
}

}
Expand Down
Loading