// ------------------------------------------------------------------------------------------- // Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. // This file is part of the AWS CDI-SDK, licensed under the BSD 2-Clause "Simplified" License. // License details at: https://github.com/aws/aws-cdi-sdk/blob/mainline/LICENSE // ------------------------------------------------------------------------------------------- /** * @file * @brief * This file contains definitions and implementation of various unit tests for checking the functionality of the * timeout.c API functions. */ #include "cdi_logger_api.h" #include "cdi_os_api.h" #include "timeout.h" //********************************************************************************************************************* //***************************************** START OF DEFINITIONS AND TYPES ******************************************** //********************************************************************************************************************* /** * @brief A structure to be used by the unit test callback function to signal to the test when the callback occurred * and whether the callback was successful. */ typedef struct CallBackUserData { uint64_t expiration_us; ///user_data_ptr; uint64_t cb_time = CdiOsGetMicroseconds() / 1000; uint64_t success_time_max = (user_data_ptr->expiration_us + 3000) / 1000; uint64_t success_time_min = (user_data_ptr->expiration_us - 500) / 1000; CdiOsSignalSet(user_data_ptr->signal); if ((cb_time >= success_time_min) && (cb_time <= success_time_max)) { user_data_ptr->pass = true; } else { user_data_ptr->pass = false; CDI_LOG_THREAD(kLogInfo, "Callback number[%d] received at time [%u]ms with expiration of[%u]ms", user_data_ptr->callback_number, cb_time, user_data_ptr->expiration_us / 1000); } } /** * @brief A test to create a timeout instance add a timer to it, let it expire and check the callback. * * @param timeout_ms is a timeout to set for the created timer * * @return true if test pass, false if failure */ static bool OneShotTimeoutTest(int timeout_ms) { bool pass = true; CallBackUserData user_data; CdiOsSignalCreate(&user_data.signal); user_data.callback_number = 1; CDI_LOG_THREAD(kLogInfo, "Performing 1 shot test for timeout of [%d]ms", timeout_ms); CdiTimeoutInstanceHandle timer_handle = NULL; if (kCdiStatusOk == CdiTimeoutCreate(NULL, &timer_handle)) { pass = true; } else { pass = false; CDI_LOG_THREAD(kLogError, "Failed to create Timeout"); } if (pass) { user_data.expiration_us = CdiOsGetMicroseconds(); user_data.expiration_us = user_data.expiration_us + (timeout_ms * 1000); TimeoutHandle timeout_handle = NULL; pass = CdiTimeoutAdd(timer_handle, &TimerCallback, 1000*timeout_ms, &user_data, &timeout_handle); if (!pass) { CDI_LOG_THREAD(kLogError, "Failed to add timeout"); } } if (pass) { bool timeout_on_timeouts; CdiOsSignalWait(user_data.signal, (timeout_ms + 1) * 2, &timeout_on_timeouts); if (timeout_on_timeouts) { CDI_LOG_THREAD(kLogError, "Timeout occurred waiting for callback function signal"); pass = false; } else if (user_data.pass) { } else { CDI_LOG_THREAD(kLogError, "Callback failed to report pass"); pass = false; } } CdiOsSignalDelete(user_data.signal); user_data.signal = NULL; CdiTimeoutDestroy(timer_handle); return pass; } /** * @brief A test to set multiple timers. * * @param num_timers sets how many timers are added to the timeout instance * @param reverse if true, each successive timer object has an earlier deadline as the list is built. * * @return true if passes, false if failed */ static bool MultipleTimersTest(unsigned int num_timers, bool reverse) { bool pass = true; if (num_timers > MAX_TIMERS) { CDI_LOG_THREAD(kLogInfo, "Attempting to set more than [%d] timers. Will attempt to add all timers but only check for [%d] of returns", MAX_TIMERS, MAX_TIMERS); } CdiTimeoutInstanceHandle timer_handle = NULL; if (kCdiStatusOk == CdiTimeoutCreate(NULL, &timer_handle)) { pass = true; } else { pass = false; CDI_LOG_THREAD(kLogError, "Failed to create timeout"); } CdiOsSleep(2); CallBackUserData user_data[MAX_TIMERS]; CdiSignalType signals_array[MAX_TIMERS]; if (pass) { for (unsigned int i=0; i= MAX_TIMERS) { CDI_LOG_THREAD(kLogWarning, "Timeout add failed because there are too many active timers. This is not considered an error."); } else { CDI_LOG_THREAD(kLogError, "Failed adding timer[%d]. There should be timers in pool available to add.", i); pass = false; } } CdiOsSleepMicroseconds(100); } } unsigned int last_cb_count = -1; bool waiting_callbacks = true; while (waiting_callbacks) { unsigned int signal_index; CdiOsSignalsWait(signals_array, num_timers, false, (num_timers * 4), &signal_index); if (signal_index == (last_cb_count + 1)) { last_cb_count = signal_index; CdiOsSignalClear(signals_array[signal_index]); if (signal_index == (MAX_TIMERS -1) || signal_index == (num_timers - 1)) { waiting_callbacks = false; } } else if (signal_index == CDI_OS_SIG_TIMEOUT) { CDI_LOG_THREAD(kLogError, "Timeout waiting for signals from callback"); waiting_callbacks = false; pass = false; } else { CDI_LOG_THREAD(kLogError, "Received callback signal out of order, received[%d]. Expected[%d]", signal_index, last_cb_count + 1); last_cb_count = signal_index; CdiOsSignalClear(signals_array[signal_index]); pass = false; if (signal_index == (MAX_TIMERS -1) || signal_index == (num_timers - 1)) { waiting_callbacks = false; } } } if (num_timers > MAX_TIMERS) { last_cb_count = MAX_TIMERS; } else { last_cb_count = num_timers; } /// Check the pass status of all of the user_data structures sent to the callback function. /// The callback function logged errors for additional detail into the actual failure. Checking here as well to /// catch any user data structures where the callback didn't occur at all. for (unsigned int i=0; i MAX_TIMERS) { CDI_LOG_THREAD(kLogInfo, "Attempting to set more than [%d] timers. Will attempt to add all timers but only check for [%d] of returns", MAX_TIMERS, MAX_TIMERS); } CdiTimeoutInstanceHandle timer_handle = NULL; if (kCdiStatusOk == CdiTimeoutCreate(NULL, &timer_handle)) { pass = true; } else { pass = false; CDI_LOG_THREAD(kLogError, "Failed to create Timeout"); } CdiOsSleep(2); CallBackUserData user_data[MAX_TIMERS]; TimeoutHandle timeout_handle[MAX_TIMERS]; if (pass) { for (unsigned int i=0; i= MAX_TIMERS) { CDI_LOG_THREAD(kLogWarning, "Timeout add failed because there are too many active timers. This is not considered an error."); } else { CDI_LOG_THREAD(kLogError, "Failed adding timer[%d]. There should be timers in pool available to add.", i); pass = false; } } } } if (pass) { for (unsigned int i=0; i