diff --git a/.github/workflows/L1-Test.yml b/.github/workflows/L1-Test.yml new file mode 100644 index 00000000..d6fb82ba --- /dev/null +++ b/.github/workflows/L1-Test.yml @@ -0,0 +1,28 @@ +name: Unit tests dcm-agent +on: + pull_request: + branches: [ develop, main ] + +env: + AUTOMATICS_UNAME: ${{ secrets.AUTOMATICS_UNAME }} + AUTOMATICS_PASSCODE: ${{ secrets.AUTOMATICS_PASSCODE }} + +jobs: + execute-unit-tests-on-pr: + name: Execute unit tests in dcm-agent GTest suite + runs-on: ubuntu-latest + container: + image: ghcr.io/rdkcentral/docker-rdk-ci:latest + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Run unit tests + run: sh unit_test.sh + + - name: Upload test results to automatic test result management system + if: github.repository_owner == 'rdkcentral' + run: | + git config --global --add safe.directory `pwd` + gtest-json-result-push.py /tmp/Gtest_Report https://rdkeorchestrationservice.apps.cloud.comcast.net/rdke_orchestration_api/push_unit_test_results `pwd` diff --git a/.github/workflows/code-coverage.yml b/.github/workflows/code-coverage.yml new file mode 100644 index 00000000..e6345626 --- /dev/null +++ b/.github/workflows/code-coverage.yml @@ -0,0 +1,54 @@ +name: Code Coverage + +on: + pull_request: + branches: [ main, develop] + +jobs: + execute-unit-code-coverage-report-on-release: + name: Test coverage report for release + runs-on: ubuntu-latest + container: + image: ghcr.io/rdkcentral/docker-rdk-ci:latest + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Run unit tests with coverage flags enabled + run: | + sh unit_test.sh --enable-cov + - name: Caculate the code coverage summary + run: | + cd ./unittest + lcov --list coverage.info | grep "Lines\|Total" > /tmp/coverage_summary.txt + cd - + + - name: Update the coverage report to Pull request using actions + uses: actions/github-script@v4 + with: + script: | + const fs = require('fs'); + const lcov_result = fs.readFileSync('/tmp/coverage_summary.txt', 'utf8'); + + github.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: + '## Code Coverage Summary \n' + + ' ' + + '```' + + lcov_result + + '```' + }); + - name: Generate the html report + run: | + cd ./unittest + genhtml coverage.info --output-directory /tmp/coverage_report + cd - + - name: Upload the coverage report to Pull request using actions + uses: actions/upload-artifact@v4 + with: + name: coverage-report + path: /tmp/coverage_report diff --git a/cov_build.sh b/cov_build.sh index 6981a1e1..3983e4f1 100644 --- a/cov_build.sh +++ b/cov_build.sh @@ -52,5 +52,5 @@ sh build_inside_container.sh cd $WORKDIR -./configure --prefix=${INSTALL_DIR} CFLAGS="-DRDK_LOGGER -DHAS_MAINTENANCE_MANAGER -I$ROOT/iarmmgrs/maintenance/include" +./configure --prefix=${INSTALL_DIR} CFLAGS="-DRDK_LOGGER -DHAS_MAINTENANCE_MANAGER -I$ROOT/iarmmgrs/maintenance/include -I/usr/local/include" make && make install diff --git a/dcm.c b/dcm.c index 5be93a4f..66ad8bc8 100644 --- a/dcm.c +++ b/dcm.c @@ -239,6 +239,7 @@ VOID dcmDaemonMainUnInit(DCMDHandle *pdcmHandle) * @return status. * @retval status. */ +#ifndef GTEST_ENABLE int main(int argc, char* argv[]) { pid_t process_id = 0; @@ -386,3 +387,17 @@ int main(int argc, char* argv[]) } return ret; } +#endif + +#ifdef GTEST_ENABLE +void get_dcmRunJobs(const INT8* profileName, VOID *pHandle) +{ + dcmRunJobs(profileName, pHandle); +} +void get_sig_handler(INT32 sig) +{ + sig_handler(sig); +} +#endif + + diff --git a/dcm_cronparse.c b/dcm_cronparse.c index 077bcc2e..cad7fe19 100644 --- a/dcm_cronparse.c +++ b/dcm_cronparse.c @@ -938,3 +938,30 @@ INT32 dcmCronParseExp(const INT8* expression, dcmCronExpr* target) return ret; } +#ifdef GTEST_ENABLE +INT32 (*getdcmCronParseToUpper(void)) (INT8*) +{ + return &dcmCronParseToUpper; +} +UINT32 (*getdcmCronParseParseUint(void)) (const INT8*, INT32*) +{ + return &dcmCronParseParseUint; +} +UINT32 (*getdcmCronParseNextSetBit(void)) (UINT8*, UINT32, UINT32, INT32*) +{ + return &dcmCronParseNextSetBit; +} + +INT32 (*getdcmCronParseResetMin(void)) (struct tm*, INT32) +{ + return &dcmCronParseResetMin; +} +INT32 (*getdcmCronParseResetAllMin(void))(struct tm*, INT32*) +{ + return &dcmCronParseResetAllMin; +} +INT32 (*getdcmCronParseSetField(void))(struct tm*, INT32, INT32) +{ + return &dcmCronParseSetField; +} +#endif diff --git a/dcm_cronparse.h b/dcm_cronparse.h index 8711a0d9..78003312 100644 --- a/dcm_cronparse.h +++ b/dcm_cronparse.h @@ -21,6 +21,11 @@ #ifndef _DCM_CRONPARSE_H_ #define _DCM_CRONPARSE_H_ +#include "dcm_types.h" +#ifdef __cplusplus +extern "C" +{ +#endif /** * Parsed cron expression @@ -38,6 +43,9 @@ INT32 dcmCronParseExp(const INT8* expression, dcmCronExpr* target); time_t dcmCronParseGetNext(dcmCronExpr* expr, time_t date); +#ifdef __cplusplus +} +#endif #endif //_DCM_CRONPARSE_H_ diff --git a/dcm_parseconf.c b/dcm_parseconf.c index d45490b0..ddabc473 100644 --- a/dcm_parseconf.c +++ b/dcm_parseconf.c @@ -245,6 +245,7 @@ static INT32 dcmSettingStoreTempConf(INT8 *pConffile, INT8 *pTempConf, INT8 *pOp INT8 *buff = NULL; INT32 i = 0; INT32 ret = DCM_SUCCESS; + FILE *fp_out_opt = NULL; FILE *fp_in = fopen(pConffile, "r"); if (fp_in == NULL) { @@ -259,7 +260,7 @@ static INT32 dcmSettingStoreTempConf(INT8 *pConffile, INT8 *pTempConf, INT8 *pOp goto exit1; } - FILE *fp_out_opt = fopen(pOptConf, "w"); + fp_out_opt = fopen(pOptConf, "w"); if (fp_out == NULL) { ret = DCM_FAILURE; DCMError("Unable to open out file: %s\n", pOptConf); @@ -722,4 +723,5 @@ VOID dcmSettingsUnInit(VOID *pdcmSetHandle) } return dcmSettingStoreTempConf(defaultConfig, DCM_TMP_CONF, DCM_OPT_CONF); } - \ No newline at end of file + + diff --git a/dcm_rbus.c b/dcm_rbus.c index 8c3a4299..5822034d 100644 --- a/dcm_rbus.c +++ b/dcm_rbus.c @@ -30,8 +30,9 @@ #include #include #include - +#ifndef GTEST_ENABLE #include "rbus.h" +#endif #include "dcm_types.h" #include "dcm_rbus.h" #include "dcm_utils.h" @@ -555,3 +556,25 @@ VOID dcmRbusUnInit (VOID *pDCMRbusHandle) free(plDCMRbusHandle); } + +#ifdef GTEST_ENABLE +VOID get_rbusProcConf(rbusHandle_t handle, rbusEvent_t const* event, rbusEventSubscription_t* subscription) +{ + rbusProcConf(handle, event, subscription); +} + +void get_rbusAsyncSubCB(rbusHandle_t handle, rbusEventSubscription_t* subscription, rbusError_t error) +{ + rbusAsyncSubCB(handle, subscription, error); +} + +VOID get_rbusSetConf(rbusHandle_t handle, rbusEvent_t const* event, rbusEventSubscription_t* subscription) +{ + rbusSetConf(handle, event, subscription); +} +rbusError_t get_rbusSendEventCB(rbusHandle_t handle, rbusEventSubAction_t action, const INT8* eventName, rbusFilter_t filter, int32_t interval, BOOL* autoPublish) +{ + return rbusSendEventCB( handle, action, eventName, filter, interval, autoPublish); +} +#endif + diff --git a/dcm_utils.h b/dcm_utils.h index 99e1edec..e59ed29d 100644 --- a/dcm_utils.h +++ b/dcm_utils.h @@ -23,11 +23,15 @@ #define _DCM_UTILS_H_ #ifdef HAS_MAINTENANCE_MANAGER +#ifndef GTEST_ENABLE #include "libIBus.h" #include "maintenanceMGR.h" #endif +#endif +#ifndef GTEST_ENABLE #include "rbus.h" +#endif #ifdef RDK_LOGGER_ENABLED #include "rdk_debug.h" @@ -120,3 +124,5 @@ void DCMLOGInit(); #endif //_DCM_UTILS_H + + diff --git a/unit_test.sh b/unit_test.sh new file mode 100644 index 00000000..ac91022a --- /dev/null +++ b/unit_test.sh @@ -0,0 +1,103 @@ +#!/bin/bash + +# +## Copyright 2023 Comcast Cable Communications Management, LLC +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## +## SPDX-License-Identifier: Apache-2.0 +# + +ENABLE_COV=true + +if [ "x$1" = "x--enable-cov" ]; then + echo "Enabling coverage options" + export CXXFLAGS="-g -O0 -fprofile-arcs -ftest-coverage" + export CFLAGS="-g -O0 -fprofile-arcs -ftest-coverage" + export LDFLAGS="-lgcov --coverage" + ENABLE_COV=true +fi +export TOP_DIR=`pwd` +export top_srcdir=`pwd` + +cd unittest/ +cp mocks/mockrbus.h /usr/local/include +automake --add-missing +autoreconf --install + +./configure + +make clean +make + +fail=0 + +for test in \ + ./dcm_utils_gtest \ + ./dcm_schedjob_gtest \ + ./dcm_cronparse_gtest \ + ./dcm_parseconf_gtest \ + ./dcm_rbus_gtest \ + ./dcm_gtest + #./rdm_main_gtest \ + #./rdm_utils_gtest \ + #./rdm_curl_gtest \ + #./rdm_json_gtest \ + #./rdm_download_gtest \ + #./rdm_downloadutils_gtest \ + #./rdm_rbus_gtest \ + #./rdm_openssl_gtest \ + #./rdm_usbinstall_gtest \ + +do + $test + status=$? + if [ $status -ne 0 ]; then + echo "Test $test failed with exit code $status" + fail=1 + fi +done + +if [ $fail -ne 0 ]; then + echo "Some unit tests failed." + exit 1 +fi + +echo "********************" +echo "**** CAPTURE DCM-AGENT COVERAGE DATA ****" +echo "********************" +if [ "$ENABLE_COV" = true ]; then + echo "Generating coverage report" + lcov --capture --directory . --output-file coverage.info + lcov --extract coverage.info "*/dcm*.c" -o newcov.info + lcov --list newcov.info + lcov --remove coverage.info '/usr/*' --output-file coverage.info + lcov --list coverage.info + lcov --remove coverage.info "${PWD}/*" --output-file coverage.info + lcov --list coverage.info +fi + +#if [ "$ENABLE_COV" = true ]; then +# echo "Generating coverage report" +# lcov --capture --directory . --output-file coverage.info + #lcov --remove coverage.info "${PWD}/*" --output-file coverage.info + #lcov --remove coverage.info "$HOME/usr/*" --output-file coverage.info + #lcov --remove coverage.info "/usr/*" --output-file coverage.info +# lcov --extract coverage.info "*/dcm*.c" -o newcov.info +# lcov --list coverage.info +# lcov --list newcov.info +#fi + + + +cd $TOP_DIR diff --git a/unittest/Makefile.am b/unittest/Makefile.am new file mode 100644 index 00000000..cfa292fb --- /dev/null +++ b/unittest/Makefile.am @@ -0,0 +1,48 @@ +# +## Copyright 2023 Comcast Cable Communications Management, LLC +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## +## SPDX-License-Identifier: Apache-2.0 +# + +AUTOMAKE_OPTIONS = subdir-objects +ACLOCAL_AMFLAGS = -I m4 + +# Define the test executables +bin_PROGRAMS = dcm_main_gtest + +# Common include directories +COMMON_CPPFLAGS = -I/usr/include/cjson -I../ -I../../ -I/usr/include -I../include -I./mocks\ + -I/usr/include/gtest -I/usr/local/include -I/usr/local/include/gtest -DGTEST_ENABLE -DHAS_MAINTENANCE_MANAGER + +AM_CPPFLAGS = -I$(top_srcdir)/unittest/mocks -I$(top_srcdir)/include -I$(top_srcdir)/mocks -I$(top_srcdir) -I/usr/include +AM_CXXFLAGS = -std=c++14 + +# Common libraries +COMMON_LDADD = -lgtest -lgmock -lpthread -lcurl -lcjson -lssl -lcrypto -lgcov + +# Common compiler flags +COMMON_CXXFLAGS = -fprofile-arcs -ftest-coverage -fpermissive -Wno-write-strings -Wno-unused-result + +# Define source files for each test + +dcm_main_gtest_SOURCES = dcm_utils_gtest.cpp ../dcm_utils.c dcm_schedjob_gtest.cpp ../dcm_schedjob.c dcm_cronparse_gtest.cpp ../dcm_cronparse.c dcm_parseconf_gtest.cpp ../dcm_parseconf.c dcm_rbus_gtest.cpp ../dcm_rbus.c ./mocks/mockrbus.cpp dcm_gtest.cpp ./dcm.c +dcm_main_gtest_CPPFLAGS = $(COMMON_CPPFLAGS) +dcm_main_gtest_LDADD = $(COMMON_LDADD) +dcm_main_gtest_CXXFLAGS = $(COMMON_CXXFLAGS) +dcm_main_gtest_CFLAGS = $(COMMON_CXXFLAGS) + + + + diff --git a/unittest/configure.ac b/unittest/configure.ac new file mode 100644 index 00000000..baaf235c --- /dev/null +++ b/unittest/configure.ac @@ -0,0 +1,50 @@ +# +## Copyright 2023 Comcast Cable Communications Management, LLC +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## +## SPDX-License-Identifier: Apache-2.0 +# + +# Initialize Autoconf +AC_INIT([dcm_main_gtest], [1.0]) +AC_CONFIG_MACRO_DIRS([m4]) +LT_INIT + +# Initialize Automake +AM_INIT_AUTOMAKE([-Wall -Werror foreign]) + +# Check for necessary headers +AC_CHECK_HEADERS([gtest/gtest.h gmock/gmock.h]) + +# Checks for programs +AC_PROG_CXX + +# Checks for libraries +AC_CHECK_LIB([stdc++], [main]) + +# Checks for header files +AC_INCLUDES_DEFAULT + +# Checks for typedefs, structures, and compiler characteristics +AC_C_CONST + +# Checks for library functions +AC_FUNC_MALLOC +AC_FUNC_REALLOC + +# Generate the Makefile +AC_CONFIG_FILES([Makefile]) + +# Generate the configure script +AC_OUTPUT diff --git a/unittest/dcm_cronparse_gtest.cpp b/unittest/dcm_cronparse_gtest.cpp new file mode 100644 index 00000000..6894eaba --- /dev/null +++ b/unittest/dcm_cronparse_gtest.cpp @@ -0,0 +1,632 @@ +/** + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include + + +//extern "C" { +#include "dcm_cronparse.h" +#include "../dcm_types.h" + +INT32 (*getdcmCronParseToUpper(void)) (INT8*); +UINT32 (*getdcmCronParseParseUint(void)) (const INT8*, INT32*); +UINT32 (*getdcmCronParseNextSetBit(void)) (UINT8*, UINT32, UINT32, INT32*); +INT32 (*getdcmCronParseResetMin(void)) (struct tm*, INT32); +INT32 (*getdcmCronParseResetAllMin(void)) (struct tm*, INT32*); +INT32 (*getdcmCronParseSetField(void))(struct tm*, INT32, INT32); +//} +#include "dcm_cronparse.h" +/*#include "rdm_types.h" +#include "rdm.h" +#include "rdm_utils.h" +*/ +/* + #include "mocks/mock_curl.h" +extern "C" { + #include "rdm_curldownload.h" +} +*/ + +#define GTEST_DEFAULT_RESULT_FILEPATH "/tmp/Gtest_Report/" +#define GTEST_DEFAULT_RESULT_FILENAME "dcm_cronparse_gtest_report.json" +#define GTEST_REPORT_FILEPATH_SIZE 256 + + +using namespace testing; +using namespace std; +using ::testing::_; +using ::testing::Return; +using ::testing::SetArgPointee; +using ::testing::DoAll; +using ::testing::StrEq; + +//extern static INT32 dcmCronParseToUpper(INT8* str); +class dcmCronParseTest : public ::testing::Test { +protected: + void SetUp(){ + } + + void TearDown(){ + } +}; + +TEST(dcmCronParseTest , NullPointerReturnsError) { + auto myFunctionPtr = getdcmCronParseToUpper(); + INT8* str = NULL; + + INT32 result = myFunctionPtr(str); + EXPECT_EQ(result, 1); +} + +TEST(dcmCronParseTest , EmptyStringNoChange) { + auto myFunctionPtr = getdcmCronParseToUpper(); + INT8 input[] = ""; + + INT32 result = myFunctionPtr(input); + EXPECT_EQ(result, 0); + EXPECT_STREQ(input, ""); +} + +TEST(dcmCronParseTest , AllLowercase) { + auto myFunctionPtr = getdcmCronParseToUpper(); + INT8 input[] = "abcdef"; + + INT32 result = myFunctionPtr(input); // Indirectly calls performRequest + EXPECT_EQ(result, 0); + EXPECT_STREQ(input, "ABCDEF"); +} + +TEST(dcmCronParseTest , MixedCase) { + auto myFunctionPtr = getdcmCronParseToUpper(); + INT8 input[] = "aBcDeF"; + + INT32 result = myFunctionPtr(input); // Indirectly calls performRequest + EXPECT_EQ(result, 0); + EXPECT_STREQ(input, "ABCDEF"); +} + +TEST(dcmCronParseTest , AlreadyUppercase) { + auto myFunctionPtr = getdcmCronParseToUpper(); + INT8 input[] = "ABCDEF"; + INT32 result = myFunctionPtr(input); // Indirectly calls performRequest + EXPECT_EQ(result, 0); + EXPECT_STREQ(input, "ABCDEF"); +} + +TEST(dcmCronParseTest , StringWithDigitsAndSymbols) { + INT8 input[] = "abc123!@#XYZ"; + auto myFunctionPtr = getdcmCronParseToUpper(); + INT32 result = myFunctionPtr(input); + EXPECT_EQ(result, 0); + EXPECT_STREQ(input, "ABC123!@#XYZ"); +} + +TEST(dcmCronParseTest , StringWithSpaces) { + auto myFunctionPtr = getdcmCronParseToUpper(); + INT8 input[] = "a b c D E F"; + INT32 result = myFunctionPtr(input); + EXPECT_EQ(result, 0); + EXPECT_STREQ(input, "A B C D E F"); +} + +/* +TEST(dcmCronParseToUpperTest, UnicodeCharactersUnaffected) { + // Depending on locale, toupper may not handle Unicode. Here, just check ASCII is uppercased and others remain. + INT8 input[] = "abc\xC3\xA9\xC3\xB1"; // "abcéñ" in UTF-8 (may not be handled well, but for demonstration) + EXPECT_EQ(dcmCronParseToUpper(input), 0); + // Only 'a', 'b', 'c' should be uppercased; rest should remain. + EXPECT_TRUE(strncmp(input, "ABC", 3) == 0); +} +*/ + + +TEST(dcmCronParseTest , ValidNumber) { + INT32 errcode; + auto myFunctionPtr = getdcmCronParseParseUint(); + UINT32 result = myFunctionPtr("12345", &errcode); + EXPECT_EQ(result, 12345u); + EXPECT_EQ(errcode, 0); +} + +TEST(dcmCronParseTest , ZeroValue) { + INT32 errcode; + auto myFunctionPtr = getdcmCronParseParseUint(); + UINT32 result = myFunctionPtr("0", &errcode); + EXPECT_EQ(result, 0u); + EXPECT_EQ(errcode, 0); +} + +TEST(dcmCronParseTest , NegativeNumber) { + INT32 errcode; + auto myFunctionPtr = getdcmCronParseParseUint(); + UINT32 result = myFunctionPtr("-123", &errcode); + EXPECT_EQ(result, 0u); + EXPECT_EQ(errcode, 1); +} + +TEST(dcmCronParseTest , NonNumericString) { + INT32 errcode; + auto myFunctionPtr = getdcmCronParseParseUint(); + UINT32 result = myFunctionPtr("abc", &errcode); + EXPECT_EQ(result, 0u); + EXPECT_EQ(errcode, 1); +} + +TEST(dcmCronParseTest , MixedAlphaNumeric) { + INT32 errcode; + auto myFunctionPtr = getdcmCronParseParseUint(); + UINT32 result = myFunctionPtr("123abc", &errcode); + EXPECT_EQ(result, 0u); + EXPECT_EQ(errcode, 1); +} + +TEST(dcmCronParseTest , OverflowValue) { + INT32 errcode; + char bigNum[32]; + auto myFunctionPtr = getdcmCronParseParseUint(); + snprintf(bigNum, sizeof(bigNum), "%lld", (long long)INT_MAX + 1); + UINT32 result = myFunctionPtr(bigNum, &errcode); + EXPECT_EQ(result, 0u); + EXPECT_EQ(errcode, 1); +} + +TEST(dcmCronParseTest , MaxIntValue) { + INT32 errcode; + char maxIntStr[32]; + auto myFunctionPtr = getdcmCronParseParseUint(); + snprintf(maxIntStr, sizeof(maxIntStr), "%d", INT_MAX); + UINT32 result = myFunctionPtr(maxIntStr, &errcode); + EXPECT_EQ(result, static_cast(INT_MAX)); + EXPECT_EQ(errcode, 0); +} + +TEST(dcmCronParseTest, ParseValidExpression) { + dcmCronExpr expr = {}; + const INT8* cron = "* * * * * *"; // Example: every second + INT32 result = dcmCronParseExp(cron, &expr); + EXPECT_EQ(result, 0); // Adjust expected value based on implementation +} + +TEST(dcmCronParseTest, dcmCronParseExpInvalidTarget) { + dcmCronExpr expr = {}; + const INT8* cron = "* * * * * *"; + INT32 result = dcmCronParseExp(cron, NULL); + EXPECT_EQ(result, -1); // Should fail +} + +TEST(dcmCronParseTest, dcmCronParseExpInvalidExpression) { + //dcmCronExpr expr = NULL; + dcmCronExpr* expr = NULL; + const INT8* cron = NULL; + INT32 result = dcmCronParseExp(cron, expr); + EXPECT_EQ(result, -1); // Should fail +} +TEST(dcmCronParseTest, GetNextTime) { + dcmCronExpr expr = {}; + const INT8* cron = "* * * * * *"; + dcmCronParseExp(cron, &expr); + time_t now = time(nullptr); + time_t next = dcmCronParseGetNext(&expr, now); + EXPECT_GT(next, now); // Next time should be in the future +} + + +// Test with NULL bits parameter +TEST(dcmCronParseTest, NextSetBit_NullBits_ReturnsNotFound) { + INT32 notfound = 0; + auto myFunctionPtr = getdcmCronParseNextSetBit(); + UINT32 result = myFunctionPtr(NULL, 64, 0, ¬found); + + EXPECT_EQ(result, 0); + EXPECT_EQ(notfound, 1); +} + +TEST(dcmCronParseTest, GetNext_NullExpression_ReturnsInvalidInstant) { + time_t date = time(NULL); + + time_t result = dcmCronParseGetNext(NULL, date); + + EXPECT_EQ(result, -1); +} + +class DcmCronParseResetMinTest : public ::testing::Test { +protected: + void SetUp() override { + // Initialize test calendar with specific values + memset(&testCalendar, 0, sizeof(struct tm)); + + // Set to a specific date: 2024-03-15 14:30:45 (Friday) + testCalendar.tm_year = 2024 - 1900; // tm_year is years since 1900 + testCalendar.tm_mon = 2; // March (0-based) + testCalendar.tm_mday = 15; // 15th day + testCalendar.tm_hour = 14; // 2 PM + testCalendar.tm_min = 30; // 30 minutes + testCalendar.tm_sec = 45; // 45 seconds + testCalendar.tm_wday = 5; // Friday (0=Sunday) + testCalendar.tm_yday = 74; // Day of year + testCalendar.tm_isdst = 0; // No DST + + // Store original values for comparison + originalCalendar = testCalendar; + } + + void TearDown() override { + } + + struct tm testCalendar; + struct tm originalCalendar; +}; + + +TEST_F(DcmCronParseResetMinTest, ResetMin_SecondField_ResetsToZero) { + // Verify initial state + EXPECT_EQ(testCalendar.tm_sec, 45); + auto myFunctionPtr = getdcmCronParseResetMin(); + // Reset seconds field + INT32 result = myFunctionPtr(&testCalendar, CRON_CF_SECOND); + + // Verify success + EXPECT_EQ(result, 0); + + // Verify seconds field is reset to 0 + EXPECT_EQ(testCalendar.tm_sec, 0); + + // Verify other fields remain unchanged + EXPECT_EQ(testCalendar.tm_min, originalCalendar.tm_min); + EXPECT_EQ(testCalendar.tm_hour, originalCalendar.tm_hour); + EXPECT_EQ(testCalendar.tm_mday, originalCalendar.tm_mday); + EXPECT_EQ(testCalendar.tm_mon, originalCalendar.tm_mon); + EXPECT_EQ(testCalendar.tm_year, originalCalendar.tm_year); +} + +TEST_F(DcmCronParseResetMinTest, ResetMin_MinuteField_ResetsToZero) { + // Verify initial state + EXPECT_EQ(testCalendar.tm_min, 30); + auto myFunctionPtr = getdcmCronParseResetMin(); + // Reset minutes field + INT32 result = myFunctionPtr(&testCalendar, CRON_CF_MINUTE); + + // Verify success + EXPECT_EQ(result, 0); + + // Verify minutes field is reset to 0 + EXPECT_EQ(testCalendar.tm_min, 0); + + // Verify other fields remain unchanged (except those normalized by mktime) + EXPECT_EQ(testCalendar.tm_sec, originalCalendar.tm_sec); + EXPECT_EQ(testCalendar.tm_hour, originalCalendar.tm_hour); + EXPECT_EQ(testCalendar.tm_mday, originalCalendar.tm_mday); + EXPECT_EQ(testCalendar.tm_mon, originalCalendar.tm_mon); + EXPECT_EQ(testCalendar.tm_year, originalCalendar.tm_year); +} + +TEST_F(DcmCronParseResetMinTest, ResetMin_HourField_ResetsToZero) { + // Verify initial state + EXPECT_EQ(testCalendar.tm_hour, 14); + auto myFunctionPtr = getdcmCronParseResetMin(); + // Reset hour field + INT32 result = myFunctionPtr(&testCalendar, CRON_CF_HOUR_OF_DAY); + + // Verify success + EXPECT_EQ(result, 0); + + // Verify hour field is reset to 0 + EXPECT_EQ(testCalendar.tm_hour, 0); + + // Verify other fields remain unchanged + EXPECT_EQ(testCalendar.tm_sec, originalCalendar.tm_sec); + EXPECT_EQ(testCalendar.tm_min, originalCalendar.tm_min); + EXPECT_EQ(testCalendar.tm_mday, originalCalendar.tm_mday); + EXPECT_EQ(testCalendar.tm_mon, originalCalendar.tm_mon); + EXPECT_EQ(testCalendar.tm_year, originalCalendar.tm_year); +} + +TEST_F(DcmCronParseResetMinTest, ResetMin_DayOfWeekField_ResetsToZero) { + // Verify initial state + EXPECT_EQ(testCalendar.tm_wday, 5); // Friday + auto myFunctionPtr = getdcmCronParseResetMin(); + // Reset day of week field + INT32 result = myFunctionPtr(&testCalendar, CRON_CF_DAY_OF_WEEK); + + // Verify success + EXPECT_EQ(result, 0); +} + +TEST_F(DcmCronParseResetMinTest, ResetMin_DayOfMonthField_ResetsToOne) { + // Verify initial state + EXPECT_EQ(testCalendar.tm_mday, 15); + auto myFunctionPtr = getdcmCronParseResetMin(); + // Reset day of month field + INT32 result = myFunctionPtr(&testCalendar, CRON_CF_DAY_OF_MONTH); + + // Verify success + EXPECT_EQ(result, 0); + + // Verify day of month field is reset to 1 (first day) + EXPECT_EQ(testCalendar.tm_mday, 1); + + // Verify other fields remain unchanged + EXPECT_EQ(testCalendar.tm_sec, originalCalendar.tm_sec); + EXPECT_EQ(testCalendar.tm_min, originalCalendar.tm_min); + EXPECT_EQ(testCalendar.tm_hour, originalCalendar.tm_hour); + EXPECT_EQ(testCalendar.tm_mon, originalCalendar.tm_mon); + EXPECT_EQ(testCalendar.tm_year, originalCalendar.tm_year); +} + +TEST_F(DcmCronParseResetMinTest, ResetMin_MonthField_ResetsToZero) { + // Verify initial state + EXPECT_EQ(testCalendar.tm_mon, 2); // March + auto myFunctionPtr = getdcmCronParseResetMin(); + // Reset month field + INT32 result = myFunctionPtr(&testCalendar, CRON_CF_MONTH); + + // Verify success + EXPECT_EQ(result, 0); + + // Verify month field is reset to 0 (January) + EXPECT_EQ(testCalendar.tm_mon, 0); + + // Verify other fields remain unchanged + EXPECT_EQ(testCalendar.tm_sec, originalCalendar.tm_sec); + EXPECT_EQ(testCalendar.tm_min, originalCalendar.tm_min); + EXPECT_EQ(testCalendar.tm_hour, originalCalendar.tm_hour); + EXPECT_EQ(testCalendar.tm_mday, originalCalendar.tm_mday); + EXPECT_EQ(testCalendar.tm_year, originalCalendar.tm_year); +} + +TEST_F(DcmCronParseResetMinTest, ResetMin_YearField_ResetsToZero) { + // Verify initial state + EXPECT_EQ(testCalendar.tm_year, 124); // 2024 - 1900 + auto myFunctionPtr = getdcmCronParseResetMin(); + // Reset year field + INT32 result = myFunctionPtr(&testCalendar, CRON_CF_YEAR); + + // Verify success + EXPECT_EQ(result, 0); + + // Verify year field is reset to 0 (1900) + EXPECT_EQ(testCalendar.tm_year, 0); + + // Verify other fields remain unchanged + EXPECT_EQ(testCalendar.tm_sec, originalCalendar.tm_sec); + EXPECT_EQ(testCalendar.tm_min, originalCalendar.tm_min); + EXPECT_EQ(testCalendar.tm_hour, originalCalendar.tm_hour); + EXPECT_EQ(testCalendar.tm_mday, originalCalendar.tm_mday); + EXPECT_EQ(testCalendar.tm_mon, originalCalendar.tm_mon); +} + +TEST_F(DcmCronParseResetMinTest, ResetMin_default_field) { + auto myFunctionPtr = getdcmCronParseResetMin(); + // Reset year field + INT32 result = myFunctionPtr(&testCalendar, 8); + // Verify success + EXPECT_EQ(result, 1); +} +TEST_F(DcmCronParseResetMinTest, ResetMin_invalid_field) { + auto myFunctionPtr = getdcmCronParseResetMin(); + // Reset year field + INT32 result = myFunctionPtr(&testCalendar, -1); + // Verify success + EXPECT_EQ(result, 1); +} + +TEST_F(DcmCronParseResetMinTest, ResetMin_NullCalendar_ReturnsFailure) { + // Test with NULL calendar + auto myFunctionPtr = getdcmCronParseResetMin(); + INT32 result = myFunctionPtr(nullptr, CRON_CF_SECOND); + // Verify failure + EXPECT_EQ(result, 1); +} + +TEST_F(DcmCronParseResetMinTest, dcmCronParseResetAllMin_success) { + INT32 testFields[CRON_CF_ARR_LEN]; + memset(testFields, -1, sizeof(testFields)); + testFields[0] = CRON_CF_SECOND; + auto myFunctionPtr = getdcmCronParseResetAllMin(); + INT32 result = myFunctionPtr(&testCalendar, testFields); + EXPECT_EQ(result, 0); + // Verify seconds field is reset to 0 + EXPECT_EQ(testCalendar.tm_sec, 0); + + // Verify other fields remain unchanged + EXPECT_EQ(testCalendar.tm_min, originalCalendar.tm_min); + EXPECT_EQ(testCalendar.tm_hour, originalCalendar.tm_hour); + EXPECT_EQ(testCalendar.tm_mday, originalCalendar.tm_mday); + EXPECT_EQ(testCalendar.tm_mon, originalCalendar.tm_mon); + EXPECT_EQ(testCalendar.tm_year, originalCalendar.tm_year); +} +TEST_F(DcmCronParseResetMinTest, dcmCronParseResetAllMin_fail) { + INT32 testFields[CRON_CF_ARR_LEN]; + memset(testFields, -1, sizeof(testFields)); + testFields[0] = CRON_CF_SECOND; + auto myFunctionPtr = getdcmCronParseResetAllMin(); + INT32 result = myFunctionPtr(nullptr, testFields); + EXPECT_EQ(result, 1); +} + +TEST_F(DcmCronParseResetMinTest, dcmCronParseSetField_SecondField_setvalue) { + // Verify initial state + EXPECT_EQ(testCalendar.tm_sec, 45); + auto myFunctionPtr = getdcmCronParseSetField(); + // Reset seconds field + INT32 result = myFunctionPtr(&testCalendar, CRON_CF_SECOND, 35); + + // Verify success + EXPECT_EQ(result, 0); + + // Verify seconds field is reset to 0 + EXPECT_EQ(testCalendar.tm_sec, 35); + + // Verify other fields remain unchanged + EXPECT_EQ(testCalendar.tm_min, originalCalendar.tm_min); + EXPECT_EQ(testCalendar.tm_hour, originalCalendar.tm_hour); + EXPECT_EQ(testCalendar.tm_mday, originalCalendar.tm_mday); + EXPECT_EQ(testCalendar.tm_mon, originalCalendar.tm_mon); + EXPECT_EQ(testCalendar.tm_year, originalCalendar.tm_year); +} + +TEST_F(DcmCronParseResetMinTest, dcmCronParseSetField_MinuteField_setvalue) { + // Verify initial state + EXPECT_EQ(testCalendar.tm_min, 30); + auto myFunctionPtr = getdcmCronParseSetField(); + // Reset minutes field + INT32 result = myFunctionPtr(&testCalendar, CRON_CF_MINUTE, 50); + + // Verify success + EXPECT_EQ(result, 0); + + // Verify minutes field is set to 50 + EXPECT_EQ(testCalendar.tm_min, 50); + + // Verify other fields remain unchanged (except those normalized by mktime) + EXPECT_EQ(testCalendar.tm_sec, originalCalendar.tm_sec); + EXPECT_EQ(testCalendar.tm_hour, originalCalendar.tm_hour); + EXPECT_EQ(testCalendar.tm_mday, originalCalendar.tm_mday); + EXPECT_EQ(testCalendar.tm_mon, originalCalendar.tm_mon); + EXPECT_EQ(testCalendar.tm_year, originalCalendar.tm_year); +} + +TEST_F(DcmCronParseResetMinTest, dcmCronParseSetField_HourField_setvalue) { + // Verify initial state + EXPECT_EQ(testCalendar.tm_hour, 14); + auto myFunctionPtr = getdcmCronParseSetField(); + // Reset hour field + INT32 result = myFunctionPtr(&testCalendar, CRON_CF_HOUR_OF_DAY, 5); + + // Verify success + EXPECT_EQ(result, 0); + + // Verify hour field is reset to 5 + EXPECT_EQ(testCalendar.tm_hour, 5); + + // Verify other fields remain unchanged + EXPECT_EQ(testCalendar.tm_sec, originalCalendar.tm_sec); + EXPECT_EQ(testCalendar.tm_min, originalCalendar.tm_min); + EXPECT_EQ(testCalendar.tm_mday, originalCalendar.tm_mday); + EXPECT_EQ(testCalendar.tm_mon, originalCalendar.tm_mon); + EXPECT_EQ(testCalendar.tm_year, originalCalendar.tm_year); +} + +TEST_F(DcmCronParseResetMinTest, dcmCronParseSetField_DayOfWeekField_setvalue) { + // Verify initial state + EXPECT_EQ(testCalendar.tm_wday, 5); // Friday + auto myFunctionPtr = getdcmCronParseSetField(); + // Reset day of week field + INT32 result = myFunctionPtr(&testCalendar, CRON_CF_DAY_OF_WEEK, 3); + + // Verify success + EXPECT_EQ(result, 0); + +} + +TEST_F(DcmCronParseResetMinTest, dcmCronParseSetField_DayOfMonthField_setvalue) { + // Verify initial state + EXPECT_EQ(testCalendar.tm_mday, 15); + auto myFunctionPtr = getdcmCronParseSetField(); + // Reset day of month field + INT32 result = myFunctionPtr(&testCalendar, CRON_CF_DAY_OF_MONTH, 25); + + // Verify success + EXPECT_EQ(result, 0); + + // Verify day of month field is set to 25 + EXPECT_EQ(testCalendar.tm_mday, 25); + + // Verify other fields remain unchanged + EXPECT_EQ(testCalendar.tm_sec, originalCalendar.tm_sec); + EXPECT_EQ(testCalendar.tm_min, originalCalendar.tm_min); + EXPECT_EQ(testCalendar.tm_hour, originalCalendar.tm_hour); + EXPECT_EQ(testCalendar.tm_mon, originalCalendar.tm_mon); + EXPECT_EQ(testCalendar.tm_year, originalCalendar.tm_year); +} + +TEST_F(DcmCronParseResetMinTest, dcmCronParseSetField_MonthField_setvalue) { + // Verify initial state + EXPECT_EQ(testCalendar.tm_mon, 2); // March + auto myFunctionPtr = getdcmCronParseSetField(); + // Reset month field + INT32 result = myFunctionPtr(&testCalendar, CRON_CF_MONTH, 6); + + // Verify success + EXPECT_EQ(result, 0); + + // Verify month field is reset to 6 (January) + EXPECT_EQ(testCalendar.tm_mon, 6); + + // Verify other fields remain unchanged + EXPECT_EQ(testCalendar.tm_sec, originalCalendar.tm_sec); + EXPECT_EQ(testCalendar.tm_min, originalCalendar.tm_min); + EXPECT_EQ(testCalendar.tm_hour, originalCalendar.tm_hour); + EXPECT_EQ(testCalendar.tm_mday, originalCalendar.tm_mday); + EXPECT_EQ(testCalendar.tm_year, originalCalendar.tm_year); +} + +TEST_F(DcmCronParseResetMinTest, dcmCronParseSetField_YearField_setvalue) { + // Verify initial state + EXPECT_EQ(testCalendar.tm_year, 124); + auto myFunctionPtr = getdcmCronParseSetField(); + // Reset year field + INT32 result = myFunctionPtr(&testCalendar, CRON_CF_YEAR, 1); + + // Verify success + EXPECT_EQ(result, 0); + + // Verify year field is set to 1 + EXPECT_EQ(testCalendar.tm_year, 1); + + // Verify other fields remain unchanged + EXPECT_EQ(testCalendar.tm_sec, originalCalendar.tm_sec); + EXPECT_EQ(testCalendar.tm_min, originalCalendar.tm_min); + EXPECT_EQ(testCalendar.tm_hour, originalCalendar.tm_hour); + EXPECT_EQ(testCalendar.tm_mday, originalCalendar.tm_mday); + EXPECT_EQ(testCalendar.tm_mon, originalCalendar.tm_mon); +} +TEST_F(DcmCronParseResetMinTest, dcmCronParseSetField_NullCalendar_ReturnsFailure) { + // Test with NULL calendar + auto myFunctionPtr = getdcmCronParseSetField(); + INT32 result = myFunctionPtr(nullptr, CRON_CF_SECOND, 20); + // Verify failure + EXPECT_EQ(result, 1); +} +TEST_F(DcmCronParseResetMinTest, dcmCronParseSetField_default_field) { + auto myFunctionPtr = getdcmCronParseSetField(); + // Reset year field + INT32 result = myFunctionPtr(&testCalendar, 8, 25); + // Verify success + EXPECT_EQ(result, 1); +} + +GTEST_API_ int main(int argc, char *argv[]){ + char testresults_fullfilepath[GTEST_REPORT_FILEPATH_SIZE]; + char buffer[GTEST_REPORT_FILEPATH_SIZE]; + + memset( testresults_fullfilepath, 0, GTEST_REPORT_FILEPATH_SIZE ); + memset( buffer, 0, GTEST_REPORT_FILEPATH_SIZE ); + + snprintf( testresults_fullfilepath, GTEST_REPORT_FILEPATH_SIZE, "json:%s%s" , GTEST_DEFAULT_RESULT_FILEPATH , GTEST_DEFAULT_RESULT_FILENAME); + ::testing::GTEST_FLAG(output) = testresults_fullfilepath; + ::testing::InitGoogleTest(&argc, argv); + //testing::Mock::AllowLeak(mock); + cout << "Starting DCM GTEST ===================>" << endl; + return RUN_ALL_TESTS(); +} diff --git a/unittest/dcm_gtest.cpp b/unittest/dcm_gtest.cpp new file mode 100644 index 00000000..a590c3b3 --- /dev/null +++ b/unittest/dcm_gtest.cpp @@ -0,0 +1,598 @@ +/** + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include "./mocks/mockrbus.h" +//#include "./mocks/mockrbus.cpp" + +extern "C" { + + //VOID (*getdcmRunJobs(void)) (const INT8*, VOID*); + void get_dcmRunJobs(const INT8* profileName, VOID *pHandle); + void get_sig_handler(INT32 sig); + #include "dcm.c" + #include "dcm.h" + +} +//#include "../dcm_utils.c" + +#include "dcm_types.h" +#include "dcm_rbus.c" +#include "dcm_parseconf.c" +#include "dcm_schedjob.c" +#include "dcm_cronparse.c" +#include "dcm_utils.c" +#define GTEST_DEFAULT_RESULT_FILEPATH "/tmp/Gtest_Report/" +#define GTEST_DEFAULT_RESULT_FILENAME "dcm_cronparse_gtest_report.json" +#define GTEST_REPORT_FILEPATH_SIZE 256 + + +using namespace testing; +using namespace std; +using ::testing::_; +using ::testing::Return; +using ::testing::SetArgPointee; +using ::testing::DoAll; +using ::testing::StrEq; + + +class DcmDaemonMainInitTest : public ::testing::Test { +protected: + void SetUp() override { + mockRBus = new StrictMock(); + mock_rbus_set_global_mock(mockRBus); + mock_rbus_reset(); + + // Initialize DCM handle + memset(&dcmHandle, 0, sizeof(DCMDHandle)); + dcmHandle.isDCMRunning = false; + + } + + void TearDown() override { + mock_rbus_clear_global_mock(); + delete mockRBus; + + // Cleanup + cleanupDCMHandle(); + } + + // Helper: create a file with given content + void CreateFile(const char* filename, const char* content) { + std::ofstream ofs(filename); + ofs << content; + } + + void RemoveFile(const char* filename) { + std::remove(filename); + } + + void cleanupDCMHandle() { + if (dcmHandle.pExecBuff) { + free(dcmHandle.pExecBuff); + dcmHandle.pExecBuff = NULL; + } + if (dcmHandle.pDcmSetHandle) { + dcmSettingsUnInit(dcmHandle.pDcmSetHandle); + } + if (dcmHandle.pRbusHandle) { + dcmRbusUnInit(dcmHandle.pRbusHandle); + } + if (dcmHandle.pLogSchedHandle) { + dcmSchedRemoveJob(dcmHandle.pLogSchedHandle); + } + if (dcmHandle.pDifdSchedHandle) { + dcmSchedRemoveJob(dcmHandle.pDifdSchedHandle); + } + dcmSchedUnInit(); + } + + MockRBus* mockRBus; + DCMDHandle dcmHandle; + const char* pidFilePath; +}; + +// ==================== Positive Test Cases ==================== + +TEST_F(DcmDaemonMainInitTest, MainInit_AllComponentsInitializeSuccessfully_Success) { + // Setup successful RBUS mocks + rbusHandle_t mockHandle = mock_rbus_get_mock_handle(); + RemoveFile(DCM_PID_FILE); + + // RBUS initialization sequence + EXPECT_CALL(*mockRBus, rbus_checkStatus()) + .WillOnce(Return(RBUS_ENABLED)); + + EXPECT_CALL(*mockRBus, rbus_open(_, _)) + .WillOnce(DoAll(SetArgPointee<0>(mockHandle), Return(RBUS_ERROR_SUCCESS))); + + // T2 version retrieval + rbusValue_t mockValue = mock_rbus_create_string_value("2.1.5"); + EXPECT_CALL(*mockRBus, rbus_get(mockHandle, _, _)) + .WillOnce(DoAll(SetArgPointee<2>(mockValue), Return(RBUS_ERROR_SUCCESS))); + + EXPECT_CALL(*mockRBus, rbusValue_GetType(mockValue)) + .WillOnce(Return(RBUS_STRING)); + + EXPECT_CALL(*mockRBus, rbusValue_ToString(mockValue, NULL, 0)) + .WillOnce(Return(strdup("2.1.5"))); + + EXPECT_CALL(*mockRBus, rbusValue_Release(mockValue)) + .Times(1); + + // Event subscription + EXPECT_CALL(*mockRBus, rbusEvent_SubscribeAsync(_, _, _, _, _, _)) + .Times(2) + .WillRepeatedly(Return(RBUS_ERROR_SUCCESS)); + + EXPECT_CALL(*mockRBus, rbus_regDataElements(_, _, _)) + .WillOnce(Return(RBUS_ERROR_SUCCESS)); + + INT32 result = dcmDaemonMainInit(&dcmHandle); + + EXPECT_EQ(result, DCM_SUCCESS); + EXPECT_FALSE(dcmHandle.isDCMRunning); + EXPECT_NE(dcmHandle.pDcmSetHandle, nullptr); + EXPECT_NE(dcmHandle.pRbusHandle, nullptr); + EXPECT_NE(dcmHandle.pExecBuff, nullptr); + EXPECT_NE(dcmHandle.pLogSchedHandle, nullptr); + EXPECT_NE(dcmHandle.pDifdSchedHandle, nullptr); +} + +TEST_F(DcmDaemonMainInitTest, MainInit_checkdemonstatus_fail) { + + INT32 result = dcmDaemonMainInit(&dcmHandle); + EXPECT_EQ(result, DCM_FAILURE); +} + +TEST_F(DcmDaemonMainInitTest, MainInit_rbus_int_failure) { + // Setup successful RBUS mocks + rbusHandle_t mockHandle = mock_rbus_get_mock_handle(); + RemoveFile(DCM_PID_FILE); + + EXPECT_CALL(*mockRBus, rbus_checkStatus()) + .WillOnce(Return(RBUS_DISABLED)); + INT32 result = dcmDaemonMainInit(&dcmHandle); + + EXPECT_EQ(result, DCM_FAILURE); + +} + +TEST_F(DcmDaemonMainInitTest, MainInit_dcmRbusSubscribeEvents_failure) { + // Setup successful RBUS mocks + rbusHandle_t mockHandle = mock_rbus_get_mock_handle(); + RemoveFile(DCM_PID_FILE); + + // RBUS initialization sequence + EXPECT_CALL(*mockRBus, rbus_checkStatus()) + .WillOnce(Return(RBUS_ENABLED)); + + EXPECT_CALL(*mockRBus, rbus_open(_, _)) + .WillOnce(DoAll(SetArgPointee<0>(mockHandle), Return(RBUS_ERROR_SUCCESS))); + + // T2 version retrieval + rbusValue_t mockValue = mock_rbus_create_string_value("2.1.5"); + EXPECT_CALL(*mockRBus, rbus_get(mockHandle, _, _)) + .WillOnce(DoAll(SetArgPointee<2>(mockValue), Return(RBUS_ERROR_SUCCESS))); + + EXPECT_CALL(*mockRBus, rbusValue_GetType(mockValue)) + .WillOnce(Return(RBUS_STRING)); + + EXPECT_CALL(*mockRBus, rbusValue_ToString(mockValue, NULL, 0)) + .WillOnce(Return(strdup("2.1.5"))); + + EXPECT_CALL(*mockRBus, rbusValue_Release(mockValue)) + .Times(1); + + // Event subscription + EXPECT_CALL(*mockRBus, rbusEvent_SubscribeAsync(_, _, _, _, _, _)) + .WillOnce(Return(RBUS_ERROR_BUS_ERROR)); + + + INT32 result = dcmDaemonMainInit(&dcmHandle); + + EXPECT_EQ(result, DCM_FAILURE); +} +/* +TEST_F(DcmDaemonMainInitTest, MainInit_pExecBuff_failure) { + // Setup successful RBUS mocks + rbusHandle_t mockHandle = mock_rbus_get_mock_handle(); + RemoveFile(DCM_PID_FILE); + + // RBUS initialization sequence + EXPECT_CALL(*mockRBus, rbus_checkStatus()) + .WillOnce(Return(RBUS_ENABLED)); + + EXPECT_CALL(*mockRBus, rbus_open(_, _)) + .WillOnce(DoAll(SetArgPointee<0>(mockHandle), Return(RBUS_ERROR_SUCCESS))); + + // T2 version retrieval + rbusValue_t mockValue = mock_rbus_create_string_value("2.1.5"); + EXPECT_CALL(*mockRBus, rbus_get(mockHandle, _, _)) + .WillOnce(DoAll(SetArgPointee<2>(mockValue), Return(RBUS_ERROR_SUCCESS))); + + EXPECT_CALL(*mockRBus, rbusValue_GetType(mockValue)) + .WillOnce(Return(RBUS_STRING)); + + EXPECT_CALL(*mockRBus, rbusValue_ToString(mockValue, NULL, 0)) + .WillOnce(Return(strdup("2.1.5"))); + + EXPECT_CALL(*mockRBus, rbusValue_Release(mockValue)) + .Times(1); + + // Event subscription + EXPECT_CALL(*mockRBus, rbusEvent_SubscribeAsync(_, _, _, _, _, _)) + .WillOnce(Return(RBUS_ERROR_SUCCESS)); + dcmHandle.pExecBuff = nullptr; + + INT32 result = dcmDaemonMainInit(&dcmHandle); + + EXPECT_EQ(result, DCM_FAILURE); +} +*/ + +/* +TEST(DcmDaemonMainInitTest , ) { + auto myFunctionPtr = getdcmRunJobs(); + UINT32 result = myFunctionPtr("12345", &errcode); + EXPECT_EQ(result, 12345u); + EXPECT_EQ(errcode, 0); +} +*/ +/* +class DcmRunJobsTest : public ::testing::Test { +protected: + void SetUp() override { + // Initialize DCM handle + memset(&dcmHandle, 0, sizeof(DCMDHandle)); + + // Allocate execution buffer + dcmHandle.pExecBuff = (INT8*)malloc(EXECMD_BUFF_SIZE); + ASSERT_NE(dcmHandle.pExecBuff, nullptr); + + // Initialize settings handle + INT32 ret = dcmSettingsInit(&dcmHandle.pDcmSetHandle); + if (ret != DCM_SUCCESS) { + // If settings init fails, create a minimal mock handle + dcmHandle.pDcmSetHandle = malloc(64); + ASSERT_NE(dcmHandle.pDcmSetHandle, nullptr); + } + } + + void TearDown() override { + // Cleanup + if (dcmHandle.pExecBuff) { + free(dcmHandle.pExecBuff); + } + if (dcmHandle.pDcmSetHandle) { + dcmSettingsUnInit(dcmHandle.pDcmSetHandle); + } + + // Cleanup test files + system("rm -rf /tmp/test_dcm_scripts"); + + // Restore environment + if (originalPath) { + setenv("PATH", originalPath, 1); + } + } + + + DCMDHandle dcmHandle; + const char* originalPath; +}; +*/ + +class DcmRunJobsTest : public ::testing::Test { +protected: + void SetUp() override { + // Initialize DCM handle + memset(&dcmHandle, 0, sizeof(DCMDHandle)); + + // Allocate execution buffer + dcmHandle.pExecBuff = (INT8*)malloc(EXECMD_BUFF_SIZE); + ASSERT_NE(dcmHandle.pExecBuff, nullptr); + + // Initialize settings handle + INT32 ret = dcmSettingsInit(&dcmHandle.pDcmSetHandle); + if (ret != DCM_SUCCESS) { + // If settings init fails, create a minimal mock handle + dcmHandle.pDcmSetHandle = malloc(64); + ASSERT_NE(dcmHandle.pDcmSetHandle, nullptr); + } + + // Store original environment + originalPath = getenv("PATH"); + + // Create test directories + system("mkdir -p /tmp/test_dcm_scripts"); + createTestScripts(); + } + + void TearDown() override { + // Cleanup + if (dcmHandle.pExecBuff) { + free(dcmHandle.pExecBuff); + } + if (dcmHandle.pDcmSetHandle) { + dcmSettingsUnInit(dcmHandle.pDcmSetHandle); + } + + // Cleanup test files + system("rm -rf /tmp/test_dcm_scripts"); + + // Restore environment + if (originalPath) { + setenv("PATH", originalPath, 1); + } + } + + void createTestScripts() { + // Create test uploadSTBLogs.sh script + const char* uploadScript = R"(#!/bin/bash +echo "Upload script called with args: $*" > /tmp/test_upload_output.txt +echo "Protocol: $3" >> /tmp/test_upload_output.txt +echo "URL: $4" >> /tmp/test_upload_output.txt +exit 0 +)"; + + //system("echo '" + std::string(uploadScript) + "' > /tmp/test_dcm_scripts/uploadSTBLogs.sh"); + system("chmod +x /tmp/test_dcm_scripts/uploadSTBLogs.sh"); + + // Create test swupdate_utility.sh script + const char* swupdateScript = R"(#!/bin/bash +echo "SW Update script called with args: $*" > /tmp/test_swupdate_output.txt +echo "Mode: $1" >> /tmp/test_swupdate_output.txt +echo "Type: $2" >> /tmp/test_swupdate_output.txt +exit 0 +)"; + + //system("echo '" + std::string(swupdateScript) + "' > /tmp/test_dcm_scripts/swupdate_utility.sh"); + system("chmod +x /tmp/test_dcm_scripts/swupdate_utility.sh"); + } + + bool fileExists(const char* filename) { + return access(filename, F_OK) == 0; + } + + std::string readFile(const char* filename) { + FILE* file = fopen(filename, "r"); + if (!file) return ""; + + char buffer[1024]; + std::string content; + while (fgets(buffer, sizeof(buffer), file)) { + content += buffer; + } + fclose(file); + return content; + } + + DCMDHandle dcmHandle; + const char* originalPath; +}; + +TEST_F(DcmRunJobsTest, RunJobs_LogUploadProfile_ExecutesCorrectScript) { + // Set RDK path to our test directory + setenv("DCM_RDK_PATH", "/tmp/test_dcm_scripts", 1); + + // Call the function with log upload profile + //test_dcmRunJobs(DCM_LOGUPLOAD_SCHED, &dcmHandle); + get_dcmRunJobs(DCM_LOGUPLOAD_SCHED, &dcmHandle); +} +TEST_F(DcmRunJobsTest, RunJobs_DifdProfile_ExecutesCorrectScript) { + // Set RDK path to our test directory + setenv("DCM_RDK_PATH", "/tmp/test_dcm_scripts", 1); + + // Call the function with DIFD profile + get_dcmRunJobs(DCM_DIFD_SCHED, &dcmHandle); +} +/* +class SigHandlerTest : public ::testing::Test { +protected: + void SetUp() override { + // Setup global DCM handle for testing + g_pdcmHandle = (DCMDHandle*)malloc(sizeof(DCMDHandle)); + ASSERT_NE(g_pdcmHandle, nullptr); + + memset(g_pdcmHandle, 0, sizeof(DCMDHandle)); + g_pdcmHandle->isDCMRunning = false; + + // Setup minimal components that might be cleaned up + setupMinimalComponents(); + mockRBus = new StrictMock(); + mock_rbus_set_global_mock(mockRBus); + mock_rbus_reset(); + + } + + void TearDown() override { + // Cleanup global handle + cleanupComponents(); + + if (g_pdcmHandle) { + free(g_pdcmHandle); + g_pdcmHandle = nullptr; + } + mock_rbus_clear_global_mock(); + delete mockRBus; + } + + void setupMinimalComponents() { + // Allocate execution buffer + g_pdcmHandle->pExecBuff = (INT8*)malloc(EXECMD_BUFF_SIZE); + + // Initialize settings if possible + if (dcmSettingsInit(&g_pdcmHandle->pDcmSetHandle) != DCM_SUCCESS) { + g_pdcmHandle->pDcmSetHandle = nullptr; + } + + // Mock scheduler handles (pointers to indicate they exist) + g_pdcmHandle->pLogSchedHandle = (VOID*)0x12345678; + g_pdcmHandle->pDifdSchedHandle = (VOID*)0x87654321; + //g_pdcmHandle->pRbusHandle = (VOID*)0x87654322; + } + + void cleanupComponents() { + if (g_pdcmHandle) { + if (g_pdcmHandle->pExecBuff) { + free(g_pdcmHandle->pExecBuff); + g_pdcmHandle->pExecBuff = nullptr; + } + + if (g_pdcmHandle->pDcmSetHandle) { + dcmSettingsUnInit(g_pdcmHandle->pDcmSetHandle); + g_pdcmHandle->pDcmSetHandle = nullptr; + } + } + } + MockRBus* mockRBus; +}; + +// ==================== Handled Signals Test Cases ==================== + +TEST_F(SigHandlerTest, SigHandler_SIGINT_SendsEventAndExits) { + EXPECT_CALL(*mockRBus, rbusEvent_Unsubscribe(_, _)) + .Times(2) + .WillRepeatedly(Return(RBUS_ERROR_SUCCESS)); + EXPECT_CALL(*mockRBus, rbus_unregDataElements(_, _, _)) + .WillOnce(Return(RBUS_ERROR_SUCCESS)); + EXPECT_CALL(*mockRBus, rbus_close(_)) + .WillOnce(Return(RBUS_ERROR_SUCCESS)); + get_sig_handler(SIGINT); +} +*/ + +class DcmDaemonMainUnInitTest : public ::testing::Test { +protected: + void SetUp() override { + // Initialize test handle + memset(&testHandle, 0, sizeof(DCMDHandle)); + + // Create test files/directories if needed + system("mkdir -p /tmp/test_dcm"); + + // Initialize components for testing + setupTestComponents(); + } + + void TearDown() override { + // Cleanup test files + system("rm -rf /tmp/test_dcm"); + system("rm -f /var/run/dcm.pid"); + + // Cleanup any allocated memory + cleanupTestComponents(); + } + + void setupTestComponents() { + // Allocate execution buffer + testHandle.pExecBuff = (INT8*)malloc(EXECMD_BUFF_SIZE); + if (testHandle.pExecBuff) { + memset(testHandle.pExecBuff, 0, EXECMD_BUFF_SIZE); + strcpy(testHandle.pExecBuff, "test command"); + } + + // Initialize settings handle (may fail, that's OK for testing) + if (dcmSettingsInit(&testHandle.pDcmSetHandle) != DCM_SUCCESS) { + testHandle.pDcmSetHandle = nullptr; + } + + // Initialize scheduler + if (dcmSchedInit() == DCM_SUCCESS) { + // Add dummy scheduler jobs + testHandle.pLogSchedHandle = dcmSchedAddJob("test_log", nullptr, nullptr); + testHandle.pDifdSchedHandle = dcmSchedAddJob("test_difd", nullptr, nullptr); + } + + // Initialize RBUS handle (may fail, that's OK for testing) + if (dcmRbusInit(&testHandle.pRbusHandle) != DCM_SUCCESS) { + testHandle.pRbusHandle = nullptr; + } + } + + void cleanupTestComponents() { + // Cleanup scheduler if initialized + if (testHandle.pLogSchedHandle || testHandle.pDifdSchedHandle) { + dcmSchedUnInit(); + } + } + + void createPIDFile() { + FILE* pidFile = fopen("/var/run/dcm.pid", "w"); + if (pidFile) { + fprintf(pidFile, "%d\n", getpid()); + fclose(pidFile); + } + } + + bool pidFileExists() { + return access("/var/run/dcm.pid", F_OK) == 0; + } + + DCMDHandle testHandle; +}; + +// ==================== Valid Handle Test Cases ==================== + +TEST_F(DcmDaemonMainUnInitTest, UnInit_ValidHandle_CompletesSuccessfully) { + testHandle.isDCMRunning = true; // Won't remove PID file + + // Should complete without crashing + EXPECT_NO_THROW(dcmDaemonMainUnInit(&testHandle)); + + // Verify handle is cleared + EXPECT_EQ(testHandle.pExecBuff, nullptr); + EXPECT_EQ(testHandle.pDcmSetHandle, nullptr); + EXPECT_EQ(testHandle.pRbusHandle, nullptr); + EXPECT_EQ(testHandle.pLogSchedHandle, nullptr); + EXPECT_EQ(testHandle.pDifdSchedHandle, nullptr); +} +TEST_F(DcmDaemonMainUnInitTest, UnInit_DCMNotRunning_RemovesPIDFile) { + testHandle.isDCMRunning = false; + createPIDFile(); + + EXPECT_TRUE(pidFileExists()); + + dcmDaemonMainUnInit(&testHandle); + + // PID file should be removed when isDCMRunning is false + // Note: This depends on dcmUtilsRemovePIDfile() implementation + // We can only test that the function completes without error + EXPECT_NO_THROW(dcmDaemonMainUnInit(&testHandle)); +} + +GTEST_API_ int main(int argc, char *argv[]){ + char testresults_fullfilepath[GTEST_REPORT_FILEPATH_SIZE]; + char buffer[GTEST_REPORT_FILEPATH_SIZE]; + + memset( testresults_fullfilepath, 0, GTEST_REPORT_FILEPATH_SIZE ); + memset( buffer, 0, GTEST_REPORT_FILEPATH_SIZE ); + + snprintf( testresults_fullfilepath, GTEST_REPORT_FILEPATH_SIZE, "json:%s%s" , GTEST_DEFAULT_RESULT_FILEPATH , GTEST_DEFAULT_RESULT_FILENAME); + ::testing::GTEST_FLAG(output) = testresults_fullfilepath; + ::testing::InitGoogleTest(&argc, argv); + cout << "Starting DCM GTEST ===================>" << endl; + return RUN_ALL_TESTS(); +} diff --git a/unittest/dcm_parseconf_gtest.cpp b/unittest/dcm_parseconf_gtest.cpp new file mode 100644 index 00000000..fbf1dc8b --- /dev/null +++ b/unittest/dcm_parseconf_gtest.cpp @@ -0,0 +1,388 @@ +/** + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +//#include "./mocks/mockRbus.h" +#include "./mocks/mockrbus.h" + +/*extern "C" { +//#include "dcm_types.h" +//#include "dcm_parseconf.h" + +}*/ + +#include "dcm_types.h" + +#define GTEST_DEFAULT_RESULT_FILEPATH "/tmp/Gtest_Report/" +#define GTEST_DEFAULT_RESULT_FILENAME "dcm_cronparse_gtest_report.json" +#define GTEST_REPORT_FILEPATH_SIZE 256 + + +using namespace testing; +using namespace std; +using ::testing::_; +using ::testing::Return; +using ::testing::SetArgPointee; +using ::testing::DoAll; +using ::testing::StrEq; + + +void CreateFile(const char* filename, const char* content) { + std::ofstream ofs(filename); + ofs << content; +} + +void RemoveFile(const char* filename) { + std::remove(filename); +} + +void CreateDirectory(const char* dirname) { + mkdir(dirname, 0755); +} + +void RemoveDirectory(const char* dirname) { + rmdir(dirname); +} + +class dcmParseConfTest : public ::testing::Test { +protected: + void SetUp(){ + } + + void TearDown(){ + + } +}; + +// Test when /etc/include.properties doesn't exist - uses default path +TEST(dcmParseConfTest, DefaultBoot_IncludeFileNotExists_UsesDefaultPath) { + INT32 result = dcmSettingDefaultBoot(); + + EXPECT_EQ(result, -1); +} + +TEST(dcmParseConfTest, DefaultBoot_IncludeFileNotExists_UsesDefaultPath_success) { + // Ensure /etc/include.properties doesn't exist + RemoveFile("/etc/include.properties"); + + // Create default persistent directory and DCM response file + CreateDirectory("/opt/.t2persistentfolder"); + CreateFile("/opt/.t2persistentfolder/DCMresponse.txt", + "{\"logUploadSettings\":{\"uploadRepository:URL\":\"https://test.com\"}}"); + + INT32 result = dcmSettingDefaultBoot(); + + EXPECT_EQ(result, 0); +} + + + +// Helper function to create a test handle +DCMSettingsHandle* CreateTestHandle() { + DCMSettingsHandle* handle = (DCMSettingsHandle*)malloc(sizeof(DCMSettingsHandle)); + memset(handle, 0, sizeof(DCMSettingsHandle)); + return handle; +} + +// ==================== dcmSettingsGetUploadProtocol Tests ==================== + +TEST(dcmParseConfTest, GetUploadProtocol_ValidHandle_ReturnsProtocol) { + DCMSettingsHandle* handle = CreateTestHandle(); + strcpy(handle->cUploadPrtl, "HTTPS"); + + INT8* protocol = dcmSettingsGetUploadProtocol(handle); + + EXPECT_NE(protocol, nullptr); + EXPECT_STREQ(protocol, "HTTPS"); + + free(handle); +} +TEST(dcmParseConfTest, GetUploadProtocol_NullHandle_ReturnsNull) { + INT8* protocol = dcmSettingsGetUploadProtocol(nullptr); + + EXPECT_EQ(protocol, nullptr); +} + + +TEST(dcmParseConfTest, GetUploadURL_ValidHandle_ReturnsURL) { + DCMSettingsHandle* handle = CreateTestHandle(); + strcpy(handle->cUploadURL, "https://test.example.com/upload"); + + INT8* url = dcmSettingsGetUploadURL(handle); + + EXPECT_NE(url, nullptr); + EXPECT_STREQ(url, "https://test.example.com/upload"); + + free(handle); +} + +TEST(dcmParseConfTest, GetUploadURL_NullHandle_ReturnsNull) { + INT8* url = dcmSettingsGetUploadURL(nullptr); + + EXPECT_EQ(url, nullptr); +} + +TEST(dcmParseConfTest, UnInit_ValidHandle_Success) { + DCMSettingsHandle* handle = CreateTestHandle(); + EXPECT_NE(handle, nullptr); + // Should not crash and handle should be freed + dcmSettingsUnInit(handle); +} + +TEST(dcmParseConfTest, UnInit_NullHandle_NoError) { + // Should handle gracefully without crashing + dcmSettingsUnInit(nullptr); +} + + +TEST(dcmParseConfTest, GetRDKPath_ValidHandle_ReturnsPath) { + DCMSettingsHandle* handle = CreateTestHandle(); + strcpy(handle->cRdkPath, "/usr/bin"); + + INT8* path = dcmSettingsGetRDKPath(handle); + + EXPECT_NE(path, nullptr); + EXPECT_STREQ(path, "/usr/bin"); + + free(handle); +} + +TEST(dcmParseConfTest, GetRDKPath_NullHandle_ReturnsNull) { + INT8* path = dcmSettingsGetRDKPath(nullptr); + + EXPECT_EQ(path, nullptr); +} + + + +// Helper function to create test JSON file +void CreateTestJSONFile(const char* filename, const char* content) { + std::ofstream ofs(filename); + if (ofs.is_open()) { + ofs << content; + ofs.close(); + } +} + +// ==================== dcmSettingParseConf Tests ==================== + +TEST(dcmParseConfTest, ParseConf_ValidHandleAndFile_Success) { + const char* validJson = R"({ + "uploadRepository:uploadProtocol": "HTTPS", + "uploadRepository:URL": "https://test.example.com/upload", + "urn:settings:TelemetryProfile:timeZone": "UTC", + "urn:settings:LogUploadSettings:UploadOnReboot": true, + "urn:settings:LogUploadSettings:PeriodicUpload": "0 */15 * * *", + "urn:settings:FirmwareDownload:difdCron": "0 2 * * *" + })"; + + CreateTestJSONFile("/tmp/test_valid_settings.json", validJson); + + DCMSettingsHandle* handle = CreateTestHandle(); + INT8 logCron[256] = {0}; + INT8 difdCron[256] = {0}; + + INT32 result = dcmSettingParseConf(handle, "/tmp/test_valid_settings.json", logCron, difdCron); + + EXPECT_EQ(result, DCM_FAILURE); + EXPECT_STREQ(handle->cUploadPrtl, ""); + EXPECT_STREQ(handle->cUploadURL, ""); + EXPECT_STREQ(handle->cTimeZone, ""); + EXPECT_STREQ(logCron, ""); + EXPECT_STREQ(difdCron, ""); + + free(handle); + std::remove("/tmp/test_valid_settings.json"); +} + +TEST(dcmParseConfTest, ParseConf_EmptyJSON_Success) { + const char* emptyJson = "{}"; + + CreateTestJSONFile("/tmp/test_empty_settings.json", emptyJson); + + DCMSettingsHandle* handle = CreateTestHandle(); + INT8 logCron[256] = {0}; + INT8 difdCron[256] = {0}; + + INT32 result = dcmSettingParseConf(handle, "/tmp/test_empty_settings.json", logCron, difdCron); + + EXPECT_EQ(result, DCM_FAILURE); + // Should use default values + EXPECT_STREQ(handle->cUploadPrtl, "HTTP"); + EXPECT_STREQ(handle->cUploadURL, DCM_DEF_LOG_URL); + EXPECT_STREQ(handle->cTimeZone, DCM_DEF_TIMEZONE); + EXPECT_STREQ(logCron, ""); + EXPECT_STREQ(difdCron, ""); + + free(handle); + std::remove("/tmp/test_empty_settings.json"); +} + + + + + + + +class DcmSettingsInitTest : public ::testing::Test { +protected: + void SetUp() override { + handle = nullptr; + // Create test property files + CreateTestPropertyFiles(); + } + + void TearDown() override { + if (handle) { + dcmSettingsUnInit(handle); + handle = nullptr; + } + // Clean up test files + CleanupTestFiles(); + } + + void CreateTestPropertyFiles() { + // Create include.properties with RDK_PATH + CreateFile(INCLUDE_PROP_FILE, + "RDK_PATH=/usr/bin\n" + "PERSISTENT_ENTRY=/opt/persistent\n" + "OTHER_PROP=value\n"); + + // Create device.properties with ENABLE_MAINTENANCE + CreateFile(DEVICE_PROP_FILE, + "ENABLE_MAINTENANCE=true\n" + "DEVICE_TYPE=STB\n" + "MODEL=TestModel\n"); + } + + void CreateTestPropertyFilesWithoutMaintenance() { + // Create include.properties with RDK_PATH + CreateFile(INCLUDE_PROP_FILE, + "RDK_PATH=/usr/bin\n" + "PERSISTENT_ENTRY=/opt/persistent\n"); + + // Create device.properties without ENABLE_MAINTENANCE + CreateFile(DEVICE_PROP_FILE, + "DEVICE_TYPE=STB\n" + "MODEL=TestModel\n"); + } + + void CreateTestPropertyFilesWithoutRDKPath() { + // Create include.properties without RDK_PATH + CreateFile(INCLUDE_PROP_FILE, + "PERSISTENT_ENTRY=/opt/persistent\n" + "OTHER_PROP=value\n"); + + // Create device.properties with ENABLE_MAINTENANCE + CreateFile(DEVICE_PROP_FILE, + "ENABLE_MAINTENANCE=true\n" + "DEVICE_TYPE=STB\n"); + } + + void CreateFile(const char* filename, const char* content) { + std::ofstream ofs(filename); + if (ofs.is_open()) { + ofs << content; + ofs.close(); + } + } + + void CleanupTestFiles() { + std::remove(INCLUDE_PROP_FILE); + std::remove(DEVICE_PROP_FILE); + } + + VOID* handle; +}; + +// Test successful initialization with all properties present +TEST_F(DcmSettingsInitTest, SuccessfulInitialization) { + INT32 result = dcmSettingsInit(&handle); + + EXPECT_EQ(result, DCM_SUCCESS); + EXPECT_NE(handle, nullptr); + + // Verify handle contains expected values + DCMSettingsHandle* dcmHandle = (DCMSettingsHandle*)handle; + EXPECT_STREQ(dcmHandle->cRdkPath, "/usr/bin"); + + // Check if maintenance manager flag is set + EXPECT_EQ(dcmSettingsGetMMFlag(), 1); +} +// Test initialization with null handle pointer +/* +TEST_F(DcmSettingsInitTest, NullHandlePointer) { + INT32 result = dcmSettingsInit(nullptr); + + // Should handle gracefully or return failure + // This depends on your implementation - adjust based on expected behavior + EXPECT_EQ(result, DCM_FAILURE); +} +*/ + +// Test initialization without RDK_PATH in properties +TEST_F(DcmSettingsInitTest, MissingRDKPath) { + CleanupTestFiles(); + CreateTestPropertyFilesWithoutRDKPath(); + + INT32 result = dcmSettingsInit(&handle); + + EXPECT_EQ(result, DCM_SUCCESS); + EXPECT_NE(handle, nullptr); + + // Should use default DCM_LIB_PATH + DCMSettingsHandle* dcmHandle = (DCMSettingsHandle*)handle; + EXPECT_STREQ(dcmHandle->cRdkPath, DCM_LIB_PATH); +} + +// Test initialization without ENABLE_MAINTENANCE in properties +TEST_F(DcmSettingsInitTest, MissingMaintenanceFlag) { + CleanupTestFiles(); + CreateTestPropertyFilesWithoutMaintenance(); + + INT32 result = dcmSettingsInit(&handle); + + EXPECT_EQ(result, DCM_SUCCESS); + EXPECT_NE(handle, nullptr); + + // Maintenance manager flag should be 0 when property is missing + EXPECT_EQ(dcmSettingsGetMMFlag(), 0); +} + + + +GTEST_API_ int main(int argc, char *argv[]){ + char testresults_fullfilepath[GTEST_REPORT_FILEPATH_SIZE]; + char buffer[GTEST_REPORT_FILEPATH_SIZE]; + + memset( testresults_fullfilepath, 0, GTEST_REPORT_FILEPATH_SIZE ); + memset( buffer, 0, GTEST_REPORT_FILEPATH_SIZE ); + + snprintf( testresults_fullfilepath, GTEST_REPORT_FILEPATH_SIZE, "json:%s%s" , GTEST_DEFAULT_RESULT_FILEPATH , GTEST_DEFAULT_RESULT_FILENAME); + ::testing::GTEST_FLAG(output) = testresults_fullfilepath; + ::testing::InitGoogleTest(&argc, argv); + //testing::Mock::AllowLeak(mock); + cout << "Starting DCM GTEST ===================>" << endl; + return RUN_ALL_TESTS(); +} diff --git a/unittest/dcm_rbus_gtest.cpp b/unittest/dcm_rbus_gtest.cpp new file mode 100644 index 00000000..b3c229f6 --- /dev/null +++ b/unittest/dcm_rbus_gtest.cpp @@ -0,0 +1,863 @@ +/** + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include "./mocks/mockrbus.h" +//#include "./mocks/mockrbus.cpp" + +extern "C" { +VOID get_rbusProcConf(rbusHandle_t handle, rbusEvent_t const* event, rbusEventSubscription_t* subscription); +void get_rbusAsyncSubCB(rbusHandle_t handle, rbusEventSubscription_t* subscription, rbusError_t error); +VOID get_rbusSetConf(rbusHandle_t handle, rbusEvent_t const* event, rbusEventSubscription_t* subscription); +rbusError_t get_rbusSendEventCB(rbusHandle_t handle, rbusEventSubAction_t action, const INT8* eventName, rbusFilter_t filter, int32_t interval, BOOL* autoPublish); +} + +//#include "../dcm_utils.c" +#include "dcm_types.h" +#define GTEST_DEFAULT_RESULT_FILEPATH "/tmp/Gtest_Report/" +#define GTEST_DEFAULT_RESULT_FILENAME "dcm_cronparse_gtest_report.json" +#define GTEST_REPORT_FILEPATH_SIZE 256 + + +using namespace testing; +using namespace std; +using ::testing::_; +using ::testing::Return; +using ::testing::SetArgPointee; +using ::testing::DoAll; +using ::testing::StrEq; + + +void CreateFile(const char* filename, const char* content) { + std::ofstream ofs(filename); + ofs << content; +} + +void RemoveFile(const char* filename) { + std::remove(filename); +} + +void CreateDirectory(const char* dirname) { + mkdir(dirname, 0755); +} + +void RemoveDirectory(const char* dirname) { + rmdir(dirname); +} + +class DcmRbusTest : public ::testing::Test { +protected: + void SetUp() override { + mockRBus = new StrictMock(); + mock_rbus_set_global_mock(mockRBus); + mock_rbus_reset(); + } + + void TearDown() override { + mock_rbus_clear_global_mock(); + delete mockRBus; + } + + MockRBus* mockRBus; +}; + +TEST_F(DcmRbusTest, dcmRbusInit_Success) { + void* handle = nullptr; + rbusHandle_t mockHandle = mock_rbus_get_mock_handle(); + + EXPECT_CALL(*mockRBus, rbus_checkStatus()) + .WillOnce(Return(RBUS_ENABLED)); + + EXPECT_CALL(*mockRBus, rbus_open(_, _)) + .WillOnce(DoAll(SetArgPointee<0>(mockHandle), Return(RBUS_ERROR_SUCCESS))); + + int result = dcmRbusInit(&handle); + + EXPECT_EQ(result, DCM_SUCCESS); + EXPECT_NE(handle, nullptr); + + // Cleanup + if (handle) { + EXPECT_CALL(*mockRBus, rbusEvent_Unsubscribe(_, _)) + .Times(2) + .WillRepeatedly(Return(RBUS_ERROR_SUCCESS)); + EXPECT_CALL(*mockRBus, rbus_unregDataElements(_, _, _)) + .WillOnce(Return(RBUS_ERROR_SUCCESS)); + EXPECT_CALL(*mockRBus, rbus_close(_)) + .WillOnce(Return(RBUS_ERROR_SUCCESS)); + + dcmRbusUnInit(handle); + } +} + + +TEST_F(DcmRbusTest, dcmRbusInit_rbuscheckstatus_failure) { + void* handle = nullptr; + rbusHandle_t mockHandle = mock_rbus_get_mock_handle(); + + EXPECT_CALL(*mockRBus, rbus_checkStatus()) + .WillOnce(Return(RBUS_DISABLED)); + + int result = dcmRbusInit(&handle); + + EXPECT_EQ(result, DCM_FAILURE); + +} + +TEST_F(DcmRbusTest, dcmRbusInit_rbusopen_failure) { + void* handle = nullptr; + rbusHandle_t mockHandle = mock_rbus_get_mock_handle(); + + EXPECT_CALL(*mockRBus, rbus_checkStatus()) + .WillOnce(Return(RBUS_ENABLED)); + + EXPECT_CALL(*mockRBus, rbus_open(_, _)) + .WillOnce(DoAll(SetArgPointee<0>(mockHandle), Return(RBUS_ERROR_BUS_ERROR))); + int result = dcmRbusInit(&handle); + + EXPECT_EQ(result, DCM_FAILURE); + +} +/* +TEST_F(DcmRbusTest, dcmRbusUnInit_rbus_event_subscribe_fail) { + InSequence seq; + void* handle = nullptr; + rbusHandle_t mockHandle = mock_rbus_get_mock_handle(); + EXPECT_CALL(*mockRBus, rbusEvent_Unsubscribe(_, _)) + .Times(2) + .WillRepeatedly(Return(RBUS_ERROR_BUS_ERROR)); + EXPECT_CALL(*mockRBus, rbus_unregDataElements(_, _, _)) + .WillOnce(Return(RBUS_ERROR_BUS_ERROR)); + EXPECT_CALL(*mockRBus, rbus_close(_)) + .WillOnce(Return(RBUS_ERROR_BUS_ERROR)); + dcmRbusUnInit(handle); + +} + + +TEST_F(DcmRbusTest, dcmRbusUnInit_rbus_close_fail) { + void* handle = nullptr; + rbusHandle_t mockHandle = mock_rbus_get_mock_handle(); + + EXPECT_CALL(*mockRBus, rbusEvent_Unsubscribe(_, _)) + .WillOnce(Return(RBUS_ERROR_SUCCESS)); + EXPECT_CALL(*mockRBus, rbus_close(_)) + .WillOnce(Return(RBUS_ERROR_BUS_ERROR)); + dcmRbusUnInit(&handle); + +} + +TEST_F(DcmRbusTest, dcmRbusUnInit_rbus_unsubscribe_fail) { + void* handle = nullptr; + rbusHandle_t mockHandle = mock_rbus_get_mock_handle(); + + EXPECT_CALL(*mockRBus, rbusEvent_Unsubscribe(_, _)) + .Times(2) + .WillRepeatedly(Return(RBUS_ERROR_BUS_ERROR)); + dcmRbusUnInit(&handle); + +} +*/ +TEST_F(DcmRbusTest, dcmRbusSendEvent_Success) { + // Setup mock DCM handle + DCMRBusHandle dcmHandle; + dcmHandle.pRbusHandle = mock_rbus_get_mock_handle(); + + EXPECT_CALL(*mockRBus, rbusValue_Init(_)) + .Times(1); + + EXPECT_CALL(*mockRBus, rbusValue_SetString(_, _)) + .WillOnce(Return(RBUS_ERROR_SUCCESS)); + + EXPECT_CALL(*mockRBus, rbusObject_Init(_, _)) + .Times(1); + + EXPECT_CALL(*mockRBus, rbusObject_SetValue(_, _, _)) + .WillOnce(Return(RBUS_ERROR_SUCCESS)); + + EXPECT_CALL(*mockRBus, rbusEvent_Publish(_, _)) + .WillOnce(Return(RBUS_ERROR_SUCCESS)); + + EXPECT_CALL(*mockRBus, rbusValue_Release(_)) + .Times(1); + + EXPECT_CALL(*mockRBus, rbusObject_Release(_)) + .Times(1); + + // Set global event subscription flag + extern int g_eventsub; + g_eventsub = 1; + + int result = dcmRbusSendEvent(&dcmHandle); + + EXPECT_EQ(result, DCM_SUCCESS); +} + +TEST_F(DcmRbusTest, SendEvent_NullHandle_Failure) { + INT32 result = dcmRbusSendEvent(NULL); + + EXPECT_EQ(result, DCM_FAILURE); +} + +TEST_F(DcmRbusTest, SendEvent_NullRbusHandle_Failure) { + DCMRBusHandle dcmHandle; + dcmHandle.pRbusHandle = NULL; + + INT32 result = dcmRbusSendEvent(&dcmHandle); + + EXPECT_EQ(result, DCM_FAILURE); +} + +TEST_F(DcmRbusTest, SendEvent_rbusValueInit_Called_rbusEventPublishFails_Failure) { + DCMRBusHandle dcmHandle; + dcmHandle.pRbusHandle = mock_rbus_get_mock_handle(); + EXPECT_CALL(*mockRBus, rbusValue_Init(_)) + .Times(1); + + EXPECT_CALL(*mockRBus, rbusValue_SetString(_, _)) + .WillOnce(Return(RBUS_ERROR_SUCCESS)); + + EXPECT_CALL(*mockRBus, rbusObject_Init(_, _)) + .Times(1); + + EXPECT_CALL(*mockRBus, rbusObject_SetValue(_, _, _)) + .WillOnce(Return(RBUS_ERROR_SUCCESS)); + + EXPECT_CALL(*mockRBus, rbusEvent_Publish(_, _)) + .WillOnce(Return(RBUS_ERROR_BUS_ERROR)); // Publish fails + + EXPECT_CALL(*mockRBus, rbusValue_Release(_)) + .Times(1); + + EXPECT_CALL(*mockRBus, rbusObject_Release(_)) + .Times(1); + + INT32 result = dcmRbusSendEvent(&dcmHandle); + + EXPECT_EQ(result, DCM_FAILURE); +} + + +TEST_F(DcmRbusTest, SubscribeEvents_AllSubscriptionsSucceed_Success) { + DCMRBusHandle dcmHandle; + dcmHandle.pRbusHandle = mock_rbus_get_mock_handle(); + strcpy(dcmHandle.confPath, "/tmp/test.conf"); + dcmHandle.eventSub = 0; + dcmHandle.schedJob = 0; + // First subscription: DCM_RBUS_SETCONF_EVENT + EXPECT_CALL(*mockRBus, rbusEvent_SubscribeAsync( + dcmHandle.pRbusHandle, + _, + _, + _, + &dcmHandle, + 0)) + .Times(2) + .WillRepeatedly(Return(RBUS_ERROR_SUCCESS)); + /* + // Second subscription: DCM_RBUS_PROCCONF_EVENT + EXPECT_CALL(*mockRBus, rbusEvent_SubscribeAsync( + dcmHandle.pRbusHandle, + _, + _, + _, + &dcmHandle, + 0)) + .WillOnce(Return(RBUS_ERROR_SUCCESS)); + */ + // Register data elements for reload event + EXPECT_CALL(*mockRBus, rbus_regDataElements( + dcmHandle.pRbusHandle, + 1, + _)) // &g_dataElements + .WillOnce(Return(RBUS_ERROR_SUCCESS)); + + INT32 result = dcmRbusSubscribeEvents(&dcmHandle); + + EXPECT_EQ(result, DCM_SUCCESS); +} +TEST_F(DcmRbusTest, SubscribeEvents_rbus_regDataElements_failure) { + DCMRBusHandle dcmHandle; + dcmHandle.pRbusHandle = mock_rbus_get_mock_handle(); + // First subscription: DCM_RBUS_SETCONF_EVENT + EXPECT_CALL(*mockRBus, rbusEvent_SubscribeAsync( + dcmHandle.pRbusHandle, + _, + _, + _, + &dcmHandle, + 0)) + .Times(2) + .WillRepeatedly(Return(RBUS_ERROR_SUCCESS)); + // Register data elements for reload event + EXPECT_CALL(*mockRBus, rbus_regDataElements( + dcmHandle.pRbusHandle, + 1, + _)) // &g_dataElements + .WillOnce(Return(RBUS_ERROR_BUS_ERROR)); + EXPECT_CALL(*mockRBus, rbusEvent_Unsubscribe(_, _)) + .Times(2) + .WillRepeatedly(Return(RBUS_ERROR_SUCCESS)); + + INT32 result = dcmRbusSubscribeEvents(&dcmHandle); + + EXPECT_EQ(result, DCM_FAILURE); +} + +TEST_F(DcmRbusTest, SubscribeEvents_secondSubscription_failure) { + InSequence seq; + DCMRBusHandle dcmHandle; + dcmHandle.pRbusHandle = mock_rbus_get_mock_handle(); + // First subscription: DCM_RBUS_SETCONF_EVENT + EXPECT_CALL(*mockRBus, rbusEvent_SubscribeAsync( + dcmHandle.pRbusHandle, + _, + _, + _, + &dcmHandle, + 0)) + .WillOnce(Return(RBUS_ERROR_SUCCESS)); + + // Second subscription: DCM_RBUS_PROCCONF_EVENT + EXPECT_CALL(*mockRBus, rbusEvent_SubscribeAsync( + dcmHandle.pRbusHandle, + _, + _, + _, + &dcmHandle, + 0)) + .WillOnce(Return(RBUS_ERROR_BUS_ERROR)); + EXPECT_CALL(*mockRBus, rbusEvent_Unsubscribe(_, _)) + .WillOnce(Return(RBUS_ERROR_SUCCESS)); + + INT32 result = dcmRbusSubscribeEvents(&dcmHandle); + + + EXPECT_EQ(result, DCM_FAILURE); +} +TEST_F(DcmRbusTest, SubscribeEvents_FirstSubscription_failure) { + InSequence seq; + DCMRBusHandle dcmHandle; + dcmHandle.pRbusHandle = mock_rbus_get_mock_handle(); + // First subscription: DCM_RBUS_SETCONF_EVENT + EXPECT_CALL(*mockRBus, rbusEvent_SubscribeAsync( + dcmHandle.pRbusHandle, + _, + _, + _, + &dcmHandle, + 0)) + .WillOnce(Return(RBUS_ERROR_BUS_ERROR)); + INT32 result = dcmRbusSubscribeEvents(&dcmHandle); + + + EXPECT_EQ(result, DCM_FAILURE); +} +TEST_F(DcmRbusTest, GetT2Version_ValidInputs_Success) { + rbusValue_t mockValue = mock_rbus_create_string_value("2.1.5"); + char versionBuffer[256]; + DCMRBusHandle dcmHandle; + dcmHandle.pRbusHandle = mock_rbus_get_mock_handle(); + memset(versionBuffer, 0, sizeof(versionBuffer)); + EXPECT_CALL(*mockRBus, rbus_get( + dcmHandle.pRbusHandle, + _, // DCM_RBUS_T2_VERSION + _)) + .WillOnce(DoAll(SetArgPointee<2>(mockValue), Return(RBUS_ERROR_SUCCESS))); + + EXPECT_CALL(*mockRBus, rbusValue_GetType(mockValue)) + .WillOnce(Return(RBUS_STRING)); + + EXPECT_CALL(*mockRBus, rbusValue_ToString(mockValue, NULL, 0)) + .WillOnce(Return(strdup("2.1.5"))); + + EXPECT_CALL(*mockRBus, rbusValue_Release(mockValue)) + .Times(1); + + INT32 result = dcmRbusGetT2Version(&dcmHandle, versionBuffer); + + EXPECT_EQ(result, DCM_SUCCESS); + EXPECT_STREQ(versionBuffer, "2.1.5"); +} + +TEST_F(DcmRbusTest, GetT2Version_with_dcm_rbushandle_null) +{ + char versionBuffer[256]; + memset(versionBuffer, 0, sizeof(versionBuffer)); + + INT32 result = dcmRbusGetT2Version(nullptr, versionBuffer); + + EXPECT_EQ(result, DCM_FAILURE); +} +TEST_F(DcmRbusTest, GetT2Version_with_t2version_null) +{ + DCMRBusHandle dcmHandle; + dcmHandle.pRbusHandle = mock_rbus_get_mock_handle(); + INT32 result = dcmRbusGetT2Version(&dcmHandle, nullptr); + + EXPECT_EQ(result, DCM_FAILURE); +} + +TEST_F(DcmRbusTest, GetT2Version_with_rbushandle_null) +{ + DCMRBusHandle dcmHandle; + char versionBuffer[256]; + memset(versionBuffer, 0, sizeof(versionBuffer)); + dcmHandle.pRbusHandle = NULL; + INT32 result = dcmRbusGetT2Version(&dcmHandle, versionBuffer); + + EXPECT_EQ(result, DCM_FAILURE); +} + +TEST_F(DcmRbusTest, GetT2Version_rbusget_fail) { + rbusValue_t mockValue = mock_rbus_create_string_value("2.1.5"); + char versionBuffer[256]; + DCMRBusHandle dcmHandle; + dcmHandle.pRbusHandle = mock_rbus_get_mock_handle(); + memset(versionBuffer, 0, sizeof(versionBuffer)); + EXPECT_CALL(*mockRBus, rbus_get( + dcmHandle.pRbusHandle, + _, // DCM_RBUS_T2_VERSION + _)) + .WillOnce(DoAll(SetArgPointee<2>(mockValue), Return(RBUS_ERROR_BUS_ERROR))); + + INT32 result = dcmRbusGetT2Version(&dcmHandle, versionBuffer); + + EXPECT_EQ(result, DCM_FAILURE); +} + +TEST_F(DcmRbusTest, GetT2Version_rbusvaluetostring_fail) { + rbusValue_t mockValue = mock_rbus_create_string_value("2.1.5"); + char versionBuffer[256]; + DCMRBusHandle dcmHandle; + dcmHandle.pRbusHandle = mock_rbus_get_mock_handle(); + memset(versionBuffer, 0, sizeof(versionBuffer)); + EXPECT_CALL(*mockRBus, rbus_get( + dcmHandle.pRbusHandle, + _, // DCM_RBUS_T2_VERSION + _)) + .WillOnce(DoAll(SetArgPointee<2>(mockValue), Return(RBUS_ERROR_SUCCESS))); + + EXPECT_CALL(*mockRBus, rbusValue_GetType(mockValue)) + .WillOnce(Return(RBUS_STRING)); + + EXPECT_CALL(*mockRBus, rbusValue_ToString(_ , _, _)) + .WillOnce(Return(NULL)); + + EXPECT_CALL(*mockRBus, rbusValue_Release(mockValue)) + .Times(1); + INT32 result = dcmRbusGetT2Version(&dcmHandle, versionBuffer); + + EXPECT_EQ(result, DCM_FAILURE); +} + +/* +TEST_F(DcmRbusTest, ProcConf_ValidInputs_SetsScheduleJobFlag) { + // Verify initial state + // EXPECT_EQ(dcmRbusHandle->schedJob, 0); + DCMRBusHandle* dcmRbusHandle; + rbusEvent_t testEvent; + rbusEventSubscription_t testSubscription; + + rbusHandle_t mockHandle = mock_rbus_get_mock_handle(); + dcmRbusHandle->pRbusHandle = mockHandle; + dcmRbusHandle->eventSub = 1; + dcmRbusHandle->schedJob = 0; // Initially not scheduled + strcpy(dcmRbusHandle->confPath, "/etc/dcm.conf"); + + // Initialize event structure + memset(&testEvent, 0, sizeof(rbusEvent_t)); + testEvent.name = "Device.X_RDKCENTRAL-COM_T2.ProcessConfig"; + testEvent.type = RBUS_EVENT_GENERAL; + testEvent.data = nullptr; + + // Initialize subscription structure + memset(&testSubscription, 0, sizeof(rbusEventSubscription_t)); + testSubscription.eventName = "Device.X_RDKCENTRAL-COM_T2.ProcessConfig"; + testSubscription.userData = nullptr; + + // Call the function + get_rbusProcConf(mockHandle, &testEvent, &testSubscription); + + // Verify schedJob flag is set + EXPECT_EQ(dcmRbusHandle->schedJob, 1); +} +*/ + +class RbusProcConfTest : public ::testing::Test { +protected: + void SetUp() override { + // Initialize mock RBUS handle + mockHandle = (rbusHandle_t)0x12345678; + + // Initialize DCM RBUS handle + dcmRbusHandle = (DCMRBusHandle*)malloc(sizeof(DCMRBusHandle)); + ASSERT_NE(dcmRbusHandle, nullptr); + memset(dcmRbusHandle, 0, sizeof(DCMRBusHandle)); + + dcmRbusHandle->pRbusHandle = mockHandle; + dcmRbusHandle->eventSub = 0; + dcmRbusHandle->schedJob = 0; // Initially not scheduled + strcpy(dcmRbusHandle->confPath, "/etc/dcm.conf"); + + // Initialize event structure + memset(&testEvent, 0, sizeof(rbusEvent_t)); + testEvent.name = "Device.X_RDKCENTRAL-COM_T2.ProcessConfig"; + testEvent.type = RBUS_EVENT_GENERAL; + testEvent.data = nullptr; // ProcConf doesn't use event data + + // Initialize subscription structure + memset(&testSubscription, 0, sizeof(rbusEventSubscription_t)); + testSubscription.eventName = "Device.X_RDKCENTRAL-COM_T2.ProcessConfig"; + testSubscription.userData = dcmRbusHandle; + //testSubscription.handler = rbusProcConf; + + mockRBus = new StrictMock(); + mock_rbus_set_global_mock(mockRBus); + mock_rbus_reset(); + } + + void TearDown() override { + if (dcmRbusHandle) { + free(dcmRbusHandle); + dcmRbusHandle = nullptr; + } + mock_rbus_clear_global_mock(); + delete mockRBus; + } + + rbusHandle_t mockHandle; + DCMRBusHandle* dcmRbusHandle; + rbusEvent_t testEvent; + rbusEventSubscription_t testSubscription; + MockRBus* mockRBus; +}; + +// ==================== Valid Input Test Cases ==================== + +TEST_F(RbusProcConfTest, ProcConf_ValidInputs_SetsScheduleJobFlag) { + // Verify initial state + EXPECT_EQ(dcmRbusHandle->schedJob, 0); + + // Call the function + get_rbusProcConf(mockHandle, &testEvent, &testSubscription); + + // Verify schedJob flag is set + EXPECT_EQ(dcmRbusHandle->schedJob, 1); +} +TEST_F(RbusProcConfTest, ProcConf_NullEvent_ReturnsEarlyWithoutCrash) { + // Store original schedJob value + INT32 originalSchedJob = dcmRbusHandle->schedJob; + + // Call with NULL event + get_rbusProcConf(mockHandle, nullptr, &testSubscription); + + // Verify schedJob is not modified + EXPECT_EQ(dcmRbusHandle->schedJob, originalSchedJob); +} + +TEST_F(RbusProcConfTest, ProcConf_NullSubscription_ReturnsEarlyWithoutCrash) { + // Store original schedJob value + INT32 originalSchedJob = dcmRbusHandle->schedJob; + + // Call with NULL subscription + get_rbusProcConf(mockHandle, &testEvent, nullptr); + + // Verify schedJob is not modified + EXPECT_EQ(dcmRbusHandle->schedJob, originalSchedJob); +} +TEST_F(RbusProcConfTest, ProcConf_NullUserData_ReturnsEarlyWithoutCrash) { + // Set userData to NULL + testSubscription.userData = nullptr; + + // Call function - should return early due to NULL userData + EXPECT_NO_THROW(get_rbusProcConf(mockHandle, &testEvent, &testSubscription)); +} + + + +TEST_F(RbusProcConfTest, rbusAsyncSubCB_subscrption_success) { + rbusError_t error = RBUS_ERROR_SUCCESS; + EXPECT_EQ(dcmRbusHandle->eventSub, 0); + // Call the function + get_rbusAsyncSubCB(mockHandle, &testSubscription, error); + EXPECT_EQ(dcmRbusHandle->eventSub, 1); + +} +TEST_F(RbusProcConfTest, rbusAsyncSubCB_subscrption_event_failure) +{ + rbusError_t error = RBUS_ERROR_BUS_ERROR; + EXPECT_EQ(dcmRbusHandle->eventSub, 0); + // Call the function + get_rbusAsyncSubCB(mockHandle, &testSubscription, error); + EXPECT_EQ(dcmRbusHandle->eventSub, 0); + +} + +TEST_F(RbusProcConfTest, rbusAsyncSubCB_subscrption_null) +{ + rbusError_t error = RBUS_ERROR_SUCCESS; + // Call the function + get_rbusAsyncSubCB(mockHandle, nullptr, error); + +} + +TEST_F( RbusProcConfTest, rbusAsyncSubCB_with_userdata_null) +{ + rbusError_t error = RBUS_ERROR_SUCCESS; + testSubscription.userData = nullptr; + get_rbusAsyncSubCB(mockHandle, &testSubscription, error); + +} + +TEST_F(RbusProcConfTest, rbusSetConf_success) +{ + rbusValue_t mockConfigValue; + const char* newConfigPath = "/etc/test.conf"; + + // Setup expectations - rbusObject_GetValue returns rbusValue_t + EXPECT_CALL(*mockRBus, rbusObject_GetValue(_, _)) + .WillOnce(Return(mockConfigValue)); + get_rbusSetConf(mockHandle, &testEvent, &testSubscription); + +} + + +TEST_F(RbusProcConfTest, rbusSetConf_event_handler_null) +{ + get_rbusSetConf(mockHandle, nullptr, &testSubscription); +} +TEST_F(RbusProcConfTest, rbusSetConf_subscription_null) +{ + get_rbusSetConf(mockHandle, &testEvent, nullptr); +} +TEST_F(RbusProcConfTest, rbusSetConf_with_userdata_null) +{ + testSubscription.userData = nullptr; + get_rbusSetConf(mockHandle, &testEvent, &testSubscription); +} +TEST_F(RbusProcConfTest ,dcmRbusGetEventSubStatus_success) { + dcmRbusHandle->eventSub = 0; + EXPECT_EQ(dcmRbusGetEventSubStatus(dcmRbusHandle), 0); + + dcmRbusHandle->eventSub = 1; + EXPECT_EQ(dcmRbusGetEventSubStatus(dcmRbusHandle), 1); + + dcmRbusHandle->eventSub = -1; + EXPECT_EQ(dcmRbusGetEventSubStatus(dcmRbusHandle), -1); + + dcmRbusHandle->eventSub = 127; + EXPECT_EQ(dcmRbusGetEventSubStatus(dcmRbusHandle), 127); +} +TEST_F(RbusProcConfTest ,dcmRbusGetEventSubStatus_dcmrbus_handle_null) { + + EXPECT_EQ(dcmRbusGetEventSubStatus(nullptr), NULL); + +} +TEST_F(RbusProcConfTest , dcmRbusGetConfPath_success) +{ + strcpy(dcmRbusHandle->confPath, "/etc/dcm.conf"); + EXPECT_EQ(dcmRbusGetConfPath(dcmRbusHandle),dcmRbusHandle->confPath ); + EXPECT_STREQ(dcmRbusHandle->confPath, "/etc/dcm.conf"); +} +TEST_F(RbusProcConfTest , dcmRbusGetConfPath_dcmrbus_handle_null) +{ + EXPECT_EQ(dcmRbusGetConfPath(nullptr), NULL); +} +TEST_F(RbusProcConfTest , dcmRbusSchedResetStatus_success) +{ + dcmRbusHandle->schedJob = 1; + dcmRbusSchedResetStatus(dcmRbusHandle); + EXPECT_EQ(dcmRbusHandle->schedJob,0); +} +TEST_F(RbusProcConfTest , dcmRbusSchedResetStatus_dcmrbus_handle_null) +{ + EXPECT_NO_THROW(dcmRbusSchedResetStatus(nullptr)); +} +TEST_F(RbusProcConfTest , dcmRbusSchedJobStatus_success) +{ + INT32 result = dcmRbusSchedJobStatus(dcmRbusHandle); + EXPECT_EQ(result,0); +} + +TEST_F(RbusProcConfTest , dcmRbusSchedJobStatus_dcm_rbus_null) +{ + INT32 result = dcmRbusSchedJobStatus(nullptr); + EXPECT_EQ(result,0); +} + +TEST_F(RbusProcConfTest , rbusSendEventCB_success) +{ + const INT8* eventName = DCM_RBUS_RELOAD_EVENT; + BOOL autoPublishFlag = 1; + int32_t testInterval = 10; + rbusFilter_t mockFilter; + mockFilter = (rbusFilter_t)0x87654321; + rbusError_t result = get_rbusSendEventCB(mockHandle, RBUS_EVENT_ACTION_SUBSCRIBE, eventName, mockFilter, testInterval, &autoPublishFlag); + EXPECT_EQ(result, RBUS_ERROR_SUCCESS); +} + +TEST_F(RbusProcConfTest , rbusSendEventCB_Eventname_null) +{ + const INT8* eventName = DCM_RBUS_RELOAD_EVENT; + BOOL autoPublishFlag = 1; + int32_t testInterval = 10; + rbusFilter_t mockFilter; + mockFilter = (rbusFilter_t)0x87654321; + rbusError_t result = get_rbusSendEventCB(mockHandle, RBUS_EVENT_ACTION_SUBSCRIBE, NULL, mockFilter, testInterval, &autoPublishFlag); + EXPECT_EQ(result, RBUS_ERROR_BUS_ERROR); +} +TEST_F(RbusProcConfTest , rbusSendEventCB_Eventname_DCM_RBUS_PROCCONF_EVENT) +{ + const INT8* eventName = DCM_RBUS_PROCCONF_EVENT; + BOOL autoPublishFlag = 1; + int32_t testInterval = 10; + rbusFilter_t mockFilter; + mockFilter = (rbusFilter_t)0x87654321; + rbusError_t result = get_rbusSendEventCB(mockHandle, RBUS_EVENT_ACTION_SUBSCRIBE, eventName, mockFilter, testInterval, &autoPublishFlag); + EXPECT_EQ(result, RBUS_ERROR_SUCCESS); +} + + + +/* +#include +#include + + +extern "C" { +#include "dcm_rbus.h" +#include "dcm_types.h" +//#include "mockrbus.cpp" +} +//#include "dcm_rbus.c" +#include "./mocks/mockrbus.h‎" +#define GTEST_DEFAULT_RESULT_FILEPATH "/tmp/Gtest_Report/" +#define GTEST_DEFAULT_RESULT_FILENAME "dcm_cronparse_gtest_report.json" +#define GTEST_REPORT_FILEPATH_SIZE 256 + + +using namespace testing; +using namespace std; +using ::testing::_; +using ::testing::Return; +using ::testing::SetArgPointee; +using ::testing::DoAll; +using ::testing::StrEq; + + +class DcmRbusTest : public ::testing::Test { +protected: + void SetUp() override { + mockRBus = new StrictMock(); + mock_rbus_set_global_mock(mockRBus); + mock_rbus_reset(); + } + + void TearDown() override { + mock_rbus_clear_global_mock(); + delete mockRBus; + } + + MockRBus* mockRBus; +}; + +TEST_F(DcmRbusTest, dcmRbusInit_Success) { + void* handle = nullptr; + rbusHandle_t mockHandle = mock_rbus_get_mock_handle(); + + EXPECT_CALL(*mockRBus, rbus_checkStatus()) + .WillOnce(Return(RBUS_ENABLED)); + + EXPECT_CALL(*mockRBus, rbus_open(_, _)) + .WillOnce(DoAll(SetArgPointee<0>(mockHandle), Return(RBUS_ERROR_SUCCESS))); + + int result = dcmRbusInit(&handle); + + EXPECT_EQ(result, DCM_SUCCESS); + EXPECT_NE(handle, nullptr); + + // Cleanup + if (handle) { + EXPECT_CALL(*mockRBus, rbusEvent_Unsubscribe(_, _)) + .Times(2) + .WillRepeatedly(Return(RBUS_ERROR_SUCCESS)); + EXPECT_CALL(*mockRBus, rbus_unregDataElements(_, _, _)) + .WillOnce(Return(RBUS_ERROR_SUCCESS)); + EXPECT_CALL(*mockRBus, rbus_close(_)) + .WillOnce(Return(RBUS_ERROR_SUCCESS)); + + dcmRbusUnInit(handle); + } +} + +TEST_F(DcmRbusTest, dcmRbusSendEvent_Success) { + // Setup mock DCM handle + DCMRBusHandle dcmHandle; + dcmHandle.pRbusHandle = mock_rbus_get_mock_handle(); + + EXPECT_CALL(*mockRBus, rbusValue_Init(_)) + .Times(1); + + EXPECT_CALL(*mockRBus, rbusValue_SetString(_, _)) + .WillOnce(Return(RBUS_ERROR_SUCCESS)); + + EXPECT_CALL(*mockRBus, rbusObject_Init(_, _)) + .Times(1); + + EXPECT_CALL(*mockRBus, rbusObject_SetValue(_, _, _)) + .WillOnce(Return(RBUS_ERROR_SUCCESS)); + + EXPECT_CALL(*mockRBus, rbusEvent_Publish(_, _)) + .WillOnce(Return(RBUS_ERROR_SUCCESS)); + + EXPECT_CALL(*mockRBus, rbusValue_Release(_)) + .Times(1); + + EXPECT_CALL(*mockRBus, rbusObject_Release(_)) + .Times(1); + + // Set global event subscription flag + extern int g_eventsub; + g_eventsub = 1; + + int result = dcmRbusSendEvent(&dcmHandle); + + EXPECT_EQ(result, DCM_SUCCESS); +} +*/ +GTEST_API_ int main(int argc, char *argv[]){ + char testresults_fullfilepath[GTEST_REPORT_FILEPATH_SIZE]; + char buffer[GTEST_REPORT_FILEPATH_SIZE]; + + memset( testresults_fullfilepath, 0, GTEST_REPORT_FILEPATH_SIZE ); + memset( buffer, 0, GTEST_REPORT_FILEPATH_SIZE ); + + snprintf( testresults_fullfilepath, GTEST_REPORT_FILEPATH_SIZE, "json:%s%s" , GTEST_DEFAULT_RESULT_FILEPATH , GTEST_DEFAULT_RESULT_FILENAME); + ::testing::GTEST_FLAG(output) = testresults_fullfilepath; + ::testing::InitGoogleTest(&argc, argv); + //testing::Mock::AllowLeak(mock); + cout << "Starting DCM GTEST ===================>" << endl; + return RUN_ALL_TESTS(); +} diff --git a/unittest/dcm_schedjob_gtest.cpp b/unittest/dcm_schedjob_gtest.cpp new file mode 100644 index 00000000..1b76d667 --- /dev/null +++ b/unittest/dcm_schedjob_gtest.cpp @@ -0,0 +1,377 @@ +/** + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include + +extern "C" { +#include "dcm_cronparse.h" +#include "../dcm_types.h" + +} +#include "dcm_utils.h" +/*#include "rdm_types.h" +#include "rdm.h" +#include "rdm_utils.h" +*/ +/* + #include "mocks/mock_curl.h" +extern "C" { + #include "rdm_curldownload.h" +} +*/ + +#define GTEST_DEFAULT_RESULT_FILEPATH "/tmp/Gtest_Report/" +#define GTEST_DEFAULT_RESULT_FILENAME "dcm_cronparse_gtest_report.json" +#define GTEST_REPORT_FILEPATH_SIZE 256 + + +using namespace testing; +using namespace std; +using ::testing::_; +using ::testing::Return; +using ::testing::SetArgPointee; +using ::testing::DoAll; +using ::testing::StrEq; +#include "dcm_schedjob.h" + + +// Mock callback function for scheduler +void MockCallback(INT8 *name, VOID *userData) { + int *callbackHit = static_cast(userData); + (*callbackHit)++; +} + +// Fixture for scheduler tests +class DcmSchedJobTest : public ::testing::Test { +protected: + VOID *schedHandle = nullptr; + int callbackHit = 0; + + virtual void SetUp() override { + schedHandle = dcmSchedAddJob((INT8*)"TestJob", MockCallback, &callbackHit); + ASSERT_NE(schedHandle, nullptr); + } + + virtual void TearDown() override { + if (schedHandle) { + dcmSchedRemoveJob(schedHandle); + schedHandle = nullptr; + } + } +}; + +TEST_F(DcmSchedJobTest, AddJobAndRemoveJob) { + // Job was added in SetUp, should be non-null + ASSERT_NE(schedHandle, nullptr); + // RemoveJob called in TearDown, should not crash +} +/* +TEST_F(DcmSchedJobTest, StartJobWithInvalidCronPatternFails) { + // Should fail with invalid cron pattern + INT32 ret = dcmSchedStartJob(schedHandle, (INT8*)"invalid pattern"); + EXPECT_EQ(ret, DCM_FAILURE); +} +*/ +/* +TEST_F(DcmSchedJobTest, StartAndStopJobWithValidCronPattern) { + // A valid cron pattern (e.g., every minute: "* * * * *") + INT32 ret = dcmSchedStartJob(schedHandle, (INT8*)"* * * * *"); + EXPECT_EQ(ret, DCM_SUCCESS); + + ret = dcmSchedStopJob(schedHandle); + EXPECT_EQ(ret, DCM_SUCCESS); +} +*/ + +/* +TEST_F(DcmSchedJobTest, SchedulerCallbackIsCalledOnTimeout) { + // Use a cron pattern that triggers almost immediately for the test + INT32 ret = dcmSchedStartJob(schedHandle, (INT8*)"* * * * *"); // every minute + EXPECT_EQ(ret, DCM_SUCCESS); + + // Wait a bit longer than a second to allow the callback to be triggered + // (Depending on cron parser implementation, you may need to adjust this) + sleep(2); + + EXPECT_GT(callbackHit, 0); + + dcmSchedStopJob(schedHandle); +} +*/ +TEST(DcmSchedJobStandaloneTest, AddJobWithNullNameReturnsNull) { + VOID *handle = dcmSchedAddJob(nullptr, MockCallback, nullptr); + EXPECT_EQ(handle, nullptr); +} + +TEST(DcmSchedJobStandaloneTest, StartJobWithNullHandleFails) { + INT32 ret = dcmSchedStartJob(nullptr, (INT8*)"* * * * *"); + EXPECT_EQ(ret, DCM_FAILURE); +} + +TEST(DcmSchedJobStandaloneTest, StopJobWithNullHandleFails) { + INT32 ret = dcmSchedStopJob(nullptr); + EXPECT_EQ(ret, DCM_FAILURE); +} + +TEST(DcmSchedJobStandaloneTest, RemoveJobWithNullHandleDoesNothing) { + // Should not crash + dcmSchedRemoveJob(nullptr); +} + +// +/* +extern "C" { +INT32 dcmCronParseExp(INT8 *pattern, dcmCronParseData *parseData) { + // Simulate success for a valid pattern, failure for "fail" + if (strcmp((const char *)pattern, "fail") == 0) { + return DCM_FAILURE; + } + return DCM_SUCCESS; +} +} */ + +class DcmSchedStartJobTest : public ::testing::Test { +protected: + DCMScheduler sched; + + void SetUp() override { + memset(&sched, 0, sizeof(sched)); + pthread_mutex_init(&sched.tMutex, nullptr); + pthread_cond_init(&sched.tCond, nullptr); + } + void TearDown() override { + pthread_mutex_destroy(&sched.tMutex); + pthread_cond_destroy(&sched.tCond); + } +}; + +TEST_F(DcmSchedStartJobTest, NullHandleReturnsFailure) { + INT32 ret = dcmSchedStartJob(nullptr, (INT8*)"* * * * *"); + EXPECT_EQ(ret, DCM_FAILURE); +} + +TEST_F(DcmSchedStartJobTest, NullPatternReturnsFailure) { + INT32 ret = dcmSchedStartJob(&sched, nullptr); + EXPECT_EQ(ret, DCM_FAILURE); +} + +TEST_F(DcmSchedStartJobTest, CronParseSuccessSetsStartSchedAndSignals) { + sched.startSched = 0; + INT32 ret = dcmSchedStartJob(&sched, (INT8*)"* * * * *"); + EXPECT_EQ(ret, DCM_SUCCESS); + EXPECT_EQ(sched.startSched, 1); +} + +TEST_F(DcmSchedStartJobTest, CronParseFailUnsetsStartSched) { + sched.startSched = 1; + INT32 ret = dcmSchedStartJob(&sched, (INT8*)"fail"); + EXPECT_EQ(ret, DCM_FAILURE); + EXPECT_EQ(sched.startSched, 0); +} + +class DcmSchedStopJobTest : public ::testing::Test { +protected: + DCMScheduler sched; + + void SetUp() override { + memset(&sched, 0, sizeof(sched)); + pthread_mutex_init(&sched.tMutex, nullptr); + pthread_cond_init(&sched.tCond, nullptr); + sched.startSched = 1; + } + void TearDown() override { + pthread_mutex_destroy(&sched.tMutex); + pthread_cond_destroy(&sched.tCond); + } +}; + +TEST_F(DcmSchedStopJobTest, NullHandleReturnsFailure) { + INT32 ret = dcmSchedStopJob(nullptr); + EXPECT_EQ(ret, DCM_FAILURE); +} + +TEST_F(DcmSchedStopJobTest, StopJobSetsStartSchedToZeroAndReturnsSuccess) { + sched.startSched = 1; + INT32 ret = dcmSchedStopJob(&sched); + EXPECT_EQ(ret, DCM_SUCCESS); + EXPECT_EQ(sched.startSched, 0); +} + + + +// Mock callback +void* mockCallback(void* arg) { return NULL; } + +// 1. Null job name +TEST(DcmSchedAddJobTest, ReturnsNullWhenJobNameIsNull) { + void* result = dcmSchedAddJob(NULL, mockCallback, NULL); + EXPECT_EQ(result, nullptr); +} +/* + +// 2. Memory allocation failure +TEST(DcmSchedAddJobTest, ReturnsNullWhenMallocFails) { + // Simulate malloc failure (if you have wrapper/mocking infra) + void* result = dcmSchedAddJob((INT8*)"Job1", mockCallback, NULL); + // Expected NULL if malloc fails internally + EXPECT_EQ(result, nullptr); +} + +// 3. Mutex initialization failure +TEST(DcmSchedAddJobTest, ReturnsNullWhenMutexInitFails) { + // Simulate pthread_mutex_init failure + void* result = dcmSchedAddJob((INT8*)"Job2", mockCallback, NULL); + EXPECT_EQ(result, nullptr); +} + +// 4. Condition variable initialization failure +TEST(DcmSchedAddJobTest, ReturnsNullWhenCondInitFails) { + // Simulate pthread_cond_init failure + void* result = dcmSchedAddJob((INT8*)"Job3", mockCallback, NULL); + EXPECT_EQ(result, nullptr); +} + +// 5. Thread creation failure +TEST(DcmSchedAddJobTest, ReturnsNullWhenThreadCreationFails) { + // Simulate pthread_create failure + void* result = dcmSchedAddJob((INT8*)"Job4", mockCallback, NULL); + EXPECT_EQ(result, nullptr); +} +*/ +// 6. Successful creation +TEST(DcmSchedAddJobTest, ReturnsValidHandleOnSuccess) { + void* handle = dcmSchedAddJob((INT8*)"Job5", mockCallback, (void*)1234); + ASSERT_NE(handle, nullptr); + + DCMScheduler* sched = (DCMScheduler*)handle; + EXPECT_STREQ(sched->name, "Job5"); + EXPECT_EQ(sched->pDcmCB, mockCallback); + EXPECT_EQ(sched->pUserData, (void*)1234); + EXPECT_FALSE(sched->terminated); + EXPECT_FALSE(sched->startSched); + + // Cleanup (important to avoid leaks or threads left running) + pthread_cancel(sched->tId); + pthread_join(sched->tId, NULL); + pthread_mutex_destroy(&sched->tMutex); + pthread_cond_destroy(&sched->tCond); + free(sched); +} + + + +/* +class DCMSchedRemoveJobTest : public ::testing::Test { + + void SetUp() override { + + } + void TearDown() override { + + } +}; + +TEST(DCMSchedRemoveJobTest, NullHandle_ShouldNotCrash) +{ + // Should simply return when pHandle == NULL + EXPECT_NO_THROW({ + dcmSchedRemoveJob(NULL); + }); +} + +TEST(DCMSchedRemoveJobTest, ValidHandle_ShouldCleanUpResources) +{ + DCMScheduler* sched = (DCMScheduler*)malloc(sizeof(DCMScheduler)); + ASSERT_NE(sched, nullptr); + + pthread_mutex_init(&sched->tMutex, NULL); + pthread_cond_init(&sched->tCond, NULL); + sched->startSched = true; + sched->terminated = false; + + // Create dummy thread + pthread_create(&sched->tId, NULL, DummyThread, sched); + + // Call function under test + dcmSchedRemoveJob(sched); + + // Nothing to directly assert since memory is freed, + // but test passes if no crash / deadlock / UB occurs. + SUCCEED(); +} + +TEST(DCMSchedRemoveJobTest, ThreadShouldBeJoinedAndTerminated) +{ + DCMScheduler* sched = (DCMScheduler*)malloc(sizeof(DCMScheduler)); + ASSERT_NE(sched, nullptr); + + pthread_mutex_init(&sched->tMutex, NULL); + pthread_cond_init(&sched->tCond, NULL); + sched->startSched = true; + sched->terminated = false; + + // Create a dummy thread to simulate scheduler + pthread_create(&sched->tId, NULL, DummyThread, sched); + + // Remove job + dcmSchedRemoveJob(sched); + + // If we reached here — thread joined and memory freed correctly + SUCCEED(); +} + +TEST(DCMSchedRemoveJobTest, MultipleRemoveCalls_ShouldNotCrash) +{ + DCMScheduler* sched = (DCMScheduler*)malloc(sizeof(DCMScheduler)); + ASSERT_NE(sched, nullptr); + + pthread_mutex_init(&sched->tMutex, NULL); + pthread_cond_init(&sched->tCond, NULL); + sched->startSched = true; + sched->terminated = false; + pthread_create(&sched->tId, NULL, DummyThread, sched); + + // First call should succeed + dcmSchedRemoveJob(sched); + + // Calling again with freed pointer should be safe if handled + // (not expected in real use, but for robustness) + EXPECT_NO_THROW({ + dcmSchedRemoveJob(NULL); + }); +} + +*/ +GTEST_API_ int main(int argc, char *argv[]){ + char testresults_fullfilepath[GTEST_REPORT_FILEPATH_SIZE]; + char buffer[GTEST_REPORT_FILEPATH_SIZE]; + + memset( testresults_fullfilepath, 0, GTEST_REPORT_FILEPATH_SIZE ); + memset( buffer, 0, GTEST_REPORT_FILEPATH_SIZE ); + + snprintf( testresults_fullfilepath, GTEST_REPORT_FILEPATH_SIZE, "json:%s%s" , GTEST_DEFAULT_RESULT_FILEPATH , GTEST_DEFAULT_RESULT_FILENAME); + ::testing::GTEST_FLAG(output) = testresults_fullfilepath; + ::testing::InitGoogleTest(&argc, argv); + //testing::Mock::AllowLeak(mock); + cout << "Starting DCM GTEST ===================>" << endl; + return RUN_ALL_TESTS(); +} diff --git a/unittest/dcm_utils_gtest.cpp b/unittest/dcm_utils_gtest.cpp new file mode 100644 index 00000000..e64f9bf0 --- /dev/null +++ b/unittest/dcm_utils_gtest.cpp @@ -0,0 +1,244 @@ +/** + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +/* +extern "C" { +#include "dcm_cronparse.h" +//#include "dcm_cronparse.c" +#include "../dcm_types.h" + + +#include "../dcm_types.h" + +} +*/ + +//#include "dcm_cronparse.h" +#include "../dcm_types.h" + +//#include "dcm_utils.h" +//#include "dcm_cronparse.h" +//#include "../dcm_types.h" + +/*#include "rdm_types.h" +#include "rdm.h" +#include "rdm_utils.h" +*/ +/* + #include "mocks/mock_curl.h" +extern "C" { + #include "rdm_curldownload.h" +} +*/ + +#define GTEST_DEFAULT_RESULT_FILEPATH "/tmp/Gtest_Report/" +#define GTEST_DEFAULT_RESULT_FILENAME "dcm_cronparse_gtest_report.json" +#define GTEST_REPORT_FILEPATH_SIZE 256 + + +using namespace testing; +using namespace std; +using ::testing::_; +using ::testing::Return; +using ::testing::SetArgPointee; +using ::testing::DoAll; +using ::testing::StrEq; + +//extern static INT32 dcmCronParseToUpper(INT8* str); +class DCMUtilsTest: public ::testing::Test { +protected: + void SetUp() override { + } + + void TearDown() override { + } +}; + +#include "dcm_utils.h" + +// Helper: create a file with given content +void CreateFile(const char* filename, const char* content) { + std::ofstream ofs(filename); + ofs << content; +} + +void RemoveFile(const char* filename) { + std::remove(filename); +} + +// Test dcmUtilsFilePresentCheck +TEST(DCMUtilsTest, FilePresentCheck_ValidFile) { + const char* fname = "/tmp/dcm_testfile_present"; + CreateFile(fname, "test"); + EXPECT_EQ(dcmUtilsFilePresentCheck(fname), DCM_SUCCESS); + RemoveFile(fname); +} + +TEST(DCMUtilsTest, FilePresentCheck_NullPtr) { + EXPECT_EQ(dcmUtilsFilePresentCheck(NULL), DCM_FAILURE); +} + +TEST(DCMUtilsTest, FilePresentCheck_FileNotExist) { + const char* fname = "/tmp/dcm_testfile_not_exist"; + RemoveFile(fname); + EXPECT_EQ(dcmUtilsFilePresentCheck(fname), DCM_FAILURE); +} + +// Test dcmUtilsCopyCommandOutput +TEST(DCMUtilsTest, CopyCommandOutput_Echo) { + char output[128]; + dcmUtilsCopyCommandOutput((INT8*)"echo hello", (INT8*)output, sizeof(output)); + EXPECT_STREQ(output, "hello"); +} + +TEST(DCMUtilsTest, CopyCommandOutput_NullOut) { + // Should not crash or segfault + dcmUtilsCopyCommandOutput((INT8*)"echo test", NULL, 0); +} + + +// Test dcmUtilsSysCmdExec +TEST(DCMUtilsTest, SysCmdExec_Valid) { + EXPECT_EQ(dcmUtilsSysCmdExec((INT8*)"echo test"), DCM_SUCCESS); +} + +TEST(DCMUtilsTest, SysCmdExec_Null) { + EXPECT_EQ(dcmUtilsSysCmdExec(NULL), DCM_FAILURE); +} + +// Test dcmUtilsGetFileEntry +TEST(DCMUtilsTest, GetFileEntry_ValidKey) { + const char* fname = "/tmp/dcm_test_kv"; + CreateFile(fname, "key1=value1\nkey2=value2\n"); + INT8* result = dcmUtilsGetFileEntry(fname, "key2"); + ASSERT_NE(result, nullptr); + EXPECT_STREQ(result, "value2"); + free(result); + RemoveFile(fname); +} + +TEST(DCMUtilsTest, GetFileEntry_KeyNotFound) { + const char* fname = "/tmp/dcm_test_kv"; + CreateFile(fname, "key1=value1\n"); + INT8* result = dcmUtilsGetFileEntry(fname, "key2"); + EXPECT_EQ(result, nullptr); + RemoveFile(fname); +} + +TEST(DCMUtilsTest, GetFileEntry_NullArgs) { + EXPECT_EQ(dcmUtilsGetFileEntry(NULL, "key"), nullptr); + EXPECT_EQ(dcmUtilsGetFileEntry("file", NULL), nullptr); +} + +// Test dcmUtilsRemovePIDfile +TEST(DCMUtilsTest, RemovePIDfile_Existing) { + // Create a fake PID file + CreateFile(DCM_PID_FILE, "1234\n"); + dcmUtilsRemovePIDfile(); + // Now file should not exist + std::ifstream ifs(DCM_PID_FILE); + EXPECT_FALSE(ifs.good()); +} + +// Test dcmUtilsCheckDaemonStatus (basic functionality) +TEST(DCMUtilsTest, CheckDaemonStatus_NewFile) { + RemoveFile(DCM_PID_FILE); + EXPECT_EQ(dcmUtilsCheckDaemonStatus(), DCM_SUCCESS); + RemoveFile(DCM_PID_FILE); +} + +TEST(DCMUtilsTest, CheckDaemonStatus_PidFileExists_ProcessNotRunning) { + // Create PID file with a PID that doesn't exist (99999) + CreateFile(DCM_PID_FILE, "99999"); + + EXPECT_EQ(dcmUtilsCheckDaemonStatus(), DCM_SUCCESS); + + // Verify new PID file was created with current process ID + EXPECT_EQ(dcmUtilsFilePresentCheck(DCM_PID_FILE), DCM_SUCCESS); +} + +// Test when PID file exists and process appears to be running +TEST(DCMUtilsTest, PidFileExists_ProcessRunning_ReturnsFailure) { + // Use PID 1 (init process) which should always exist on Linux systems + CreateFile(DCM_PID_FILE, "1"); + + EXPECT_EQ(dcmUtilsCheckDaemonStatus(), DCM_FAILURE); + + // Original PID file should still exist + EXPECT_EQ(dcmUtilsFilePresentCheck(DCM_PID_FILE), DCM_SUCCESS); +} +/* +// Test when PID file exists with current process PID (self-detection) +TEST(DCMUtilsTest, PidFileExists_CurrentProcess_ReturnsFailure) { + // Create PID file with current process PID + pid_t currentPid = getpid(); + char pidStr[32]; + snprintf(pidStr, sizeof(pidStr), "%d", currentPid); + CreateFile(DCM_PID_FILE, pidStr); + + EXPECT_EQ(dcmUtilsCheckDaemonStatus(), DCM_FAILURE); +} +*/ +/* +// Alternative test: Create read-only file that cannot be overwritten +TEST(DCMUtilsTest, CannotOverwriteReadOnlyPidFile_ReturnsFailure) { + // First create a PID file with invalid PID so it passes the first check + std::ofstream ofs(DCM_PID_FILE); + ofs << "99999"; // Non-existent PID + ofs.close(); + + // Make the file read-only (no write permissions) + ASSERT_EQ(chmod(DCM_PID_FILE, 0444), 0) << "Failed to make file read-only"; + + // This should fail when trying to open for writing + EXPECT_EQ(dcmUtilsCheckDaemonStatus(), DCM_FAILURE); + + // Restore write permissions for cleanup + chmod(DCM_PID_FILE, 0644); +} +*/ +// Test dcmIARMEvntSend (does nothing) +TEST(DCMUtilsTest, IARMEvntSend) { + EXPECT_EQ(dcmIARMEvntSend(0), DCM_SUCCESS); +} + +TEST(DCMUtilsTest, LogInit_Success) { + DCMLOGInit(); +} + +GTEST_API_ int main(int argc, char *argv[]){ + char testresults_fullfilepath[GTEST_REPORT_FILEPATH_SIZE]; + char buffer[GTEST_REPORT_FILEPATH_SIZE]; + + memset( testresults_fullfilepath, 0, GTEST_REPORT_FILEPATH_SIZE ); + memset( buffer, 0, GTEST_REPORT_FILEPATH_SIZE ); + + snprintf( testresults_fullfilepath, GTEST_REPORT_FILEPATH_SIZE, "json:%s%s" , GTEST_DEFAULT_RESULT_FILEPATH , GTEST_DEFAULT_RESULT_FILENAME); + ::testing::GTEST_FLAG(output) = testresults_fullfilepath; + ::testing::InitGoogleTest(&argc, argv); + //testing::Mock::AllowLeak(mock); + cout << "Starting DCM GTEST ===================>" << endl; + return RUN_ALL_TESTS(); +} + + diff --git a/unittest/mocks/mockrbus.cpp b/unittest/mocks/mockrbus.cpp new file mode 100644 index 00000000..db862ce4 --- /dev/null +++ b/unittest/mocks/mockrbus.cpp @@ -0,0 +1,418 @@ +#include "mockrbus.h" +#include +#include +#include + +// Global mock instance +MockRBus* g_mockRBus = nullptr; + +// Mock value and object structures for internal use +struct MockRBusValue { + rbusValueType_t type; + char stringValue[256]; + int intValue; + bool boolValue; + double doubleValue; + uint32_t uint32Value; + int32_t int32Value; +}; + +struct MockRBusObject { + char name[256]; + struct { + char key[64]; + MockRBusValue* value; + } properties[10]; + int propertyCount; +}; + +// Static variables to track mock state +static char g_mockStringBuffer[512] = "mock_default_value"; +static rbusHandle_t g_mockHandle = (rbusHandle_t)0x12345678; +static int g_mockCallCount = 0; + +// Stored callback pointers for test triggering +static rbusEventHandler_t g_storedEventHandler = nullptr; +static rbusEventSubAsyncHandler_t g_storedAsyncHandler = nullptr; +static void* g_storedUserData = nullptr; + +// Mock control functions +void mock_rbus_reset() { + g_mockCallCount = 0; + strcpy(g_mockStringBuffer, "mock_default_value"); + g_storedEventHandler = nullptr; + g_storedAsyncHandler = nullptr; + g_storedUserData = nullptr; +} + +void mock_rbus_set_global_mock(MockRBus* mockRBus) { + g_mockRBus = mockRBus; +} + +void mock_rbus_clear_global_mock() { + g_mockRBus = nullptr; +} + +void mock_rbus_set_string_value(const char* value) { + if (value) { + strncpy(g_mockStringBuffer, value, sizeof(g_mockStringBuffer) - 1); + g_mockStringBuffer[sizeof(g_mockStringBuffer) - 1] = '\0'; + } +} + +rbusHandle_t mock_rbus_get_mock_handle() { + return g_mockHandle; +} + +rbusValue_t mock_rbus_create_string_value(const char* str) { + MockRBusValue* mockValue = (MockRBusValue*)malloc(sizeof(MockRBusValue)); + if (mockValue) { + memset(mockValue, 0, sizeof(MockRBusValue)); + mockValue->type = RBUS_STRING; + if (str) { + strncpy(mockValue->stringValue, str, sizeof(mockValue->stringValue) - 1); + mockValue->stringValue[sizeof(mockValue->stringValue) - 1] = '\0'; + } + } + return (rbusValue_t)mockValue; +} + +rbusObject_t mock_rbus_create_object(const char* name) { + MockRBusObject* mockObject = (MockRBusObject*)malloc(sizeof(MockRBusObject)); + if (mockObject) { + memset(mockObject, 0, sizeof(MockRBusObject)); + if (name) { + strncpy(mockObject->name, name, sizeof(mockObject->name) - 1); + mockObject->name[sizeof(mockObject->name) - 1] = '\0'; + } + } + return (rbusObject_t)mockObject; +} + +// Helper functions for triggering callbacks +void mock_rbus_trigger_event_callback(rbusEventHandler_t handler, rbusHandle_t handle, + rbusEvent_t* event, rbusEventSubscription_t* subscription) { + if (handler && event && subscription) { + handler(handle, event, subscription); + } +} + +void mock_rbus_trigger_async_callback(rbusEventSubAsyncHandler_t handler, rbusHandle_t handle, + rbusEventSubscription_t* subscription, rbusError_t error) { + if (handler && subscription) { + handler(handle, subscription, error); + } +} + +extern "C" { + +// RBUS API Mock Implementations +rbusStatus_t rbus_checkStatus(void) { + if (g_mockRBus) { + return g_mockRBus->rbus_checkStatus(); + } + return RBUS_ENABLED; // Default success for basic functionality +} + +rbusError_t rbus_open(rbusHandle_t* handle, const char* componentName) { + g_mockCallCount++; + if (g_mockRBus) { + return g_mockRBus->rbus_open(handle, componentName); + } + // Default implementation + if (handle) { + *handle = g_mockHandle; + return RBUS_ERROR_SUCCESS; + } + return RBUS_ERROR_INVALID_INPUT; +} + +rbusError_t rbus_close(rbusHandle_t handle) { + g_mockCallCount++; + if (g_mockRBus) { + return g_mockRBus->rbus_close(handle); + } + return RBUS_ERROR_SUCCESS; // Default success +} + +rbusError_t rbus_get(rbusHandle_t handle, const char* paramName, rbusValue_t* paramValue) { + g_mockCallCount++; + if (g_mockRBus) { + return g_mockRBus->rbus_get(handle, paramName, paramValue); + } + + // Default implementation - create a mock value + if (paramValue) { + MockRBusValue* mockValue = (MockRBusValue*)malloc(sizeof(MockRBusValue)); + if (mockValue) { + memset(mockValue, 0, sizeof(MockRBusValue)); + mockValue->type = RBUS_STRING; + strcpy(mockValue->stringValue, g_mockStringBuffer); + *paramValue = (rbusValue_t)mockValue; + return RBUS_ERROR_SUCCESS; + } + return RBUS_ERROR_OUT_OF_RESOURCES; + } + return RBUS_ERROR_INVALID_INPUT; +} + +rbusError_t rbus_regDataElements(rbusHandle_t handle, int numElements, rbusDataElement_t* elements) { + g_mockCallCount++; + if (g_mockRBus) { + return g_mockRBus->rbus_regDataElements(handle, numElements, elements); + } + return RBUS_ERROR_SUCCESS; // Default success +} + +rbusError_t rbus_unregDataElements(rbusHandle_t handle, int numElements, rbusDataElement_t* elements) { + g_mockCallCount++; + if (g_mockRBus) { + return g_mockRBus->rbus_unregDataElements(handle, numElements, elements); + } + return RBUS_ERROR_SUCCESS; // Default success +} + +// Event functions +rbusError_t rbusEvent_SubscribeAsync(rbusHandle_t handle, const char* eventName, + rbusEventHandler_t handler, rbusEventSubAsyncHandler_t asyncHandler, + void* userData, int timeout) { + g_mockCallCount++; + if (g_mockRBus) { + return g_mockRBus->rbusEvent_SubscribeAsync(handle, eventName, handler, asyncHandler, userData, timeout); + } + + // Store callbacks for potential triggering in tests + g_storedEventHandler = handler; + g_storedAsyncHandler = asyncHandler; + g_storedUserData = userData; + + // Default implementation - simulate successful subscription + if (asyncHandler && userData) { + // Create a mock subscription for the callback + rbusEventSubscription_t subscription = {0}; + subscription.eventName = eventName; + subscription.userData = userData; + + // Trigger the async callback with success + asyncHandler(handle, &subscription, RBUS_ERROR_SUCCESS); + } + + return RBUS_ERROR_SUCCESS; +} + +rbusError_t rbusEvent_Unsubscribe(rbusHandle_t handle, const char* eventName) { + g_mockCallCount++; + if (g_mockRBus) { + return g_mockRBus->rbusEvent_Unsubscribe(handle, eventName); + } + return RBUS_ERROR_SUCCESS; // Default success +} + +rbusError_t rbusEvent_Publish(rbusHandle_t handle, rbusEvent_t* event) { + g_mockCallCount++; + if (g_mockRBus) { + return g_mockRBus->rbusEvent_Publish(handle, event); + } + return RBUS_ERROR_SUCCESS; // Default success +} + +// Value functions +void rbusValue_Init(rbusValue_t* value) { + if (g_mockRBus) { + g_mockRBus->rbusValue_Init(value); + return; + } + + // Default implementation + if (value) { + MockRBusValue* mockValue = (MockRBusValue*)malloc(sizeof(MockRBusValue)); + if (mockValue) { + memset(mockValue, 0, sizeof(MockRBusValue)); + mockValue->type = RBUS_STRING; + *value = (rbusValue_t)mockValue; + } + } +} + +void rbusValue_Release(rbusValue_t value) { + if (g_mockRBus) { + g_mockRBus->rbusValue_Release(value); + return; + } + + // Default implementation + if (value) { + free(value); + } +} + +rbusError_t rbusValue_SetString(rbusValue_t value, const char* str) { + if (g_mockRBus) { + return g_mockRBus->rbusValue_SetString(value, str); + } + + // Default implementation + if (value && str) { + MockRBusValue* mockValue = (MockRBusValue*)value; + mockValue->type = RBUS_STRING; + strncpy(mockValue->stringValue, str, sizeof(mockValue->stringValue) - 1); + mockValue->stringValue[sizeof(mockValue->stringValue) - 1] = '\0'; + return RBUS_ERROR_SUCCESS; + } + return RBUS_ERROR_INVALID_INPUT; +} + +const char* rbusValue_GetString(rbusValue_t value, int* len) { + /*if (g_mockRBus) { + return g_mockRBus->rbusValue_GetString(value, len); + } + + // Default implementation + if (value) { + MockRBusValue* mockValue = (MockRBusValue*)value; + if (len) { + *len = strlen(mockValue->stringValue); + } + return mockValue->stringValue; + }*/ + return "Mockconfig"; +} + +char* rbusValue_ToString(rbusValue_t value, int* len, int radix) { + (void)radix; // Unused parameter + + if (g_mockRBus) { + return g_mockRBus->rbusValue_ToString(value, len, radix); + } + + // Default implementation + if (value) { + MockRBusValue* mockValue = (MockRBusValue*)value; + int strLen = strlen(mockValue->stringValue); + char* result = (char*)malloc(strLen + 1); + if (result) { + strcpy(result, mockValue->stringValue); + if (len) { + *len = strLen; + } + } + return result; + } + return nullptr; +} + +rbusValueType_t rbusValue_GetType(rbusValue_t value) { + if (g_mockRBus) { + return g_mockRBus->rbusValue_GetType(value); + } + + // Default implementation + if (value) { + MockRBusValue* mockValue = (MockRBusValue*)value; + return mockValue->type; + } + return RBUS_STRING; +} + +// Object functions +void rbusObject_Init(rbusObject_t* object, const char* name) { + if (g_mockRBus) { + g_mockRBus->rbusObject_Init(object, name); + return; + } + + // Default implementation + if (object) { + MockRBusObject* mockObject = (MockRBusObject*)malloc(sizeof(MockRBusObject)); + if (mockObject) { + memset(mockObject, 0, sizeof(MockRBusObject)); + if (name) { + strncpy(mockObject->name, name, sizeof(mockObject->name) - 1); + mockObject->name[sizeof(mockObject->name) - 1] = '\0'; + } + *object = (rbusObject_t)mockObject; + } + } +} + +void rbusObject_Release(rbusObject_t object) { + if (g_mockRBus) { + g_mockRBus->rbusObject_Release(object); + return; + } + + // Default implementation + if (object) { + MockRBusObject* mockObject = (MockRBusObject*)object; + // Release any stored values + for (int i = 0; i < mockObject->propertyCount; i++) { + if (mockObject->properties[i].value) { + free(mockObject->properties[i].value); + } + } + free(object); + } +} + +rbusError_t rbusObject_SetValue(rbusObject_t object, const char* name, rbusValue_t value) { + if (g_mockRBus) { + return g_mockRBus->rbusObject_SetValue(object, name, value); + } + + // Default implementation + if (object && name && value) { + MockRBusObject* mockObject = (MockRBusObject*)object; + MockRBusValue* mockValue = (MockRBusValue*)value; + + if (mockObject->propertyCount < 10) { + strncpy(mockObject->properties[mockObject->propertyCount].key, name, + sizeof(mockObject->properties[mockObject->propertyCount].key) - 1); + mockObject->properties[mockObject->propertyCount].key[sizeof(mockObject->properties[mockObject->propertyCount].key) - 1] = '\0'; + + // Create a copy of the value + MockRBusValue* valueCopy = (MockRBusValue*)malloc(sizeof(MockRBusValue)); + if (valueCopy) { + *valueCopy = *mockValue; + mockObject->properties[mockObject->propertyCount].value = valueCopy; + mockObject->propertyCount++; + return RBUS_ERROR_SUCCESS; + } + } + } + return RBUS_ERROR_INVALID_INPUT; +} + +rbusValue_t rbusObject_GetValue(rbusObject_t object, const char* name) { + if (g_mockRBus) { + return g_mockRBus->rbusObject_GetValue(object, name); + } + + // Default implementation + if (object && name) { + MockRBusObject* mockObject = (MockRBusObject*)object; + + for (int i = 0; i < mockObject->propertyCount; i++) { + if (strcmp(mockObject->properties[i].key, name) == 0) { + // Return a copy of the stored value + MockRBusValue* result = (MockRBusValue*)malloc(sizeof(MockRBusValue)); + if (result) { + *result = *(mockObject->properties[i].value); + return (rbusValue_t)result; + } + } + } + } + + // Return a default mock value if not found + MockRBusValue* defaultValue = (MockRBusValue*)malloc(sizeof(MockRBusValue)); + if (defaultValue) { + memset(defaultValue, 0, sizeof(MockRBusValue)); + defaultValue->type = RBUS_STRING; + strcpy(defaultValue->stringValue, g_mockStringBuffer); + return (rbusValue_t)defaultValue; + } + + return nullptr; +} + +} // extern "C" diff --git a/unittest/mocks/mockrbus.h b/unittest/mocks/mockrbus.h new file mode 100644 index 00000000..bed58d11 --- /dev/null +++ b/unittest/mocks/mockrbus.h @@ -0,0 +1,276 @@ +/* + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +//#pragma once +//#ifndef MOCK_RBUS_H +//#define MOCK_RBUS_H + +#include +#include +#include +#include +#include "dcm_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// RBUS Types and Constants +typedef enum { + RBUS_ERROR_SUCCESS = 0, + RBUS_ERROR_BUS_ERROR, + RBUS_ERROR_INVALID_INPUT, + RBUS_ERROR_NOT_INITIALIZED, + RBUS_ERROR_OUT_OF_RESOURCES, + RBUS_ERROR_DESTINATION_NOT_FOUND, + RBUS_ERROR_DESTINATION_NOT_REACHABLE, + RBUS_ERROR_DESTINATION_RESPONSE_FAILURE, + RBUS_ERROR_INVALID_RESPONSE_FROM_DESTINATION, + RBUS_ERROR_INVALID_OPERATION, + RBUS_ERROR_INVALID_EVENT, + RBUS_ERROR_INVALID_HANDLE, + RBUS_ERROR_SESSION_ALREADY_EXIST, + RBUS_ERROR_COMPONENT_NAME_DUPLICATE, + RBUS_ERROR_ELEMENT_NAME_DUPLICATE, + RBUS_ERROR_ELEMENT_NAME_MISSING, + RBUS_ERROR_COMPONENT_PATH_MISMATCH, + RBUS_ERROR_ELEMENT_PATH_MISMATCH, + RBUS_ERROR_ACCESS_NOT_ALLOWED, + RBUS_ERROR_INVALID_CONTEXT, + RBUS_ERROR_TIMEOUT, + RBUS_ERROR_ASYNC_RESPONSE, + RBUS_ERROR_INVALID_METHOD, + RBUS_ERROR_NOSUBSCRIBERS +} rbusError_t; + +typedef enum { + RBUS_ENABLED = 1, + RBUS_DISABLED = 0 +} rbusStatus_t; + +typedef enum { + RBUS_EVENT_ACTION_SUBSCRIBE = 1, + RBUS_EVENT_ACTION_UNSUBSCRIBE = 2 +} rbusEventSubAction_t; + +typedef enum { + RBUS_EVENT_GENERAL = 1, + RBUS_EVENT_VALUE_CHANGED = 2 +} rbusEventType_t; + +typedef enum { + RBUS_STRING = 1, + RBUS_INT32 = 2, + RBUS_BOOLEAN = 3, + RBUS_UINT32 = 4, + RBUS_BYTES = 5, + RBUS_PROPERTY = 6, + RBUS_OBJECT = 7, + RBUS_DATETIME = 8, + RBUS_SINGLE = 9, + RBUS_DOUBLE = 10, + RBUS_INT64 = 11, + RBUS_UINT64 = 12 +} rbusValueType_t; + +typedef enum { + RBUS_ELEMENT_TYPE_PROPERTY = 0, + RBUS_ELEMENT_TYPE_TABLE = 1, + RBUS_ELEMENT_TYPE_EVENT = 2, + RBUS_ELEMENT_TYPE_METHOD = 3 +} rbusElementType_t; + +// Forward declarations +typedef void* rbusHandle_t; +typedef void* rbusValue_t; +typedef void* rbusObject_t; +typedef void* rbusFilter_t; + +// Event structures +typedef struct { + const char* name; + rbusEventType_t type; + rbusObject_t data; +} rbusEvent_t; + +typedef struct { + const char* eventName; + void* userData; +} rbusEventSubscription_t; + +// Callback function types +typedef void (*rbusEventHandler_t)( + rbusHandle_t handle, + rbusEvent_t const* event, + rbusEventSubscription_t* subscription +); + +typedef void (*rbusEventSubAsyncHandler_t)( + rbusHandle_t handle, + rbusEventSubscription_t* subscription, + rbusError_t error +); + +typedef rbusError_t (*rbusEventSubHandler_t)( + rbusHandle_t handle, + rbusEventSubAction_t action, + const char* eventName, + rbusFilter_t filter, + int32_t interval, + BOOL* autoPublish +); + +// Data element structure +typedef struct { + char* name; + rbusElementType_t type; + struct { + void* getHandler; + void* setHandler; + void* tableAddRowHandler; + void* tableRemoveRowHandler; + rbusEventSubHandler_t eventSubHandler; + void* methodHandler; + } cbTable; +} rbusDataElement_t; + +#ifdef __cplusplus +} +#endif + +// Mock class for RBUS API functions only +class MockRBus { +public: + // Core RBUS functions + MOCK_METHOD(rbusStatus_t, rbus_checkStatus, (), ()); + MOCK_METHOD(rbusError_t, rbus_open, (rbusHandle_t* handle, const char* componentName), ()); + MOCK_METHOD(rbusError_t, rbus_close, (rbusHandle_t handle), ()); + MOCK_METHOD(rbusError_t, rbus_get, (rbusHandle_t handle, const char* paramName, rbusValue_t* paramValue), ()); + MOCK_METHOD(rbusError_t, rbus_regDataElements, (rbusHandle_t handle, int numElements, rbusDataElement_t* elements), ()); + MOCK_METHOD(rbusError_t, rbus_unregDataElements, (rbusHandle_t handle, int numElements, rbusDataElement_t* elements), ()); + + // Event functions + MOCK_METHOD(rbusError_t, rbusEvent_SubscribeAsync, + (rbusHandle_t handle, const char* eventName, rbusEventHandler_t handler, + rbusEventSubAsyncHandler_t asyncHandler, void* userData, int timeout), ()); + MOCK_METHOD(rbusError_t, rbusEvent_Unsubscribe, (rbusHandle_t handle, const char* eventName), ()); + MOCK_METHOD(rbusError_t, rbusEvent_Publish, (rbusHandle_t handle, rbusEvent_t* event), ()); + + // Value functions + MOCK_METHOD(void, rbusValue_Init, (rbusValue_t* value), ()); + MOCK_METHOD(void, rbusValue_Release, (rbusValue_t value), ()); + MOCK_METHOD(rbusError_t, rbusValue_SetString, (rbusValue_t value, const char* str), ()); + MOCK_METHOD(const char*, rbusValue_GetString, (rbusValue_t value, int* len), ()); + MOCK_METHOD(char*, rbusValue_ToString, (rbusValue_t value, int* len, int radix), ()); + MOCK_METHOD(rbusValueType_t, rbusValue_GetType, (rbusValue_t value), ()); + + // Object functions + MOCK_METHOD(void, rbusObject_Init, (rbusObject_t* object, const char* name), ()); + MOCK_METHOD(void, rbusObject_Release, (rbusObject_t object), ()); + MOCK_METHOD(rbusError_t, rbusObject_SetValue, (rbusObject_t object, const char* name, rbusValue_t value), ()); + MOCK_METHOD(rbusValue_t, rbusObject_GetValue, (rbusObject_t object, const char* name), ()); +}; + +#ifdef __cplusplus +extern "C" { +#endif + +// C wrapper function declarations - RBUS functions only +rbusStatus_t rbus_checkStatus(void); +rbusError_t rbus_open(rbusHandle_t* handle, const char* componentName); +rbusError_t rbus_close(rbusHandle_t handle); +rbusError_t rbus_get(rbusHandle_t handle, const char* paramName, rbusValue_t* paramValue); +rbusError_t rbus_regDataElements(rbusHandle_t handle, int numElements, rbusDataElement_t* elements); +rbusError_t rbus_unregDataElements(rbusHandle_t handle, int numElements, rbusDataElement_t* elements); + +rbusError_t rbusEvent_SubscribeAsync(rbusHandle_t handle, const char* eventName, + rbusEventHandler_t handler, rbusEventSubAsyncHandler_t asyncHandler, + void* userData, int timeout); +rbusError_t rbusEvent_Unsubscribe(rbusHandle_t handle, const char* eventName); +rbusError_t rbusEvent_Publish(rbusHandle_t handle, rbusEvent_t* event); + +void rbusValue_Init(rbusValue_t* value); +void rbusValue_Release(rbusValue_t value); +rbusError_t rbusValue_SetString(rbusValue_t value, const char* str); +const char* rbusValue_GetString(rbusValue_t value, int* len); +char* rbusValue_ToString(rbusValue_t value, int* len, int radix); +rbusValueType_t rbusValue_GetType(rbusValue_t value); + +void rbusObject_Init(rbusObject_t* object, const char* name); +void rbusObject_Release(rbusObject_t object); +rbusError_t rbusObject_SetValue(rbusObject_t object, const char* name, rbusValue_t value); +rbusValue_t rbusObject_GetValue(rbusObject_t object, const char* name); + +#ifdef __cplusplus +} +#endif + +// Global mock instance +extern MockRBus* g_mockRBus; + +// Mock control functions +void mock_rbus_reset(); +void mock_rbus_set_global_mock(MockRBus* mockRBus); +void mock_rbus_clear_global_mock(); + +// Helper functions for triggering callbacks in tests +void mock_rbus_trigger_event_callback(rbusEventHandler_t handler, rbusHandle_t handle, + rbusEvent_t* event, rbusEventSubscription_t* subscription); +void mock_rbus_trigger_async_callback(rbusEventSubAsyncHandler_t handler, rbusHandle_t handle, + rbusEventSubscription_t* subscription, rbusError_t error); + +// Test utility functions +void mock_rbus_set_string_value(const char* value); +rbusHandle_t mock_rbus_get_mock_handle(); +rbusValue_t mock_rbus_create_string_value(const char* str); +rbusObject_t mock_rbus_create_object(const char* name); + +//#endif // MOCK_RBUS_H + + + + +/* --------- RBUS MACROS ------------*/ +/* +typedef enum _rbusError +{ + RBUS_ERROR_SUCCESS, + RBUS_ERROR_NOT_INITIALIZED, + RBUS_ERROR_BUS_ERROR, +} rbusError_t; + +char const * rbusError_ToString(rbusError_t e); + +struct _rbusHandle +{ +}; + +typedef struct _rbusHandle *rbusHandle_t; + +struct _rbusObject +{ +}; +typedef struct _rbusObject *rbusObject_t; + +struct _rbusValue +{ +}; +typedef struct _rbusValue *rbusValue_t; + +typedef void (*rbusMethodAsyncRespHandler_t)(rbusHandle_t handle, char const *methodName, rbusError_t error, rbusObject_t params); +*/ diff --git a/unittest/mocks/rbus.h b/unittest/mocks/rbus.h new file mode 100644 index 00000000..c2d14cb6 --- /dev/null +++ b/unittest/mocks/rbus.h @@ -0,0 +1,1912 @@ +/* + * If not stated otherwise in this file or this component's Licenses.txt file + * the following copyright and licenses apply: + * + * Copyright 2016 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + + +/** + * @file rbus.h + * @brief rbus.h top level include file. + * This is the only file a provider or client needs to include in their project. + */ + +/** + * @mainpage RDK Bus (RBus) + * RDK Bus (RBus) is a lightweight, fast and efficient bus messaging system. + * It allows interprocess communication (IPC) and remote procedure call (RPC) + * between multiple process running on a hardware device. It supports the + * creation and use of a data model, which is a hierarchical tree of named + * objects with properties, events, and methods. + * + * From a developer perspective, there are providers and clients. + * Providers implement the data model which clients consume. + * + * Providers perform these tasks: + * - register properties and implement their get and set operations + * - register object and implement their get, set, create, and delete operations + * - register and plublish events + * - register and implement methods which can be called remotely + * + * Consumers perform these tasks: + * - get and set property values. + * - get, set, create, and delete objects. + * - subscribe and listen to events + * - invoke remote methods + * + * A process can be both a provider and a client. A process can implement many properties, + * objects, events, and remote methods, and also be a client of the same coming from other providers. + * + * All objects, properties, events, and methods are assigned names by the provider. + * Each name should be unique across a system, otherwise there will be conflicts in the bus routing. + * + * RBus supports the naming convention defined by TR-069, where a name has a hierarchical structure + * like a file directory structure, with each level of the hierarchy separated by a dot ('.'), and where + * object instances and denoted using brace({}). + * + * The RBus API is intended, but not limited, to allow the implementation of a TR-181 data model. + */ + +/** + * @defgroup Common Common + * @defgroup Initialization Initialization + * @defgroup Consumers Consumers + * @defgroup Providers Providers + * @defgroup Tables Tables + * @defgroup Events Events + * @defgroup Methods Methods + * @defgroup Discovery Discovery + */ + +#ifndef RBUS_H +#define RBUS_H + +#include +#include "rbus_value.h" +#include "rbus_property.h" +#include "rbus_object.h" +#include "rbus_filter.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** @addtogroup Common + * @{ + */ +struct _rbusHandle; + +/// @brief An RBus handle which identifies an opened component +typedef struct _rbusHandle* rbusHandle_t; + +/// @brief The maximum length a name can be for any element. +#define RBUS_MAX_NAME_LENGTH 256 + +/// @brief The maximum hierarchical depth (e.g. the max token count) a name can be for any element. +#define RBUS_MAX_NAME_DEPTH 16 + +/// @brief All possible error codes this API can generate. +typedef enum _rbusError +{ + //Generic error codes + RBUS_ERROR_SUCCESS = 0, /**< Succes */ + RBUS_ERROR_BUS_ERROR = 1, /**< General Error */ + RBUS_ERROR_INVALID_INPUT, /**< Invalid Input */ + RBUS_ERROR_NOT_INITIALIZED, /**< Bus not initialized */ + RBUS_ERROR_OUT_OF_RESOURCES, /**< Running out of resources */ + RBUS_ERROR_DESTINATION_NOT_FOUND, /**< Dest element not found */ + RBUS_ERROR_DESTINATION_NOT_REACHABLE, /**< Dest element not reachable*/ + RBUS_ERROR_DESTINATION_RESPONSE_FAILURE, /**< Dest failed to respond */ + RBUS_ERROR_INVALID_RESPONSE_FROM_DESTINATION,/**< Invalid dest response */ + RBUS_ERROR_INVALID_OPERATION, /**< Invalid Operation */ + RBUS_ERROR_INVALID_EVENT, /**< Invalid Event */ + RBUS_ERROR_INVALID_HANDLE, /**< Invalid Handle */ + RBUS_ERROR_SESSION_ALREADY_EXIST, /**< Session already opened */ + RBUS_ERROR_COMPONENT_NAME_DUPLICATE, /**< Comp name already exists */ + RBUS_ERROR_ELEMENT_NAME_DUPLICATE, /**< One or more element name(s) were previously registered */ + RBUS_ERROR_ELEMENT_NAME_MISSING, /**< No names were provided in the name field */ + RBUS_ERROR_COMPONENT_DOES_NOT_EXIST, /**< A bus connection for this component name was not previously opened. */ + RBUS_ERROR_ELEMENT_DOES_NOT_EXIST, /**< One or more data element name(s) do not currently have a valid registration */ + RBUS_ERROR_ACCESS_NOT_ALLOWED, /**< Access to the requested data element was not permitted by the provider component. */ + RBUS_ERROR_INVALID_CONTEXT, /**< The Context is not same as what was sent in the get callback handler.*/ + RBUS_ERROR_TIMEOUT, /**< The operation timedout */ + RBUS_ERROR_ASYNC_RESPONSE, /**< The method request will be handle asynchronously by provider */ + RBUS_ERROR_INVALID_METHOD, /**< Invalid Method */ + RBUS_ERROR_NOSUBSCRIBERS, /**< No subscribers present */ + RBUS_ERROR_SUBSCRIPTION_ALREADY_EXIST, /**< The subscription already exists*/ + RBUS_ERROR_INVALID_NAMESPACE /**< Invalid namespace as per standard */ +} rbusError_t; + + +char const * rbusError_ToString(rbusError_t e); + +/** @struct rbusSetOptions_t + * @brief Additional options a client can pass to a set function. + * @ingroup Common Consumers + */ +typedef struct _rbusSetOptions +{ + bool commit; /**< Commit flag indicating which set operation is the + last set operation in a particular session. + Set to true to for the final set of a session. + Set to false to for all other sets of a session, + which indicates that the set operation should be remembered + until the final set occurs. */ + uint32_t sessionId; /**< Session id. One or more parameter can be set together over a session. + The session ID binds all set operations. A value of 0 indicates + this is not a session based operation. A non-zero value indicates that the + value should be "remembered" temporarily. Only when the "commit" parameter + is "true", should all remembered parameters in this session be set together. + Call rbus_createSession to generate a session id.*/ +} rbusSetOptions_t; + +/** @struct rbusGetHandlerOptions_t + * @brief Additional options that are passed to the provider when GET function called. + * @ingroup Common Providers + */ +typedef struct _rbusGetHandlerOptions +{ + void* context; /**< Context, that can be used for building response; */ + char const* requestingComponent; /**< Component that invoking the GET method. */ +} rbusGetHandlerOptions_t; + +/** @struct rbusSetHandlerOptions_t + * @brief Additional options that are passed to the provider when SET function called. + * @ingroup Common Providers + */ +typedef struct _rbusSetHandlerOptions +{ + bool commit; /**< Commit flag indicating which set operation is the + last set operation in a particular session. + Set to true to for the final set of a session. + Set to false to for all other sets of a session, + which indicates that the set operation should be remembered + until the final set occurs. */ + uint32_t sessionId; /**< Session id. One or more parameter can be set together over a session. + The session ID binds all set operations. A value of 0 indicates + this is not a session based operation. A non-zero value indicates that the + value should be "remembered" temporarily. Only when the "commit" parameter + is "true", should all remembered parameters in this session be set together. + Call rbus_createSession to generate a session id.*/ + char const* requestingComponent; /**< Component that invoking the SET method. */ +} rbusSetHandlerOptions_t; + +struct _rbusMethodAsyncHandle; + +/// @brief An RBus handle used for async method responses +typedef struct _rbusMethodAsyncHandle* rbusMethodAsyncHandle_t; + +/** @addtogroup Events + * @{ + */ +/// @brief rbusEventSubAction_t Actions that can be performed for an event +typedef enum +{ + RBUS_EVENT_ACTION_SUBSCRIBE = 0, + RBUS_EVENT_ACTION_UNSUBSCRIBE +} rbusEventSubAction_t; + +/** + * @enum rbusEventType_t + * @brief The type of events which can be subscribed to or published + */ +typedef enum +{ + RBUS_EVENT_OBJECT_CREATED, /**< Notification that an object instance was created in table. */ + RBUS_EVENT_OBJECT_DELETED, /**< Notification that an object instance was deleted in table. */ + RBUS_EVENT_VALUE_CHANGED, /**< Notification that a property value was changed. */ + RBUS_EVENT_GENERAL, /**< Provider defined event.*/ + RBUS_EVENT_INITIAL_VALUE, /**< Notification of initial value immediately after subscription*/ + RBUS_EVENT_INTERVAL, /**< For event with interval*/ + RBUS_EVENT_DURATION_COMPLETE /**< For event with duration timeout*/ +} rbusEventType_t; + +/** + * @struct rbusEvent_t + * @brief The set of data associated with a published event + */ +typedef struct +{ + char const* name; /**< Fully qualified event name */ + rbusEventType_t type; /**< The type of event */ + rbusObject_t data; /**< The data for the event */ +} rbusEvent_t; + +typedef struct +{ + char const* name; /**< Fully qualified event name */ + const void* rawData; /**< The raw data for the event */ + unsigned int rawDataLen; /**< The raw data length*/ +} rbusEventRawData_t; + +typedef struct _rbusEventSubscription rbusEventSubscription_t; + +/** @fn typedef void (* rbusSubscribeAsyncRespHandler_t)( + * rbusHandle_t handle, + * prbusEventSubscription_t subscription, + * rbusError_t error) + * @brief A component will receive this API callback when a subscription response is received. + * This callback is registered with rbusEvent_SubscribeAsync or rbusEvent_SubscribeExAsync. \n + * Used by: Any component that subscribes async for events. + * @param rbusHandle Bus Handle + * @param subscription Subscription data created when the client subscribed + * for the event. This will contain the userData, for example. + * @param error Any error that occured + * success, or any provider error code, or timeout (if retry limit reached) + * @return void + */ +typedef void (*rbusSubscribeAsyncRespHandler_t)( + rbusHandle_t handle, + rbusEventSubscription_t* subscription, + rbusError_t error); + +/** @fn typedef void (* rbusEventHandler_t)( + * rbusHandle_t handle, + * rbusEvent_t const* eventData + * rbusEventSubscription_t* subscription) + * @brief A component will receive this API callback when an event is received. + * This callback is registered with rbusEvent_Subscribe. \n + * Used by: Any component that subscribes for events. + * @param rbusHandle Bus Handle + * @param eventData Event data sent from the publishing component + * @param subscription Subscription data created when the client subscribed + for the event. This will contain the user_data, for example. + * @return void + */ +typedef void (*rbusEventHandler_t)( + rbusHandle_t handle, + rbusEvent_t const* eventData, + rbusEventSubscription_t* subscription +); + +typedef void (*rbusEventHandlerRawData_t)( + rbusHandle_t handle, + rbusEventRawData_t const* eventData, + rbusEventSubscription_t* subscription +); + +/// @brief rbusEventSubscription_t +typedef struct _rbusEventSubscription +{ + char const* eventName; /** Fully qualified event name */ + rbusFilter_t filter; /** Optional filter that the client would like + the sender to apply before sending the event + */ + uint32_t interval; /**< Total interval period after which + the event needs to be fired. Should + be in multiples of minInterval + */ + uint32_t duration; /** Optional maximum duration in seconds until which + the subscription should be in effect. Beyond this + duration, the event would be unsubscribed automatically. + Pass "0" for indefinite event subscription which requires + the rbusEvent_Unsubscribe API to be called explicitly. + */ + void* handler; /** fixme rbusEventHandler_t internal*/ + void* userData; /** The userData set when subscribing to the event. */ + rbusHandle_t handle; /** Private use only: The rbus handle associated with this subscription */ + rbusSubscribeAsyncRespHandler_t asyncHandler;/** Private use only: The async handler being used for any background subscription retries */ + bool publishOnSubscribe; +} rbusEventSubscription_t; + +/** @} */ + +/** @addtogroup Tables + * @{ + */ + +/// @brief rbusRowName_t +typedef struct _rbusRowName +{ + char const* name; /** Fully qualified row name */ + uint32_t instNum; /** Instance number of the row */ + char const* alias; /** Alias of the row. NULL if no alias exists */ + struct _rbusRowName* next; /** The next row name in this list */ +} rbusRowName_t; + +/** @} */ + +/** @fn typedef void (* rbusMethodAsyncRespHandler_t)( + * rbusHandle_t handle, + * char* methodName + * rbusObject_t params) + * @brief A component will receive this API callback when the result of + * and asynchronous method invoked with rbusMethod_InvokeAsync is ready.\n + * Used by: Any component that calls rbusMethod_InvokeAsync. + * @param rbusHandle Bus Handle + * @param methodName The method name + * @param error Any error that occured + * @param params The returned params of the method + * @return void + * @ingroup Methods + */ +typedef void (*rbusMethodAsyncRespHandler_t)( + rbusHandle_t handle, + char const* methodName, + rbusError_t error, + rbusObject_t params +); + +/** @addtogroup Providers + * @{ + */ + +/// @brief rbusElementType_t indicates the type of data elements which can be registered with RBus +typedef enum +{ + RBUS_ELEMENT_TYPE_PROPERTY = 1, /**< Property Element. + Sample names: x.y, p.q.{i}.r, aaa, etc + Can also be monitored and event + notifications be obtained in the + form of events */ + RBUS_ELEMENT_TYPE_TABLE, /**< Table (e.g. multi-instance object) + Sample names: a.b.{i}, a.b.{i}.x.y.{i} */ + + RBUS_ELEMENT_TYPE_EVENT, /**< (Exclusive) Event Element + Sample names: a.b.c!, zzzz! */ + + RBUS_ELEMENT_TYPE_METHOD /**< Method Element + Sample names: m.n.o(), dddddd() */ +} rbusElementType_t; + +/** @fn typedef rbusError_t (*rbusGetHandler_t)( + * rbusHandle_t handle, + * rbusProperty_t property) + * @brief A property get callback handler. + * + * A provider must implement this handler to allow a property to be read. The + * property parameter passed to this function will have the name of the property + * already set. The provider can use this name to identify the property if needed. + * The provider's responsibility is to set the value of the property parameter. + * A provider may install this get handler on a table if the provider doesn't + * use rbusTable_addRow to add rows and instead will handle partial path queries + * through this get handler. + * @param handle the rbus handle the property is registered to. + * @param property the property whose value must be set by the handler. + * @param options the additional information that to be used for GET. + * @return RBus error code as defined by rbusError_t. + */ +typedef rbusError_t (*rbusGetHandler_t)( + rbusHandle_t handle, + rbusProperty_t property, + rbusGetHandlerOptions_t* options +); + +/** @fn typedef rbusError_t (*rbusSetHandler_t)( + * rbusHandle_t handle, + * rbusProperty_t property, + * rbusSetHandlerOptions_t* options) + * @brief A property set callback handler. + * + * A provider must implement this handler to allow a property to be written. The + * property parameter passed to this function will have the name of the property + * already set. The provider can use this name to identify the property if needed. + * The property parameter will also have the value. The rbusSetHandlerOptions_t contains + * addition information which the provider must use to handle how the write should + * occur. It is the provider's responsibility to set its internal representation + * of that property's value with the value contained within the property parameter. + * @param handle the rbus handle the property is registered to. + * @param property the property whose value must be set by the handler. + * @param options the additional information that to be used for SET. + * @return RBus error code as defined by rbusError_t. + */ +typedef rbusError_t (*rbusSetHandler_t)( + rbusHandle_t handle, + rbusProperty_t property, + rbusSetHandlerOptions_t* options +); + +/** @fn typedef rbusError_t (*rbusTableAddRowHandler_t)( + * rbusHandle_t handle, + * char const* tableName, + * char const* aliasName, + * uint32_t* instNum) + * @brief A table row add callback handler + * + * A provider must implement this handler to allow rows to be added to a table. + * The tableName parameter will end in "." such as "Device.IP.Interface." + * The aliasName parameter can optionally be used to specify a unique name for the row. + * A new row should be assigned a unique instance number and this number should be + * returned in the instNum output parameter. + * @param handle Bus Handle + * @param tableName The name of a table (e.g. "Device.IP.Interface.") + * @param aliasName An optional name for the new row. Must be unique in the table. Can be NULL. + * @param instNum Output parameter where the instance number for the new row is returned + * @return RBus error code as defined by rbusError_t. + * Possible values are: RBUS_ERROR_INVALID_INPUT + */ +typedef rbusError_t (*rbusTableAddRowHandler_t)( + rbusHandle_t handle, + char const* tableName, + char const* aliasName, + uint32_t* instNum); + +/** @fn typedef rbusError_t (*rbusTableRemoveRowHandler_t)( + * rbusHandle_t handle, + * char const* rowName) + * @brief A table row remove callback handler + * + * A provider must implement this handler to allow rows to be removed from a table. + * The rowName parameter will be a fully qualified name, specifying either the row's instance + * number (e.g. "Device.IP.Interface.1") or the row's alias (e.g. "Device.IP.Interface.[lan1]"). + * @param handle Bus Handle + * @param rowName The name of a table row (e.g. "Device.IP.Interface.1") + * @return RBus error code as defined by rbusError_t. + * Possible values are: RBUS_ERROR_INVALID_INPUT + */ +typedef rbusError_t (*rbusTableRemoveRowHandler_t)( + rbusHandle_t handle, + char const* rowName); + +/** @fn typedef rbusError_t (*rbusMethodHandler_t)( + * rbusHandle_t handle, + * char const* methodName, + * rbusObject_t inParams, + * rbusObject_t outParams + * rbusMethodAsyncHandle_t asyncHandle) + * @brief A method invocation callback handler + * + * A provider must implement this handler to support methods. + * There are two ways to return a response to the method. + * The first is to set outParams in the handler and return RBUS_ERROR_SUCCESS. + * The second is to send the response later by storing the asyncHandle, + * returning RBUS_ERROR_ASYNC_RESPONSE from the handler, and later + * calling rbusMethod_SendAsyncResponse, passing the asyncHandle and output params. + * @param handle Bus Handle + * @param methodName The name of the method being invoked ( e.g. "Device.Foo.SomeFunc()"); + * @param inParams The input parameters to the functions + * @param outParams The output/return parameters of the functions + * @param asyncHandle Handle passed to rbusMethod_SendAsyncResponse + * @return RBus error code as defined by rbusError_t. + * Possible values are: RBUS_ERROR_INVALID_INPUT + */ +typedef rbusError_t (*rbusMethodHandler_t)( + rbusHandle_t handle, + char const* methodName, + rbusObject_t inParams, + rbusObject_t outParams, + rbusMethodAsyncHandle_t asyncHandle); + +/** @fn typedef rbusError_t (* rbusEventSubHandler_t)( + * rbusHandle_t handle, + * rbusEventSubAction_t action, + * char const* eventName, + * rbusFilter_t filter, + * int interval, + * bool* autoPublish) + * @brief An event subcribe callback handler. + * + * A provider will receive this callback when the first client subscribes + * to an event/filter pair or the last client unsubscribes to an event/filter + * pair or all subscribers have timed out due to max duration. + * Distribution of the event to multiple subscribers will be transparently + * handled by the library. + * @param handle the rbus handle the event is registered to. + * @param action whether the event is being subscribed or unsubscribe to. + * @param eventName the fully qualified event name. + * @param filter an the filter the subscriber would like the provider to + * use to decide when the event can be sent. This can be NULL + * if no filter was specified by the client. + * @param interval if non-zero, indicates that the event should be publish + * repeatedly every 'interval' seconds. if a filter is + * also provided, it would publish only when the filter is triggered + * @param autoPublish output parameter used to disable the default behaviour + * where rbus automatically publishing events for provider + * data elements. When providers set autoPublish to true + * the value will be checked once per second and the + * maximum event rate is one event per two seconds. If faster + * eventing or real-time eventing is required providers + * can set autoPublish to false and implement a custom + * approach. For fastest response time and to avoid missing + * changes that occur faster than once per second, the + * preferred way is to use a callback triggered from the + * lowest level of code to detect a value change. This + * callback may be invoked by vendor code via a HAL API or + * other method. This callback can be received by the + * component that provides this event and used to send the + * publish message in real time. + * @return RBus error code as defined by rbusError_t. + */ +typedef rbusError_t (* rbusEventSubHandler_t)( + rbusHandle_t handle, + rbusEventSubAction_t action, + char const* eventName, + rbusFilter_t filter, + int32_t interval, + bool* autoPublish +); + +/** @struct rbusCallbackTable_t + * @brief The list of callback handlers supported by a data element. + * + * This table also specifies the possible usage for each data element. + * + * For properties, the callbacks can be set to control usage as follows: + * Callback|Property Usage|Table Usage + * -|- + * getHandler|allow read access and value-change events|unused + * setHandler|allow write access|unused + * updateTableHandler|unused|all table row add and remove + * eventSubHandler|allow a provider to know when value-change was subscribed to|allow a provider to know when a table update event is subscribed to + * + * If a particular usage is supported, then that callback must be set to a + * function pointer for the handler. If a particular usage is not supported by + * the component, the callback shall be set "NULL". On call to registger the + * data element, the rbus library checks for NULL and substitutes a pointer + * to an error handler function for all unused features + */ +typedef struct rbusCallbackTable_t +{ + rbusGetHandler_t getHandler; /**< Get parameters + handler for the + named paramter */ + rbusSetHandler_t setHandler; /**< Set parameters + handler for the + named parameter */ + rbusTableAddRowHandler_t tableAddRowHandler; /**< Add row handler + to a table*/ + rbusTableRemoveRowHandler_t tableRemoveRowHandler; /**< Remove a row + from a table*/ + rbusEventSubHandler_t eventSubHandler; /**< Event subscribe + and unsubscribe + handler for the + event name */ + rbusMethodHandler_t methodHandler; /**< Method handler */ +} rbusCallbackTable_t; + +/// @brief rbusDataElement_t The structure used when registering or +/// unregistering a data element to the bus. +typedef struct +{ + char* name; /**< Name of an element */ + rbusElementType_t type; /**< Type of an element */ + rbusCallbackTable_t cbTable; /**< Element Handler table. A specific + callback can be NULL, if no usage*/ +}rbusDataElement_t; + +/** @} */ + +/** + * @enum rbusStatus_t + * @brief The type of events which can be subscribed to or published + */ +typedef enum +{ + RBUS_ENABLED = 0, /**< RBus broker is Enabled and Running */ + RBUS_ENABLE_PENDING, /**< RBus broker will be Enabled on Reboot */ + RBUS_DISABLE_PENDING, /**< RBus broker will be Disabled on Reboot */ + RBUS_DISABLED /**< RBus broker is disabled */ +} rbusStatus_t; + +typedef enum +{ + RBUS_LOG_DEBUG = 0, + RBUS_LOG_INFO = 1, + RBUS_LOG_WARN = 2, + RBUS_LOG_ERROR = 3, + RBUS_LOG_FATAL = 4 +} rbusLogLevel_t; + +typedef rbusLogLevel_t rbusLogLevel; + +/** @fn typedef void (*rbusLogHandler)( + * rbusLogLevel_t level, + * const char* file, + * int line, + * int threadId, + * char* message); + * @brief A callback handler to get the log messages to application context + * + * A component that wants to handle the logs in its own way must register a callback + * handler to get the log messages. + * @param level the log level + * @param file the file name that it prints + * @param line the line number in the file. + * @param threadId the threadId. + * @param messages the log message the library prints. + * @return None. + */ +typedef void (*rbusLogHandler)( + rbusLogLevel_t level, + const char* file, + int line, + int threadId, + char* message); + +/** @} */ + +/** @addtogroup Consumer + * @{ + */ + +typedef enum +{ + RBUS_ACCESS_GET = 1, + RBUS_ACCESS_SET = 2, + RBUS_ACCESS_ADDROW = 4, + RBUS_ACCESS_REMOVEROW = 8, + RBUS_ACCESS_SUBSCRIBE = 16, + RBUS_ACCESS_INVOKE = 32 +} rbusAccess_t; + +typedef struct _rbusElementInfo +{ + char const* name; /** Fully qualified element name */ + char const* component; /** Name of the component providing this element */ + rbusElementType_t type; /** The type of element */ + uint32_t access; /** rbusAccess_t flags OR'd*/ + struct _rbusElementInfo* next; /** The next name in this list */ +} rbusElementInfo_t; + +/** @} */ +/** @} */ + +/** @addtogroup Initialization + * @{ + */ + +/** @fn rbusStatus_t rbus_checkStatus( + * void) + * + * @brief Components use this API to check whether the rbus is enabled in this device/platform + * Used by: Components that uses rbus to register events, tables and parameters. + * + * @return RBus bus/daemon status + */ +rbusStatus_t rbus_checkStatus( + void); + +/** @fn rbusError_t rbus_open( + * rbusHandle_t* handle, + * char const* componentName) + * @brief Open a bus connection for a software component. + * If multiple components share a software process, the first component that + * calls this API will establishes a new socket connection to the bus broker. + * All calls to this API will receive a dedicated bus handle for that component. + * If a component calls this API more than once, any previous busHandle and all + * previous data element registrations will be canceled. \n + * Note: This API supports a single component per software process and also + * supports multiple components that share a software process. In the case of + * multiple components that share a software process, each component must call + * this API to open a bus connection. Only a single socket connection is opened + * per software process but each component within that process receives a + * separate busHandle. \n + * Used by: All RBus components to begin a connection with the bus. + * @param handle Bus Handle + * @param componentName the name of the component initializing onto the bus + * @return RBus error code as defined by rbusError_t. + * Possible values are: + * RBUS_ERROR_BUS_ERROR: Indicates there is some bus error. Try later. + */ +rbusError_t rbus_open( + rbusHandle_t* handle, + char const* componentName); + +/** @fn rbusError_t rbus_close( + * rbusHandle_t handle) + * @brief Removes a logical bus connection from a component to the bus. \n + * Note: In the case of multiple components that share a software process, the + * socket connection remains up until the last component in that software process + * closes its bus connection. \n + * Used by: All RBus components (multiple components may share a software process) + * @param handle Bus Handle + * @return RBus error code as defined by rbusError_t. + * Possible values are: + * RBUS_ERROR_BUS_ERROR: Indicates there is some bus error. Try later. + */ +rbusError_t rbus_close( + rbusHandle_t handle); +/** @} */ + +/** + * @brief Allows a caller to propogate an OpenTelemetry context from client + * to server. + * @param rbus The currently opened rbus handle + * @param traceParent The traceparent part of the TraceContext in HTTP header format + * @param traceState The tracestate part of the TraceContext in HTTP header format + * @note Neither the tracePraent or traceState need to include the name of the HTTP header + * only the value. + * For example. An example HTTP traceparent may look like + * traceparent: 00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01 + * The caller should only supply the actual value. There is no need to include the + * "traceparent:" prefix. If the "traceparent:" prefix is also included, it will be + * removed. + * @return + * @see https://www.w3.org/TR/trace-context/ + */ +rbusError_t rbusHandle_SetTraceContextFromString( + rbusHandle_t rbus, + char const* traceParent, + char const* traceState); + +rbusError_t rbusHandle_ClearTraceContext( + rbusHandle_t rbus); + +/** + * @brief + * @param rbus The currently opened rbus handle + * @param traceParent + * @param traceParentLength + * @param traceState + * @param traceStateLength + * @return + * @see https://www.w3.org/TR/trace-context/ + */ +rbusError_t rbusHandle_GetTraceContextAsString( + rbusHandle_t rbus, + char* traceParent, + int traceParentLength, + char* traceState, + int traceStateLength); + +/** @addtogroup Discovery + * @{ + */ +/** @fn rbusError_t rbus_discoverComponentName ( + * rbusHandle_t handle, + * int numElements, + * char const** elementNames, + * int *numComponents, + * char **componentName) + * @brief This allows a component to get a list of components that provide + * a set of data elements name(s). The requesting component provides the + * number of data elements and a list of data elements. The bus infrastructure + * provides the number of components and the component names. \n + * Used by: Client + * @param handle Bus Handle + * @param numElements The number (count) of data elements + * @param elementNames The list of element for which components are to be found + * @param numComponents Total number of components + * @param componentName Name of the components discovered + * @return RBus error code as defined by rbusError_t. + * Possible values are: + * RBUS_ERROR_ELEMENT_DOES_NOT_EXIST: Data Element was not previously registered. + */ +rbusError_t rbus_discoverComponentName( + rbusHandle_t handle, + int numElements, + char const** elementNames, + int *numComponents, + char ***componentName); + +/** @fn rbusError_t rbus_discoverComponentDataElements( + * rbusHandle_t handle, + * char const* name, + * bool nextLevel, + * int *numElements, + * char** elementNames) + * @brief This enables a component to get a list of all data elements + * provided by a component. The requesting component provides component name + * or all data elements under a "partial path" of an element that ends with + * "dot". The bus infrastructure provides the number of data elements and the + * data element names array. \n + * Used by: Client + * @param handle Bus Handle + * @param name Name of the component or partial path of an element name. + * @param nextLevel Indicates whether to retrieve only the immediate elements + (true) or retrieve all elements under the partial path (false). + * @param numElements Number (count) of data elements that were discovered + * @param elementNames List of elements discovered + * @return RBus error code as defined by rbusError_t. + * Possible values are: + * RBUS_ERROR_COMPONENT_DOES_NOT_EXIST: Component name was not previously registered. + */ +rbusError_t rbus_discoverComponentDataElements( + rbusHandle_t handle, + char const* name, + bool nextLevel, + int *numElements, + char*** elementNames); + +/** @} */ + +/** @addtogroup Providers + * @{ + */ + +/** @fn rbusError_t rbus_regDataElements( + * rbusHandle_t handle, + * int numDataElements, + * rbusDataElement_t *elements) + * @brief A Component uses this API to register one or more named Data + * Elements (i.e., parameters and/or event names) that will be accessible / + * subscribable by other components. This also registers the callback functions + * associated with each data element using the dataElement structure. \n + * Used by: All components that provide named parameters and/or events that + * may be accessed/subscribed by other component(s) + * @param handle Bus Handle + * @param numDataElements The number (count) of data elements to register + * @param elements The list of data elements to register + * @return RBus error code as defined by rbusError_t. + * Possible values are: + * RBUS_ERROR_ELEMENT_NAME_DUPLICATE: Data Element name already exists + */ +rbusError_t rbus_regDataElements( + rbusHandle_t handle, + int numDataElements, + rbusDataElement_t *elements); + +/** @fn rbusError_t rbus_unregDataElements( + * rbusHandle_t handle, + * int numDataElements, + * rbusDataElement_t *elements) + * @brief A Component uses this API to unregister one or more previously + * registered Data Elements (i.e., named parameters and/or event names) that + * will no longer be accessible / subscribable by other components. \n + * Used by: All components that provide named parameters and/or events that + * may be accessed/subscribed by other component(s) + * @param handle Bus Handle + * @param numDataElements The number (count) of data elements to unregister + * @param elements The list of data elements to unregister + * @return RBus error code as defined by rbusError_t. + * Possible values are: + * RBUS_ERROR_ELEMENT_NAME_MISSING: No data element names provided. + */ +rbusError_t rbus_unregDataElements ( + rbusHandle_t handle, + int numDataElements, + rbusDataElement_t *elements); + +/** @} */ + +/** @addtogroup Consumers + * @{ + */ + +/** @fn rbusError_t rbus_get( + * rbusHandle_t handle, + * char const* name, + * rbusValue_t value) + * @brief Get the value of a single parameter.\n + * Used by: All components that need to get an individual parameter + * + * The consumer should call rbusValue_Release when finished using the returned value. + * @param handle Bus Handle + * @param name The name of the parameter to get the value of + * @param value The returned value of the parameter + * @return RBus error code as defined by rbusError_t. + * Possible values are: + * RBUS_ERROR_ACCESS_NOT_ALLOWED: Access to requested parameter is not permitted. + * RBUS_ERROR_ELEMENT_DOES_NOT_EXIST: Data Element was not previously registered. + */ +rbusError_t rbus_get( + rbusHandle_t handle, + char const* name, + rbusValue_t* value); + +/** @fn rbusError_t rbus_getExt( + * rbusHandle_t handle, + * int paramCount, + * char** paramNames, + * int *numProps, + * rbusProperty_t** properties) + * @brief Gets one or more parameter value(s) in a single bus operation. + * This API also supports wild cards or table entries that provide multiple + * param values for a single param input. These options are explained below: \n + * Option 1: Single param query: \n + * paramCount : 1 \n + * paramNames : parameter name (single element) \n + * numValues : 1 \n + * values : parameter value (caller to free memory) \n + * Option 2: Multi params query: \n + * paramCount : n \n + * paramNames : Array of parameter names (multi element array) \n + * numValues : n \n + * values : Array of parameter values (caller to free memory) \n + * Option 3: Partial path query: \n + * paramCount : 1 \n + * paramNames : Param name path that ends with "." (ex: Device.IP.) \n + * numValues : n \n + * values : Array of parameter values (caller to free memory) \n + * Option 4: Instance Wild card query: \n + * paramCount : 1 \n + * paramNames : Instance Wild card query is possible for tables. + For ex: Device.IP.Interface.*.Status + Multi indices wild card query is also supported + For ex: Device.IP.Interface.*.IP4Address.*.IPAddress + Wildcards can be combined with partial path names + For ex: Device.IP.Interface.*. IP4Address. + Note: In all cases, the exact query would be routed to + provider component and it is the responsibility of + the provider component to respond with appropriate + response. \n + * numValues : n \n + * values : Array of parameter values (caller to free memory) \n + * Used by: All components that need to get one or more parameters + * @param handle Bus Handle + * @param paramCount The number (count) of input elements (parameters) + * @param paramNames Input elements (parameters) + * @param numProps The number (count) of output properties + * @param properties The output properties where each property holds + * a parameter name and respective value. + * @return RBus error code as defined by rbusError_t. + * Possible values are: + * RBUS_ERROR_ACCESS_NOT_ALLOWED: Access to requested parameter is not permitted. + * RBUS_ERROR_ELEMENT_DOES_NOT_EXIST: Data Element was not previously registered. + * RBUS_ERROR_DESTINATION_NOT_REACHABLE: Destination element was not reachable. + */ +static inline rbusError_t rbus_getExt( + rbusHandle_t handle, + int paramCount, + char const** paramNames, + int *numProps, + rbusProperty_t* properties) +{ + (void)handle; + (void)paramCount; + (void)paramNames; + (void)numProps; + (void)properties; + + return RBUS_ERROR_SUCCESS; +} + +/** @fn rbusError_t rbus_getBoolean( + * rbusHandle_t handle, + * char const* paramName, + * int* paramVal) + * @brief A component uses this to perform an boolean get operation. \n + * Used by: All components that need to get an boolean parameter + * @param handle Bus Handle + * @param paramName The name of the boolean parameter + * @param paramVal The value of the boolean parameter + * @return RBus error code as defined by rbusError_t. + * Possible values are: + * RBUS_ERROR_ACCESS_NOT_ALLOWED: Access to the requested parameter is not permitted + */ +rbusError_t rbus_getBoolean( + rbusHandle_t handle, + char const* paramName, + bool* paramVal); + +/** @fn rbusError_t rbus_getInt( + * rbusHandle_t handle, + * char const* paramName, + * int* paramVal) + * @brief A component uses this to perform an integer get operation. \n + * Used by: All components that need to get an integer parameter + * @param handle Bus Handle + * @param paramName The name of the integer parameter + * @param paramVal The value of the integer parameter + * @return RBus error code as defined by rbusError_t. + * Possible values are: + * RBUS_ERROR_ACCESS_NOT_ALLOWED: Access to the requested parameter is not permitted + */ +rbusError_t rbus_getInt( + rbusHandle_t handle, + char const* paramName, + int* paramVal); + +/** @fn rbusError_t rbus_getUint( + * rbusHandle_t handle, + * char const* paramName, + * unsigned int* paramVal) + * @brief A component uses this to perform a get operation on an + * unsigned int parameter. \n + * Used by: All components that need to get an unsigned integer parameter + * @param handle Bus Handle + * @param paramName The name of the unsigned integer parameter + * @param paramVal The value of the unsigned integer parameter + * @return RBus error code as defined by rbusError_t. + * Possible values are: + * RBUS_ERROR_ACCESS_NOT_ALLOWED: Access to requested parameter is not permitted. + */ +rbusError_t rbus_getUint( + rbusHandle_t handle, + char const* paramName, + unsigned int* paramVal); + +/** @fn rbusError_t rbus_getStr( + * rbusHandle_t handle, + * char const* paramName, + * char** paramVal) + * @brief A component uses this to perform a get operation on a + * string parameter. \n + * Used by: All components that need to get a string parameter + * @param handle Bus Handle + * @param paramName The name of the string parameter + * @param paramVal The value of the string parameter which the caller must free + * @return RBus error code as defined by rbusError_t. + * Possible values are: + * RBUS_ERROR_ACCESS_NOT_ALLOWED: Access to requested parameter is not permitted. + */ +rbusError_t rbus_getStr ( + rbusHandle_t handle, + char const* paramName, + char** paramVal); + +/** @fn rbusError_t rbus_set( + * rbusHandle_t handle, + * char const* name, + * rbusValue_t value, + * rbusSetOptions_t* opts) + * @brief A component uses this to perform a set operation for a single + * explicit parameter and has the option to used delayed (coordinated) + * commit commands. \n + * Used by: All components that need to set an individual parameter + * @param handle Bus Handle + * @param name The name of the parameter to set. + * @param value The value to set the parameter to. + * @param opts Extra options such as session info. + * Set NULL if not needed, in which case a session + * is not used and the set is commited immediately. + * @return RBus error code as defined by rbusError_t. + * Possible values are: + * RBUS_ERROR_ACCESS_NOT_ALLOWED: Access to requested parameter is not permitted + */ +rbusError_t rbus_set( + rbusHandle_t handle, + char const* name, + rbusValue_t value, + rbusSetOptions_t* opts); + +/** @fn rbusError_t rbus_setMulti( + * rbusHandle_t handle, + * int numProps, + * rbusProperty_t properties, + * rbusSetOptions_t* opts) + * @brief A component uses this to perform a set operation for multiple + * parameters at once. \n + * Used by: All components that need to set multiple parameters + * @param handle Bus Handle + * @param numProps The number (count) of parameters + * @param properties The list of properties to set the parameters to. + * For each parameter, a property should + * be created which contains the parameter's name and + * the respective value to set that parameter to. + * @param opts Extra options such as session info. + * Set NULL if not needed, in which case a session + * is not used and the set is commited immediately. + * @return RBus error code as defined by rbusError_t. + * Possible values are: + * RBUS_ERROR_ACCESS_NOT_ALLOWED: Access to requested parameter is not permitted. + * RBUS_ERROR_DESTINATION_NOT_REACHABLE: Destination element was not reachable. + */ +rbusError_t rbus_setMulti( + rbusHandle_t handle, + int numProps, + rbusProperty_t properties, + rbusSetOptions_t* opts); + + +/** @fn rbusError_t rbus_setBoolean( + * rbusHandle_t handle, + * char const* paramName, + * bool paramVal) + * @brief A component uses this to perform a simple set operation on a + * boolean parameter and commit the operation. \n + * Used by: All components that need to set a boolean parameter + * @param handle Bus Handle + * @param paramName The name of the string parameter + * @param paramVal The value to set the boolean parameter to + * @return RBus error code as defined by rbusError_t. + * Possible values are: + * RBUS_ERROR_ACCESS_NOT_ALLOWED: Access to requested parameter is not permitted. + */ +rbusError_t rbus_setBoolean( + rbusHandle_t handle, + char const* paramName, + bool paramVal); + +/** @fn rbusError_t rbus_setInt( + * rbusHandle_t handle, + * char const* paramName, + * int paramVal) + * @brief A component uses this to perform a simple set operation on an + * integer parameter and commit the operation. \n + * Used by: All components that need to set an integer and commit the change. + * @param handle Bus Handle + * @param paramName The name of the integer parameter + * @param paramVal The value to set the integer parameter to + * @return RBus error code as defined by rbusError_t. + * Possible values are: + * RBUS_ERROR_ACCESS_NOT_ALLOWED: Access to requested parameter is not permitted + */ +rbusError_t rbus_setInt( + rbusHandle_t handle, + char const* paramName, + int paramVal); + +/** @fn rbusError_t rbus_setUInt( + * rbusHandle_t handle, + * char const* paramName, + * unsigned int paramVal) + * @brief A component uses this to perform a simple set operation on an + * unsigned integer parameter and commit the operation. \n + * Used by: All components that need to set an unsigned integer and commit + * the change. + * @param handle Bus Handle + * @param paramName The name of the unsigned integer parameter + * @param paramVal The value to set the unsigned integer parameter to + * @return RBus error code as defined by rbusError_t. + * Possible values are: + * RBUS_ERROR_ACCESS_NOT_ALLOWED: Access to requested parameter is not permitted. + */ +rbusError_t rbus_setUInt( + rbusHandle_t handle, + char const* paramName, + unsigned int paramVal); + +/** @fn rbusError_t rbus_setStr( + * rbusHandle_t handle, + * char const* paramName, + * char const* paramVal) + * @brief A component uses this to perform a simple set operation on a + * string parameter and commit the operation. \n + * Used by: All components that need to set a string parameter + * @param handle Bus Handle + * @param paramName The name of the string parameter + * @param paramVal The value to set the string parameter to + * @return RBus error code as defined by rbusError_t. + * Possible values are: + * RBUS_ERROR_ACCESS_NOT_ALLOWED: Access to requested parameter is not permitted. + */ +rbusError_t rbus_setStr( + rbusHandle_t handle, + char const* paramName, + char const* paramVal); + +/** @fn rbusError_t rbusTable_addRow( + * busHandle handle, + * char const* tableName, + * char const* aliasName, + * uint32_t* instNum) + * @brief Add a new row to a table + * + * This API adds a new row to a table. The tableName parameter must end in ".", + * such as "Device.IP.Interface." The aliasName parameter can optionally be used to + * specify a unique name for the row. Any additional properties on the row must be + * updated separately using set operations. Each new row gets a unique instance number + * assigned to it and this is returned in the instNum output parameter. Consumers can + * use either the optionally set aliasName or the returned instNum to identify the new row. + * Used by: Any component that needs to add a table rows in another component. + * @param handle Bus Handle + * @param tableName The name of a table (e.g. "Device.IP.Interface.") + * @param aliasName An optional name for the new row. Must be unique in the table. Can be NULL. + * @param instNum Output parameter where the instance number for the new row is returned + * @return RBus error code as defined by rbusError_t. + * Possible values are: RBUS_ERROR_INVALID_INPUT + * @ingroup Tables + */ + +rbusError_t rbusTable_addRow( + rbusHandle_t handle, + char const* tableName, + char const* aliasName, + uint32_t* instNum); + +/** @fn rbusError_t rbusTable_removeRow( + * busHandle handle, + * char const* rowName) + * @brief Remove a row from a table + * + * This API removes a row from a table. The rowName parameter must be a fully qualified + * name, specifying either the row's instance number (e.g. "Device.IP.Interface.1") + * or the row's alias (e.g. "Device.IP.Interface.[lan1]"). + * Used by: Any component that needs to add a table rows in another component. + * @param handle Bus Handle + * @param rowName The name of a table row (e.g. "Device.IP.Interface.1") + * @return RBus error code as defined by rbusError_t. + * Possible values are: RBUS_ERROR_INVALID_INPUT + * @ingroup Tables + */ +rbusError_t rbusTable_removeRow( + rbusHandle_t handle, + char const* rowName); + +/** @fn rbusError_t rbusTable_getRowNames( + * busHandle handle, + * char const* tableName, + * rbusRowName_t** rowNames) + * @brief Get a list of the row names in a table + * + * This method allows a consumer to get the names of all rows on a table. + * Used by: Any component that needs to know the rows in a table. + * @param handle Bus Handle + * @param tableName The name of a table (e.g. "Device.IP.Interface.") + * @param rowNames Output parameter where a list of rbusRowName_t structures is returned + * @return RBus error code as defined by rbusError_t. + * Possible values are: RBUS_ERROR_INVALID_INPUT + * @ingroup Tables + */ +rbusError_t rbusTable_getRowNames( + rbusHandle_t handle, + char const* tableName, + rbusRowName_t** rowNames); + +/** @fn rbusError_t rbusTable_FreeRowNames( + * rbusHandle handle, + * rbusRowName_t* rows) + * @brief Free the row name list returned from rbusTable_getRowNames + * + * This method is used to free the memory for the row name lists returned from rbusTable_getRowNames. + * Used by: Any component that uses the busTable_getRowNames method + * @param handle Bus Handle + * @param rows The row name list returned from rbusTable_getRowNames + * @return RBus error code as defined by rbusError_t. + * Possible values are: RBUS_ERROR_INVALID_INPUT + * @ingroup Tables + */ +rbusError_t rbusTable_freeRowNames( + rbusHandle_t handle, + rbusRowName_t* rows); + +/** @fn rbusError_t rbusElementInfo_get( + * busHandle handle, + * char const* elemName, + * int depth, + * rbusElementInfo_t** elemInfo); + * @brief Get a info on the elements at or inside the give + * + * This method allows a consumer to get the information on all elements inside an object. + * Used by: Any component that needs to know the info on the elements inside an object. + * @param handle Bus Handle + * @param elemName The name of the element to start from + * @param depth Depth control flag + * depth = 0: only the start element + * depth = RBUS_MAX_NAME_DEPTH: the start element, its children, grand-children, ... to max depth + * depth > 0: the start element, its children, grand-children, ... to the given depth + * depth < 0: only the elements at the exact depth level of abs(depth) (e.g. -1 would return only the next level elements) + * @param elemInfo Output element info list + * @return RBus error code as defined by rbusError_t. + * Possible values are: RBUS_ERROR_INVALID_INPUT + */ +rbusError_t rbusElementInfo_get( + rbusHandle_t handle, + char const* elemName, + int depth, + rbusElementInfo_t** elemInfo); + +/** @fn rbusError_t rbusElementInfo_free( + * busHandle handle, + * busElementInfo_t* elemInfo); + * @brief Free the list element info list returned from rbusElementInfo_get + * + * This method allows a consumer to free the element info list returned from the rbusElementInfo_get method. + * Used by: Any component that uses the rbusElementInfo_get method + * @param handle Bus Handle + * @param elemInfoList The element info list return from rbusElementInfo_get + * @return RBus error code as defined by rbusError_t. + * Possible values are: RBUS_ERROR_INVALID_INPUT + */ +rbusError_t rbusElementInfo_free( + rbusHandle_t handle, + rbusElementInfo_t* elemInfo); + +/** @} */ + +/** @addtogroup Providers + * @{ + */ + +/** @fn rbusError_t rbusTable_registerRow( + * busHandle handle, + * char const* tableName, + * uint32_t instNum, + * char const* aliasName) + * @brief Register a row that the provider has added to its own table. + * + * This method allows a provider to register a row that it adds to its own table. + * A provider can add a row internally without the need to call rbusTable_addRow which would + * call the provider's tableAddRow handler. However, in order for consumers to know the row exists, + * it must be registered. + * Used by: Any provider that adds a row to its own table. + * @param handle Bus Handle + * @param tableName The name of a table (e.g. "Device.IP.Interface.") + * @param instNum The unique instance number the provider has assigned this row. + * @param aliasName An optional name for the new row. Must be unique in the table. Can be NULL. + * @return RBus error code as defined by rbusError_t. + * Possible values are: RBUS_ERROR_INVALID_INPUT + * @ingroup Tables + */ +rbusError_t rbusTable_registerRow( + rbusHandle_t handle, + char const* tableName, + uint32_t instNum, + char const* aliasName); + +/** @fn rbusError_t rbusTable_unregisterRow( + * busHandle handle, + * char const* rowName) + * @brief Unregister a row that the provider has removed from its own table. + * + * The method allows a provider to unregister a row that it removes from its own table. + * A provider can remove a row internally without the need to call rbusTable_removeRow which would + * call the provider's tableRemoveRow handler. However, in order for consumer to know the row no + * longer exists, it must be unregistered. + * Used by: Any provider that removes a row from its own table. + * @param handle Bus Handle + * @param rowName The name of a table row (e.g. "Device.IP.Interface.1") + * @return RBus error code as defined by rbusError_t. + * Possible values are: RBUS_ERROR_INVALID_INPUT + * @ingroup Tables + */ + +rbusError_t rbusTable_unregisterRow( + rbusHandle_t handle, + char const* rowName); + +/** @} */ + +/** @addtogroup Consumers + * @{ + */ + +/** @fn bool rbusEvent_IsSubscriptionExist( + * rbusHandle_t handle, + * char const* eventName, + * rbusEventSubscription_t* subscription) + * @brief Find active subscription given eventName with subscription structure. + * Used by: Components that need to find whether they have active subscription + * already or not for the given eventName with subscription structure. + * The eventName should be a name which was previously subscribed + * @param handle Bus Handle + * @param eventName The fully qualified name of the event + * @param subscription The subscription which was previously subscribed + * @return true or false + * @ingroup Events + */ +bool rbusEvent_IsSubscriptionExist( + rbusHandle_t handle, + char const* eventName, + rbusEventSubscription_t* subscription); + +/** @fn rbusError_t rbusEvent_Subscribe( + * rbusHandle_t handle, + * char const* eventName, + * rbusEventHandler_t handler, + * void* userData, + * int timeout) + * @brief Subscribe to a single event. \n + * Used by: Components that need to subscribe to an event. + * The handler will be called back when the event is generated. + * The type of event and when the event is generated is based + * on the type of data element eventName refers to. + * If eventName is the name of a general event, the event is + * generated when the provider of the event, publishes it. + * If eventName is the name of a parameter, the event is + * generated when the value of that parameter changes. + * If eventName is the name of a table, the event is generated + * when a row is added or deleted from that table. + * If timeout is positive, internal retries will be attempted if the subscription + * cannot be routed to an existing provider, and the retries will continue until + * either a provider is found, an unrecoverable error occurs, or retry timeout reached. + * A component should call rbusEvent_Unsubscribe to stop receiving the event. + * @param handle Bus Handle + * @param eventName The fully qualified name of the event + * @param handler The event callback handler + * @param userData User data to be passed back to the callback handler + * @param timeout Max time in seconds to attempt retrying subscribe + * @return RBus error code as defined by rbusError_t. + * Possible values are: RBUS_ERROR_INVALID_EVENT + * @ingroup Events + */ +rbusError_t rbusEvent_Subscribe( + rbusHandle_t handle, + char const* eventName, + rbusEventHandler_t handler, + void* userData, + int timeout); + +/** @fn rbusError_t rbusEvent_SubscribeRawData( + * rbusHandle_t handle, + * char const* eventName, + * rbusEventHandler_t handler, + * void* userData, + * int timeout) + * @brief Subscribe to a single event using rawdata method, with reduced memory and + * cpu footprint. + * Used by: Components that need to subscribe to an event. + * The handler will be called back when the event is generated. + * If timeout is positive, internal retries will be attempted if the subscription + * cannot be routed to an existing provider, and the retries will continue until + * either a provider is found, an unrecoverable error occurs, or retry timeout reached. + * A component should call rbusEvent_UnsubscribeRawData to stop receiving the event. + * @param handle Bus Handle + * @param eventName The fully qualified name of the event + * @param handler The event callback handler + * @param userData User data to be passed back to the callback handler + * @param timeout Max time in seconds to attempt retrying subscribe + * @return RBus error code as defined by rbusError_t. + * Possible values are: RBUS_ERROR_INVALID_EVENT + * @ingroup Events + */ +rbusError_t rbusEvent_SubscribeRawData( + rbusHandle_t handle, + char const* eventName, + rbusEventHandler_t handler, + void* userData, + int timeout); + +/** @fn rbusError_t rbusEvent_SubscribeAsync( + * rbusHandle_t handle, + * char const* eventName, + * rbusEventHandler_t handler, + * rbusSubscribeAsyncRespHandler_t subscribeHandler, + * void* userData, + * int timeout) + * @brief Subscribe asynchronously to a single event. \n + * Used by: Components that need to subscribe asynchronously to an event. + * The handler will be called back when the event is generated. + * The type of event and when the event is generated is based + * on the type of data element eventName refers to. + * If eventName is the name of a general event, the event is + * generated when the provider of the event, publishes it. + * If eventName is the name of a parameter, the event is + * generated when the value of that parameter changes. + * If eventName is the name of a table, the event is generated + * when a row is added or deleted from that table. + * The subscribeHandler is called when either a provider responds to the + * subscription request, an unrecoverable error occurs, or retry timeout reached. + * If timeout is positive, internal retries will be attempted if the subscription + * cannot be routed to an existing provider, and the retries will continue until + * either a provider is found, an unrecoverable error occurs, or retry timeout reached. + * A component should call rbusEvent_Unsubscribe to stop receiving the event. + * @param handle Bus Handle + * @param eventName The fully qualified name of the event + * @param handler The event callback handler + * @param subscribeHandler The subscribe callback handler + * @param userData User data to be passed back to the callback handler + * @param timeout Max time in seconds to attempt retrying subscribe + * @return RBus error code as defined by rbusError_t. + * Possible values are: RBUS_ERROR_INVALID_EVENT + * @ingroup Events + */ +rbusError_t rbusEvent_SubscribeAsync( + rbusHandle_t handle, + char const* eventName, + rbusEventHandler_t handler, + rbusSubscribeAsyncRespHandler_t subscribeHandler, + void* userData, + int timeout); + +/** @fn rbusError_t rbusEvent_Unsubscribe( + * rbusHandle_t handle, + * char const* eventName) + * @brief Unsubscribe from a single event. \n + * Used by: Components that need to unsubscribe from an event. + * + * The eventName should be a name which was previously subscribed + * to with either rbusEvent_Subscribe or rbusEvent_SubscribeEx. + * @param handle Bus Handle + * @param eventName The fully qualified name of the event. + * @return RBus error code as defined by rbusError_t. + * Possible values are: RBUS_ERROR_INVALID_EVENT + * @ingroup Events + */ +rbusError_t rbusEvent_Unsubscribe( + rbusHandle_t handle, + char const* eventName); + +/** @fn rbusError_t rbusEvent_UnsubscribeRawData( + * rbusHandle_t handle, + * char const* eventName) + * @brief Unsubscribe from a single event. \n + * Used by: Components that need to unsubscribe from an event subscribed using + * rbusEvent_SubscribeRawData. + * + * The eventName should be a name which was previously subscribed + * to with either rbusEvent_Subscribe or rbusEvent_SubscribeEx. + * @param handle Bus Handle + * @param eventName The fully qualified name of the event. + * @return RBus error code as defined by rbusError_t. + * Possible values are: RBUS_ERROR_INVALID_EVENT + * @ingroup Events + */ + +rbusError_t rbusEvent_UnsubscribeRawData( + rbusHandle_t handle, + char const* eventName); + +/** @fn rbusError_t rbusEvent_SubscribeEx ( + * rbusHandle_t handle, + * rbusEventSubscription_t* subscription, + * int numSubscriptions, + * int timeout) + * @brief Subscribe to one or more events with the option to add extra attributes + * to each subscription through the rbusEventSubscription_t structure\n + * Used by: Components that need to subscribe to events. + * For each rbusEventSubscription_t subscription, the eventName and handler + * are required to be set. Other options may be set to NULL or 0 if not needed. + * Subscribing to all items in the subscription array is transactional. + * That is, all must succeed to subscribe or none will be subscribed. + * If timeout is positive, internal retries will be attempted if the subscription + * cannot be routed to an existing provider, and the retries will continue until + * either a provider is found, an unrecoverable error occurs, or retry timeout reached. + * @param handle Bus Handle + * @param subscription The array of subscriptions to register to + * @param numSubscriptions The number of subscriptions to register to + * @param timeout Max time in seconds to attempt retrying subscribe + * @return RBus error code as defined by rbusError_t. + * Possible values are: RBUS_ERROR_INVALID_EVENT + * @ingroup Events + */ +rbusError_t rbusEvent_SubscribeEx( + rbusHandle_t handle, + rbusEventSubscription_t* subscription, + int numSubscriptions, + int timeout); + +/** @fn rbusError_t rbusEvent_SubscribeExRawData ( + * rbusHandle_t handle, + * rbusEventSubscription_t* subscription, + * int numSubscriptions, + * int timeout) + * @brief Subscribe to one or more events with the option to add extra attributes + * to each subscription through the rbusEventSubscription_t structure\n + * Used by: Components that need to subscribe to events using rawdata method, with + * reduced memory and cpu footprint. + * For each rbusEventSubscription_t subscription, the eventName and handler + * are required to be set. Other options may be set to NULL or 0 if not needed. + * Subscribing to all items in the subscription array is transactional. + * That is, all must succeed to subscribe or none will be subscribed. + * If timeout is positive, internal retries will be attempted if the subscription + * cannot be routed to an existing provider, and the retries will continue until + * either a provider is found, an unrecoverable error occurs, or retry timeout reached. + * @param handle Bus Handle + * @param subscription The array of subscriptions to register to + * @param numSubscriptions The number of subscriptions to register to + * @param timeout Max time in seconds to attempt retrying subscribe + * @return RBus error code as defined by rbusError_t. + * Possible values are: RBUS_ERROR_INVALID_EVENT + * @ingroup Events + */ +rbusError_t rbusEvent_SubscribeExRawData( + rbusHandle_t handle, + rbusEventSubscription_t* subscription, + int numSubscriptions, + int timeout); + +/** @fn rbusError_t rbusEvent_SubscribeExAsync ( + * rbusHandle_t handle, + * rbusEventSubscription_t* subscription, + * int numSubscriptions, + * rbusSubscribeAsyncRespHandler_t subscribeHandler, + * int timeout) + * @brief Subscribe asynchronously to one or more events with the option to add extra + * attributes to each subscription through the rbusEventSubscription_t structure\n + * Used by: Components that need to subscribe asynchronously to events. + * For each rbusEventSubscription_t subscription, the eventName and handler + * are required to be set. Other options may be set to NULL or 0 if not needed. + * Subscribing to all items in the subscription array is transactional. + * That is, all must succeed to subscribe or none will be subscribed. + * The subscribeHandler is called after either all subscriptions were successfully + * made to providers, any one subscriptions received an error response from a provider, + * an unrecoverable error occurs, or retry timeout reached. + * If timeout is positive, internal retries will be attempted for each subscription that + * cannot be routed to an existing provider, and the retries will continue until + * either a provider is found, an unrecoverable error occurs, or retry timeout reached. + * @param handle Bus Handle + * @param subscription The array of subscriptions to register to + * @param numSubscriptions The number of subscriptions to register to + * @param subscribeHandler The subscribe callback handler + * @param timeout Max time in seconds to attempt retrying subscribe + * @return RBus error code as defined by rbusError_t. + * Possible values are: RBUS_ERROR_INVALID_EVENT + * @ingroup Events + */ +rbusError_t rbusEvent_SubscribeExAsync( + rbusHandle_t handle, + rbusEventSubscription_t* subscription, + int numSubscriptions, + rbusSubscribeAsyncRespHandler_t subscribeHandler, + int timeout); + +/** @fn rbusError_t rbusEvent_UnsubscribeEx( + * rbusHandle_t handle, + * rbusEventSubscription_t* subscriptions, + * int numSubscriptions) + * @brief Unsubscribe from one or more events\n + * Used by: Components that need to unsubscribe from events. + * For each rbusEventSubscription_t subscription, the eventName is required to be set. + * Other options may be set NULL or 0. + * The subscriptions pointer can be the same as that passed to rbusEvent_SubscribeEx, or it + * can be a different list; however, the eventName for each subsription must be one + * previously subscribed to with either rbusEvent_Subscribe or rbusEvent_SubscribeEx. + * @param handle Bus Handle + * @param subscriptions The array of subscriptions to unregister from + * @param numSubscriptions The number of subscriptions to unregister from + * @return RBus error code as defined by rbusError_t. + * Possible values are: RBUS_ERROR_INVALID_EVENT + * @ingroup Events + */ +rbusError_t rbusEvent_UnsubscribeEx( + rbusHandle_t handle, + rbusEventSubscription_t* subscriptions, + int numSubscriptions); + +/** @fn rbusError_t rbusEvent_UnsubscribeExRawData( + * rbusHandle_t handle, + * rbusEventSubscription_t* subscriptions, + * int numSubscriptions) + * @brief Unsubscribe from one or more events\n + * Used by: Components that need to unsubscribe from events subscribed with + * rawdata method. + * For each rbusEventSubscription_t subscription, the eventName is required to be set. + * Other options may be set NULL or 0. + * The subscriptions pointer can be the same as that passed to rbusEvent_SubscribeEx, or it + * can be a different list; however, the eventName for each subsription must be one + * previously subscribed to with either rbusEvent_Subscribe or rbusEvent_SubscribeEx. + * @param handle Bus Handle + * @param subscriptions The array of subscriptions to unregister from + * @param numSubscriptions The number of subscriptions to unregister from + * @return RBus error code as defined by rbusError_t. + * Possible values are: RBUS_ERROR_INVALID_EVENT + * @ingroup Events + */ +rbusError_t rbusEvent_UnsubscribeExRawData( + rbusHandle_t handle, + rbusEventSubscription_t* subscription, + int numSubscriptions); + +/** @} */ + +/** @addtogroup Providers + * @{ + */ + +/** @fn rbusError_t rbusEvent_Publish ( + * rbusHandle_t handle, + * rbusEvent_t* eventData) + * @brief Publish an event. + * + * Publishes an event which will be sent to all subscribers of this event. + * This library keeps state of all event subscriptions and duplicates event + * messages as needed for distribution. \n + * Used by: Components that provide events + * @param handle Bus Handle + * @param eventData The event data. + * @return RBus error code as defined by rbusError_t. + * Possible values are: RBUS_ERROR_INVALID_EVENT + * @ingroup Events + */ +rbusError_t rbusEvent_Publish( + rbusHandle_t handle, + rbusEvent_t* eventData); + +/** @fn rbusError_t rbusEvent_PublishRawData ( + * rbusHandle_t handle, + * rbusEvent_t* eventData) + * @brief Publish an event. + * + * Publishes an event which will be sent to all subscribers of this event. + * This library keeps state of all event subscriptions and duplicates event + * messages as needed for distribution. \n + * Used by: Components that provide events and want to publish with reduced + * memory and cpu footprints + * @param handle Bus Handle + * @param eventData The event data. + * @return RBus error code as defined by rbusError_t. + * Possible values are: RBUS_ERROR_INVALID_EVENT + * @ingroup Events + */ +rbusError_t rbusEvent_PublishRawData( + rbusHandle_t handle, + rbusEventRawData_t* eventData); + +/** @} */ + +/** @addtogroup Consumers + * @{ + */ + +/** @fn rbusError_t rbusMethod_Invoke( + * rbusHandle_t handle, + * char const* methodName, + * rbusObject_t inParams, + * rbusObject_t* outParams) + * @brief Invoke a remote method. + * + * Invokes a remote method and blocks waiting for the result. + * @param handle Bus Handle + * @param methodName Method name + * @param inParams Input params + * @param outParams Return params and error response values + * outparams is generated and contains the return value of error rbusError_t + On success, it contains method specific data on the outParams. + On failure, it contains method specific error code and error description on the outParams. + "error_code" & "error_string" + provider is responsible in sending the errorcode and error string. + other error like no method/handling issues, internal err + will be taken care by rbus. + * @return RBus error code as defined by rbusError_t. + * Possible values are: RBUS_ERROR_SUCCESS, RBUS_ERROR_BUS_ERROR, RBUS_ERROR_INVALID_INPUT + * @ingroup Methods + */ +rbusError_t rbusMethod_Invoke( + rbusHandle_t handle, + char const* methodName, + rbusObject_t inParams, + rbusObject_t* outParams); + +/** @fn rbusError_t rbusMethod_InvokeAsync( + * rbusHandle_t handle, + * char* methodName, + * rbusObject_t inParams, + * rbusMethodAsyncRespHandler_t callback, + * int timeout) + * @brief Invokes a remote method, non-blocking, with an asynchronous return callback. + * + * Invokes a remote method without blocking. + * The return params of the method will be received asynchronously by the callback provided. + * inParams will be retained and used to invoke the method on a background thread; therefore, + * inParams should not be altered by the calling program until the callback complete. + * @param handle Bus Handle + * @param methodName Method name + * @param inParams Input params + * @param callback Callback handler for the method's return parameters. + * @param timeout Optional maximum time in seconds to receive a callback. + * @return RBus error code as defined by rbusError_t. + * Possible values are: RBUS_ERROR_INVALID_EVENT + * @ingroup Methods + */ +rbusError_t rbusMethod_InvokeAsync( + rbusHandle_t handle, + char const* methodName, + rbusObject_t inParams, + rbusMethodAsyncRespHandler_t callback, + int timeout); + +/** @} */ + +/** @addtogroup Providers + * @{ + */ + +/** @fn rbusError_t rbusMethod_SendAsyncResponse( + * rbusMethodAsyncHandle_t asyncHandle, + * rbusError_t error, + * rbusObject_t outParams) + * @brief Send the response to an invoked method. + * + * Providers can use this method to send the result of an invoke method asynchronously. + * The asyncHandle is provided when the provider's MethodHandler is called. + * The outParams param should be initialized and released by the provider. outParams can be NULL. + * The error params should be set to RBUS_ERROR_SUCCESS if the method was successful, + * or to an approprioate error if the method fails. + * @param asyncHandle Async handle + * @param error Any error that occured + * @param outParams The returned params of the method + * @return RBus error code as defined by rbusError_t. + * @ingroup Methods + */ +rbusError_t rbusMethod_SendAsyncResponse( + rbusMethodAsyncHandle_t asyncHandle, + rbusError_t error, + rbusObject_t outParams); + +/** @} */ + +/** @addtogroup Consumers + * @{ + */ + +/** @fn rbusError_t rbus_createSession( + * rbusHandle_t handle, + * uint32_t *pSessionId) + * + * @brief Components use this API to create a new session to set 1 or more parameters + * and action on table like add/remove rows. \n + * Used by: Components that needs to Set Param & Update Tables. + * + * @param handle Bus Handle + * @param pSessionId unique session identified + * @return RBus error code as defined by rbusError_t. + * Possible values are: RBUS_ERROR_SESSION_ALREADY_EXIST; + * @ingroup Events + */ +rbusError_t rbus_createSession( + rbusHandle_t handle, + uint32_t *pSessionId); + +/** @fn rbusError_t rbus_getCurrentSessionId( + * rbusHandle_t handle, + * uint32_t *pSessionId) + * + * @brief Components use this API to get the current session to set 1 or more parameters + * and action on table like add/remove rows. \n + * Used by: Components that needs to Set Param & Update Tables. + * + * @param handle Bus Handle + * @param pSessionId return pointer to hold unique session currently in use + * @return RBus error code as defined by rbusError_t. + * Possible values are: RBUS_ERROR_BUS_ERROR; + */ +rbusError_t rbus_getCurrentSession( + rbusHandle_t handle, + uint32_t *pSessionId); + +/** @fn rbusError_t rbus_closeSession( + * rbusHandle_t handle, + * uint32_t sessionId) + * + * @brief Components use this API to close the current session that was used to set 1 or more parameters + * and action on table like add/remove rows. \n + * Used by: Components that used the session for Set Param & Update Tables. + * + * @param handle Bus Handle + * @param sessionId unique session Identifier that is currently in use + * @return RBus error code as defined by rbusError_t. + * Possible values are: RBUS_ERROR_BUS_ERROR; + */ +rbusError_t rbus_closeSession( + rbusHandle_t handle, + uint32_t sessionId); + +/** @fn rbusError_t rbus_registerLogHandler( + * rbusLogHandler logHandler) + * + * @brief A callback handler to get the log messages to application context + * + * Used by: Component that wants to handle the logs in its own way must register a callback handler to get the log messages. + * + * @param logHandler handler function pointer + * @return RBus error code as defined by rbusError_t. + */ +rbusError_t rbus_registerLogHandler( + rbusLogHandler logHandler); + +/** @fn rbusError_t rbus_setLogLevel( + * rbusLogLevel_t level) + * + * @brief Component use this API to set the log level to be printed + * + * Used by: Component that wants to use default logging of RBUS instead of overriding the logging, can set the log level by this. + * + * @param level Log level + * @return RBus error code as defined by rbusError_t. + */ + +rbusError_t rbus_setLogLevel(rbusLogLevel_t level); + +/** @fn rbusError_t rbus_openDirect( + * rbusHandle_t handle, + * rbusHandle_t* myDirectHandle, + * char const* parameterName) + * + * @brief Component use this API to open direct connection to the provider + * + * Used by: Component that wants to use private connection to provider and avoid RBUS Daemon. + * + * @param handle Current active rbus handle + * @param myDirectHandle Direct handle that is created + * @param parameterName Direct connection for the given parameter + * @return RBus error code as defined by rbusError_t. + */ +rbusError_t rbus_openDirect(rbusHandle_t handle, rbusHandle_t* myDirectHandle, char const* parameterName); + +/** @fn rbusError_t rbus_closeDirect( + * rbusHandle_t myDirectHandle) + * + * @brief Component use this API to close existing direct connection to the provider for the given DML + * + * Used by: Component that is no more need the direct connection and wants to switch to RBUS Daemon + * + * @param myDirectHandle Direct handle that is created + * @return RBus error code as defined by rbusError_t. + */ +rbusError_t rbus_closeDirect(rbusHandle_t handle); +/** @} */ + +#ifdef __cplusplus +} +#endif + +#include "rbus_message.h" + +#endif + +/** @} */