From a87b00508293e29872c60b1f5147fee23bc191f6 Mon Sep 17 00:00:00 2001 From: hgfell683 <107510770+hgfell683@users.noreply.github.com> Date: Tue, 13 Oct 2026 10:52:42 -0400 Subject: [PATCH] RDKEMW-26925 : L1 unit tests --- .github/workflows/L1-tests.yml | 47 +++ .github/workflows/tests-trigger.yml | 15 + cov_build.sh | 2 +- tests/Makefile.am | 13 + tests/mocks/libCMAF.h | 21 ++ tests/mocks/uidev.h | 19 ++ tests/run_l1_tests.sh | 34 +++ tests/test_iarm_event_sender.c | 375 +++++++++++++++++++++++ tests/test_key_simulator.c | 262 ++++++++++++++++ tests/test_legacy_key_simulator.c | 176 +++++++++++ tests/test_mfr_utils.c | 209 +++++++++++++ tests/test_power_state_monitor.c | 457 ++++++++++++++++++++++++++++ tests/test_query_power_state.c | 281 +++++++++++++++++ tests/test_set_power_state.c | 273 +++++++++++++++++ tests/test_uinput.c | 306 +++++++++++++++++++ tests/test_uinput_main.c | 8 + 16 files changed, 2497 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/L1-tests.yml create mode 100644 .github/workflows/tests-trigger.yml create mode 100644 tests/Makefile.am create mode 100644 tests/mocks/libCMAF.h create mode 100644 tests/mocks/uidev.h create mode 100644 tests/run_l1_tests.sh create mode 100644 tests/test_iarm_event_sender.c create mode 100644 tests/test_key_simulator.c create mode 100644 tests/test_legacy_key_simulator.c create mode 100644 tests/test_mfr_utils.c create mode 100644 tests/test_power_state_monitor.c create mode 100644 tests/test_query_power_state.c create mode 100644 tests/test_set_power_state.c create mode 100644 tests/test_uinput.c create mode 100644 tests/test_uinput_main.c diff --git a/.github/workflows/L1-tests.yml b/.github/workflows/L1-tests.yml new file mode 100644 index 0000000..54e735b --- /dev/null +++ b/.github/workflows/L1-tests.yml @@ -0,0 +1,47 @@ +name: L1-tests + +on: + workflow_call: + +permissions: + contents: read + +jobs: + L1-tests: + name: Build and run L1 unit tests + runs-on: ubuntu-22.04 + container: + image: ghcr.io/rdkcentral/docker-rdk-ci:latest + steps: + - name: Checkout sys_mon_tools + uses: actions/checkout@v4 + + - name: Prepare RDK build dependencies + run: ./build_dependencies.sh + + - name: Install coverage tools + run: | + apt-get update + apt-get install -y lcov + + - name: Build and run L1 tests + run: bash ./tests/run_l1_tests.sh + + - name: Generate coverage report + if: success() + run: | + lcov --capture --directory . --output-file coverage.info + lcov --remove coverage.info '/usr/*' '*/tests/*' '*/stubs/*' --output-file filtered_coverage.info + genhtml filtered_coverage.info --output-directory coverage --title "sys_mon_tools L1 coverage" + lcov --summary filtered_coverage.info + + - name: Upload L1 artifacts + if: always() + uses: actions/upload-artifact@v4 + with: + name: artifacts-L1-sys_mon_tools + path: | + coverage/ + filtered_coverage.info + tests/test-suite.log + if-no-files-found: warn \ No newline at end of file diff --git a/.github/workflows/tests-trigger.yml b/.github/workflows/tests-trigger.yml new file mode 100644 index 0000000..3435e65 --- /dev/null +++ b/.github/workflows/tests-trigger.yml @@ -0,0 +1,15 @@ +name: tests-trigger + +on: + push: + branches: [ main, develop, 'sprint/**', 'release/**' ] + pull_request: + branches: [ main, develop, 'sprint/**', 'release/**', 'topic/RDK*', 'feature/RDK*' ] + workflow_dispatch: + +permissions: + contents: read + +jobs: + trigger-L1: + uses: ./.github/workflows/L1-tests.yml \ No newline at end of file diff --git a/cov_build.sh b/cov_build.sh index 79efba8..c7184de 100755 --- a/cov_build.sh +++ b/cov_build.sh @@ -75,4 +75,4 @@ standaloneBuildConfigure #cp $IARMMGRS_PATH/mfr/common/include/mfrApi.h /usr/local/include make VERBOSE=1 AM_CPPFLAGS="-I${WORKDIR}/stubs -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -I/usr/include/libsoup-3.0 -I/usr/local/include -I${IARMMGRS_PATH}/sysmgr/include -I${IARMBUS_PATH}/core -I${IARMBUS_PATH}/core/include -I${IARMMGRS_PATH}/hal/include -I${IARMMGRS_PATH}/mfr/include -I${IARMMGRS_PATH}/mfr/common/include -I$ROOT/devicesettings/rpc/include -I$ROOT/rdk-halif-device_settings/include" \ -AM_LDFLAGS="-L/usr/local/lib -lIARMBus -lWPEFrameworkPowerController" CXXFLAGS="-fpermissive -I${WORKDIR}/stubs" +AM_LDFLAGS="-L/usr/local/lib -lIARMBus -lWPEFrameworkPowerController" CXXFLAGS="-fpermissive -I${WORKDIR}/stubs" \ No newline at end of file diff --git a/tests/Makefile.am b/tests/Makefile.am new file mode 100644 index 0000000..ad5fcec --- /dev/null +++ b/tests/Makefile.am @@ -0,0 +1,13 @@ +EXTRA_DIST = \ + run_l1_tests.sh \ + test_iarm_event_sender.c \ + test_key_simulator.c \ + test_legacy_key_simulator.c \ + test_mfr_utils.c \ + test_power_state_monitor.c \ + test_query_power_state.c \ + test_set_power_state.c \ + test_uinput.c \ + test_uinput_main.c \ + mocks/libCMAF.h \ + mocks/uidev.h diff --git a/tests/mocks/libCMAF.h b/tests/mocks/libCMAF.h new file mode 100644 index 0000000..08a39ba --- /dev/null +++ b/tests/mocks/libCMAF.h @@ -0,0 +1,21 @@ +#ifndef TEST_MOCK_LIB_CMAF_H +#define TEST_MOCK_LIB_CMAF_H + +#include + +typedef enum { + CMAF_RESULT_SUCCESS = 0, + CMAF_RESULT_INVALID_PARAM = 1 +} CMAF_Result_t; + +typedef enum { + CMAF_MEMTYPE_PROCESSSHARE = 0 +} CMAF_MemType_t; + +typedef int CMAF_EventId_t; + +CMAF_Result_t CMAF_Init(const char *group_name, const char *program_name, void **context); +CMAF_Result_t CMAF_Malloc(void *context, CMAF_MemType_t memory_type, size_t size, void **buffer); +CMAF_Result_t CMAF_NotifyEvent(void *context, CMAF_EventId_t event_id, void *event_data); + +#endif \ No newline at end of file diff --git a/tests/mocks/uidev.h b/tests/mocks/uidev.h new file mode 100644 index 0000000..1af604a --- /dev/null +++ b/tests/mocks/uidev.h @@ -0,0 +1,19 @@ +#ifndef TEST_MOCK_UIDEV_H +#define TEST_MOCK_UIDEV_H + +#define UIDEV_PROCESSGROUP_NAME "UIEventSimulator" +#define UIDEV_EVENT_IRKEY 1 + +typedef struct { + int keyType; + int keyCode; +} UIDev_IRKeyData_t; + +typedef struct { + int id; + union { + UIDev_IRKeyData_t irkey; + } data; +} UIDev_EventData_t; + +#endif \ No newline at end of file diff --git a/tests/run_l1_tests.sh b/tests/run_l1_tests.sh new file mode 100644 index 0000000..106cf72 --- /dev/null +++ b/tests/run_l1_tests.sh @@ -0,0 +1,34 @@ +#!/bin/bash +set -ex + +WORKDIR="$(pwd)" +ROOT=/usr +IARMBUS_PATH="$ROOT/iarmbus" +IARMMGRS_PATH="$ROOT/iarmmgrs" + +export CFLAGS="-O2 -Wall -fPIC --coverage -I./include -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -I/usr/include -I/usr/local/include -I${WORKDIR}/stubs -I/usr/include/dbus-1.0 -I/usr/lib/x86_64-linux-gnu/dbus-1.0/include -I/usr/include/libsoup-2.4 -I/usr/include/gssdp-1.0" +export CC=gcc + +BUILD_AM_CPPFLAGS="-I${WORKDIR}/stubs -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -I/usr/include/libsoup-3.0 -I/usr/local/include -I${IARMMGRS_PATH}/sysmgr/include -I${IARMBUS_PATH}/core -I${IARMBUS_PATH}/core/include -I${IARMMGRS_PATH}/hal/include -I${IARMMGRS_PATH}/mfr/include -I${IARMMGRS_PATH}/mfr/common/include -I${ROOT}/devicesettings/rpc/include -I${ROOT}/rdk-halif-device_settings/include" + +GLIB_CFLAGS="$(pkg-config --cflags glib-2.0)" +GLIB_LIBS="$(pkg-config --libs glib-2.0)" + +build_and_run() +{ + test_name="$1" + test_source="$2" + test_cflags="$3" + test_libs="$4" + ${CC} ${CFLAGS} ${BUILD_AM_CPPFLAGS} ${test_cflags} -o "${WORKDIR}/tests/${test_name}" "${WORKDIR}/tests/${test_source}" ${test_libs} + "${WORKDIR}/tests/${test_name}" +} + +build_and_run l1_uinput_tests test_uinput_main.c "-DUINPUT_VERSION=5 -I${WORKDIR}/key_simulator" "" +build_and_run test_set_power_state test_set_power_state.c "" "-lpthread" +build_and_run test_query_power_state test_query_power_state.c "" "" +build_and_run test_mfr_utils test_mfr_utils.c "" "" +build_and_run test_key_simulator test_key_simulator.c "-I${WORKDIR}/key_simulator" "" +build_and_run test_iarm_event_sender test_iarm_event_sender.c "${GLIB_CFLAGS}" "${GLIB_LIBS}" +build_and_run test_power_state_monitor test_power_state_monitor.c "${GLIB_CFLAGS}" "${GLIB_LIBS} -lpthread" +build_and_run test_legacy_key_simulator test_legacy_key_simulator.c "-I${WORKDIR}/key_simulator -I${WORKDIR}/tests/mocks" "" \ No newline at end of file diff --git a/tests/test_iarm_event_sender.c b/tests/test_iarm_event_sender.c new file mode 100644 index 0000000..03d0542 --- /dev/null +++ b/tests/test_iarm_event_sender.c @@ -0,0 +1,375 @@ +#include +#include +#include +#include + +#define main iarm_event_sender_main +#include "../iarm-event-sender/IARM_event_sender.c" +#undef main + +#define MAX_CAPTURED_EVENT_SIZE 4096 + +static int assertion_failures; +static int bus_init_calls; +static int bus_connect_calls; +static int bus_disconnect_calls; +static int bus_term_calls; +static int broadcast_calls; +static IARM_Result_t broadcast_result = IARM_RESULT_SUCCESS; +static IARM_EventId_t captured_event_id; +static size_t captured_event_size; +static char captured_owner[128]; +static unsigned char captured_event[MAX_CAPTURED_EVENT_SIZE]; + +#define CHECK(condition) do { \ + if (!(condition)) { \ + fprintf(stderr, "%s:%d: check failed: %s\n", __FILE__, __LINE__, #condition); \ + assertion_failures++; \ + } \ +} while (0) + +IARM_Result_t IARM_Bus_Init(const char *name) +{ + (void)name; + bus_init_calls++; + return IARM_RESULT_SUCCESS; +} + +IARM_Result_t IARM_Bus_Connect(void) +{ + bus_connect_calls++; + return IARM_RESULT_SUCCESS; +} + +IARM_Result_t IARM_Bus_Disconnect(void) +{ + bus_disconnect_calls++; + return IARM_RESULT_SUCCESS; +} + +IARM_Result_t IARM_Bus_Term(void) +{ + bus_term_calls++; + return IARM_RESULT_SUCCESS; +} + +IARM_Result_t IARM_Bus_BroadcastEvent(const char *ownerName, IARM_EventId_t eventId, + void *argument, size_t argumentLength) +{ + broadcast_calls++; + captured_event_id = eventId; + captured_event_size = argumentLength; + snprintf(captured_owner, sizeof(captured_owner), "%s", ownerName); + if (argumentLength <= sizeof(captured_event)) { + memcpy(captured_event, argument, argumentLength); + } + return broadcast_result; +} + +static void reset_mocks(void) +{ + bus_init_calls = 0; + bus_connect_calls = 0; + bus_disconnect_calls = 0; + bus_term_calls = 0; + broadcast_calls = 0; + broadcast_result = IARM_RESULT_SUCCESS; + captured_event_id = 0; + captured_event_size = 0; + captured_owner[0] = '\0'; + memset(captured_event, 0, sizeof(captured_event)); +} + +static void test_parse_argument_types(void) +{ + ArgValue value; + + CHECK(parseArg("-17", ARG_INT, &value)); + CHECK(value.type == ARG_INT); + CHECK(value.val.i == -17); + CHECK(parseArg("TRUE", ARG_BOOL, &value)); + CHECK(value.val.b); + CHECK(parseArg("false", ARG_BOOL, &value)); + CHECK(!value.val.b); + CHECK(!parseArg("yes", ARG_BOOL, &value)); + CHECK(parseArg("language", ARG_STRING, &value)); + CHECK(strcmp(value.val.s, "language") == 0); +} + +static void test_system_state_event_payload(void) +{ + GString *name = g_string_new("GatewayConnEvent"); + IARM_Bus_SYSMgr_EventData_t *event_data; + + reset_mocks(); + CHECK(sendIARMEvent(name, 1) == IARM_RESULT_SUCCESS); + CHECK(broadcast_calls == 1); + CHECK(strcmp(captured_owner, IARM_BUS_SYSMGR_NAME) == 0); + CHECK(captured_event_id == IARM_BUS_SYSMGR_EVENT_SYSTEMSTATE); + CHECK(captured_event_size == sizeof(IARM_Bus_SYSMgr_EventData_t)); + event_data = (IARM_Bus_SYSMgr_EventData_t *)captured_event; + CHECK(event_data->data.systemStates.stateId == IARM_BUS_SYSMGR_SYSSTATE_GATEWAY_CONNECTION); + CHECK(event_data->data.systemStates.state == 1); + CHECK(event_data->data.systemStates.error == 0); + CHECK(bus_init_calls == 1); + CHECK(bus_connect_calls == 1); + CHECK(bus_disconnect_calls == 1); + CHECK(bus_term_calls == 1); + g_string_free(name, TRUE); +} + +static void test_custom_state_and_intrusion_payloads(void) +{ + GString *name; + IARM_Bus_SYSMgr_EventData_t *state_event; + IARM_Bus_SYSMgr_IntrusionData_t *intrusion_event; + char payload[] = "test intrusion payload"; + + reset_mocks(); + CHECK(sendCustomIARMEvent(31, 2, 8) == IARM_RESULT_SUCCESS); + CHECK(broadcast_calls == 1); + CHECK(captured_event_id == IARM_BUS_SYSMGR_EVENT_SYSTEMSTATE); + state_event = (IARM_Bus_SYSMgr_EventData_t *)captured_event; + CHECK(state_event->data.systemStates.stateId == 31); + CHECK(state_event->data.systemStates.state == 2); + CHECK(state_event->data.systemStates.error == 8); + + reset_mocks(); + name = g_string_new("IntrusionEvent"); + CHECK(sendIARMEventPayload(name, payload) == IARM_RESULT_SUCCESS); + CHECK(broadcast_calls == 1); + CHECK(captured_event_id == IARM_BUS_SYSMGR_EVENT_INTRUSION); + intrusion_event = (IARM_Bus_SYSMgr_IntrusionData_t *)captured_event; + CHECK(strcmp(intrusion_event->intrusionData, payload) == 0); + g_string_free(name, TRUE); +} + +static void test_dsmgr_event_dispatch_and_invalid_arguments(void) +{ + char *valid_argv[] = { "IARM_event_sender", "DSMgr_EventRxSense", "23", NULL }; + char *invalid_argv[] = { "IARM_event_sender", "DSMgr_HdmiHotPlug", "maybe", NULL }; + char *wrong_count_argv[] = { "IARM_event_sender", "DSMgr_EventRxSense", NULL }; + char *unknown_event_argv[] = { "IARM_event_sender", "DSMgr_Unknown", NULL }; + IARM_Bus_DSMgr_EventData_t *event_data; + + reset_mocks(); + handleIARMEvents(3, valid_argv); + CHECK(broadcast_calls == 1); + CHECK(captured_event_id == IARM_BUS_DSMGR_EVENT_RX_SENSE); + event_data = (IARM_Bus_DSMgr_EventData_t *)captured_event; + CHECK(event_data->data.hdmi_rxsense.status == 23); + CHECK(bus_init_calls == 1); + CHECK(bus_disconnect_calls == 1); + + reset_mocks(); + handleIARMEvents(3, invalid_argv); + CHECK(broadcast_calls == 0); + CHECK(bus_init_calls == 0); + + reset_mocks(); + handleIARMEvents(2, wrong_count_argv); + CHECK(broadcast_calls == 0); + CHECK(bus_init_calls == 0); + + reset_mocks(); + handleIARMEvents(2, unknown_event_argv); + CHECK(broadcast_calls == 0); + CHECK(bus_init_calls == 0); +} + +static void test_all_registered_dsmgr_handlers(void) +{ + size_t event_index; + + for (event_index = 0; event_index < (size_t)iarmEventTableSize; event_index++) { + char *argv[MAX_ARG_NO_TYPE + 3] = { "IARM_event_sender", NULL }; + char values[MAX_ARG_NO_TYPE][16]; + IARM_EventEntry *entry = &iarmEventTable[event_index]; + int argument_index; + + argv[1] = (char *)entry->name; + for (argument_index = 0; argument_index < entry->argc_expected; argument_index++) { + switch (entry->arg_types[argument_index]) { + case ARG_INT: + snprintf(values[argument_index], sizeof(values[argument_index]), "%d", argument_index + 1); + break; + case ARG_BOOL: + snprintf(values[argument_index], sizeof(values[argument_index]), "true"); + break; + case ARG_STRING: + snprintf(values[argument_index], sizeof(values[argument_index]), "sample"); + break; + } + argv[argument_index + 2] = values[argument_index]; + } + + reset_mocks(); + handleIARMEvents(entry->argc_expected + 2, argv); + CHECK(broadcast_calls == 1); + CHECK(bus_init_calls == 1); + CHECK(bus_connect_calls == 1); + CHECK(bus_disconnect_calls == 1); + CHECK(bus_term_calls == 1); + } +} + +static void test_broadcast_error_is_propagated(void) +{ + GString *name = g_string_new("NTPReceivedEvent"); + + reset_mocks(); + broadcast_result = IARM_RESULT_INVALID_PARAM; + CHECK(sendIARMEvent(name, 5) == IARM_RESULT_INVALID_PARAM); + CHECK(broadcast_calls == 1); + g_string_free(name, TRUE); +} + +static void test_cli_usage_and_invalid_arguments(void) +{ + char *missing_argv[] = { "IARM_event_sender", NULL }; + char *invalid_argv[] = { "IARM_event_sender", "unknown", NULL }; + + reset_mocks(); + CHECK(iarm_event_sender_main(1, missing_argv) == 1); + CHECK(broadcast_calls == 0); + + reset_mocks(); + CHECK(iarm_event_sender_main(2, invalid_argv) == 1); + CHECK(broadcast_calls == 0); +} + +static void test_cli_routes_system_custom_and_dsmgr_events(void) +{ + char *system_state_argv[] = { "IARM_event_sender", "GatewayConnEvent", "1", NULL }; + char *filter_argv[] = { "IARM_event_sender", "EISSFilterEvent", "5", NULL }; + char *custom_argv[] = { "IARM_event_sender", "CustomEvent", "41", "2", "9", NULL }; + IARM_Bus_SYSMgr_EventData_t *system_event; + + reset_mocks(); + CHECK(iarm_event_sender_main(3, system_state_argv) == 0); + CHECK(broadcast_calls == 1); + CHECK(captured_event_id == IARM_BUS_SYSMGR_EVENT_SYSTEMSTATE); + system_event = (IARM_Bus_SYSMgr_EventData_t *)captured_event; + CHECK(system_event->data.systemStates.stateId == IARM_BUS_SYSMGR_SYSSTATE_GATEWAY_CONNECTION); + CHECK(system_event->data.systemStates.state == 1); + + reset_mocks(); + CHECK(iarm_event_sender_main(3, filter_argv) == 0); + CHECK(broadcast_calls == 1); + CHECK(captured_event_id == IARM_BUS_SYSMGR_EVENT_EISS_FILTER_STATUS); + system_event = (IARM_Bus_SYSMgr_EventData_t *)captured_event; + CHECK(system_event->data.eissEventData.filterStatus == 5); + + reset_mocks(); + CHECK(iarm_event_sender_main(5, custom_argv) == 0); + CHECK(broadcast_calls == 1); + CHECK(captured_event_id == IARM_BUS_SYSMGR_EVENT_SYSTEMSTATE); + system_event = (IARM_Bus_SYSMgr_EventData_t *)captured_event; + CHECK(system_event->data.systemStates.stateId == 41); + CHECK(system_event->data.systemStates.state == 2); + CHECK(system_event->data.systemStates.error == 9); + +} + +static void test_cli_intrusion_payload_and_truncation(void) +{ + char payload[] = "intrusion payload from cli"; + char long_payload[IARM_BUS_SYSMGR_Intrusion_MaxLen + 64]; + char *normal_argv[] = { "IARM_event_sender", "IntrusionEvent", "unused", payload, NULL }; + char *long_argv[] = { "IARM_event_sender", "IntrusionEvent", "unused", long_payload, NULL }; + IARM_Bus_SYSMgr_IntrusionData_t *event_data; + + reset_mocks(); + CHECK(iarm_event_sender_main(4, normal_argv) == 0); + CHECK(broadcast_calls == 1); + CHECK(captured_event_id == IARM_BUS_SYSMGR_EVENT_INTRUSION); + event_data = (IARM_Bus_SYSMgr_IntrusionData_t *)captured_event; + CHECK(strcmp(event_data->intrusionData, payload) == 0); + + memset(long_payload, 'x', sizeof(long_payload) - 1); + long_payload[sizeof(long_payload) - 1] = '\0'; + reset_mocks(); + CHECK(iarm_event_sender_main(4, long_argv) == 0); + CHECK(broadcast_calls == 1); + event_data = (IARM_Bus_SYSMgr_IntrusionData_t *)captured_event; + CHECK(strchr(event_data->intrusionData, INTRU_ABREV) != NULL); + CHECK(strstr(event_data->intrusionData, JSON_TERM) != NULL); +} + +static void test_cli_usb_mount_and_detection_events(void) +{ + char *mount_argv[] = { "IARM_event_sender", "USBMountChangedEvent", "1", "/dev/sda1", "/media/usb", NULL }; + char *detect_argv[] = { "IARM_event_sender", "usbdetected", "add", "vendor", "product", "device", NULL }; + IARM_Bus_SYSMgr_EventData_t *event_data; + + reset_mocks(); + CHECK(iarm_event_sender_main(5, mount_argv) == 0); + CHECK(broadcast_calls == 1); + CHECK(captured_event_id == IARM_BUS_SYSMGR_EVENT_USB_MOUNT_CHANGED); + event_data = (IARM_Bus_SYSMgr_EventData_t *)captured_event; + CHECK(event_data->data.usbMountData.mounted == 1); + CHECK(strcmp(event_data->data.usbMountData.device, "/dev/sda1") == 0); + CHECK(strcmp(event_data->data.usbMountData.dir, "/media/usb") == 0); + + reset_mocks(); + CHECK(iarm_event_sender_main(6, detect_argv) == 0); + CHECK(broadcast_calls == 1); + CHECK(captured_event_id == IARM_BUS_SYSMGR_SYSSTATE_USB_DETECTED); + event_data = (IARM_Bus_SYSMgr_EventData_t *)captured_event; + CHECK(event_data->data.usbData.inserted == 0); + CHECK(strcmp(event_data->data.usbData.vendor, "vendor") == 0); + CHECK(strcmp(event_data->data.usbData.productid, "product") == 0); + CHECK(strcmp(event_data->data.usbData.devicename, "device") == 0); +} + +static void test_eiss_app_id_payload_dispatch(void) +{ + GString *event_name = g_string_new("EISSAppIdEvent"); + char payload[] = "0123456789abcdefghijklmn"; + IARM_Bus_SYSMgr_EventData_t *event_data; + + reset_mocks(); + CHECK(sendIARMEventPayload(event_name, payload) == IARM_RESULT_SUCCESS); + CHECK(broadcast_calls == 1); + CHECK(captured_event_id == IARM_BUS_SYSMGR_EVENT_EISS_APP_ID_UPDATE); + event_data = (IARM_Bus_SYSMgr_EventData_t *)captured_event; + CHECK(event_data->data.eissAppIDList.count == 4); + CHECK(memcmp(event_data->data.eissAppIDList.idList, payload, + sizeof(event_data->data.eissAppIDList.idList)) == 0); + g_string_free(event_name, TRUE); +} + +static void test_cli_peripheral_payload_route(void) +{ + char *peripheral_argv[] = { "IARM_event_sender", "PeripheralUpgradeEvent", "/firmware", "image.bin", NULL }; + + reset_mocks(); + CHECK(iarm_event_sender_main(4, peripheral_argv) == 0); + CHECK(broadcast_calls == 0); + CHECK(bus_init_calls == 1); + CHECK(bus_disconnect_calls == 1); + CHECK(bus_term_calls == 1); +} + +int main(void) +{ + test_parse_argument_types(); + test_system_state_event_payload(); + test_custom_state_and_intrusion_payloads(); + test_dsmgr_event_dispatch_and_invalid_arguments(); + test_all_registered_dsmgr_handlers(); + test_broadcast_error_is_propagated(); + test_cli_usage_and_invalid_arguments(); + test_cli_routes_system_custom_and_dsmgr_events(); + test_cli_intrusion_payload_and_truncation(); + test_cli_usb_mount_and_detection_events(); + test_eiss_app_id_payload_dispatch(); + test_cli_peripheral_payload_route(); + + if (assertion_failures != 0) { + fprintf(stderr, "%d assertion(s) failed\n", assertion_failures); + return 1; + } + puts("IARM event sender L1 tests passed"); + return 0; +} \ No newline at end of file diff --git a/tests/test_key_simulator.c b/tests/test_key_simulator.c new file mode 100644 index 0000000..238c6b6 --- /dev/null +++ b/tests/test_key_simulator.c @@ -0,0 +1,262 @@ +#include +#include +#include +#include + +#include "UIEventSimulator.h" +#include "uInputInternal.h" + +#define main key_simulator_main +#include "../key_simulator/IARM_BUS_UIEventSimulator.c" +#undef main + +#define MAX_SENT_EVENTS 128 + +typedef struct { + int key_type; + int key_code; +} sent_event_t; + +static int assertion_failures; +static int bus_init_calls; +static int bus_connect_calls; +static int bus_disconnect_calls; +static int bus_term_calls; +static int uinput_init_calls; +static int uinput_term_calls; +static int sleep_calls; +static int usleep_calls; +static int dispatcher_available = 1; +static sent_event_t sent_events[MAX_SENT_EVENTS]; +static size_t sent_event_count; + +#define CHECK(condition) do { \ + if (!(condition)) { \ + fprintf(stderr, "%s:%d: check failed: %s\n", __FILE__, __LINE__, #condition); \ + assertion_failures++; \ + } \ +} while (0) + +static void mock_dispatcher(int event_key_code, int event_key_type, int source) +{ + (void)source; + if (sent_event_count < MAX_SENT_EVENTS) { + sent_events[sent_event_count].key_type = event_key_type; + sent_events[sent_event_count].key_code = event_key_code; + sent_event_count++; + } +} + +IARM_Result_t IARM_Bus_Init(const char *name) +{ + (void)name; + bus_init_calls++; + return IARM_RESULT_SUCCESS; +} + +IARM_Result_t IARM_Bus_Connect(void) +{ + bus_connect_calls++; + return IARM_RESULT_SUCCESS; +} + +IARM_Result_t IARM_Bus_Disconnect(void) +{ + bus_disconnect_calls++; + return IARM_RESULT_SUCCESS; +} + +IARM_Result_t IARM_Bus_Term(void) +{ + bus_term_calls++; + return IARM_RESULT_SUCCESS; +} + +int UINPUT_init(void) +{ + uinput_init_calls++; + return 0; +} + +int UINPUT_term(void) +{ + uinput_term_calls++; + return 0; +} + +uinput_dispatcher_t UINPUT_GetDispatcher(void) +{ + return dispatcher_available ? mock_dispatcher : NULL; +} + +int usleep(useconds_t usec) +{ + (void)usec; + usleep_calls++; + return 0; +} + +unsigned int sleep(unsigned int seconds) +{ + (void)seconds; + sleep_calls++; + return 0; +} + +static void reset_mocks(void) +{ + bus_init_calls = 0; + bus_connect_calls = 0; + bus_disconnect_calls = 0; + bus_term_calls = 0; + uinput_init_calls = 0; + uinput_term_calls = 0; + sleep_calls = 0; + usleep_calls = 0; + dispatcher_available = 1; + sent_event_count = 0; + memset(sent_events, 0, sizeof(sent_events)); + executableName = "key-simulator-test"; + keyCode = -1; + interval = 1; + repeat = 1; + pressAndHold = 0; + duration = 5; +} + +static void test_key_code_lookup(void) +{ + char known_command[] = "menu"; + char unknown_command[] = "not-a-key"; + + reset_mocks(); + CHECK(findKeyCode(known_command) == IARM_RESULT_SUCCESS); + CHECK(keyCode == KED_MENU); + CHECK(findKeyCode(unknown_command) == IARM_RESULT_INVALID_PARAM); +} + +static void test_repeated_key_down_up_sequence(void) +{ + reset_mocks(); + keyCode = KED_DIGIT4; + repeat = 3; + interval = 2; + + CHECK(sendCommand() == IARM_RESULT_SUCCESS); + CHECK(sent_event_count == 6); + CHECK(sent_events[0].key_type == KET_KEYDOWN); + CHECK(sent_events[0].key_code == KED_DIGIT4); + CHECK(sent_events[1].key_type == KET_KEYUP); + CHECK(sent_events[2].key_type == KET_KEYDOWN); + CHECK(sent_events[5].key_type == KET_KEYUP); + CHECK(sleep_calls == 3); + CHECK(usleep_calls == 4); +} + +static void test_press_and_hold_repeats_then_releases(void) +{ + reset_mocks(); + keyCode = KED_ARROWUP; + repeat = 1; + duration = 2; + pressAndHold = 1; + + CHECK(sendCommand() == IARM_RESULT_SUCCESS); + CHECK(sent_event_count == 42); + CHECK(sent_events[0].key_type == KET_KEYDOWN); + CHECK(sent_events[1].key_type == KET_KEYREPEAT); + CHECK(sent_events[40].key_type == KET_KEYREPEAT); + CHECK(sent_events[41].key_type == KET_KEYUP); +} + +static void test_dispatcher_absence_is_handled(void) +{ + reset_mocks(); + dispatcher_available = 0; + sendKeyEventToIARM(KET_KEYDOWN, KED_MENU); + CHECK(sent_event_count == 0); +} + +static void test_start_and_stop_lifecycle(void) +{ + reset_mocks(); + + CHECK(UIEventSimulator_Start() == IARM_RESULT_SUCCESS); + CHECK(bus_init_calls == 1); + CHECK(bus_connect_calls == 1); + CHECK(uinput_init_calls == 1); + + CHECK(UIEventSimulator_Stop() == IARM_RESULT_SUCCESS); + CHECK(bus_disconnect_calls == 1); + CHECK(bus_term_calls == 1); + CHECK(uinput_term_calls == 1); +} + +static void test_cli_lifecycle_and_options(void) +{ + char *no_command_argv[] = { "key-simulator", NULL }; + char *help_argv[] = { "key-simulator", "-h", NULL }; + char *unknown_option_argv[] = { "key-simulator", "-zvalue", NULL }; + char *invalid_key_argv[] = { "key-simulator", "-knot-a-key", NULL }; + char *repeat_argv[] = { "key-simulator", "-kmenu", "-r2", "-i0", NULL }; + char *hold_argv[] = { "key-simulator", "-p1", "-kmenu", "-r1", "-i0", NULL }; + + reset_mocks(); + CHECK(key_simulator_main(1, no_command_argv) == 0); + CHECK(bus_init_calls == 1); + CHECK(bus_disconnect_calls == 1); + CHECK(uinput_term_calls == 1); + CHECK(sent_event_count == 0); + + reset_mocks(); + CHECK(key_simulator_main(2, help_argv) == 0); + CHECK(bus_init_calls == 1); + CHECK(bus_disconnect_calls == 1); + CHECK(sent_event_count == 0); + + reset_mocks(); + CHECK(key_simulator_main(2, unknown_option_argv) == 0); + CHECK(bus_disconnect_calls == 1); + CHECK(sent_event_count == 0); + + reset_mocks(); + CHECK(key_simulator_main(2, invalid_key_argv) == 0); + CHECK(bus_init_calls == 1); + CHECK(bus_disconnect_calls == 0); + CHECK(sent_event_count == 0); + + reset_mocks(); + CHECK(key_simulator_main(4, repeat_argv) == 0); + CHECK(sent_event_count == 4); + CHECK(sent_events[0].key_type == KET_KEYDOWN); + CHECK(sent_events[1].key_type == KET_KEYUP); + CHECK(sent_events[2].key_type == KET_KEYDOWN); + CHECK(sent_events[3].key_type == KET_KEYUP); + CHECK(bus_disconnect_calls == 1); + + reset_mocks(); + CHECK(key_simulator_main(5, hold_argv) == 0); + CHECK(sent_event_count == 22); + CHECK(sent_events[0].key_type == KET_KEYDOWN); + CHECK(sent_events[1].key_type == KET_KEYREPEAT); + CHECK(sent_events[20].key_type == KET_KEYREPEAT); + CHECK(sent_events[21].key_type == KET_KEYUP); + CHECK(bus_disconnect_calls == 1); +} + +int main(void) +{ + test_key_code_lookup(); + test_repeated_key_down_up_sequence(); + test_press_and_hold_repeats_then_releases(); + test_dispatcher_absence_is_handled(); + test_start_and_stop_lifecycle(); + test_cli_lifecycle_and_options(); + + if (assertion_failures != 0) { + fprintf(stderr, "%d assertion(s) failed\n", assertion_failures); + return 1; + } + puts("key simulator L1 tests passed"); + return 0; +} \ No newline at end of file diff --git a/tests/test_legacy_key_simulator.c b/tests/test_legacy_key_simulator.c new file mode 100644 index 0000000..4626fbc --- /dev/null +++ b/tests/test_legacy_key_simulator.c @@ -0,0 +1,176 @@ +#include +#include +#include +#include + +#define main legacy_key_simulator_main +#include "../key_simulator/IARM_UIEventSimulator.c" +#undef main + +static int assertion_failures; +static int cmaf_init_calls; +static int cmaf_malloc_calls; +static int cmaf_notify_calls; +static int sleep_calls; +static int usleep_calls; +static CMAF_Result_t cmaf_init_result = CMAF_RESULT_SUCCESS; +static UIDev_EventData_t sent_events[32]; +static size_t sent_event_count; + +#define CHECK(condition) do { \ + if (!(condition)) { \ + fprintf(stderr, "%s:%d: check failed: %s\n", __FILE__, __LINE__, #condition); \ + assertion_failures++; \ + } \ +} while (0) + +CMAF_Result_t CMAF_Init(const char *group_name, const char *program_name, void **context) +{ + (void)group_name; + (void)program_name; + cmaf_init_calls++; + *context = (void *)1; + return cmaf_init_result; +} + +CMAF_Result_t CMAF_Malloc(void *context, CMAF_MemType_t memory_type, size_t size, void **buffer) +{ + (void)context; + (void)memory_type; + cmaf_malloc_calls++; + *buffer = calloc(1, size); + return (*buffer == NULL) ? CMAF_RESULT_INVALID_PARAM : CMAF_RESULT_SUCCESS; +} + +CMAF_Result_t CMAF_NotifyEvent(void *context, CMAF_EventId_t event_id, void *event_data) +{ + (void)context; + CHECK(event_id == UIDEV_EVENT_IRKEY); + CHECK(event_data != NULL); + if (event_data != NULL && sent_event_count < sizeof(sent_events) / sizeof(sent_events[0])) { + sent_events[sent_event_count++] = *(UIDev_EventData_t *)event_data; + } + cmaf_notify_calls++; + free(event_data); + return CMAF_RESULT_SUCCESS; +} + +int usleep(useconds_t usec) +{ + (void)usec; + usleep_calls++; + return 0; +} + +unsigned int sleep(unsigned int seconds) +{ + (void)seconds; + sleep_calls++; + return 0; +} + +static void reset_mocks(void) +{ + cmaf_init_calls = 0; + cmaf_malloc_calls = 0; + cmaf_notify_calls = 0; + sleep_calls = 0; + usleep_calls = 0; + cmaf_init_result = CMAF_RESULT_SUCCESS; + sent_event_count = 0; + memset(sent_events, 0, sizeof(sent_events)); + keyCode = -1; + interval = 1; + repeat = 1; + executableName = "legacy-simulator-test"; + m_grpCtx = NULL; +} + +static void test_key_code_lookup(void) +{ + char menu_command[] = "menu"; + char digit_command[] = "7"; + char unknown_command[] = "not-a-key"; + + reset_mocks(); + CHECK(findKeyCode(menu_command) == CMAF_RESULT_SUCCESS); + CHECK(keyCode == KED_MENU); + + CHECK(findKeyCode(digit_command) == CMAF_RESULT_SUCCESS); + CHECK(keyCode == KED_DIGIT7); + + CHECK(findKeyCode(unknown_command) == CMAF_RESULT_INVALID_PARAM); + CHECK(keyCode == KED_DIGIT7); +} + +static void test_cli_help_and_no_command(void) +{ + char *help_argv[] = { "legacy-simulator", "-h", NULL }; + char *no_command_argv[] = { "legacy-simulator", NULL }; + + reset_mocks(); + CHECK(legacy_key_simulator_main(2, help_argv) == 0); + CHECK(cmaf_init_calls == 1); + CHECK(cmaf_malloc_calls == 0); + + reset_mocks(); + CHECK(legacy_key_simulator_main(1, no_command_argv) == 0); + CHECK(cmaf_init_calls == 1); + CHECK(cmaf_notify_calls == 0); +} + +static void test_cli_key_repeat_and_option_handling(void) +{ + char *repeat_argv[] = { "legacy-simulator", "-kmenu", "-r2", "-i0", NULL }; + char *unknown_option_argv[] = { "legacy-simulator", "-zvalue", NULL }; + + reset_mocks(); + CHECK(legacy_key_simulator_main(4, repeat_argv) == 0); + CHECK(cmaf_init_calls == 1); + CHECK(cmaf_malloc_calls == 4); + CHECK(cmaf_notify_calls == 4); + CHECK(sent_event_count == 4); + CHECK(sent_events[0].id == UIDEV_EVENT_IRKEY); + CHECK(sent_events[0].data.irkey.keyType == KET_KEYDOWN); + CHECK(sent_events[0].data.irkey.keyCode == KED_MENU); + CHECK(sent_events[1].data.irkey.keyType == KET_KEYUP); + CHECK(sent_events[2].data.irkey.keyType == KET_KEYDOWN); + CHECK(sent_events[3].data.irkey.keyType == KET_KEYUP); + CHECK(sleep_calls == 2); + + reset_mocks(); + CHECK(legacy_key_simulator_main(2, unknown_option_argv) == 0); + CHECK(cmaf_init_calls == 1); + CHECK(cmaf_notify_calls == 0); +} + +static void test_cli_invalid_key_and_cmaf_init_failure(void) +{ + char *invalid_key_argv[] = { "legacy-simulator", "-knot-a-key", NULL }; + char *key_argv[] = { "legacy-simulator", "-kmenu", NULL }; + + reset_mocks(); + CHECK(legacy_key_simulator_main(2, invalid_key_argv) == 0); + CHECK(cmaf_init_calls == 1); + CHECK(cmaf_notify_calls == 0); + + reset_mocks(); + cmaf_init_result = CMAF_RESULT_INVALID_PARAM; + CHECK(legacy_key_simulator_main(2, key_argv) == 0); + CHECK(cmaf_init_calls == 1); + CHECK(cmaf_notify_calls == 0); +} + +int main(void) +{ + test_key_code_lookup(); + test_cli_help_and_no_command(); + test_cli_key_repeat_and_option_handling(); + test_cli_invalid_key_and_cmaf_init_failure(); + if (assertion_failures != 0) { + fprintf(stderr, "%d assertion(s) failed\n", assertion_failures); + return 1; + } + puts("legacy key simulator L1 tests passed"); + return 0; +} \ No newline at end of file diff --git a/tests/test_mfr_utils.c b/tests/test_mfr_utils.c new file mode 100644 index 0000000..ebf67d6 --- /dev/null +++ b/tests/test_mfr_utils.c @@ -0,0 +1,209 @@ +#include +#include +#include +#include + +#define main mfr_util_main +#include "../mfr-utils/sys_mfr_utils.c" +#undef main + +static int assertion_failures; +static int bus_init_calls; +static int bus_connect_calls; +static int bus_call_calls; +static int bus_free_calls; +static int bus_disconnect_calls; +static int bus_term_calls; +static mfrSerializedType_t requested_type; +static const char response_value[] = "unit-test-mfr-value"; + +#define CHECK(condition) do { \ + if (!(condition)) { \ + fprintf(stderr, "%s:%d: check failed: %s\n", __FILE__, __LINE__, #condition); \ + assertion_failures++; \ + } \ +} while (0) + +IARM_Result_t IARM_Malloc(IARM_MemType_t type, size_t size, void **ptr) +{ + (void)type; + *ptr = calloc(1, size); + return (*ptr == NULL) ? IARM_RESULT_INVALID_PARAM : IARM_RESULT_SUCCESS; +} + +IARM_Result_t IARM_Free(IARM_MemType_t type, void *allocation) +{ + (void)type; + bus_free_calls++; + free(allocation); + return IARM_RESULT_SUCCESS; +} + +IARM_Result_t IARM_Bus_Init(const char *name) +{ + (void)name; + bus_init_calls++; + return IARM_RESULT_SUCCESS; +} + +IARM_Result_t IARM_Bus_Connect(void) +{ + bus_connect_calls++; + return IARM_RESULT_SUCCESS; +} + +IARM_Result_t IARM_Bus_Call(const char *ownerName, const char *methodName, + void *argument, size_t argumentLength) +{ + IARM_Bus_MFRLib_GetSerializedData_Param_t *parameter = argument; + + (void)ownerName; + (void)methodName; + CHECK(argumentLength == sizeof(*parameter)); + requested_type = parameter->type; + parameter->bufLen = sizeof(response_value) - 1; + memcpy(parameter->buffer, response_value, sizeof(response_value)); + bus_call_calls++; + return IARM_RESULT_SUCCESS; +} + +IARM_Result_t IARM_Bus_Disconnect(void) +{ + bus_disconnect_calls++; + return IARM_RESULT_SUCCESS; +} + +IARM_Result_t IARM_Bus_Term(void) +{ + bus_term_calls++; + return IARM_RESULT_SUCCESS; +} + +static void reset_mocks(void) +{ + bus_init_calls = 0; + bus_connect_calls = 0; + bus_call_calls = 0; + bus_free_calls = 0; + bus_disconnect_calls = 0; + bus_term_calls = 0; + requested_type = (mfrSerializedType_t)0; +} + +static int run_mfr_with_output(int argc, char **argv, char *output, size_t outputSize) +{ + FILE *capture; + int saved_stdout; + int result; + size_t output_length; + + fflush(stdout); + saved_stdout = dup(STDOUT_FILENO); + capture = tmpfile(); + if (saved_stdout < 0 || capture == NULL) { + if (saved_stdout >= 0) { + close(saved_stdout); + } + if (capture != NULL) { + fclose(capture); + } + return -1; + } + if (dup2(fileno(capture), STDOUT_FILENO) < 0) { + close(saved_stdout); + fclose(capture); + return -1; + } + + result = mfr_util_main(argc, argv); + fflush(stdout); + fseek(capture, 0, SEEK_SET); + output_length = fread(output, 1, outputSize - 1, capture); + output[output_length] = '\0'; + dup2(saved_stdout, STDOUT_FILENO); + close(saved_stdout); + fclose(capture); + return result; +} + +static void test_parameter_validation(void) +{ + CHECK(validateParams("--CurrentImageFilename") == 0); + CHECK(validateParams("--FlashedFilename") == 1); + CHECK(validateParams("--Modelname") == 2); + CHECK(validateParams("--HardwareId") == 3); + CHECK(validateParams("--Manufacturer") == 4); + CHECK(validateParams("--MfgSerialnumber") == 5); +#if defined(YOCTO_BUILD) + CHECK(validateParams("--PDRIVersion") == 6); +#else + CHECK(validateParams("--PDRIVersion") == -1); +#endif + CHECK(validateParams("--unknown") == -1); +} + +static void test_cli_queries_matching_type_and_prints_result(void) +{ + struct { + char *option; + mfrSerializedType_t expected_type; + } cases[] = { + { "--CurrentImageFilename", mfrSERIALIZED_TYPE_IMAGENAME }, + { "--FlashedFilename", mfrSERIALIZED_TYPE_IMAGENAME }, + { "--Modelname", mfrSERIALIZED_TYPE_MODELNAME }, + { "--HardwareId", mfrSERIALIZED_TYPE_HWID }, + { "--Manufacturer", mfrSERIALIZED_TYPE_MANUFACTURER }, + { "--MfgSerialnumber", mfrSERIALIZED_TYPE_MANUFACTURING_SERIALNUMBER }, +#if defined(YOCTO_BUILD) + { "--PDRIVersion", mfrSERIALIZED_TYPE_PDRIVERSION }, +#endif + }; + size_t index; + + for (index = 0; index < sizeof(cases) / sizeof(cases[0]); index++) { + char *argv[] = { "mfr_util", cases[index].option, NULL }; + char output[256]; + + reset_mocks(); + CHECK(run_mfr_with_output(2, argv, output, sizeof(output)) == 0); + CHECK(strcmp(output, "unit-test-mfr-value\n") == 0); + CHECK(requested_type == cases[index].expected_type); + CHECK(bus_init_calls == 1); + CHECK(bus_connect_calls == 1); + CHECK(bus_call_calls == 1); + CHECK(bus_free_calls == 1); + CHECK(bus_disconnect_calls == 1); + CHECK(bus_term_calls == 1); + } +} + +static void test_invalid_cli_arguments_do_not_call_bus(void) +{ + char *invalid_argv[] = { "mfr_util", "--unknown", NULL }; + char *missing_argv[] = { "mfr_util", NULL }; + char output[1024]; + + reset_mocks(); + CHECK(run_mfr_with_output(2, invalid_argv, output, sizeof(output)) == -1); + CHECK(strstr(output, "Usage : mfr_util") != NULL); + CHECK(bus_call_calls == 0); + + reset_mocks(); + CHECK(run_mfr_with_output(1, missing_argv, output, sizeof(output)) == -1); + CHECK(strstr(output, "Usage : mfr_util") != NULL); + CHECK(bus_call_calls == 0); +} + +int main(void) +{ + test_parameter_validation(); + test_cli_queries_matching_type_and_prints_result(); + test_invalid_cli_arguments_do_not_call_bus(); + + if (assertion_failures != 0) { + fprintf(stderr, "%d assertion(s) failed\n", assertion_failures); + return 1; + } + puts("mfr_util L1 tests passed"); + return 0; +} \ No newline at end of file diff --git a/tests/test_power_state_monitor.c b/tests/test_power_state_monitor.c new file mode 100644 index 0000000..4e42b75 --- /dev/null +++ b/tests/test_power_state_monitor.c @@ -0,0 +1,457 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "power_controller.h" + +FILE *__wrap_fopen(const char *path, const char *mode); +int __wrap_fclose(FILE *file); +int __wrap_fprintf(FILE *file, const char *format, ...); +int __wrap_remove(const char *path); +int __wrap_stat(const char *path, struct stat *buffer); +int __wrap_pthread_create(pthread_t *thread, const pthread_attr_t *attributes, + void *(*start_routine)(void *), void *argument); +int __wrap_pthread_detach(pthread_t thread); +GAsyncQueue *__wrap_g_async_queue_new(void); +gpointer __wrap_g_async_queue_pop(GAsyncQueue *queue); + +#define fopen __wrap_fopen +#define fclose __wrap_fclose +#define fprintf __wrap_fprintf +#define remove __wrap_remove +#define stat(path, buffer) __wrap_stat((path), (buffer)) +#define pthread_create __wrap_pthread_create +#define pthread_detach __wrap_pthread_detach +#define g_async_queue_new __wrap_g_async_queue_new +#define g_async_queue_pop __wrap_g_async_queue_pop +#define main power_state_monitor_main +#include "../power-state-monitor/powerStateMonitorMain.c" +#undef main +#undef g_async_queue_pop +#undef g_async_queue_new +#undef pthread_detach +#undef pthread_create +#undef stat +#undef remove +#undef fprintf +#undef fclose +#undef fopen + +static int assertion_failures; +static int power_init_calls; +static int is_operational = 1; +static uint32_t connect_results[8]; +static size_t connect_result_count; +static size_t connect_result_index; +static int connect_calls; +static int get_state_calls; +static int register_callback_calls; +static PowerController_PowerModeChangedCb registered_callback; +static void *registered_callback_userdata; +static int create_thread_calls; +static int detach_thread_calls; +static int run_thread_in_mock; +static int thread_create_result; +static int remove_calls; +static int close_file_calls; +static int fprintf_calls; +static int stat_calls; +static int sleep_calls; +static int async_queue_new_calls; +static int async_queue_pop_calls; +static int async_queue_available = 1; +static int escape_on_queue_pop; +static jmp_buf queue_pop_escape; +static int open_file_success = 1; +static int mock_lightsleep_exists; +static int mock_power_on_exists; +static int power_on_after_sleep; +static int remove_result; +static uint32_t get_state_result = POWER_CONTROLLER_ERROR_NONE; +static PowerController_PowerState_t current_state = POWER_STATE_ON; +static char opened_path[512]; +static char opened_mode[16]; +static char removed_path[512]; + +#define CHECK(condition) do { \ + if (!(condition)) { \ + fprintf(stderr, "%s:%d: check failed: %s\n", __FILE__, __LINE__, #condition); \ + assertion_failures++; \ + } \ +} while (0) + +void PowerController_Init(void) +{ + power_init_calls++; +} + +bool PowerController_IsOperational(void) +{ + return is_operational != 0; +} + +uint32_t PowerController_Connect(void) +{ + uint32_t result = POWER_CONTROLLER_ERROR_NONE; + + connect_calls++; + if (connect_result_index < connect_result_count) { + result = connect_results[connect_result_index++]; + } + if (result == POWER_CONTROLLER_ERROR_NONE) { + is_operational = 1; + } + return result; +} + +uint32_t PowerController_GetPowerState(PowerController_PowerState_t *currentState, + PowerController_PowerState_t *previousState) +{ + get_state_calls++; + *currentState = current_state; + *previousState = POWER_STATE_UNKNOWN; + return get_state_result; +} + +uint32_t PowerController_RegisterPowerModeChangedCallback( + PowerController_PowerModeChangedCb callback, void *userdata) +{ + register_callback_calls++; + registered_callback = callback; + registered_callback_userdata = userdata; + return POWER_CONTROLLER_ERROR_NONE; +} + +FILE *__wrap_fopen(const char *path, const char *mode) +{ + snprintf(opened_path, sizeof(opened_path), "%s", path); + snprintf(opened_mode, sizeof(opened_mode), "%s", mode); + return open_file_success ? (FILE *)(uintptr_t)1 : NULL; +} + +int __wrap_fclose(FILE *file) +{ + (void)file; + close_file_calls++; + return 0; +} + +int __wrap_fprintf(FILE *file, const char *format, ...) +{ + (void)file; + (void)format; + fprintf_calls++; + return 0; +} + +int __wrap_remove(const char *path) +{ + remove_calls++; + snprintf(removed_path, sizeof(removed_path), "%s", path); + return remove_result; +} + +int __wrap_stat(const char *path, struct stat *buffer) +{ + int exists = 0; + + (void)buffer; + stat_calls++; + if (strcmp(path, TMP_LIGHTSLEEP_ON) == 0) { + exists = mock_lightsleep_exists; + } else if (strcmp(path, TMP_POWER_ON) == 0) { + exists = mock_power_on_exists; + } + if (exists) { + return 0; + } + errno = ENOENT; + return -1; +} + +int __wrap_pthread_create(pthread_t *thread, const pthread_attr_t *attributes, + void *(*start_routine)(void *), void *argument) +{ + (void)attributes; + create_thread_calls++; + if (thread_create_result != 0) { + return thread_create_result; + } + *thread = (pthread_t)1; + if (run_thread_in_mock) { + start_routine(argument); + } + return thread_create_result; +} + +int __wrap_pthread_detach(pthread_t thread) +{ + (void)thread; + detach_thread_calls++; + return 0; +} + +GAsyncQueue *__wrap_g_async_queue_new(void) +{ + async_queue_new_calls++; + return async_queue_available ? (GAsyncQueue *)(uintptr_t)1 : NULL; +} + +gpointer __wrap_g_async_queue_pop(GAsyncQueue *queue) +{ + (void)queue; + async_queue_pop_calls++; + if (escape_on_queue_pop) { + longjmp(queue_pop_escape, 1); + } + return NULL; +} + +int usleep(useconds_t usec) +{ + (void)usec; + return 0; +} + +unsigned int sleep(unsigned int seconds) +{ + (void)seconds; + sleep_calls++; + if (power_on_after_sleep) { + mock_power_on_exists = 1; + power_on_after_sleep = 0; + } + return 0; +} + +static void reset_mocks(void) +{ + power_init_calls = 0; + is_operational = 1; + memset(connect_results, 0, sizeof(connect_results)); + connect_result_count = 0; + connect_result_index = 0; + connect_calls = 0; + get_state_calls = 0; + register_callback_calls = 0; + registered_callback = NULL; + registered_callback_userdata = NULL; + create_thread_calls = 0; + detach_thread_calls = 0; + run_thread_in_mock = 0; + thread_create_result = 0; + remove_calls = 0; + close_file_calls = 0; + fprintf_calls = 0; + stat_calls = 0; + sleep_calls = 0; + async_queue_new_calls = 0; + async_queue_pop_calls = 0; + async_queue_available = 1; + escape_on_queue_pop = 0; + open_file_success = 1; + mock_lightsleep_exists = 0; + mock_power_on_exists = 0; + power_on_after_sleep = 0; + remove_result = 0; + get_state_result = POWER_CONTROLLER_ERROR_NONE; + current_state = POWER_STATE_ON; + opened_path[0] = '\0'; + opened_mode[0] = '\0'; + removed_path[0] = '\0'; + gpowerState = POWER_STATE_ON; +} + +static void test_on_and_standby_flag_updates(void) +{ + reset_mocks(); + setModeSettings(1); + CHECK(strcmp(opened_path, TMP_POWER_ON) == 0); + CHECK(strcmp(opened_mode, "w+") == 0); + CHECK(close_file_calls == 1); + CHECK(strcmp(removed_path, "/tmp/.standby") == 0); + CHECK(create_thread_calls == 0); + + reset_mocks(); + setModeSettings(0); + CHECK(strcmp(opened_path, "/tmp/.standby") == 0); + CHECK(strcmp(removed_path, TMP_POWER_ON) == 0); + CHECK(create_thread_calls == 1); + CHECK(detach_thread_calls == 1); + + reset_mocks(); + setModeSettings(7); + CHECK(opened_path[0] == '\0'); + CHECK(remove_calls == 0); + CHECK(create_thread_calls == 0); +} + +static void test_power_mode_callback_updates_state_flags(void) +{ + reset_mocks(); + _lightsleepEventHandler(POWER_STATE_STANDBY, POWER_STATE_ON, NULL); + CHECK(gpowerState == POWER_STATE_ON); + CHECK(strcmp(opened_path, TMP_POWER_ON) == 0); + + reset_mocks(); + _lightsleepEventHandler(POWER_STATE_ON, POWER_STATE_STANDBY_LIGHT_SLEEP, NULL); + CHECK(gpowerState == POWER_STATE_STANDBY_LIGHT_SLEEP); + CHECK(strcmp(opened_path, "/tmp/.standby") == 0); + CHECK(create_thread_calls == 1); +} + +static void test_initial_state_sets_power_flags(void) +{ + reset_mocks(); + current_state = POWER_STATE_ON; + CHECK(power_monitor_init() == 0); + CHECK(power_init_calls == 1); + CHECK(get_state_calls == 1); + CHECK(strcmp(opened_path, TMP_POWER_ON) == 0); + + reset_mocks(); + current_state = POWER_STATE_STANDBY_DEEP_SLEEP; + CHECK(power_monitor_init() == 0); + CHECK(strcmp(opened_path, "/tmp/.standby") == 0); + CHECK(create_thread_calls == 1); + + reset_mocks(); + get_state_result = POWER_CONTROLLER_ERROR_UNAVAILABLE; + CHECK(power_monitor_init() == 0); + CHECK(get_state_calls == 1); + CHECK(opened_path[0] == '\0'); + + reset_mocks(); + current_state = POWER_STATE_UNKNOWN; + CHECK(power_monitor_init() == 0); + CHECK(get_state_calls == 1); + CHECK(opened_path[0] == '\0'); + + reset_mocks(); + get_state_result = POWER_CONTROLLER_ERROR_GENERAL; + CHECK(power_monitor_init() == 0); + CHECK(get_state_calls == 1); + CHECK(opened_path[0] == '\0'); +} + +static void test_power_controller_connection_retries(void) +{ + reset_mocks(); + is_operational = 0; + connect_results[0] = POWER_CONTROLLER_ERROR_UNAVAILABLE; + connect_results[1] = POWER_CONTROLLER_ERROR_NOT_EXIST; + connect_results[2] = POWER_CONTROLLER_ERROR_GENERAL; + connect_results[3] = POWER_CONTROLLER_ERROR_NONE; + connect_result_count = 4; + + CHECK(power_monitor_init() == 0); + CHECK(connect_calls == 4); + CHECK(connect_result_index == 4); + CHECK(get_state_calls == 1); +} + +static void test_monitor_thread_file_and_exit_paths(void) +{ + reset_mocks(); + mock_lightsleep_exists = 1; + CHECK(lightsleep_monitor(NULL) == NULL); + CHECK(stat_calls == 1); + CHECK(opened_path[0] == '\0'); + + reset_mocks(); + run_thread_in_mock = 1; + power_on_after_sleep = 1; + setModeSettings(0); + CHECK(create_thread_calls == 1); + CHECK(detach_thread_calls == 1); + CHECK(stat_calls == 3); + CHECK(sleep_calls == 1); + CHECK(fprintf_calls == 2); + CHECK(remove_calls == 2); + CHECK(close_file_calls == 4); + + reset_mocks(); + run_thread_in_mock = 1; + mock_power_on_exists = 1; + remove_result = -1; + setModeSettings(0); + CHECK(fprintf_calls == 1); + CHECK(remove_calls == 2); + CHECK(sleep_calls == 0); + + reset_mocks(); + open_file_success = 0; + mock_power_on_exists = 1; + CHECK(lightsleep_monitor(NULL) == NULL); + CHECK(close_file_calls == 0); + CHECK(fprintf_calls == 0); + CHECK(remove_calls == 1); +} + +static void test_flag_file_and_thread_failures(void) +{ + reset_mocks(); + open_file_success = 0; + remove_result = -1; + setModeSettings(1); + CHECK(close_file_calls == 0); + CHECK(remove_calls == 1); + + reset_mocks(); + thread_create_result = 1; + setModeSettings(0); + CHECK(create_thread_calls == 1); + CHECK(detach_thread_calls == 0); +} + +static void test_main_registers_callback_and_handles_queue(void) +{ + char *argv[] = { "power-state-monitor", NULL }; + int jump_result; + + reset_mocks(); + async_queue_available = 0; + CHECK(power_state_monitor_main(1, argv) == 1); + CHECK(register_callback_calls == 1); + CHECK(registered_callback == _lightsleepEventHandler); + CHECK(registered_callback_userdata == NULL); + CHECK(async_queue_new_calls == 1); + CHECK(async_queue_pop_calls == 0); + + reset_mocks(); + escape_on_queue_pop = 1; + jump_result = setjmp(queue_pop_escape); + if (jump_result == 0) { + power_state_monitor_main(1, argv); + CHECK(0); + } + CHECK(register_callback_calls == 1); + CHECK(registered_callback == _lightsleepEventHandler); + CHECK(async_queue_new_calls == 1); + CHECK(async_queue_pop_calls == 1); +} + +int main(void) +{ + test_on_and_standby_flag_updates(); + test_power_mode_callback_updates_state_flags(); + test_initial_state_sets_power_flags(); + test_power_controller_connection_retries(); + test_monitor_thread_file_and_exit_paths(); + test_flag_file_and_thread_failures(); + test_main_registers_callback_and_handles_queue(); + + if (assertion_failures != 0) { + fprintf(stderr, "%d assertion(s) failed\n", assertion_failures); + return 1; + } + puts("power-state-monitor L1 tests passed"); + return 0; +} \ No newline at end of file diff --git a/tests/test_query_power_state.c b/tests/test_query_power_state.c new file mode 100644 index 0000000..33e85b1 --- /dev/null +++ b/tests/test_query_power_state.c @@ -0,0 +1,281 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "power_controller.h" + +int __wrap_open(const char *path, int flags, ...); +off_t __wrap_lseek(int fd, off_t offset, int whence); +ssize_t __wrap_read(int fd, void *buffer, size_t count); +int __wrap_close(int fd); + +#define open __wrap_open +#define lseek __wrap_lseek +#define read __wrap_read +#define close __wrap_close +#define main query_power_state_main +#include "../iarm_query_powerstate/IARM_Bus_CheckPowerStatus.c" +#undef main +#undef close +#undef read +#undef lseek +#undef open + +#define FAKE_SETTINGS_FD 55 + +static int assertion_failures; +static int power_init_calls; +static int power_term_calls; +static int get_power_state_calls; +static int settings_open_calls; +static int settings_close_calls; +static int settings_read_calls; +static int settings_seek_calls; +static int settings_open_result = FAKE_SETTINGS_FD; +static int settings_read_result = sizeof(PWRMgr_Settings_t) - PADDING_SIZE; +static off_t settings_seek_result; +static uint32_t get_power_state_result; +static PowerController_PowerState_t current_power_state = POWER_STATE_ON; +static PowerController_PowerState_t previous_power_state = POWER_STATE_UNKNOWN; +static PWRMgr_PowerState_t settings_power_state = PWRMGR_POWERSTATE_ON; + +#define CHECK(condition) do { \ + if (!(condition)) { \ + fprintf(stderr, "%s:%d: check failed: %s\n", __FILE__, __LINE__, #condition); \ + assertion_failures++; \ + } \ +} while (0) + +void PowerController_Init(void) +{ + power_init_calls++; +} + +void PowerController_Term(void) +{ + power_term_calls++; +} + +uint32_t PowerController_GetPowerState(PowerController_PowerState_t *currentState, + PowerController_PowerState_t *previousState) +{ + get_power_state_calls++; + *currentState = current_power_state; + *previousState = previous_power_state; + return get_power_state_result; +} + +int __wrap_open(const char *path, int flags, ...) +{ + (void)flags; + CHECK(strcmp(path, "/opt/uimgr_settings.bin") == 0); + settings_open_calls++; + return settings_open_result; +} + +off_t __wrap_lseek(int fd, off_t offset, int whence) +{ + (void)fd; + CHECK(offset == 0); + CHECK(whence == SEEK_SET); + settings_seek_calls++; + return settings_seek_result; +} + +ssize_t __wrap_read(int fd, void *buffer, size_t count) +{ + PWRMgr_Settings_t *settings = (PWRMgr_Settings_t *)buffer; + + (void)fd; + CHECK(count == sizeof(PWRMgr_Settings_t) - PADDING_SIZE); + settings_read_calls++; + settings->powerState = settings_power_state; + return settings_read_result; +} + +int __wrap_close(int fd) +{ + if (fd == FAKE_SETTINGS_FD) { + settings_close_calls++; + return 0; + } + return close(fd); +} + +static void reset_mocks(void) +{ + power_init_calls = 0; + power_term_calls = 0; + get_power_state_calls = 0; + settings_open_calls = 0; + settings_close_calls = 0; + settings_read_calls = 0; + settings_seek_calls = 0; + settings_open_result = FAKE_SETTINGS_FD; + settings_read_result = sizeof(PWRMgr_Settings_t) - PADDING_SIZE; + settings_seek_result = 0; + get_power_state_result = POWER_CONTROLLER_ERROR_NONE; + current_power_state = POWER_STATE_ON; + previous_power_state = POWER_STATE_UNKNOWN; + settings_power_state = PWRMGR_POWERSTATE_ON; +} + +static int run_with_output(int argc, char **argv, char *output, size_t output_size) +{ + FILE *capture; + int saved_stdout; + int result; + size_t output_length; + + fflush(stdout); + saved_stdout = dup(STDOUT_FILENO); + capture = tmpfile(); + if (saved_stdout < 0 || capture == NULL) { + if (saved_stdout >= 0) { + close(saved_stdout); + } + if (capture != NULL) { + fclose(capture); + } + return -1; + } + + if (dup2(fileno(capture), STDOUT_FILENO) < 0) { + close(saved_stdout); + fclose(capture); + return -1; + } + + result = query_power_state_main(argc, argv); + fflush(stdout); + fseek(capture, 0, SEEK_SET); + output_length = fread(output, 1, output_size - 1, capture); + output[output_length] = '\0'; + dup2(saved_stdout, STDOUT_FILENO); + close(saved_stdout); + fclose(capture); + return result; +} + +static void test_power_controller_states(void) +{ + struct { + PowerController_PowerState_t state; + const char *expected; + } cases[] = { + { POWER_STATE_OFF, "OFF\n" }, + { POWER_STATE_STANDBY, "STANDBY\n" }, + { POWER_STATE_ON, "ON\n" }, + { POWER_STATE_STANDBY_LIGHT_SLEEP, "LIGHTSLEEP\n" }, + { POWER_STATE_STANDBY_DEEP_SLEEP, "DEEPSLEEP\n" }, + { POWER_STATE_UNKNOWN, "Unknown\n" } + }; + size_t index; + + for (index = 0; index < sizeof(cases) / sizeof(cases[0]); index++) { + char *argv[] = { "QueryPowerState", "-c", NULL }; + char output[512]; + + reset_mocks(); + current_power_state = cases[index].state; + CHECK(run_with_output(2, argv, output, sizeof(output)) == 0); + CHECK(strcmp(output, cases[index].expected) == 0); + CHECK(power_init_calls == 1); + CHECK(get_power_state_calls == 1); + CHECK(power_term_calls == 1); + } +} + +static void test_power_controller_errors(void) +{ + char *argv[] = { "QueryPowerState", "-c", NULL }; + char output[512]; + + reset_mocks(); + get_power_state_result = POWER_CONTROLLER_ERROR_UNAVAILABLE; + CHECK(run_with_output(2, argv, output, sizeof(output)) == 0); + CHECK(strstr(output, "PowerManager plugin unavailable") != NULL); + + reset_mocks(); + get_power_state_result = POWER_CONTROLLER_ERROR_GENERAL; + CHECK(run_with_output(2, argv, output, sizeof(output)) == 0); + CHECK(strstr(output, "Error :: Unknown") != NULL); +} + +static void test_help_does_not_connect_to_power_controller(void) +{ + char *argv[] = { "QueryPowerState", "-h", NULL }; + char output[512]; + + reset_mocks(); + CHECK(run_with_output(2, argv, output, sizeof(output)) == 0); + CHECK(strstr(output, "Usage:") != NULL); + CHECK(power_init_calls == 0); + CHECK(get_power_state_calls == 0); +} + +static void test_settings_file_state_and_failures(void) +{ + struct { + PWRMgr_PowerState_t state; + const char *expected; + } cases[] = { + { PWRMGR_POWERSTATE_OFF, "OFF\n" }, + { PWRMGR_POWERSTATE_STANDBY, "STANDBY\n" }, + { PWRMGR_POWERSTATE_ON, "ON\n" }, + { PWRMGR_POWERSTATE_STANDBY_LIGHT_SLEEP, "LIGHTSLEEP\n" }, + { PWRMGR_POWERSTATE_STANDBY_DEEP_SLEEP, "DEEPSLEEP\n" }, + { PWRMGR_POWERSTATE_MAX, "Unknown Power state\n" } + }; + size_t index; + + for (index = 0; index < sizeof(cases) / sizeof(cases[0]); index++) { + char *argv[] = { "QueryPowerState", NULL }; + char output[512]; + + reset_mocks(); + settings_power_state = cases[index].state; + CHECK(run_with_output(1, argv, output, sizeof(output)) == 0); + CHECK(strcmp(output, cases[index].expected) == 0); + CHECK(settings_open_calls == 1); + CHECK(settings_read_calls == 1); + CHECK(settings_close_calls == 1); + } + + { + char *argv[] = { "QueryPowerState", NULL }; + char output[512]; + + reset_mocks(); + settings_open_result = -1; + CHECK(run_with_output(1, argv, output, sizeof(output)) == 0); + CHECK(strstr(output, "Error in reading PWRMgr settings File") != NULL); + CHECK(settings_read_calls == 0); + + reset_mocks(); + settings_seek_result = -1; + CHECK(run_with_output(1, argv, output, sizeof(output)) == 0); + CHECK(strstr(output, "Error in seeking PWRMgr settings File") != NULL); + CHECK(settings_read_calls == 0); + CHECK(settings_close_calls == 1); + } +} + +int main(void) +{ + test_power_controller_states(); + test_power_controller_errors(); + test_help_does_not_connect_to_power_controller(); + test_settings_file_state_and_failures(); + + if (assertion_failures != 0) { + fprintf(stderr, "%d assertion(s) failed\n", assertion_failures); + return 1; + } + puts("QueryPowerState L1 tests passed"); + return 0; +} \ No newline at end of file diff --git a/tests/test_set_power_state.c b/tests/test_set_power_state.c new file mode 100644 index 0000000..d864979 --- /dev/null +++ b/tests/test_set_power_state.c @@ -0,0 +1,273 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "power_controller.h" + +#define main set_power_state_main +#include "../iarm_set_powerstate/IARM_BUS_SetPowerStatus.c" +#undef main + +static int assertion_failures; +static int init_calls; +static int term_calls; +static int set_state_calls; +static int add_client_calls; +static int remove_client_calls; +static int register_callback_calls; +static int unregister_callback_calls; +static int complete_calls; +static int delay_calls; +static int sleep_calls; +static int usleep_calls; +static int operational = 1; +static int invoke_prechange; +static int set_state_result; +static int last_key_code; +static int last_wait_seconds; +static int last_delay_seconds[ARRAY_SIZE]; +static int last_delay_count; +static uint32_t last_client_id; +static int last_transaction_id; +static PowerController_PowerState_t last_power_state; +static PowerController_PowerModePreChangeCb registered_callback; +static void *registered_userdata; +static char last_client_name[256]; + +#define CHECK(condition) do { \ + if (!(condition)) { \ + fprintf(stderr, "%s:%d: check failed: %s\n", __FILE__, __LINE__, #condition); \ + assertion_failures++; \ + } \ +} while (0) + +void PowerController_Init(void) +{ + init_calls++; +} + +void PowerController_Term(void) +{ + term_calls++; +} + +bool PowerController_IsOperational(void) +{ + return operational != 0; +} + +uint32_t PowerController_Connect(void) +{ + return POWER_CONTROLLER_ERROR_NONE; +} + +uint32_t PowerController_SetPowerState(const int keyCode, + const PowerController_PowerState_t powerstate, + const char *reason) +{ + (void)reason; + set_state_calls++; + last_key_code = keyCode; + last_power_state = powerstate; + if (invoke_prechange && registered_callback != NULL) { + registered_callback(POWER_STATE_ON, powerstate, 17, 5, registered_userdata); + } + return (uint32_t)set_state_result; +} + +uint32_t PowerController_RegisterPowerModePreChangeCallback( + PowerController_PowerModePreChangeCb callback, void *userdata) +{ + register_callback_calls++; + registered_callback = callback; + registered_userdata = userdata; + return POWER_CONTROLLER_ERROR_NONE; +} + +uint32_t PowerController_UnRegisterPowerModePreChangeCallback( + PowerController_PowerModePreChangeCb callback) +{ + unregister_callback_calls++; + CHECK(callback == registered_callback); + registered_callback = NULL; + registered_userdata = NULL; + return POWER_CONTROLLER_ERROR_NONE; +} + +uint32_t PowerController_AddPowerModePreChangeClient(const char *clientName, + uint32_t *clientId) +{ + add_client_calls++; + snprintf(last_client_name, sizeof(last_client_name), "%s", clientName); + *clientId = 1234; + last_client_id = *clientId; + return POWER_CONTROLLER_ERROR_NONE; +} + +uint32_t PowerController_RemovePowerModePreChangeClient(const uint32_t clientId) +{ + remove_client_calls++; + last_client_id = clientId; + return POWER_CONTROLLER_ERROR_NONE; +} + +uint32_t PowerController_PowerModePreChangeComplete(const uint32_t clientId, + const int transactionId) +{ + complete_calls++; + last_client_id = clientId; + last_transaction_id = transactionId; + return POWER_CONTROLLER_ERROR_NONE; +} + +uint32_t PowerController_DelayPowerModeChangeBy(const uint32_t clientId, + const int transactionId, + const int delayPeriod) +{ + (void)clientId; + last_transaction_id = transactionId; + if (last_delay_count < ARRAY_SIZE) { + last_delay_seconds[last_delay_count++] = delayPeriod; + } + delay_calls++; + return POWER_CONTROLLER_ERROR_NONE; +} + +int usleep(useconds_t usec) +{ + (void)usec; + usleep_calls++; + return 0; +} + +unsigned int sleep(unsigned int seconds) +{ + sleep_calls++; + last_wait_seconds = (int)seconds; + return 0; +} + +static void reset_mocks(void) +{ + init_calls = 0; + term_calls = 0; + set_state_calls = 0; + add_client_calls = 0; + remove_client_calls = 0; + register_callback_calls = 0; + unregister_callback_calls = 0; + complete_calls = 0; + delay_calls = 0; + sleep_calls = 0; + usleep_calls = 0; + operational = 1; + invoke_prechange = 0; + set_state_result = POWER_CONTROLLER_ERROR_NONE; + last_key_code = -1; + last_wait_seconds = 0; + last_delay_count = 0; + last_client_id = 0; + last_transaction_id = 0; + last_power_state = POWER_STATE_UNKNOWN; + registered_callback = NULL; + registered_userdata = NULL; + last_client_name[0] = '\0'; + memset(last_delay_seconds, 0, sizeof(last_delay_seconds)); + optind = 1; +} + +static int run_set_power_state(int argc, char **argv) +{ + reset_mocks(); + return set_power_state_main(argc, argv); +} + +static void test_power_state_arguments(void) +{ + struct { + char *argument; + PowerController_PowerState_t expected; + } cases[] = { + { "ON", POWER_STATE_ON }, + { "STANDBY", POWER_STATE_STANDBY }, + { "LIGHTSLEEP", POWER_STATE_STANDBY_LIGHT_SLEEP }, + { "DEEPSLEEP", POWER_STATE_STANDBY_DEEP_SLEEP }, + { "OFF", POWER_STATE_OFF } + }; + size_t index; + + for (index = 0; index < sizeof(cases) / sizeof(cases[0]); index++) { + char *argv[] = { "SetPowerState", cases[index].argument, NULL }; + CHECK(run_set_power_state(2, argv) == 0); + CHECK(set_state_calls == 1); + CHECK(last_power_state == cases[index].expected); + CHECK(last_key_code == 0); + CHECK(init_calls == 1); + CHECK(term_calls == 1); + } +} + +static void test_invalid_and_missing_state_skip_controller(void) +{ + char *invalid_argv[] = { "SetPowerState", "INVALID", NULL }; + char *missing_argv[] = { "SetPowerState", NULL }; + + CHECK(run_set_power_state(2, invalid_argv) == EXIT_FAILURE); + CHECK(init_calls == 0); + CHECK(run_set_power_state(1, missing_argv) == EXIT_FAILURE); + CHECK(init_calls == 0); +} + +static void test_nop_skips_power_state_call(void) +{ + char *argv[] = { "SetPowerState", "NOP", NULL }; + + CHECK(run_set_power_state(2, argv) == 0); + CHECK(init_calls == 1); + CHECK(set_state_calls == 0); + CHECK(term_calls == 1); +} + +static void test_client_acknowledgement_and_positive_delays(void) +{ + char *argv[] = { + "SetPowerState", "--client", "unit-test", "--ack", "0", + "--delay", "2,0,-1,3", "ON", NULL + }; + + invoke_prechange = 0; + reset_mocks(); + invoke_prechange = 1; + + CHECK(set_power_state_main(8, argv) == 0); + CHECK(add_client_calls == 1); + CHECK(strcmp(last_client_name, "unit-test") == 0); + CHECK(remove_client_calls == 1); + CHECK(last_client_id == 1234); + CHECK(complete_calls == 1); + CHECK(last_transaction_id == 17); + CHECK(delay_calls == 2); + CHECK(last_delay_count == 2); + CHECK(last_delay_seconds[0] == 2); + CHECK(last_delay_seconds[1] == 3); + CHECK(unregister_callback_calls == 1); +} + +int main(void) +{ + test_power_state_arguments(); + test_invalid_and_missing_state_skip_controller(); + test_nop_skips_power_state_call(); + test_client_acknowledgement_and_positive_delays(); + + if (assertion_failures != 0) { + fprintf(stderr, "%d assertion(s) failed\n", assertion_failures); + return 1; + } + puts("SetPowerState L1 tests passed"); + return 0; +} \ No newline at end of file diff --git a/tests/test_uinput.c b/tests/test_uinput.c new file mode 100644 index 0000000..06534b9 --- /dev/null +++ b/tests/test_uinput.c @@ -0,0 +1,306 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "IrInputRemoteKeyCodes.h" +#include "uInputInternal.h" + +#define FAKE_DEVICE_FD 42 +#define MAX_IOCTL_CALLS 512 +#define MAX_INPUT_EVENTS 32 + +typedef struct { + unsigned long request; + int value; +} ioctl_call_t; + +static int assertion_failures; +static int open_result = FAKE_DEVICE_FD; +static int fail_ioctl_request; +static int fail_input_write; +static int open_calls; +static int close_calls; +static int destroy_calls; +static int setup_ioctl_calls; +static int input_write_calls; +static char opened_path[64]; +static int opened_flags; +static ioctl_call_t ioctl_calls[MAX_IOCTL_CALLS]; +static size_t ioctl_call_count; +static struct input_event input_events[MAX_INPUT_EVENTS]; +static size_t input_event_count; +static struct uinput_setup configured_setup; + +#define CHECK(condition) do { \ + if (!(condition)) { \ + fprintf(stderr, "%s:%d: check failed: %s\n", __FILE__, __LINE__, #condition); \ + assertion_failures++; \ + } \ +} while (0) + +int __wrap_open(const char *path, int flags, ...) +{ + open_calls++; + snprintf(opened_path, sizeof(opened_path), "%s", path); + opened_flags = flags; + return open_result; +} + +int __wrap_ioctl(int fd, unsigned long request, ...) +{ + va_list arguments; + int value = 0; + + (void)fd; + if (request != UI_DEV_DESTROY) { + va_start(arguments, request); + if (request == UI_DEV_SETUP) { + struct uinput_setup *setup = va_arg(arguments, struct uinput_setup *); + configured_setup = *setup; + setup_ioctl_calls++; + } else { + value = va_arg(arguments, int); + } + va_end(arguments); + } else { + destroy_calls++; + } + + if (ioctl_call_count < MAX_IOCTL_CALLS) { + ioctl_calls[ioctl_call_count].request = request; + ioctl_calls[ioctl_call_count].value = value; + ioctl_call_count++; + } + + if (request == (unsigned long)fail_ioctl_request) { + errno = EIO; + return -1; + } + return 0; +} + +ssize_t __wrap_write(int fd, const void *buffer, size_t size) +{ + (void)fd; + if (size == sizeof(struct input_event)) { + input_write_calls++; + if (fail_input_write) { + errno = EIO; + return -1; + } + if (input_event_count < MAX_INPUT_EVENTS) { + memcpy(&input_events[input_event_count], buffer, size); + input_event_count++; + } + } + return (ssize_t)size; +} + +int __wrap_close(int fd) +{ + (void)fd; + close_calls++; + return 0; +} + +#define open __wrap_open +#define ioctl __wrap_ioctl +#define write __wrap_write +#define close __wrap_close +#include "../key_simulator/uinput.c" +#undef close +#undef write +#undef ioctl +#undef open + +static void reset_mocks(void) +{ + UINPUT_term(); + open_result = FAKE_DEVICE_FD; + fail_ioctl_request = 0; + fail_input_write = 0; + open_calls = 0; + close_calls = 0; + destroy_calls = 0; + setup_ioctl_calls = 0; + input_write_calls = 0; + opened_path[0] = '\0'; + opened_flags = 0; + ioctl_call_count = 0; + input_event_count = 0; + memset(ioctl_calls, 0, sizeof(ioctl_calls)); + memset(input_events, 0, sizeof(input_events)); + memset(&configured_setup, 0, sizeof(configured_setup)); +} + +static int has_ioctl(unsigned long request, int value) +{ + size_t index; + + for (index = 0; index < ioctl_call_count; index++) { + if (ioctl_calls[index].request == request && ioctl_calls[index].value == value) { + return 1; + } + } + return 0; +} + +static void check_event(size_t index, unsigned short type, unsigned short code, int value) +{ + CHECK(index < input_event_count); + if (index < input_event_count) { + CHECK(input_events[index].type == type); + CHECK(input_events[index].code == code); + CHECK(input_events[index].value == value); + } +} + +static void test_open_failure_returns_no_dispatcher(void) +{ + reset_mocks(); + open_result = -1; + + CHECK(UINPUT_init() == 0); + CHECK(open_calls == 1); + CHECK(UINPUT_GetDispatcher() == NULL); +} + +static void test_successful_setup_creates_device(void) +{ + reset_mocks(); + + CHECK(UINPUT_init() == 0); + CHECK(strcmp(opened_path, "/dev/uinput") == 0); + CHECK(opened_flags == (O_WRONLY | O_SYNC)); + CHECK(has_ioctl(UI_SET_EVBIT, EV_KEY)); + CHECK(has_ioctl(UI_SET_EVBIT, EV_SYN)); + CHECK(has_ioctl(UI_DEV_SETUP, 0)); + CHECK(has_ioctl(UI_DEV_CREATE, 0)); + CHECK(UINPUT_GetDispatcher() != NULL); + CHECK(setup_ioctl_calls == 1); + CHECK(strcmp(configured_setup.name, "key-simulator") == 0); + CHECK(configured_setup.id.bustype == BUS_USB); + CHECK(configured_setup.id.vendor == 0xbeef); + CHECK(configured_setup.id.product == 0xfedc); + CHECK(configured_setup.id.version == 1); + + UINPUT_term(); + CHECK(destroy_calls == 1); + CHECK(close_calls == 1); + CHECK(UINPUT_GetDispatcher() == NULL); +} + +static void test_modifier_key_down_up_and_repeat_events(void) +{ + uinput_dispatcher_t dispatcher; + + reset_mocks(); + UINPUT_init(); + dispatcher = UINPUT_GetDispatcher(); + CHECK(dispatcher != NULL); + if (dispatcher == NULL) { + return; + } + + dispatcher(KED_CHANNELUP, KET_KEYDOWN, 0); + CHECK(input_event_count == 4); + check_event(0, EV_KEY, KEY_LEFTCTRL, 1); + check_event(1, EV_SYN, SYN_REPORT, 0); + check_event(2, EV_KEY, KEY_UP, 1); + check_event(3, EV_SYN, SYN_REPORT, 0); + + input_event_count = 0; + dispatcher(KED_CHANNELUP, KET_KEYUP, 0); + CHECK(input_event_count == 4); + check_event(0, EV_KEY, KEY_UP, 0); + check_event(2, EV_KEY, KEY_LEFTCTRL, 0); + + input_event_count = 0; + dispatcher(KED_CHANNELUP, KET_KEYREPEAT, 0); + CHECK(input_event_count == 2); + check_event(0, EV_KEY, KEY_UP, 2); + check_event(1, EV_SYN, SYN_REPORT, 0); + + UINPUT_term(); +} + +static void test_regular_key_and_unknown_key_events(void) +{ + uinput_dispatcher_t dispatcher; + + reset_mocks(); + UINPUT_init(); + dispatcher = UINPUT_GetDispatcher(); + CHECK(dispatcher != NULL); + if (dispatcher == NULL) { + return; + } + + dispatcher(KED_DIGIT1, KET_KEYDOWN, 0); + CHECK(input_event_count == 2); + check_event(0, EV_KEY, KEY_1, 1); + check_event(1, EV_SYN, SYN_REPORT, 0); + + input_event_count = 0; + dispatcher(UINT32_MAX, KET_KEYUP, 0); + CHECK(input_event_count == 2); + check_event(0, EV_KEY, KEY_RESERVED, 0); + + UINPUT_term(); +} + +static void test_setup_failure_destroys_and_closes_device(void) +{ + reset_mocks(); + fail_ioctl_request = UI_DEV_CREATE; + + CHECK(UINPUT_init() == 0); + CHECK(UINPUT_GetDispatcher() == NULL); + CHECK(destroy_calls == 1); + CHECK(close_calls == 1); +} + +static void test_event_write_failure_terminates_device(void) +{ + uinput_dispatcher_t dispatcher; + + reset_mocks(); + UINPUT_init(); + dispatcher = UINPUT_GetDispatcher(); + CHECK(dispatcher != NULL); + if (dispatcher == NULL) { + return; + } + + fail_input_write = 1; + dispatcher(KED_DIGIT1, KET_KEYDOWN, 0); + CHECK(input_write_calls == 1); + CHECK(destroy_calls == 1); + CHECK(close_calls == 1); + CHECK(UINPUT_GetDispatcher() == NULL); +} + +int run_uinput_tests(void) +{ + test_open_failure_returns_no_dispatcher(); + test_successful_setup_creates_device(); + test_modifier_key_down_up_and_repeat_events(); + test_regular_key_and_unknown_key_events(); + test_setup_failure_destroys_and_closes_device(); + test_event_write_failure_terminates_device(); + + if (assertion_failures != 0) { + fprintf(stderr, "%d assertion(s) failed\n", assertion_failures); + return 1; + } + puts("uinput L1 tests passed"); + return 0; +} \ No newline at end of file diff --git a/tests/test_uinput_main.c b/tests/test_uinput_main.c new file mode 100644 index 0000000..3612d4d --- /dev/null +++ b/tests/test_uinput_main.c @@ -0,0 +1,8 @@ +#include "test_uinput.c" + +#undef main + +int main(void) +{ + return run_uinput_tests(); +} \ No newline at end of file