diff --git a/CMakeLists.txt b/CMakeLists.txt index 5ce1a276..f369aadb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,6 +6,7 @@ option(ENABLE_FORMAT_CHECKS "Run format checks during configuration stage and ge option(SKIP_FORMAT_REPORTS "Skip generation of format XML reports in build directory" ON) option(ENABLE_STATIC_CHECKS "Configure a build tree for static code analysis" OFF) option(ENABLE_COVERAGE "Build unit tests with coverage information to use with GCOV and LCOV" ON) +option(ENABLE_INTEGRATION_TESTING "Build for integration tests application" OFF) ## Include config file if it exists include(kiso_defaults.cmake OPTIONAL) @@ -17,14 +18,16 @@ if (NOT DEFINED CMAKE_TOOLCHAIN_FILE AND NOT ${ENABLE_TESTING}) include(cmake/ArmToolchain.cmake) endif() -message("------------- KISO CONFIG -------------") -message("Building Kiso tests: ${ENABLE_TESTING}") -message(" ... with coverage: ${ENABLE_COVERAGE}") -message("Kiso Board Path: ${KISO_BOARD_PATH}") -message("Kiso OS: ${KISO_OS_LIB}") -message("Kiso Application Path: ${KISO_APPLICATION_PATH}") -message("Project Config Path: ${PROJECT_CONFIG_PATH}") -message("------------- KISO CONFIG -------------") +message("---------------------------------- KISO CONFIG ----------------------------------") +message("Building Kiso tests: ${ENABLE_TESTING}") +message(" ... with coverage: ${ENABLE_COVERAGE}") +message("Building Kiso integration tests: ${ENABLE_INTEGRATION_TESTING}") +message(" ... with entry in: ${KISO_INTEGRATION_TEST_ENTRY_PATH}") +message("Kiso Board Path: ${KISO_BOARD_PATH}") +message("Kiso OS: ${KISO_OS_LIB}") +message("Kiso Application Path: ${KISO_APPLICATION_PATH}") +message("Project Config Path: ${PROJECT_CONFIG_PATH}") +message("---------------------------------- KISO CONFIG ----------------------------------") project (Kiso C) @@ -89,7 +92,12 @@ if(${ENABLE_STATIC_CHECKS}) endif() ## Add application code -add_subdirectory(${KISO_APPLICATION_PATH} ${CMAKE_CURRENT_BINARY_DIR}/applications/${KISO_APPLICATION_NAME}) +if(${ENABLE_INTEGRATION_TESTING}) + add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/testing/integration/test-executor) + add_subdirectory(${KISO_INTEGRATION_TEST_ENTRY_PATH}) +else(${ENABLE_INTEGRATION_TESTING}) + add_subdirectory(${KISO_APPLICATION_PATH} ${CMAKE_CURRENT_BINARY_DIR}/applications/${KISO_APPLICATION_NAME}) +endif(${ENABLE_INTEGRATION_TESTING}) include(KisoLibsConfig) @@ -98,6 +106,9 @@ include(KisoLibsConfig) add_subdirectory(core/essentials) add_subdirectory(core/utils) add_subdirectory(core/connectivity/cellular) +if(${ENABLE_INTEGRATION_TESTING}) + add_subdirectory(core/testing) +endif(${ENABLE_INTEGRATION_TESTING}) ## Add thirdparty libs add_subdirectory(thirdparty) diff --git a/Jenkinsfile b/Jenkinsfile index 0d54d4d6..b48c8d07 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -85,7 +85,9 @@ pipeline { script { - echo "run integration-tests placeholder" + echo "build integration-tests" + sh 'cmake . -Bbuilddir-integration -G"Ninja" -DENABLE_INTEGRATION_TESTING=1 -DKISO_INTEGRATION_TEST_ENTRY_PATH="core/essentials/test/integration" -DKISO_BOARD_NAME="NucleoF767"' + sh 'cmake --build builddir-integration' } } } diff --git a/boards/NucleoF767/bsp/config/core/essentials/Kiso_BSPConfig.h b/boards/NucleoF767/bsp/config/core/essentials/Kiso_BSPConfig.h index 52c59475..49616eb1 100644 --- a/boards/NucleoF767/bsp/config/core/essentials/Kiso_BSPConfig.h +++ b/boards/NucleoF767/bsp/config/core/essentials/Kiso_BSPConfig.h @@ -48,6 +48,10 @@ #define KISO_FEATURE_BSP_BUTTON 1 #endif +#ifndef KISO_FEATURE_BSP_GENERIC_UART +#define KISO_FEATURE_BSP_GENERIC_UART 1 +#endif + #ifndef KISO_FEATURE_BSP_CELLULAR_SARAR4N4 #define KISO_FEATURE_BSP_CELLULAR_SARAR4N4 0 #endif diff --git a/boards/NucleoF767/bsp/include/BSP_NucleoF767.h b/boards/NucleoF767/bsp/include/BSP_NucleoF767.h index 5486b4a6..188e2bb8 100644 --- a/boards/NucleoF767/bsp/include/BSP_NucleoF767.h +++ b/boards/NucleoF767/bsp/include/BSP_NucleoF767.h @@ -38,6 +38,7 @@ enum BSP_NUCLEOF767y_Modules_E MODULE_BSP_API_BOARD, MODULE_BSP_API_LED, MODULE_BSP_API_TEST_IF, + MODULE_BSP_API_GENERICUART, MODULE_BSP_TIME, MODULE_BSP_BUTTON, }; diff --git a/boards/NucleoF767/bsp/source/bsp_api_button.c b/boards/NucleoF767/bsp/source/bsp_api_button.c index 631a6f00..708e679e 100644 --- a/boards/NucleoF767/bsp/source/bsp_api_button.c +++ b/boards/NucleoF767/bsp/source/bsp_api_button.c @@ -28,7 +28,6 @@ /*---------------------- LOCAL FUNCTIONS DECLARATION ----------------------------------------------------------------*/ void EXTI15_10_IRQHandler(void); -void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin); /*---------------------- VARIABLES DECLARATION ----------------------------------------------------------------------*/ diff --git a/boards/NucleoF767/bsp/source/bsp_api_genericuart.c b/boards/NucleoF767/bsp/source/bsp_api_genericuart.c new file mode 100644 index 00000000..ec27839f --- /dev/null +++ b/boards/NucleoF767/bsp/source/bsp_api_genericuart.c @@ -0,0 +1,230 @@ +/******************************************************************************** +* Copyright (c) 2010-2020 Robert Bosch GmbH +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* Robert Bosch GmbH - initial contribution +* +********************************************************************************/ + +#include "Kiso_BSP_GenericUart.h" + +#if KISO_FEATURE_BSP_GENERIC_UART + +#include "Kiso_Basics.h" +#include "Kiso_Retcode.h" +#include "stm32/stm32f7/Kiso_MCU_STM32F7_UART_Handle.h" +#include "Kiso_HAL_Delay.h" +#include "BSP_NucleoF767.h" +#include "protected/gpio.h" + +/*---------------------- MACROS DEFINITION --------------------------------------------------------------------------*/ + +#undef KISO_MODULE_ID +#define KISO_MODULE_ID MODULE_BSP_API_GENERICUART + +#define UART_INT_PRIORITY UINT32_C(10) +#define UART_SUBPRIORITY UINT32_C(1) + +/*---------------------- LOCAL FUNCTIONS DECLARATION ----------------------------------------------------------------*/ + +void USART2_IRQHandler(void); + +/*---------------------- VARIABLES DECLARATION ----------------------------------------------------------------------*/ + +static uint8_t bspState = (uint8_t)BSP_STATE_INIT; /**< BSP State of the cellular module */ + +/** + * Static structure storing the UART handle for Test Interface + */ +static struct MCU_UART_S uartCtrlStruct = + { + .TxMode = KISO_HAL_TRANSFER_MODE_INTERRUPT, + .RxMode = KISO_HAL_TRANSFER_MODE_INTERRUPT, + .Datarate = 115200U, + .huart.Instance = USART2, + .huart.Init.BaudRate = 115200U, + .huart.Init.WordLength = UART_WORDLENGTH_8B, + .huart.Init.StopBits = UART_STOPBITS_1, + .huart.Init.Parity = UART_PARITY_NONE, + .huart.Init.Mode = UART_MODE_TX_RX, + .huart.Init.HwFlowCtl = UART_HWCONTROL_NONE, + .huart.Init.OverSampling = UART_OVERSAMPLING_16, + .huart.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE, + .huart.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT, +}; +/*---------------------- EXPOSED FUNCTIONS IMPLEMENTATION -----------------------------------------------------------*/ + +/** + * See API interface for function documentation + * @retval RETCODE_OK in case of success. + * @retval RETCODE_INCONSISTENT_STATE in case the module is not in a state to allow connecting. + */ +Retcode_T BSP_GenericUart_Connect(uint32_t id) +{ + KISO_UNUSED(id); + Retcode_T retcode = RETCODE_OK; + + if (!(bspState & (uint8_t)BSP_STATE_TO_CONNECTED)) + { + retcode = RETCODE(RETCODE_SEVERITY_ERROR, RETCODE_INCONSISTENT_STATE); + } + if (RETCODE_OK == retcode) + { + /* IOSV bit MUST be set to access GPIO port G[2:15] */ + __HAL_RCC_PWR_CLK_ENABLE(); + __HAL_RCC_LPTIM1_CLK_ENABLE(); + + GPIO_InitTypeDef GPIO_InitStruct = {0}; + + /* UART RX/TX GPIO pin configuration */ + GPIO_OpenClockGate(GPIO_PORT_D, PIND_USART2_TX | PIND_USART2_RX); + + GPIO_InitStruct.Pin = PIND_USART2_TX | PIND_USART2_RX; + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; + GPIO_InitStruct.Alternate = GPIO_AF7_USART2; + + HAL_GPIO_Init(GPIOD, &GPIO_InitStruct); + + bspState = (uint8_t)BSP_STATE_CONNECTED; + } + return retcode; +} + +/** + * See API interface for function documentation + * @retval RETCODE_OK in case of success. + * @retval RETCODE_INCONSISTENT_STATE in case the module is not in a state to allow enabling. + */ +Retcode_T BSP_GenericUart_Enable(uint32_t id) +{ + KISO_UNUSED(id); + Retcode_T retcode = RETCODE_OK; + + if (!(bspState & (uint8_t)BSP_STATE_TO_ENABLED)) + { + retcode = RETCODE(RETCODE_SEVERITY_ERROR, RETCODE_INCONSISTENT_STATE); + } + if (RETCODE_OK == retcode) + { + __HAL_RCC_USART2_CLK_ENABLE(); + __HAL_RCC_USART2_FORCE_RESET(); + __HAL_RCC_USART2_RELEASE_RESET(); + __GPIOD_CLK_ENABLE(); + /* Configure the UART resource */ + if (HAL_OK != HAL_UART_Init(&uartCtrlStruct.huart)) + { + retcode = RETCODE(RETCODE_SEVERITY_ERROR, RETCODE_BSP_UART_INIT_FAILED); + } + } + if (RETCODE_OK == retcode) + { + NVIC_ClearPendingIRQ(USART2_IRQn); + HAL_NVIC_SetPriority(USART2_IRQn, UART_INT_PRIORITY, UART_SUBPRIORITY); + HAL_NVIC_EnableIRQ(USART2_IRQn); + + bspState = (uint8_t)BSP_STATE_ENABLED; + } + return retcode; +} + +/** + * See API interface for function documentation + * @retval RETCODE_OK in case of success. + * @retval RETCODE_INCONSISTENT_STATE in case the module is not in a state to allow disabling. + */ +Retcode_T BSP_GenericUart_Disable(uint32_t id) +{ + KISO_UNUSED(id); + Retcode_T retcode = RETCODE_OK; + + if (!(bspState & (uint8_t)BSP_STATE_TO_DISABLED)) + { + retcode = RETCODE(RETCODE_SEVERITY_ERROR, RETCODE_INCONSISTENT_STATE); + } + if (RETCODE_OK == retcode) + { + /* Disable interrupts and deactivate UART peripheral */ + HAL_NVIC_DisableIRQ(USART2_IRQn); + /* Clear the pending interrupt */ + HAL_NVIC_ClearPendingIRQ(USART2_IRQn); + + if (HAL_OK != HAL_UART_DeInit(&uartCtrlStruct.huart)) + { + retcode = RETCODE(RETCODE_SEVERITY_ERROR, RETCODE_BSP_UART_DEINIT_FAILED); + } + } + if (RETCODE_OK == retcode) + { + __USART2_CLK_DISABLE(); + bspState = (uint8_t)BSP_STATE_DISABLED; + } + return retcode; +} + +/** + * See API interface for function documentation + * @retval RETCODE_OK in case of success. + * @retval RETCODE_INCONSISTENT_STATE in case the module is not in a state to allow disconnecting. + */ +Retcode_T BSP_GenericUart_Disconnect(uint32_t id) +{ + KISO_UNUSED(id); + Retcode_T retcode = RETCODE_OK; + if (!(bspState & (uint8_t)BSP_STATE_TO_DISCONNECTED)) + { + retcode = RETCODE(RETCODE_SEVERITY_ERROR, RETCODE_INCONSISTENT_STATE); + } + if (RETCODE_OK == retcode) + { + HAL_GPIO_DeInit(GPIOD, PIND_USART2_TX | PIND_USART2_RX); + GPIO_CloseClockGate(GPIO_PORT_D, PIND_USART2_TX | PIND_USART2_RX); + } + if (RETCODE_OK == retcode) + { + bspState = (uint8_t)BSP_STATE_DISCONNECTED; + } + return retcode; +} + +/** + * See API interface for function documentation + * @return A pointer to the UART control structure + */ +HWHandle_T BSP_GenericUart_GetHandle(uint32_t id) +{ + KISO_UNUSED(id); + return (HWHandle_T)&uartCtrlStruct; +} + +/** + * This function is not in use. + */ +Retcode_T BSP_GenericUart_UserControl(uint32_t control, void *param) +{ + KISO_UNUSED(control); + KISO_UNUSED(param); + + return RETCODE(RETCODE_SEVERITY_ERROR, RETCODE_NOT_SUPPORTED); +} + +/*---------------------- LOCAL FUNCTIONS IMPLEMENTATION -------------------------------------------------------------*/ + +/** + * Interrupt Service Routine handling USART2 IRQ. Forwards call to MCU Layer for handling. + */ +void USART2_IRQHandler(void) +{ + if (uartCtrlStruct.IrqCallback) + { + uartCtrlStruct.IrqCallback((UART_T)&uartCtrlStruct); + } +} +#endif /* KISO_FEATURE_BSP_TEST_INTERFACE */ diff --git a/boards/NucleoF767/bsp/source/bsp_api_testif.c b/boards/NucleoF767/bsp/source/bsp_api_testif.c index 1b4bd875..324f55fc 100644 --- a/boards/NucleoF767/bsp/source/bsp_api_testif.c +++ b/boards/NucleoF767/bsp/source/bsp_api_testif.c @@ -1,5 +1,5 @@ /******************************************************************************** -* Copyright (c) 2010-2019 Robert Bosch GmbH +* Copyright (c) 2010-2020 Robert Bosch GmbH * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License 2.0 which is available at @@ -16,6 +16,7 @@ #if KISO_FEATURE_BSP_TEST_INTERFACE +#include "Kiso_Basics.h" #include "stm32/stm32f7/Kiso_MCU_STM32F7_UART_Handle.h" #include "Kiso_HAL_Delay.h" #include "BSP_NucleoF767.h" @@ -27,10 +28,7 @@ #define KISO_MODULE_ID MODULE_BSP_API_TEST_IF #define TESTIF_UART_INT_PRIORITY UINT32_C(10) -#define TESTIF_UART_SUBPRIORITY UINT32_C(0) - -#define PINB_DBG_TX GPIO_PIN_8 -#define PINB_DBG_RX GPIO_PIN_9 +#define TESTIF_UART_SUBPRIORITY UINT32_C(1) /*---------------------- LOCAL FUNCTIONS DECLARATION ----------------------------------------------------------------*/ @@ -77,16 +75,22 @@ Retcode_T BSP_TestInterface_Connect(void) } if (RETCODE_OK == retcode) { - GPIO_InitTypeDef BSP_GPIOInitStruct = {0}; + /* IOSV bit MUST be set to access GPIO port G[2:15] */ + __HAL_RCC_PWR_CLK_ENABLE(); + __HAL_RCC_LPTIM1_CLK_ENABLE(); + + GPIO_InitTypeDef GPIO_InitStruct = {0}; + + /* UART RX/TX GPIO pin configuration */ + GPIO_OpenClockGate(GPIO_PORT_D, PIND_USART3_TX | PIND_USART3_RX); - GPIO_OpenClockGate(GPIO_PORT_D, PINB_DBG_TX | PINB_DBG_RX); - /* Configure RX TX as alternate function push pull */ - BSP_GPIOInitStruct.Pin = PINB_DBG_TX | PINB_DBG_RX; - BSP_GPIOInitStruct.Mode = GPIO_MODE_AF_PP; - BSP_GPIOInitStruct.Pull = GPIO_NOPULL; - BSP_GPIOInitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; - BSP_GPIOInitStruct.Alternate = GPIO_AF7_USART3; - HAL_GPIO_Init(GPIOD, &BSP_GPIOInitStruct); + GPIO_InitStruct.Pin = PIND_USART3_TX | PIND_USART3_RX; + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; + GPIO_InitStruct.Alternate = GPIO_AF7_USART3; + + HAL_GPIO_Init(GPIOD, &GPIO_InitStruct); bspState = (uint8_t)BSP_STATE_CONNECTED; } @@ -108,11 +112,10 @@ Retcode_T BSP_TestInterface_Enable(void) } if (RETCODE_OK == retcode) { - /* Enable the UART clock */ __HAL_RCC_USART3_CLK_ENABLE(); __HAL_RCC_USART3_FORCE_RESET(); __HAL_RCC_USART3_RELEASE_RESET(); - + __GPIOD_CLK_ENABLE(); /* Configure the UART resource */ if (HAL_OK != HAL_UART_Init(&testIf_UARTStruct.huart)) { @@ -121,6 +124,7 @@ Retcode_T BSP_TestInterface_Enable(void) } if (RETCODE_OK == retcode) { + NVIC_ClearPendingIRQ(USART3_IRQn); HAL_NVIC_SetPriority(USART3_IRQn, TESTIF_UART_INT_PRIORITY, TESTIF_UART_SUBPRIORITY); HAL_NVIC_EnableIRQ(USART3_IRQn); @@ -146,6 +150,9 @@ Retcode_T BSP_TestInterface_Disable(void) { /* Disable interrupts and deactivate UART peripheral */ HAL_NVIC_DisableIRQ(USART3_IRQn); + /* Clear the pending interrupt */ + HAL_NVIC_ClearPendingIRQ(USART3_IRQn); + if (HAL_OK != HAL_UART_DeInit(&testIf_UARTStruct.huart)) { retcode = RETCODE(RETCODE_SEVERITY_ERROR, RETCODE_BSP_UART_DEINIT_FAILED); @@ -173,8 +180,8 @@ Retcode_T BSP_TestInterface_Disconnect(void) } if (RETCODE_OK == retcode) { - HAL_GPIO_DeInit(GPIOD, PINB_DBG_TX | PINB_DBG_RX); - GPIO_CloseClockGate(GPIO_PORT_D, PINB_DBG_TX | PINB_DBG_RX); + HAL_GPIO_DeInit(GPIOD, PIND_USART3_TX | PIND_USART3_RX); + GPIO_CloseClockGate(GPIO_PORT_D, PIND_USART3_TX | PIND_USART3_RX); } if (RETCODE_OK == retcode) { @@ -206,7 +213,7 @@ Retcode_T BSP_TestInterface_Control(uint32_t command, void *arg) /*---------------------- LOCAL FUNCTIONS IMPLEMENTATION -------------------------------------------------------------*/ /** - * Interrupt Service Routine handling USART1 IRQ. Forwards call to MCU Layer for handling. + * Interrupt Service Routine handling USART3 IRQ. Forwards call to MCU Layer for handling. */ void USART3_IRQHandler(void) { diff --git a/boards/NucleoF767/bsp/source/protected/gpio.h b/boards/NucleoF767/bsp/source/protected/gpio.h index ba6217ce..8d6de803 100644 --- a/boards/NucleoF767/bsp/source/protected/gpio.h +++ b/boards/NucleoF767/bsp/source/protected/gpio.h @@ -35,6 +35,10 @@ #define PINB_LED_G GPIO_PIN_0 #define PINB_LED_B GPIO_PIN_7 #define PINC_USR_BUTTON GPIO_PIN_13 +#define PIND_USART2_TX GPIO_PIN_5 +#define PIND_USART2_RX GPIO_PIN_6 +#define PIND_USART3_TX GPIO_PIN_8 +#define PIND_USART3_RX GPIO_PIN_9 /*---------------------- EXPORTED TYPES ------------------------------------------------------------------------------*/ /** diff --git a/ci/testing_config/core/essentials/Kiso_BSPConfig.h b/ci/testing_config/core/essentials/Kiso_BSPConfig.h index c740d18e..be489aa2 100644 --- a/ci/testing_config/core/essentials/Kiso_BSPConfig.h +++ b/ci/testing_config/core/essentials/Kiso_BSPConfig.h @@ -42,6 +42,7 @@ /* BSP Features */ #define KISO_FEATURE_BSP_LED 1 #define KISO_FEATURE_BSP_BUTTON 1 +#define KISO_FEATURE_BSP_GENERIC_UART 1 #define KISO_FEATURE_BSP_CELLULAR_SARAR4N4 1 #define KISO_FEATURE_BSP_GNSS_MAXM8 1 #define KISO_FEATURE_BSP_BMA280 1 diff --git a/cmake/KisoLibsConfig.cmake b/cmake/KisoLibsConfig.cmake index cba4be35..fe23baff 100644 --- a/cmake/KisoLibsConfig.cmake +++ b/cmake/KisoLibsConfig.cmake @@ -72,7 +72,6 @@ if(NOT KISO_STATIC_CONFIG) configure_file(${ABS_BOARD_CONFIG_PATH}/${HEADER} ${DEST} COPYONLY) endforeach(HEADER ${BOARD_CONF_FILES}) - # Copy app-specific config files in intermediary directory # APP_CONFIG_PATH is not required - only act if present if(NOT APP_CONFIG_PATH) message(STATUS "APP_CONFIG_PATH not set to a valid path. Not using application-specific configuration.") diff --git a/core/essentials/test/integration/CMakeLists.txt b/core/essentials/test/integration/CMakeLists.txt new file mode 100644 index 00000000..81e8d939 --- /dev/null +++ b/core/essentials/test/integration/CMakeLists.txt @@ -0,0 +1,30 @@ +cmake_minimum_required(VERSION 3.6) + +project ("Essentials integration test entry" C ASM) + +# the checks will be executed as it would be on the desired compile step +if(${ENABLE_STATIC_CHECKS}) + set(CMAKE_C_CLANG_TIDY ${CLANG_TIDY} --extra-arg=--target=arm-none-eabi --extra-arg=-mthumb --extra-arg=--sysroot=${CMAKE_SYSROOT} -checks=-*,readability-*,clang-analyzer-*,-clang-analyzer-cplusplus*) + set(CMAKE_CXX_CLANG_TIDY ${CLANG_TIDY} --extra-arg=--target=arm-none-eabi --extra-arg=--sysroot=${CMAKE_SYSROOT} -checks=-*,readability-*,clang-analyzer-*,-clang-analyzer-cplusplus*) +endif() + +## Only compilable for a target +if(${CMAKE_CROSSCOMPILING}) + file(GLOB TEST_ENTRY_SOURCES + source/*.c + ) + add_library(testentry STATIC ${TEST_ENTRY_SOURCES}) + + target_include_directories(testentry + PRIVATE + source + ) + # List of additional libs from board_config.cmake + target_link_libraries(testentry testing essentials ${KISO_BOARD_LIBS}) +endif(${CMAKE_CROSSCOMPILING}) + +# Include the tests for this module +if(${CMAKE_TESTING_ENABLED}) + #add_subdirectory(testentry/test) +endif() + diff --git a/core/essentials/test/integration/readme.md b/core/essentials/test/integration/readme.md new file mode 100644 index 00000000..aa2d943b --- /dev/null +++ b/core/essentials/test/integration/readme.md @@ -0,0 +1,7 @@ +# Integration Tests: Essentials + +This is the integration test package for Essentials. It contains integration test suites for: + +* UART - testing sending and receiving data over standard UART. + +Check `TestEntry.c` to see which integration test suites are loaded. diff --git a/core/essentials/test/integration/source/TestEntry.c b/core/essentials/test/integration/source/TestEntry.c new file mode 100644 index 00000000..7954f9a5 --- /dev/null +++ b/core/essentials/test/integration/source/TestEntry.c @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (c) 2010-2020 Robert Bosch GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Robert Bosch GmbH - initial contribution + * + ******************************************************************************/ + +/** + * @file + * + * @brief + * Implements the following functionalities specified in template.h + */ +#include "Kiso_Testing.h" +#include "TestSuiteUart.h" + +#undef KISO_MODULE_ID +#define KISO_MODULE_ID KISO_MODULE_ID_TEST_ENTRY + +#define TEST_ENTRY_ID 1 + +Retcode_T TestEntry_Initialize(void *param1, uint32_t param2); +static Retcode_T TestEntry_Setup(CCMsg_T *ccmsg); +static Retcode_T TestEntry_Teardown(CCMsg_T *ccmsg); + +Retcode_T TestEntry_Initialize(void *param1, uint32_t param2) +{ + KISO_UNUSED(param1); + KISO_UNUSED(param2); + + Retcode_T retcode = RETCODE_OK; + retcode = Tests_Initialize(TEST_ENTRY_ID, TestEntry_Setup, TestEntry_Teardown); + if (RETCODE_OK == retcode) + { + retcode = TestSuiteUart_Initialize((uint8_t)1); + } + return retcode; +} + +static Retcode_T TestEntry_Setup(CCMsg_T *ccmsg) +{ + KISO_UNUSED(ccmsg); + return RETCODE_OK; +} + +static Retcode_T TestEntry_Teardown(CCMsg_T *ccmsg) +{ + KISO_UNUSED(ccmsg); + return RETCODE_OK; +} diff --git a/core/essentials/test/integration/source/TestSuiteUart.c b/core/essentials/test/integration/source/TestSuiteUart.c new file mode 100644 index 00000000..26f57c9b --- /dev/null +++ b/core/essentials/test/integration/source/TestSuiteUart.c @@ -0,0 +1,272 @@ +/******************************************************************************* + * Copyright (c) 2010-2020 Robert Bosch GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Robert Bosch GmbH - initial contribution + * + ******************************************************************************/ + +/** + * @file + * + * @brief + * Implements test cases for uart comminication verification + */ +#include "TestSuiteUart.h" +#include "Kiso_Basics.h" +#include "Kiso_Testing.h" +#include "Kiso_CmdProcessor.h" +#include "Kiso_MCU_UART.h" +#include "Kiso_BSP_GenericUart.h" +#include "FreeRTOS.h" +#include "semphr.h" + +#undef KISO_MODULE_ID +#define KISO_MODULE_ID 0 + +#define UART_BUFFER_LEN (5) +#define DATA_TRANSFER_TIMEOUT_MS UINT32_C(200) +#define UART_DEVICE UINT32_C(1) +#define MSG_BUFFER_SIZE (32) + +enum TestSuiteUart_TestCases_E +{ + TEST_CASE_FUNCTIONAL_TEST_ID = 1 +}; + +static Retcode_T TestCase_FctTest_Setup(CCMsg_T *ccmsg); +static void TestCase_FctTest_Run(CCMsg_T *ccmsg); +static Retcode_T TestCase_FctTest_Teardown(CCMsg_T *ccmsg); +static void UartISRCallback(UART_T uart, struct MCU_UART_Event_S event); + +static UART_T UartHdl = 0; +static xSemaphoreHandle TxSignal = 0; +static xSemaphoreHandle RxSignal = 0; + +Retcode_T TestSuiteUart_Initialize(uint8_t sId) +{ + Retcode_T retcode = RETCODE_OK; + + retcode = Tests_RegisterTestSuite(sId, TestCase_FctTest_Setup, TestCase_FctTest_Teardown); + + if (RETCODE_OK == retcode) + { + retcode = Tests_RegisterTestCase(sId, TEST_CASE_FUNCTIONAL_TEST_ID, TestCase_FctTest_Setup, TestCase_FctTest_Run, TestCase_FctTest_Teardown); + } + return retcode; +} + +/** + * @brief Performs the setup operation of the functional test of uart in interrupt mode + * @details This function initializes the uart interface in interrupt mode and creates the necessary + * synchronisation ressources. + */ +static Retcode_T TestCase_FctTest_Setup(CCMsg_T *ccmsg) +{ + KISO_UNUSED(ccmsg); + Retcode_T retcode = RETCODE_OK; + + UartHdl = (UART_T)BSP_GenericUart_GetHandle(UART_DEVICE); + if (NULL == UartHdl) + { + retcode = RETCODE(RETCODE_SEVERITY_ERROR, RETCODE_NULL_POINTER); + } + if (RETCODE_OK == retcode) + { + TxSignal = xSemaphoreCreateBinary(); + if (NULL == TxSignal) + { + return RETCODE(RETCODE_SEVERITY_FATAL, RETCODE_SEMAPHORE_ERROR); + } + } + if (RETCODE_OK == retcode) + { + RxSignal = xSemaphoreCreateBinary(); + if (NULL == RxSignal) + { + return RETCODE(RETCODE_SEVERITY_FATAL, RETCODE_SEMAPHORE_ERROR); + } + } + if (RETCODE_OK == retcode) + { + retcode = BSP_GenericUart_Connect(UART_DEVICE); + } + if (RETCODE_OK == retcode) + { + retcode = MCU_UART_Initialize(UartHdl, UartISRCallback); + } + if (RETCODE_OK == retcode) + { + retcode = BSP_GenericUart_Enable(UART_DEVICE); + } + return retcode; +} + +/** + * @brief Deinitializes the uart interface + */ +static Retcode_T TestCase_FctTest_Teardown(CCMsg_T *ccmsg) +{ + KISO_UNUSED(ccmsg); + Retcode_T retcode; + + retcode = MCU_UART_Deinitialize(UartHdl); + if (RETCODE_OK == retcode) + { + retcode = BSP_GenericUart_Disable(UART_DEVICE); + } + if (RETCODE_OK == retcode) + { + retcode = BSP_GenericUart_Disconnect(UART_DEVICE); + } + if (RETCODE_OK == retcode) + { + vSemaphoreDelete(TxSignal); + } + if (RETCODE_OK == retcode) + { + vSemaphoreDelete(RxSignal); + } + return retcode; +} + +/** + * This Test will put the uart receiver into receive mode and send data via the transmitter the data will be + * looped back to the receiver at hardware level (e.g. wiring TX line to RX line) + * the test will succede if the transmit operation succeeded and if the received data matches the transmitted data + */ +static void TestCase_FctTest_Run(CCMsg_T *ccmsg) +{ + KISO_UNUSED(ccmsg); + + Retcode_T retcode; + uint8_t dataOut[UART_BUFFER_LEN]; + uint8_t dataIn[UART_BUFFER_LEN] = {0}; + char msg[MSG_BUFFER_SIZE] = "SUCCESS"; + + for (uint8_t i = 0; i < UART_BUFFER_LEN; i++) + { + dataOut[i] = i; + } + + retcode = MCU_UART_Receive(UartHdl, dataIn, UART_BUFFER_LEN); + + if (RETCODE_OK == retcode) + { + retcode = MCU_UART_Send(UartHdl, dataOut, UART_BUFFER_LEN); + } + if (RETCODE_OK == retcode) + { + if (pdTRUE != xSemaphoreTake(TxSignal, DATA_TRANSFER_TIMEOUT_MS)) + { + retcode = RETCODE(RETCODE_SEVERITY_ERROR, RETCODE_SEMAPHORE_ERROR); + strcpy(msg, "FAIL"); + } + } + + if (RETCODE_OK == retcode) + { + if (pdTRUE == xSemaphoreTake(RxSignal, DATA_TRANSFER_TIMEOUT_MS)) + { + uint8_t tries = 1; + // we received something... now wait till we receive EVERYTHING. + while (pdTRUE == xSemaphoreTake(RxSignal, DATA_TRANSFER_TIMEOUT_MS)) + { + /* Semaphore should signal at most UART_BUFFER_LEN times (also + * counting the first semaphore signal above) */ + tries++; + if (tries > UART_BUFFER_LEN) + { + retcode = RETCODE(RETCODE_SEVERITY_ERROR, RETCODE_UNEXPECTED_BEHAVIOR); + strcpy(msg, "FAIL"); + break; + } + } + + for (uint8_t i = 0; RETCODE_OK == retcode && i < UART_BUFFER_LEN; i++) + { + if (dataIn[i] != dataOut[i]) + { + retcode = RETCODE(RETCODE_SEVERITY_ERROR, RETCODE_UNEXPECTED_BEHAVIOR); + strcpy(msg, "FAIL"); + } + } + } + else + { + retcode = RETCODE(RETCODE_SEVERITY_ERROR, RETCODE_SEMAPHORE_ERROR); + strcpy(msg, "FAIL"); + } + } + Tests_SendReport(Retcode_GetCode(retcode), msg); +} + +static void UartISRCallback(UART_T uart, struct MCU_UART_Event_S event) +{ + KISO_UNUSED(uart); + Retcode_T Rc = RETCODE_OK; + + if (UINT8_C(1) == event.TxComplete) + { + if (RETCODE_OK == Rc) + { + BaseType_t higherPriorityTaskWoken = pdFALSE; + + if (NULL != TxSignal) + { + if (pdTRUE == xSemaphoreGiveFromISR(TxSignal, &higherPriorityTaskWoken)) + { + portYIELD_FROM_ISR(higherPriorityTaskWoken); + } + else + { + /* ignore... semaphore has already been given */ + } + } + else + { + Rc = RETCODE(RETCODE_SEVERITY_ERROR, RETCODE_NULL_POINTER); + } + } + } + + if (UINT8_C(1) == event.RxComplete) + { + if (RETCODE_OK == Rc) + { + BaseType_t higherPriorityTaskWoken = pdFALSE; + + if (NULL != RxSignal) + { + if (pdTRUE == xSemaphoreGiveFromISR(RxSignal, &higherPriorityTaskWoken)) + { + portYIELD_FROM_ISR(higherPriorityTaskWoken); + } + else + { + /* ignore... semaphore has already been given */ + } + } + else + { + Rc = RETCODE(RETCODE_SEVERITY_ERROR, RETCODE_NULL_POINTER); + } + } + } + + if (UINT8_C(1) == event.TxError) + { + Rc = RETCODE(RETCODE_SEVERITY_ERROR, RETCODE_FAILURE); + } + + if (RETCODE_OK != Rc) + { + Retcode_RaiseErrorFromIsr(Rc); + } +} diff --git a/core/essentials/test/integration/source/TestSuiteUart.h b/core/essentials/test/integration/source/TestSuiteUart.h new file mode 100644 index 00000000..f170590e --- /dev/null +++ b/core/essentials/test/integration/source/TestSuiteUart.h @@ -0,0 +1,47 @@ +/******************************************************************************* + * Copyright (c) 2010-2020 Robert Bosch GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Robert Bosch GmbH - initial contribution + * + ******************************************************************************/ + +/** + * @file + * @defgroup + * @ingroup + * @{ + * + * @brief Provides an API for the following functionality + * + */ +#ifndef TESTSUITE_UART_H_ +#define TESTSUITE_UART_H_ + +#include "Kiso_Retcode.h" + +enum TestSuiteUart_Retcodes_E +{ + TESTSUITE_SETUP_TRIGGERED_SEVERAL_TIMES = RETCODE_FIRST_CUSTOM_CODE, + TESTSUITE_RUN_TRIGGERED_SEVERAL_TIMES, + TestSuite_Teardown_TRIGGERED_SEVERAL_TIMES, +}; + +/** + * @brief Initializes the uart test suite + * @details This function will register the uart test suites in the Testing module TestSuites register and will + * also register for execution all the test cases belonging to this test suite + * @param id is the identifier to be given to the test suite it will be used in the communication protocol between + * the test executor and the test controller @see todo: add link to docu + */ +Retcode_T TestSuiteUart_Initialize(uint8_t id); + +/** @} */ + +#endif /* TESTSUITE_UART_H_ */ diff --git a/core/essentials/test/integration/specs/UART_Test_Spec.md b/core/essentials/test/integration/specs/UART_Test_Spec.md new file mode 100644 index 00000000..9538501b --- /dev/null +++ b/core/essentials/test/integration/specs/UART_Test_Spec.md @@ -0,0 +1,46 @@ +# Test Entry 1: Essentials + +## Test Suite 1.1: UART + +### Description + +This suite aims at testing the basic functionality of the MCU UART APIs. + +### Setup + +The test setup consists of the test coordinator and one (1) test participant. + +* Test Coordinator (on PC) +* The DUT is connected to the PC via UART which is the Test Coordination Channel. The embedded C testling code is flashed onto the DUT. + +### Teardown + +No special teardown + +### Test Cases + +#### TC 1.1.1: Functional Test + +##### Setup + +1. Get device handle of BSP initialized generic UART + * UART must be configured in loopback mode, echoing any data sent out +2. Allocate OS signal semaphores used as signal from IRQ +3. Connect the generic UART BSP +4. Initialize UART MCU with UART handle +5. Enable generic UART BSP + +##### Run + +1. Initiate UART receive process +2. Start send process dummy data of a few bytes size +3. Wait for send to completed + * Maximum timeout depends on baud-rate and data length +4. Expect to receive echo + +##### Teardown + +1. Deinitialize UART MCU, deactivating IRQs +2. Disable generic UART BSP +3. Disconnect generic UART BSP +4. Free OS signal semaphores diff --git a/core/essentials/test/integration/test-protocol.txt b/core/essentials/test/integration/test-protocol.txt new file mode 100644 index 00000000..e69de29b diff --git a/core/testing/CMakeLists.txt b/core/testing/CMakeLists.txt new file mode 100644 index 00000000..eb7afcb4 --- /dev/null +++ b/core/testing/CMakeLists.txt @@ -0,0 +1,40 @@ +cmake_minimum_required(VERSION 3.6) + +project ("Kiso Testing" C) + +## Interface library +add_library(testing_int INTERFACE) +target_include_directories(testing_int +INTERFACE + include +) +target_link_libraries(testing_int INTERFACE essentials_int) + +## Enable static code analysis +# the checks will be executed as it would be on the desired compile step +if(${ENABLE_STATIC_CHECKS}) + set(CMAKE_C_CLANG_TIDY ${CLANG_TIDY} --extra-arg=--target=arm-none-eabi --extra-arg=--sysroot=${CMAKE_SYSROOT}) + set(CMAKE_CXX_CLANG_TIDY ${CLANG_TIDY} --extra-arg=--target=arm-none-eabi --extra-arg=--sysroot=${CMAKE_SYSROOT}) +endif() + +## Make sure we are only compiling them with the proper toolchain +if(${CMAKE_CROSSCOMPILING}) + + file(GLOB TESTING_SOURCES + ./source/*.c + ) + add_library(testing STATIC EXCLUDE_FROM_ALL ${TESTING_SOURCES}) + target_include_directories(testing + PUBLIC + include + PRIVATE + source/protected + ) + target_link_libraries(testing testing_int essentials utils ${KISO_OS_LIB}) + +endif(${CMAKE_CROSSCOMPILING}) + +## Add tests +if(${CMAKE_TESTING_ENABLED}) + #add_subdirectory(test) +endif() diff --git a/core/testing/include/Kiso_Testing.h b/core/testing/include/Kiso_Testing.h new file mode 100644 index 00000000..64080452 --- /dev/null +++ b/core/testing/include/Kiso_Testing.h @@ -0,0 +1,192 @@ +/********************************************************************************************************************** + * Copyright (c) 2010#2019 Robert Bosch GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl#2.0. + * + * SPDX#License#Identifier: EPL#2.0 + * + * Contributors: + * Robert Bosch GmbH # initial contribution + * + **********************************************************************************************************************/ + +/** + * @file + * @defgroup + * @ingroup + * @{ + * + * @brief This module manages the integration testing protocol and communication between the device under test + and the test coordinator + * + */ + +#ifndef KISO_TESTING_H_ +#define KISO_TESTING_H_ + +/*###################### INCLUDED HEADERS ############################################################################*/ + +#include "Kiso_Basics.h" +#include "Kiso_Retcode.h" +#include "Kiso_Assert.h" +#include "Kiso_Testing_Config.h" + +/*###################### MACROS DEFINITION ###########################################################################*/ + +/*###################### TYPE DEFINITIONS ############################################################################*/ +/** + * @brief Enumerates the internal units in the Kiso_Testing library which could return Retcode conform error + * codes. In case of error, one can refer to this enumeration to identify which source file has issued + * it. +*/ +enum KISO_TESTING_Modules_E +{ + KISO_MODULE_ID_TESTING = 1, + KISO_MODULE_ID_TESTING_TESTREGISTRY, /**< KISO_TESTS_MODULE_ID_TESTREGISTRY */ + KISO_MODULE_ID_TESTING_CCHANNEL, /**< KISO_TESTS_MODULE_ID_CCHANNEL */ + KISO_MODULE_ID_TESTING_SERIALMSGTRANSCEIVER, /**< KISO_TESTS_MODULE_ID_SERIALMSGTRANSCEIVER */ + KISO_MODULE_ID_TESTING_TESTRUNNER, /**< KISO_TESTS_MODULE_ID_TESTRUNNER */ + KISO_MODULE_ID_TESTING_SERIAL, /**< KISO_TESTS_MODULE_ID_SERIAL */ + KISO_MODULE_ID_TESTING_TESTENTRY, +}; + +/** + * @brief Enumerates the special return codes that could be returned from the different units in the + * Kiso_Testing library. +*/ +enum KISO_TESTING_Retcodes_E +{ + RETCODE_TESTING_SUITE_ALREADY_REGISTERED = RETCODE_FIRST_CUSTOM_CODE, + RETCODE_TESTING_CASE_ALREADY_REGISTERED, + RETCODE_TESTING_CCHANNEL_INITIALISATION_FAILED, + RETCODE_TESTING_INCOMPLETE_MESSAGE_RECEIVED, + RETCODE_TESTING_CRC_MISMATCH, + RETCODE_TESTING_VERSION_MISMATCH, + RETCODE_TESTING_TLVELEMENT_NOT_FOUND, + RETCODE_TESTING_REPORT_TIMEOUT, + RETCODE_TESTING_CCHANNEL_NOT_SPECIFIED, +}; + +/** + * @brief Enumerates the types of messages exchanged between the Test_Executor and the Test_Controller + */ +enum CCMsg_MessageType_E +{ + CCMSG_TYPE_COMMAND, + CCMSG_TYPE_REPORT, + CCMSG_TYPE_ACK, +}; + +/** + * @brief Encapsulates the elements composing the message header. + */ +typedef struct MessageHeader_S +{ + uint8_t messageInfo; /**< version is 2 bits, message type is 2 bits and the remaining 4 bits are reserved */ + uint8_t messageToken; + uint8_t messageType; + uint8_t errorCode; + uint8_t testEntry; + uint8_t testSuite; + uint8_t testCase; + uint8_t payloadLength; +} MsgHdr_T; + +/** + * @brief Encapsulates the elements composing a TLV object(type, length and value) + */ +typedef struct TlvElement_S +{ + uint8_t type; + uint8_t length; + char *value; +} TlvElt_T; + +/** + * @brief Encapsulates the elements composing a message.. + */ +typedef struct CCMsg_S +{ + MsgHdr_T header; /**< Header of the message */ + uint8_t payload[TEST_MAX_PAYLOAD_LENGTH]; + TlvElt_T tlvArray[CCHANNEL_MAX_NUMBER_OF_TLV_ELEMENTS]; /**< Parsed TLV array (from receive buffer) */ + uint8_t payloadIndex; + uint8_t tlvArrayIndex; + uint8_t numberOfTlvElements; /**< Number of TLV elements for the message */ + uint8_t rebootCounter; + bool isFree : 1; /**< This bit indicates whether the received message is processed by test runner or not */ +} CCMsg_T; + +/** + * @brief Defines a prototype type for the setup functions of the test suites and test cases. + */ +typedef Retcode_T (*SetupFct_T)(CCMsg_T *ccmsg); + +/** + * @brief Defines a prototype type for the tear down functions of the test suites and test cases. + */ +typedef Retcode_T (*TearDownFct_T)(CCMsg_T *ccmsg); + +/** + * @brief Sefines a prototype type for the run functions of the test cases. + */ +typedef void (*RunFct_T)(CCMsg_T *ccmsg); + +/*###################### EXPORTED FUNCTIONS PROTOTYPES ###############################################################*/ + +/** + * @brief Initializes the Testing Framework. + * @details this + * @param[in] eId Id of the Test Entry + * @return RETCODE_OK if initialized successfully error code otherwise + */ +Retcode_T Tests_Initialize(uint8_t eId, SetupFct_T setup, TearDownFct_T teardown); + +/** + * @brief Registers a Test Suite + * @details todo mak explain what the function does currently + * @param[in] sId The identifier of the TestSuite to register + * @param[in] setup A reference to the setup function of the TestSuite + * @param[in] teardown A reference to the tear down function of the TestSuite + * @note setup and tear down functions pointers can be null if nothing has to be done. + * @return RETCODE_OK in case of success, error code otherwise. + */ +Retcode_T Tests_RegisterTestSuite(uint8_t sId, SetupFct_T setup, TearDownFct_T teardown); + +/** + * @brief Registers a Test Case + * @details todo mak explain what the function does + * @param[in] sId identifier of the Test Suite the test to register + * @param[in] cId identifier of the Test Case to register + * @param[in] setup A reference to the setup function of the Test Suite + * @param[in] run A reference to the run function of the Test Suite + * @param[in] teardown A reference to the tear down function of the Test Suite + * @note setup and tear down functions pointers can be null if nothing has to be done. + * @return RETCODE_OK in case of success, error code otherwise. + */ +Retcode_T Tests_RegisterTestCase(uint8_t sId, uint8_t cId, SetupFct_T setup, RunFct_T run, TearDownFct_T teardown); + +/** + * @brief Sends a result of a test case execution. + * @details todo mak explain what the function does + * @param[in] result The test result code (0: success / otherwise: failure) + * @param[in] reason A 0-terminating string stating a reason. It can be NULL, if no reason should be sent. + */ +void Tests_SendReport(uint8_t result, char *reason); + +/** + * @brief Gets a tlv element using the type of the tlvElement input + * @details todo mak explain what the function does + * @param[in] ccmsg A reference to the message in which to look for the element + * @param[in,out] tlvElement A reference to where to store the element if found. + * tlvElement->type is used as input to find the element's value + * @return RETCODE_OK in case of success, error code otherwise. + * todo mak: make explicit the type search. interface is hiding information. + */ +Retcode_T Tests_GetTlvElement(CCMsg_T *ccmsg, TlvElt_T *tlvElement); + +#endif /* KISO_TESTING_H_ */ + +/** @} */ diff --git a/core/testing/include/Kiso_Testing_Config.h b/core/testing/include/Kiso_Testing_Config.h new file mode 100644 index 00000000..8e91d272 --- /dev/null +++ b/core/testing/include/Kiso_Testing_Config.h @@ -0,0 +1,54 @@ +/*----------------------------------------------------------------------------*/ +/* + * Copyright (C) Bosch Connected Devices and Solutions GmbH. + * All Rights Reserved. Confidential. + * + * Distribution only to people who need to know this information in + * order to do their job.(Need-to-know principle). + * Distribution to persons outside the company, only if these persons + * signed a non-disclosure agreement. + * Electronic transmission, e.g. via electronic mail, must be made in + * encrypted form. + */ +/*----------------------------------------------------------------------------*/ + +/** + * @file Add a brief description here. + * + * Put here the documentation of this header file. Explain the interface exposed + * by this header, e.g. what is the purpose of use, how to use it, etc. + */ + +#ifndef KISO_TESTING_CONFIG_H +#define KISO_TESTING_CONFIG_H + +#ifndef TEST_RUNNER_TASK_PRIO +#define TEST_RUNNER_TASK_PRIO 2 +#endif + +#ifndef TEST_RUNNER_TASK_STACK_DEPTH +#define TEST_RUNNER_TASK_STACK_DEPTH 1024 +#endif + +#ifndef TEST_RUNNER_QUEUE_SIZE +#define TEST_RUNNER_QUEUE_SIZE 5U +#endif + +#ifndef TEST_MAX_NUMBER_OF_TEST_SUITES +#define TEST_MAX_NUMBER_OF_TEST_SUITES 16 +#endif + +/** Macros defining the maximum number of Test Cases per Test Suite */ +#ifndef TEST_MAX_NUMBER_OF_TEST_CASES_PER_TEST_SUITE +#define TEST_MAX_NUMBER_OF_TEST_CASES_PER_TEST_SUITE 16 +#endif + +#ifndef CCHANNEL_MAX_NUMBER_OF_TLV_ELEMENTS +#define CCHANNEL_MAX_NUMBER_OF_TLV_ELEMENTS 2 +#endif + +#ifndef TEST_MAX_PAYLOAD_LENGTH +#define TEST_MAX_PAYLOAD_LENGTH 248U +#endif + +#endif /* KISO_TESTING_CONFIG_H */ diff --git a/core/testing/source/CChannel.c b/core/testing/source/CChannel.c new file mode 100644 index 00000000..ba864be6 --- /dev/null +++ b/core/testing/source/CChannel.c @@ -0,0 +1,312 @@ +/******************************************************************************* + * Copyright (c) 2010-2020 Robert Bosch GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Robert Bosch GmbH - initial contribution + * + ******************************************************************************/ + +/** + * @file + * + * @brief + * This module is responsible for the transmission and reception of data through + * the communication medium, which can be UART / UDP / BLE and so on. + */ + +#include "Kiso_Testing.h" +#include "CChannel.h" +#include "TestRunner.h" +#include "SerialCChannel.h" +#include "SerialMsgTransceiver.h" + +#undef KISO_MODULE_ID +#define KISO_MODULE_ID KISO_MODULE_ID_TESTING_CCHANNEL + +#define MAX_NUMBER_OF_INCOMING_MESSAGE 2 + +#define CCHANNEL_TLV_TYPE_REASON 112 + +#define CCHANNEL_REPORT_TYPE 110 + +#define CCHANNEL_NUM_OF_SEND_RETRIES 4 + +#define MSG_INIT_OK ("CCHANNEL INIT OK\r\n") + +static CCMsg_T *allocCCMessage(void); +static Retcode_T sendMessage(CCMsg_T *ccmsg); +static void parseTlvElements(CCMsg_T *ccmsg); +static void freeAllCCMsg(void); + +static CCMsg_T msgPool[MAX_NUMBER_OF_INCOMING_MESSAGE]; +static CCMsg_T ackMessage; +static CCMsg_T reportMessage; + +Retcode_T CChannel_Initialize(void) +{ + freeAllCCMsg(); + Retcode_T retcode = Serial_Initialize(); + + if (RETCODE_OK == retcode) + { + char msg[sizeof(MSG_INIT_OK)] = MSG_INIT_OK; + retcode = Serial_Send((void *)msg, strlen(msg)); + } + return retcode; +} + +Retcode_T CChannel_Deinitialize(void) +{ + return Serial_Deinitialize(); +} + +void CChannel_FreeCCMsg(CCMsg_T *ccmsg) +{ + (void)memset(ccmsg, 0, sizeof(CCMsg_T)); + + ccmsg->isFree = true; +} + +void CChannel_ReceiveEventHandler(uint8_t *buffer, uint8_t length) +{ + if (CCMSG_VERSION != ((buffer[0] & CCMSG_VERSION_MASK) >> CCMSG_VERSION_SHIFT)) + { + //if the framework version does not match we just ignore this message + return; + } + + CCMsg_T *ccmsg = allocCCMessage(); + + if (NULL == ccmsg) + { + Retcode_RaiseError(RETCODE(RETCODE_SEVERITY_ERROR, RETCODE_OUT_OF_RESOURCES)); + return; + } + + (void)memcpy(ccmsg, buffer, length); + + parseTlvElements(ccmsg); + + TestRunner_ProcessMessage(ccmsg); +} + +void CChannel_PrepareAck(CCMsg_T *ccmsg) +{ + (void)memset(&ackMessage, 0, sizeof(CCMsg_T)); + + (void)memcpy(&ackMessage, ccmsg, CCHANNEL_HEADER_LENGTH); + + ackMessage.header.messageInfo = CCMSG_CREATE_TYPE(CCHANNEL_MSG_TYPE_ACK); + ackMessage.header.payloadLength = 0; +} + +Retcode_T CChannel_SendAck(uint8_t result) +{ + ackMessage.header.errorCode = result; + + assert(ackMessage.header.messageInfo == CCMSG_CREATE_TYPE(CCHANNEL_MSG_TYPE_ACK)); + assert(ackMessage.header.payloadLength == 0); + + return sendMessage(&ackMessage); +} + +Retcode_T CChannel_ResendAck(void) +{ + return sendMessage(&ackMessage); +} + +void CChannel_PrepareReport(CCMsg_T *ccmsg) +{ + (void)memset(&reportMessage, 0, sizeof(CCMsg_T)); + + (void)memcpy(&reportMessage, ccmsg, CCHANNEL_HEADER_LENGTH); + + reportMessage.header.messageType = CCHANNEL_REPORT_TYPE; + reportMessage.header.messageInfo = CCMSG_CREATE_TYPE(CCHANNEL_MSG_TYPE_REPORT); + reportMessage.header.payloadLength = 0; +} + +Retcode_T CChannel_SendReport(uint8_t result, char *reason) +{ + Retcode_T retcode = RETCODE_OK; + + reportMessage.header.errorCode = result; + + assert(reportMessage.header.payloadLength == 0); + + if (NULL != reason) + { + TlvElt_T reasonTlv; + reasonTlv.type = CCHANNEL_TLV_TYPE_REASON; + reasonTlv.length = strlen(reason); + reasonTlv.value = reason; + + retcode = CChannel_AddTlvElement(&reportMessage, &reasonTlv); + + if ((strlen(reason) + 2) != reportMessage.header.payloadLength) + { + return RETCODE(RETCODE_SEVERITY_WARNING, (uint32_t)RETCODE_UNEXPECTED_BEHAVIOR); + } + } + + assert(reportMessage.header.messageInfo == CCMSG_CREATE_TYPE(CCHANNEL_MSG_TYPE_REPORT)); + + if (RETCODE_OK == retcode) + { + retcode = sendMessage(&reportMessage); + } + + return retcode; +} + +Retcode_T CChannel_ResendReport(void) +{ + Retcode_T retcode = RETCODE_OK; + + retcode = sendMessage(&reportMessage); + + return retcode; +} + +bool CChannel_DoesAckMatchReport(CCMsg_T *ccack) +{ + bool result = true; + MsgHdr_T msgHdr = reportMessage.header; + MsgHdr_T ackHdr = ccack->header; + + if ((ackHdr.messageToken != msgHdr.messageToken) || (ackHdr.testEntry != msgHdr.testEntry) || (ackHdr.testSuite != msgHdr.testSuite) || (ackHdr.testCase != msgHdr.testCase)) + { + result = false; + } + + return result; +} + +Retcode_T CChannel_GetTlvElement(CCMsg_T *ccmsg, TlvElt_T *tlvElement) +{ + if ((NULL == ccmsg) || (NULL == tlvElement)) + { + return RETCODE(RETCODE_SEVERITY_ERROR, RETCODE_INVALID_PARAM); + } + else + { + for (uint32_t i = 0; i < ccmsg->numberOfTlvElements; i++) + { + if (tlvElement->type == ccmsg->tlvArray[i].type) + { + if (NULL == ccmsg->tlvArray[i].value) + { + return RETCODE(RETCODE_SEVERITY_ERROR, RETCODE_NULL_POINTER); + } + + tlvElement->value = ccmsg->tlvArray[i].value; + tlvElement->length = ccmsg->tlvArray[i].length; + return RETCODE_OK; + } + } + } + return RETCODE(RETCODE_SEVERITY_ERROR, RETCODE_TESTING_TLVELEMENT_NOT_FOUND); +} + +Retcode_T CChannel_AddTlvElement(CCMsg_T *ccmsg, TlvElt_T *tlvElement) +{ + if ((NULL == ccmsg) || (NULL == tlvElement) || (NULL == tlvElement->value)) + { + return (RETCODE(RETCODE_SEVERITY_ERROR, (uint32_t)RETCODE_NULL_POINTER)); + } + else + { + + if (CCHANNEL_MAX_NUMBER_OF_TLV_ELEMENTS == ccmsg->numberOfTlvElements) + { + //we allow only 20 TLV elements in a message + return RETCODE(RETCODE_SEVERITY_ERROR, (uint32_t)RETCODE_OUT_OF_RESOURCES); + } + + if (CCHANNEL_PAYLOAD_MAX_SIZE <= (ccmsg->payloadIndex + 2 + tlvElement->length)) + { + //we allow only 248 as the maximum paylaod size + return RETCODE(RETCODE_SEVERITY_ERROR, (uint32_t)RETCODE_OUT_OF_RESOURCES); + } + + TlvElt_T *currentTlvElement = &ccmsg->tlvArray[ccmsg->numberOfTlvElements]; + + currentTlvElement->type = tlvElement->type; + ccmsg->payload[ccmsg->payloadIndex++] = tlvElement->type; + currentTlvElement->length = tlvElement->length; + ccmsg->payload[ccmsg->payloadIndex++] = tlvElement->length; + + currentTlvElement->value = (char *)&(ccmsg->payload[ccmsg->payloadIndex]); + (void)memcpy(&ccmsg->payload[ccmsg->payloadIndex], tlvElement->value, tlvElement->length); + + ccmsg->payloadIndex += tlvElement->length; + + ccmsg->header.payloadLength += tlvElement->length + 2; + ccmsg->numberOfTlvElements++; + + return RETCODE_OK; + } +} + +static CCMsg_T *allocCCMessage(void) +{ + CCMsg_T *ccmsg = NULL; + + for (uint8_t i = 0; i < MAX_NUMBER_OF_INCOMING_MESSAGE; i++) + { + if (msgPool[i].isFree) + { + ccmsg = &msgPool[i]; + ccmsg->isFree = false; + break; + } + } + + return ccmsg; +} + +static Retcode_T sendMessage(CCMsg_T *ccmsg) +{ + Retcode_T retcode = RETCODE_OK; + uint32_t retries = CCHANNEL_NUM_OF_SEND_RETRIES; + do + { + retcode = SerialMsgTransceiver_Send((uint8_t *)ccmsg, CCHANNEL_HEADER_LENGTH + ccmsg->header.payloadLength); + retries--; + } while (retries > 0 && RETCODE_OK != retcode); + + return retcode; +} + +static void parseTlvElements(CCMsg_T *ccmsg) +{ + for (uint8_t i = 0; i < ccmsg->header.payloadLength;) + { + if (ccmsg->numberOfTlvElements == CCHANNEL_MAX_NUMBER_OF_TLV_ELEMENTS) + { + return; + } + + ccmsg->tlvArray[ccmsg->numberOfTlvElements].type = ccmsg->payload[i++]; + ccmsg->tlvArray[ccmsg->numberOfTlvElements].length = ccmsg->payload[i++]; + ccmsg->tlvArray[ccmsg->numberOfTlvElements].value = (char *)&(ccmsg->payload[i]); + i += ccmsg->tlvArray[ccmsg->numberOfTlvElements].length; + ccmsg->numberOfTlvElements++; + } +} + +static void freeAllCCMsg(void) +{ + (void)memset(msgPool, 0, sizeof(msgPool)); + + for (uint32_t i = 0; i < MAX_NUMBER_OF_INCOMING_MESSAGE; i++) + { + msgPool[i].isFree = true; + } +} diff --git a/core/testing/source/Serial.c b/core/testing/source/Serial.c new file mode 100644 index 00000000..cf742c6b --- /dev/null +++ b/core/testing/source/Serial.c @@ -0,0 +1,206 @@ +/******************************************************************************* + * Copyright (c) 2010-2020 Robert Bosch GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Robert Bosch GmbH - initial contribution + * + ******************************************************************************/ + +/** + * @file + * + * @brief + * todo add brief description Implements the following functionalities specified in template.h + */ +#include "Kiso_Testing.h" +#include "Kiso_Testing_Config.h" +#include "Kiso_BSP_TestInterface.h" +#include "Kiso_MCU_UART.h" +#include "Kiso_GuardedTask.h" +#include "Kiso_RingBuffer.h" +#include "FreeRTOS.h" +#include "semphr.h" +#include "SerialMsgTransceiver.h" +#include "SerialCChannel.h" + +#undef KISO_MODULE_ID +#define KISO_MODULE_ID KISO_MODULE_ID_TESTING_SERIAL + +#define WAIT_TIME_FOR_SINGLE_UART_TRANSMISSION UINT32_C(500) + +#define MAX_BUFFER_SIZE UINT16_C(300) + +#ifndef SERIAL_TASK_PRIO +#define SERIAL_TASK_PRIO (1UL) +#endif /* SERiAL_TASK_PRIO */ + +#ifndef SERIAL_TASK_STACK_DEPTH +#define SERIAL_TASK_STACK_DEPTH (128UL) +#endif /* SERIAL_TASK_STACK_DEPTH */ + +static void uartEventsCallbackFunc(UART_T uart, struct MCU_UART_Event_S event); + +static GuardedTask_T serialGuardedTask; +volatile uint32_t serialReceivedCnt = 0; +static SemaphoreHandle_t TransmitDataSemaphoreHandle = NULL; +static RingBuffer_T serialRingBuffer; +static uint8_t serialBuffer[MAX_BUFFER_SIZE]; +static uint8_t RxBuffer; +static UART_T TestInterfaceUart; +#ifndef NDEBUG +volatile uint32_t TestUartErrorCount = 0; /* number of errors post mortem */ +#endif /* NDEBUG */ + +/* The description is defined at function declaration */ +Retcode_T Serial_Initialize(void) +{ + Retcode_T rc = RETCODE_OK; + + rc = BSP_TestInterface_Connect(); + if (RETCODE_OK == rc) + { + TestInterfaceUart = (UART_T)BSP_TestInterface_GetUARTHandle(); + if (NULL == TestInterfaceUart) + { + rc = RETCODE(RETCODE_SEVERITY_ERROR, RETCODE_NULL_POINTER); + } + } + + if (RETCODE_OK == rc) + { + rc = MCU_UART_Initialize(TestInterfaceUart, uartEventsCallbackFunc); + } + + if (RETCODE_OK == rc) + { + rc = BSP_TestInterface_Enable(); + } + + if (RETCODE_OK == rc) + { + rc = GuardedTask_Initialize(&serialGuardedTask, SerialMsgTransceiver_Receive, "SERIAL_TASK", SERIAL_TASK_PRIO, SERIAL_TASK_STACK_DEPTH); + } + + if (RETCODE_OK == rc) + { + TransmitDataSemaphoreHandle = xSemaphoreCreateBinary(); + if (NULL == TransmitDataSemaphoreHandle) + { + rc = RETCODE(RETCODE_SEVERITY_WARNING, (uint32_t)RETCODE_OUT_OF_RESOURCES); + } + } + + if (RETCODE_OK == rc) + { + RingBuffer_Initialize(&serialRingBuffer, serialBuffer, MAX_BUFFER_SIZE); + } + + if (RETCODE_OK == rc) + { + /* start the receive process, this will enable UART interrupts and trigger a callback on receive */ + rc = MCU_UART_Receive(TestInterfaceUart, &RxBuffer, 1UL); + } + + return (rc); +} + +Retcode_T Serial_Deinitialize(void) +{ + Retcode_T rc = MCU_UART_Deinitialize(TestInterfaceUart); + if (RETCODE_OK == rc) + { + rc = BSP_TestInterface_Disable(); + } + + if (RETCODE_OK == rc) + { + rc = BSP_TestInterface_Disconnect(); + } + + if (RETCODE_OK == rc) + { + rc = GuardedTask_Deinitialize(&serialGuardedTask); + } + return rc; +} + +Retcode_T Serial_Send(void *data, uint32_t len) +{ + Retcode_T retCode; + retCode = MCU_UART_Send(TestInterfaceUart, (uint8_t *)data, len); + + if (RETCODE_OK == retCode) + { + /* Waiting here for the transmit complete event */ + if (pdTRUE != xSemaphoreTake(TransmitDataSemaphoreHandle, (WAIT_TIME_FOR_SINGLE_UART_TRANSMISSION / portTICK_RATE_MS))) + { + retCode = RETCODE(RETCODE_SEVERITY_WARNING, (uint32_t)RETCODE_SEMAPHORE_ERROR); + } + } + + return (retCode); +} + +Retcode_T Serial_Receive(void *data, uint32_t len) +{ + uint32_t numberOfReadBytes = 0; + Retcode_T retcode = RETCODE_OK; + + numberOfReadBytes = RingBuffer_Read(&serialRingBuffer, (uint8_t *)data, len); + + if (numberOfReadBytes != len) + { + retcode = RETCODE(RETCODE_SEVERITY_ERROR, RETCODE_UNEXPECTED_BEHAVIOR); + } + + return retcode; +} + +static void uartEventsCallbackFunc(UART_T uart, struct MCU_UART_Event_S event) +{ + KISO_UNUSED(uart); /* not used in One-Byte-Mode */ + + Retcode_T retcode = RETCODE_OK; + + /* Signal the guarded task to indicate that the receive is complete */ + if (event.RxComplete) + { + serialReceivedCnt++; + + if (1UL == RingBuffer_Write(&serialRingBuffer, (uint8_t *)&RxBuffer, 1UL)) + { + (void)GuardedTask_SignalFromIsr(&serialGuardedTask); + } + } + else if (event.TxComplete) + { + portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE; + + if (pdTRUE == xSemaphoreGiveFromISR(TransmitDataSemaphoreHandle, &xHigherPriorityTaskWoken)) + { + portYIELD_FROM_ISR(xHigherPriorityTaskWoken); + } + else + { + /* ignore... semaphore has already been given */ + } + } + else if (event.RxError) + { + retcode = RETCODE(RETCODE_SEVERITY_ERROR, RETCODE_FAILURE); + } + else if (event.TxError) + { + retcode = RETCODE(RETCODE_SEVERITY_ERROR, RETCODE_FAILURE); + } + if (RETCODE_OK != retcode) + { + Retcode_RaiseErrorFromIsr(retcode); + } +} diff --git a/core/testing/source/SerialMsgTransceiver.c b/core/testing/source/SerialMsgTransceiver.c new file mode 100644 index 00000000..dbf5e887 --- /dev/null +++ b/core/testing/source/SerialMsgTransceiver.c @@ -0,0 +1,258 @@ +/******************************************************************************* + * Copyright (c) 2010-2020 Robert Bosch GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Robert Bosch GmbH - initial contribution + * + ******************************************************************************/ + +/** + * @file + * + * @brief + * Implements the following functionalities specified in template.h + */ +#include "Kiso_Testing.h" +#include "SerialMsgTransceiver.h" +#include "SerialCChannel.h" +#include "CChannel.h" +#include "Kiso_GuardedTask.h" +#include + +#undef KISO_MODULE_ID +#define KISO_MODULE_ID KISO_MODULE_ID_TESTING_SERIALMSGTRANSCEIVER + +#define PAYLOAD_LENGTH_INDEX 7 +#define RECEIVE_BUFFER_SIZE 256 + +#define START 0xC0 +#define ESC 0xDB +#define ESC_START 0xDC +#define ESC_ESC 0xDD + +#define CRC_DEFAULT UINT8_C(0) +#define CRC_BIT_SHIFT_4 UINT16_C(4) /** 4 bit shifting for CRC calculation */ +#define CRC_BIT_SHIFT_5 UINT16_C(5) /** 5 bit shifting for CRC calculation */ +#define CRC_BIT_SHIFT_8 UINT16_C(8) /** 8 bit shifting for CRC calculation */ +#define CRC_BIT_SHIFT_12 UINT16_C(12) /** 12 bit shifting for CRC calculation */ +#define CRC_INIT_VALUE UINT16_C(0) /** Default Initialize value for flag */ +#define CRC_BYTE_MASK UINT16_C(0xFF) + +enum Receiver_State_E +{ + WAITING_FOR_START, + RECEIVING_HEADER, + RECEIVING_PAYLOAD, + RECEIVED_DONE +}; + +static uint16_t calculateCRC(const void *buffer, uint8_t length); + +static uint8_t receiveBuffer[RECEIVE_BUFFER_SIZE]; +static uint32_t serialConsumedCnt = 0; /* number of characters received */ +static uint32_t msgCnt = 0; /* diagnostic: number of messages received */ +static uint32_t msgOK = 0; /* diagnostic: number of messages received */ +static uint32_t msgNOK = 0; /* diagnostic: number of messages received */ + +Retcode_T SerialMsgTransceiver_Send(uint8_t *message, uint8_t length) +{ + Retcode_T ReturnValue; + uint8_t sendBuffer[length * 2]; //todo variable length array dangerous + uint8_t j = 0; + + uint16_t crc = calculateCRC(message, length); + + sendBuffer[j++] = START; + + if (((crc >> CHAR_BIT) & CRC_BYTE_MASK) == START) + { + sendBuffer[j++] = ESC; + sendBuffer[j++] = ESC_START; + } + else if (((crc >> CHAR_BIT) & CRC_BYTE_MASK) == ESC) + { + sendBuffer[j++] = ESC; + sendBuffer[j++] = ESC_ESC; + } + else + { + sendBuffer[j++] = (crc >> CHAR_BIT) & CRC_BYTE_MASK; + } + + if ((crc & CRC_BYTE_MASK) == START) + { + sendBuffer[j++] = ESC; + sendBuffer[j++] = ESC_START; + } + else if ((crc & CRC_BYTE_MASK) == ESC) + { + sendBuffer[j++] = ESC; + sendBuffer[j++] = ESC_ESC; + } + else + { + sendBuffer[j++] = crc & CRC_BYTE_MASK; + } + + for (uint32_t i = 0; i < length; i++) + { + if (message[i] == START) + { + sendBuffer[j++] = ESC; + sendBuffer[j++] = ESC_START; + } + else if (message[i] == ESC) + { + sendBuffer[j++] = ESC; + sendBuffer[j++] = ESC_ESC; + } + else + { + sendBuffer[j++] = message[i]; + } + } + + ReturnValue = Serial_Send(sendBuffer, j); + + return (ReturnValue); +} + +/** @todo Verify total length, limited to 256! what to do if exceeded? + * The description is defined at function declaration */ +void SerialMsgTransceiver_Receive(void) +{ + static uint8_t receivingState = WAITING_FOR_START; + static uint8_t receivedByte; + static uint8_t nReceivedByte = 0; + static uint8_t payloadLength = 0; + static bool receivedEsc = false; + static uint8_t testCnt = 0; + + /* The serialReceivedCnt is incremented with every received byte in the ISR + The difference betweeb the serialReceivedCnt and serialConsumedCnt repflects the number of bytes available in the buffer + We consume characters while the difference is > 0 + There is no need to check overflows since both numbers are of uint32_t and the result will always state the difference. + */ + while ((serialReceivedCnt - serialConsumedCnt) > 0) + { + if (RETCODE_OK != Serial_Receive((uint8_t *)&receivedByte, UINT32_C(1))) + { + Retcode_RaiseError(RETCODE(RETCODE_SEVERITY_WARNING, RETCODE_FAILURE)); + return; + } + serialConsumedCnt++; + + if (WAITING_FOR_START == receivingState) + { + if (START == receivedByte) + { + nReceivedByte = 0; + (void)memset(receiveBuffer, 0, sizeof(receiveBuffer)); + receivingState = RECEIVING_HEADER; + } + } + else + { + if (START == receivedByte) + { + Retcode_RaiseError(RETCODE(RETCODE_SEVERITY_WARNING, RETCODE_TESTING_INCOMPLETE_MESSAGE_RECEIVED)); + nReceivedByte = 0; + (void)memset(receiveBuffer, 0, sizeof(receiveBuffer)); + msgNOK++; + receivingState = RECEIVING_HEADER; + } + else if (receivedEsc) + { + receivedEsc = false; + if (ESC_START == receivedByte) + { + receiveBuffer[nReceivedByte++] = START; + } + else if (ESC_ESC == receivedByte) + { + receiveBuffer[nReceivedByte++] = ESC; + } + else + { + // error should not happen + Retcode_RaiseError(RETCODE(RETCODE_SEVERITY_WARNING, RETCODE_INCONSITENT_STATE)); + receivingState = WAITING_FOR_START; + } + } + else if (ESC == receivedByte) + { + receivedEsc = true; + } + else + { + receiveBuffer[nReceivedByte++] = receivedByte; + } + + if (RECEIVING_HEADER == receivingState) + { + if (CCHANNEL_HEADER_LENGTH + 2 == nReceivedByte) + { + payloadLength = receiveBuffer[PAYLOAD_LENGTH_INDEX + 2]; + + if (0 == payloadLength) + { + receivingState = RECEIVED_DONE; + } + else + { + receivingState = RECEIVING_PAYLOAD; + } + } + } + else if (RECEIVING_PAYLOAD == receivingState) + { + if (payloadLength + CCHANNEL_HEADER_LENGTH + 2 == nReceivedByte) + { + receivingState = RECEIVED_DONE; + } + } + } + } + + if (RECEIVED_DONE == receivingState) + { + uint16_t calculatedCRC = calculateCRC(&receiveBuffer[2], CCHANNEL_HEADER_LENGTH + payloadLength); + uint16_t expectedCRC = ((receiveBuffer[0] & CRC_BYTE_MASK) << CHAR_BIT) + (receiveBuffer[1] & CRC_BYTE_MASK); + + msgCnt++; + if (calculatedCRC == expectedCRC) + { + CChannel_ReceiveEventHandler(&receiveBuffer[2], CCHANNEL_HEADER_LENGTH + payloadLength); + receivingState = WAITING_FOR_START; + msgOK++; + } + else + { + Retcode_RaiseError(RETCODE(RETCODE_SEVERITY_WARNING, RETCODE_TESTING_CRC_MISMATCH)); + receivingState = WAITING_FOR_START; + msgNOK++; + } + testCnt++; + } +} + +static uint16_t calculateCRC(const void *buffer, uint8_t length) +{ + uint16_t crc = CRC_DEFAULT; + + for (uint8_t i = 0; i < length; i++) + { + crc = (crc >> (CRC_BIT_SHIFT_8)) | (crc << (CRC_BIT_SHIFT_8)); + crc ^= ((const uint8_t *)buffer)[i]; + crc ^= (crc & (CRC_BYTE_MASK)) >> (CRC_BIT_SHIFT_4); + crc ^= crc << (CRC_BIT_SHIFT_12); + crc ^= (crc & (CRC_BYTE_MASK)) << (CRC_BIT_SHIFT_5); + } + return crc; +} diff --git a/core/testing/source/TestRegistry.c b/core/testing/source/TestRegistry.c new file mode 100644 index 00000000..e774bd7b --- /dev/null +++ b/core/testing/source/TestRegistry.c @@ -0,0 +1,325 @@ +/******************************************************************************* + * Copyright (c) 2010-2020 Robert Bosch GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Robert Bosch GmbH - initial contribution + * + ******************************************************************************/ + +/** + * @file + * + * @brief + */ +#include "Kiso_Testing.h" +#include "TestRegistry.h" + +#undef KISO_MODULE_ID +#define KISO_MODULE_ID KISO_MODULE_ID_TESTING_TESTREGISTRY + +static TstSte_T *lookupTestSuite(uint8_t sId); +static TstCse_T *lookupTestCase(TstSte_T *testSuite, uint8_t cId); + +static TstEnt_T testEntry; + +/* @see TestRegistry.h for function description */ +void TestRegistry_Initialize(uint8_t eId, SetupFct_T setup, TearDownFct_T teardown) +{ + (void)memset(&testEntry, 0, sizeof(TstEnt_T)); + + testEntry.id = eId; + testEntry.setup = setup; + testEntry.teardown = teardown; +} + +/* @see TestRegistry.h for function description */ +Retcode_T TestRegistry_RegisterTestSuite(uint8_t sId, SetupFct_T setup, TearDownFct_T teardown) +{ + if (TEST_MAX_NUMBER_OF_TEST_SUITES <= testEntry.numTestSuites) + { + return RETCODE(RETCODE_SEVERITY_ERROR, (uint32_t)RETCODE_OUT_OF_RESOURCES); + } + + TstSte_T *suite = lookupTestSuite(sId); + + if (NULL != suite) + { + /* The test suite with the given ID is already registered */ + return RETCODE(RETCODE_SEVERITY_WARNING, (uint32_t)RETCODE_TESTING_SUITE_ALREADY_REGISTERED); + } + + suite = &testEntry.testSuites[testEntry.numTestSuites]; + testEntry.numTestSuites++; + + suite->id = sId; + suite->setup = setup; + suite->teardown = teardown; + + return RETCODE_OK; +} + +/* @see TestRegistry.h for function description */ +Retcode_T TestRegistry_RegisterTestCase(uint8_t sId, uint8_t cId, SetupFct_T setup, RunFct_T run, TearDownFct_T teardown) +{ + if (NULL == run) + { + return RETCODE(RETCODE_SEVERITY_ERROR, (uint32_t)RETCODE_INVALID_PARAM); + } + + TstSte_T *suite = lookupTestSuite(sId); + + if (NULL == suite) + { + /* The suite is not found */ + return RETCODE(RETCODE_SEVERITY_WARNING, (uint32_t)RETCODE_INVALID_PARAM); + } + + if (TEST_MAX_NUMBER_OF_TEST_CASES_PER_TEST_SUITE <= suite->numTestCases) + { + return RETCODE(RETCODE_SEVERITY_ERROR, (uint32_t)RETCODE_OUT_OF_RESOURCES); + } + + TstCse_T *testCase = lookupTestCase(suite, cId); + + if (NULL != testCase) + { + /* A test case with the given ID is already registered */ + return RETCODE(RETCODE_SEVERITY_WARNING, (uint32_t)RETCODE_TESTING_CASE_ALREADY_REGISTERED); + } + + testCase = &suite->testCases[suite->numTestCases]; + suite->numTestCases++; + + testCase->id = cId; + testCase->setup = setup; + testCase->run = run; + testCase->teardown = teardown; + + return RETCODE_OK; +} + +/* @see TestRegistry.h for function description */ +TstEnt_T *TestRegistry_LookupTestEntry(uint8_t eId) +{ + TstEnt_T *theTestEntry = NULL; + + if (eId == testEntry.id) + { + theTestEntry = &testEntry; + } + + return theTestEntry; +} + +/* @see TestRegistry.h for function description */ +TstSte_T *TestRegistry_LookupTestSuite(uint8_t eId, uint8_t sId) +{ + TstSte_T *testSuite = NULL; + + if (eId == testEntry.id) + { + testSuite = lookupTestSuite(sId); + } + + return testSuite; +} + +/* @see TestRegistry.h for function description */ +TstCse_T *TestRegistry_LookupTestCase(uint8_t eId, uint8_t sId, uint8_t cId) +{ + TstSte_T *testSuite = TestRegistry_LookupTestSuite(eId, sId); + TstCse_T *testCase = NULL; + + if (NULL != testSuite) + { + testCase = lookupTestCase(testSuite, cId); + } + + return testCase; +} + +/* @see TestRegistry.h for function description */ +Retcode_T TestEntry_Setup(TstEnt_T *theTestEntry, CCMsg_T *ccmsg) +{ + + Retcode_T retcode = RETCODE_OK; + if ((NULL == theTestEntry) || (NULL == ccmsg)) + { + /* The suite is not found */ + retcode = RETCODE(RETCODE_SEVERITY_WARNING, (uint32_t)RETCODE_NULL_POINTER); + return (retcode); + } + + /* If test section setup pointer are null, it means that there is nothing to be done and that we can just send an + Acknowledgement with status OK.*/ + if (NULL != theTestEntry->setup) + { + retcode = theTestEntry->setup(ccmsg); + } + return retcode; +} + +/* @see TestRegistry.h for function description */ +Retcode_T TestEntry_Teardown(TstEnt_T *theTestEntry, CCMsg_T *ccmsg) +{ + + Retcode_T retcode = RETCODE_OK; + + if ((NULL == theTestEntry) || (NULL == ccmsg)) + { + /* The suite is not found */ + retcode = RETCODE(RETCODE_SEVERITY_WARNING, (uint32_t)RETCODE_NULL_POINTER); + return (retcode); + } + + /* + If test section teardown pointer are null, it means that there is nothing to be done and that we can just send an + Acknowledgement with status OK.*/ + if (NULL != theTestEntry->teardown) + { + retcode = theTestEntry->teardown(ccmsg); + } + return retcode; +} + +/* @see TestRegistry.h for function description */ +Retcode_T TestSuite_Setup(TstSte_T *testSuite, CCMsg_T *ccmsg) +{ + + Retcode_T retcode = RETCODE_OK; + + if ((NULL == testSuite) || (NULL == ccmsg)) + { + /* The suite is not found */ + retcode = RETCODE(RETCODE_SEVERITY_WARNING, (uint32_t)RETCODE_NULL_POINTER); + return (retcode); + } + /* If test suite setup pointer are null, it means that there is nothing to be done and that we can just send an + Acknowledgement with status OK.*/ + if (NULL != testSuite->setup) + { + retcode = testSuite->setup(ccmsg); + } + + return retcode; +} + +/* @see TestRegistry.h for function description */ +Retcode_T TestSuite_Teardown(TstSte_T *testSuite, CCMsg_T *ccmsg) +{ + Retcode_T retcode = RETCODE_OK; + if ((NULL == testSuite) || (NULL == ccmsg)) + { + /* The suite is not found */ + retcode = RETCODE(RETCODE_SEVERITY_WARNING, (uint32_t)RETCODE_NULL_POINTER); + return (retcode); + } + /* If test suite teardown pointer are null, it means that there is nothing to be done and that we can just send an + Acknowledgement with status OK.*/ + if (NULL != testSuite->teardown) + { + retcode = testSuite->teardown(ccmsg); + } + + return retcode; +} + +/* @see TestRegistry.h for function description */ +Retcode_T TestCase_Setup(TstCse_T *testCase, CCMsg_T *ccmsg) +{ + + Retcode_T retcode = RETCODE_OK; + + if ((NULL == testCase) || (NULL == ccmsg)) + { + /* The suite is not found */ + retcode = RETCODE(RETCODE_SEVERITY_WARNING, (uint32_t)RETCODE_NULL_POINTER); + return (retcode); + } + if (NULL != testCase->setup) + { + retcode = testCase->setup(ccmsg); + } + + return retcode; +} + +/* @see TestRegistry.h for function description */ +void TestCase_Run(TstCse_T *testCase, CCMsg_T *ccmsg) +{ + + if ((NULL == testCase) || (NULL == ccmsg) || (NULL == testCase->run)) + { + Retcode_RaiseError(RETCODE(RETCODE_SEVERITY_ERROR, (uint32_t)RETCODE_NULL_POINTER)); + } + else + { + testCase->run(ccmsg); + } +} + +/* @see TestRegistry.h for function description */ +Retcode_T TestCase_Teardown(TstCse_T *testCase, CCMsg_T *ccmsg) +{ + + Retcode_T retcode = RETCODE_OK; + + if ((NULL == testCase) || (NULL == ccmsg)) + { + /* The suite is not found */ + retcode = RETCODE(RETCODE_SEVERITY_WARNING, (uint32_t)RETCODE_NULL_POINTER); + } + else + { + if (NULL != testCase->teardown) + { + retcode = testCase->teardown(ccmsg); + } + } + + return retcode; +} + +/** + * @brief finds a test suite by suite Id in the test suites registry. +*/ +static TstSte_T *lookupTestSuite(uint8_t sId) +{ + TstSte_T *testSuite = NULL; + + for (uint32_t i = 0; i < testEntry.numTestSuites; i++) + { + if (sId == testEntry.testSuites[i].id) + { + testSuite = &testEntry.testSuites[i]; + break; + } + } + + return testSuite; +} + +/** + * @brief finds a test case by case Id in the test cases registry of the referenced test suite . +*/ +static TstCse_T *lookupTestCase(TstSte_T *testSuite, uint8_t cId) +{ + TstCse_T *testCase = NULL; + + for (uint32_t i = 0; i < testSuite->numTestCases; i++) + { + if (cId == testSuite->testCases[i].id) + { + testCase = &testSuite->testCases[i]; + break; + } + } + + return testCase; +} diff --git a/core/testing/source/TestRunner.c b/core/testing/source/TestRunner.c new file mode 100644 index 00000000..fe09b301 --- /dev/null +++ b/core/testing/source/TestRunner.c @@ -0,0 +1,363 @@ +/******************************************************************************* + * Copyright (c) 2010-2020 Robert Bosch GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Robert Bosch GmbH - initial contribution + * + ******************************************************************************/ + +/** + * @file + * + * @brief + * The TestRunner dispatches the commands to the test sections and test suites and processes the acknowledgments + * received from the test controller. + */ +#include "Kiso_Testing.h" +#include "TestRunner.h" +#include "Kiso_CmdProcessor.h" +#include "CChannel.h" +#include "TestRegistry.h" +#include "FreeRTOS.h" +#include "timers.h" + +#undef KISO_MODULE_ID +#define KISO_MODULE_ID KISO_MODULE_ID_TESTING_TESTRUNNER + +#define TEST_ENTRY_SETUP 1 +#define TEST_SUITE_SETUP 2 +#define TEST_CASE_SETUP 3 +#define TEST_CASE_RUN 13 +#define TEST_ENTRY_TEARDOWN 21 +#define TEST_SUITE_TEARDOWN 22 +#define TEST_CASE_TEARDOWN 23 + +#define RUNNER_TIME_TO_WAIT_FOR_ACK_MS (UINT8_C(3000)) + +/** Structure containing the handler for the Test Runner */ +struct TestRunner_S +{ + uint8_t id; /**< Message token corresponding to the message type */ + uint8_t numberOfRetries; + CmdProcessor_T cmdProcessor; + TimerHandle_t timer; + uint8_t lastReceivedMessageToken; + bool waitingForAck; +}; + +static void dispatcher(CCMsg_T *ccmsg, uint32_t unusedParameter); +static void processAck(CCMsg_T *ccmsg); +static void processCommand(CCMsg_T *ccmsg); +static void ackTimerCallbackFunction(TimerHandle_t timer); +static bool isMsgAnAck(CCMsg_T *ccmsg); +static bool isMsgACommand(CCMsg_T *ccmsg); + +static struct TestRunner_S testRunner; + +/* @see TestRunner.h for function description */ +Retcode_T TestRunner_Initialize(void) +{ + Retcode_T retcode = RETCODE_OK; + testRunner.id = 0U; + + retcode = CmdProcessor_Initialize(&testRunner.cmdProcessor, + "Test Runner", + TEST_RUNNER_TASK_PRIO, + TEST_RUNNER_TASK_STACK_DEPTH, + TEST_RUNNER_QUEUE_SIZE); + + if (RETCODE_OK == retcode) + { + testRunner.timer = xTimerCreate((const char *const) "Test Runner Timer", + RUNNER_TIME_TO_WAIT_FOR_ACK_MS, + pdFALSE, + NULL, + ackTimerCallbackFunction); + if (NULL == testRunner.timer) + { + retcode = RETCODE(RETCODE_SEVERITY_ERROR, RETCODE_OUT_OF_RESOURCES); + } + } + if (RETCODE_OK == retcode) + { + retcode = CChannel_Initialize(); + } + return retcode; +} + +/* @see TestRunner.h for function description */ +void TestRunner_ProcessMessage(CCMsg_T *ccmsg) +{ + Retcode_T retcode = CmdProcessor_Enqueue(&testRunner.cmdProcessor, + (CmdProcessor_Func_T)dispatcher, + ccmsg, + 0); + + if (RETCODE_OK != retcode) + { + Retcode_RaiseError(retcode); + } +} + +/* @see TestRunner.h for function description */ +void TestRunner_SendReport(uint8_t result, char *reason) +{ + Retcode_T retcode = RETCODE_OK; + + retcode = CChannel_SendReport(result, reason); + + if (RETCODE_OK == retcode) + { + if (pdFAIL == xTimerStart(testRunner.timer, RUNNER_TIME_TO_WAIT_FOR_ACK_MS)) + { + Retcode_RaiseError(RETCODE(RETCODE_SEVERITY_ERROR, RETCODE_OUT_OF_RESOURCES)); + } + testRunner.numberOfRetries = 3; + testRunner.waitingForAck = true; + } + else + { + Retcode_RaiseError(retcode); + } +} + +static void dispatcher(CCMsg_T *ccmsg, uint32_t unusedParameter) +{ + KISO_UNUSED(unusedParameter); + + if (isMsgAnAck(ccmsg)) + { + processAck(ccmsg); + } + else if (isMsgACommand(ccmsg)) + { + processCommand(ccmsg); + } + else + { + //@todo handle the fact that the type is not supported + // CChannel_PrepareAck(ccmsg); + // CChannel_SendAck(RETCODE_OK); + } + + CChannel_FreeCCMsg(ccmsg); +} + +static void processAck(CCMsg_T *ccmsg) +{ + if (CChannel_DoesAckMatchReport(ccmsg)) + { + + if (pdFAIL == xTimerStop(testRunner.timer, 0)) + { + //@todo raise some error? + } + + testRunner.waitingForAck = false; + testRunner.numberOfRetries = 0; + } + else + { + //@todo do we just ignore it? or raise an error? + } +} + +static void processCommand(CCMsg_T *ccmsg) +{ + Retcode_T retcode = RETCODE(RETCODE_SEVERITY_ERROR, RETCODE_FAILURE); + + if (testRunner.waitingForAck) + { + /** + * this means the Test Coordinator did receive the Report but the ack was lost + * so we can just stop the timer and continue + */ + if (true != xTimerStop(testRunner.timer, 0)) + { + //@todo raise some error? + } + testRunner.waitingForAck = false; + testRunner.numberOfRetries = 0; + } + if (testRunner.lastReceivedMessageToken == ccmsg->header.messageToken) + { + /** + * if we receive again a command, we shouldn't do the associated action + * we should however resend the ack. + */ + retcode = CChannel_ResendAck(); + + if (RETCODE_OK != retcode) + { + Retcode_RaiseError(retcode); + } + return; + } + + uint8_t result = RETCODE_OK; + + TstEnt_T *testEntry = TestRegistry_LookupTestEntry(ccmsg->header.testEntry); + TstSte_T *testSuite = TestRegistry_LookupTestSuite(ccmsg->header.testEntry, ccmsg->header.testSuite); + TstCse_T *testCase = TestRegistry_LookupTestCase(ccmsg->header.testEntry, + ccmsg->header.testSuite, ccmsg->header.testCase); + + testRunner.lastReceivedMessageToken = ccmsg->header.messageToken; + + CChannel_PrepareAck(ccmsg); + + switch (ccmsg->header.messageType) + { + case TEST_ENTRY_SETUP: + if (NULL != testEntry) + { + retcode = TestEntry_Setup(testEntry, ccmsg); + } + else + { + /* @todo: The error code in Retcode_T occupies 16 bits. But, the argument type passed to CChannel_SendAck() is uint8_t. + * The test coordination protocol has an error code of 8 bits. This is a problem, as we would end up in overflow + * and due to the unsigned nature of the variable, we would wrap around. This needs to be fixed. */ + result = (uint8_t)RETCODE_INVALID_PARAM; + } + break; + case TEST_SUITE_SETUP: + if (NULL != testSuite) + { + retcode = TestSuite_Setup(testSuite, ccmsg); + } + else + { + result = (uint8_t)RETCODE_INVALID_PARAM; + } + break; + case TEST_CASE_SETUP: + if (NULL != testCase) + { + retcode = TestCase_Setup(testCase, ccmsg); + } + else + { + result = (uint8_t)RETCODE_INVALID_PARAM; + } + break; + case TEST_CASE_RUN: + if (NULL != testCase) + { + retcode = CChannel_SendAck(RETCODE_OK); + + if (RETCODE_OK != retcode) + { + Retcode_RaiseError(retcode); + } + else + { + CChannel_PrepareReport(ccmsg); + + TestCase_Run(testCase, ccmsg); + } + return; + } + else + { + result = (uint8_t)RETCODE_INVALID_PARAM; + } + break; + case TEST_ENTRY_TEARDOWN: + if (NULL != testEntry) + { + retcode = TestEntry_Teardown(testEntry, ccmsg); + } + else + { + result = (uint8_t)RETCODE_INVALID_PARAM; + } + break; + case TEST_SUITE_TEARDOWN: + if (NULL != testSuite) + { + retcode = TestSuite_Teardown(testSuite, ccmsg); + } + else + { + result = (uint8_t)RETCODE_INVALID_PARAM; + } + break; + case TEST_CASE_TEARDOWN: + if (NULL != testCase) + { + retcode = TestCase_Teardown(testCase, ccmsg); + } + else + { + result = (uint8_t)RETCODE_INVALID_PARAM; + } + break; + default: + result = (uint8_t)RETCODE_INVALID_PARAM; + break; + } + + if ((uint8_t)RETCODE_INVALID_PARAM != result) + { + result = Retcode_GetCode(retcode); + } + + retcode = CChannel_SendAck(result); + + if (RETCODE_OK != retcode) + { + Retcode_RaiseError(retcode); + } +} + +static void ackTimerCallbackFunction(TimerHandle_t timer) +{ + KISO_UNUSED(timer); + Retcode_T retcode = RETCODE_OK; + + if (testRunner.numberOfRetries > 0) + { + testRunner.numberOfRetries--; + + retcode = CChannel_ResendReport(); + + if (RETCODE_OK == retcode) + { + if (true != xTimerStart(testRunner.timer, RUNNER_TIME_TO_WAIT_FOR_ACK_MS)) + { + Retcode_RaiseError(RETCODE(RETCODE_SEVERITY_ERROR, (uint32_t)RETCODE_OUT_OF_RESOURCES)); + } + } + else + { + Retcode_RaiseError(retcode); + } + } + else + { + testRunner.waitingForAck = false; + Retcode_RaiseError(RETCODE(RETCODE_SEVERITY_WARNING, RETCODE_TESTING_REPORT_TIMEOUT)); + } +} + +/** + * @brief checks the type of the message and returns true if it is an acknowledgement message. + */ +static bool isMsgAnAck(CCMsg_T *ccmsg) +{ + return CCMSG_GET_TYPE(ccmsg) == CCHANNEL_MSG_TYPE_ACK; +} + +/** + * @brief checks the type of the message and returns true if it is a command message. + */ +static bool isMsgACommand(CCMsg_T *ccmsg) +{ + return CCMSG_GET_TYPE(ccmsg) == CCHANNEL_MSG_TYPE_COMMAND; +} diff --git a/core/testing/source/Testing.c b/core/testing/source/Testing.c new file mode 100644 index 00000000..969e0043 --- /dev/null +++ b/core/testing/source/Testing.c @@ -0,0 +1,59 @@ +/******************************************************************************* + * Copyright (c) 2010-2020 Robert Bosch GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Robert Bosch GmbH - initial contribution + * + ******************************************************************************/ + +/** + * @file + * + * @brief + * This file implements the promised API functions in Kiso_Testing.h with help of the components TestRunner TestRegistry + * and CChannel. + */ +#include "Kiso_Testing.h" +#include "TestRegistry.h" +#include "TestRunner.h" +#include "CChannel.h" + +#undef KISO_MODULE_ID +#define KISO_MODULE_ID KISO_MODULE_ID_TESTING + +/* @see Kiso_Testing.h for function description */ +Retcode_T Tests_Initialize(uint8_t eId, SetupFct_T setup, TearDownFct_T teardown) +{ + TestRegistry_Initialize(eId, setup, teardown); + return TestRunner_Initialize(); +} + +/* @see Kiso_Testing.h for function description */ +Retcode_T Tests_RegisterTestSuite(uint8_t sId, SetupFct_T setup, TearDownFct_T teardown) +{ + return TestRegistry_RegisterTestSuite(sId, setup, teardown); +} + +/* @see Kiso_Testing.h for function description */ +Retcode_T Tests_RegisterTestCase(uint8_t sId, uint8_t cId, SetupFct_T setup, RunFct_T run, TearDownFct_T teardown) +{ + return TestRegistry_RegisterTestCase(sId, cId, setup, run, teardown); +} + +/* @see Kiso_Testing.h for function description */ +void Tests_SendReport(uint8_t result, char *reason) +{ + TestRunner_SendReport(result, reason); +} + +/* @see Kiso_Testing.h for function description */ +Retcode_T Tests_GetTlvElement(CCMsg_T *ccmsg, TlvElt_T *tlvElement) +{ + return CChannel_GetTlvElement(ccmsg, tlvElement); +} diff --git a/core/testing/source/protected/CChannel.h b/core/testing/source/protected/CChannel.h new file mode 100644 index 00000000..6cf75d09 --- /dev/null +++ b/core/testing/source/protected/CChannel.h @@ -0,0 +1,160 @@ +/******************************************************************************* + * Copyright (c) 2010-2020 Robert Bosch GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Robert Bosch GmbH - initial contribution + * + ******************************************************************************/ + +/** + * @file + * @defgroup + * @ingroup + * @{ + * + * @brief communication channel handling APIs. + * @details + * This file is part of the test executor software component in the integration testing framework. it provides + * internally used API functions managing the communication protocol between the test executor and the test controller. + * + */ + +#ifndef KISO_CCHANNEL_H_ +#define KISO_CCHANNEL_H_ + +#include "Kiso_Testing.h" + +#define CCMSG_VERSION 1 + +#define CCMSG_TYPE_MASK 0x30 +#define CCMSG_VERSION_MASK 0xC0 +#define CCMSG_VERSION_SHIFT (6) + +#define CCMSG_GET_VERSION(ccmsg) (((ccmsg->header.messageInfo & CCMSG_VERSION_MASK) >> 6) & 0x03) +#define CCMSG_GET_TYPE(ccmsg) (((ccmsg->header.messageInfo & CCMSG_TYPE_MASK) >> 4) & 0x03) + +#define CCMSG_CREATE_TYPE(type) ((((type << 4) & 0x30) + ((CCMSG_VERSION << 6) & CCMSG_VERSION_MASK)) & 0xFF) + +#define CCHANNEL_MSG_TYPE_COMMAND 0 +#define CCHANNEL_MSG_TYPE_REPORT 1 +#define CCHANNEL_MSG_TYPE_ACK 2 + +#define CCHANNEL_MESSAGE_MAX_LENGTH 256 +#define CCHANNEL_HEADER_LENGTH 8 +#define CCHANNEL_PAYLOAD_MAX_SIZE CCHANNEL_MESSAGE_MAX_LENGTH - CCHANNEL_HEADER_LENGTH + +#define NO_REBOOT 0 + +/** + * @brief Initializes the CChannel + * + * @return RETCODE_OK in case of success error code otherwise. + */ +Retcode_T CChannel_Initialize(void); + +/** + * @brief Frees the message after having been processed + * + * @param[in] ccmsg The reference to the message to free + */ +void CChannel_FreeCCMsg(CCMsg_T *ccmsg); + +/** + * @brief Prepares the Ack corresponding to the message + * + * @param[in] ccmsg The reference to the message we are preparing the ack for + */ +void CChannel_PrepareAck(CCMsg_T *ccmsg); + +/** + * @brief Sends the ack previously prepared + * + * @param[in] result error code to set in the ack before sending it + * + * @retcode RETCODE_OK (=0) on success. Any other value means a failure + */ +Retcode_T CChannel_SendAck(uint8_t result); + +/** + * @brief Resends the last ack. This is used in case we receive twice the same command + * + * @return RETCODE_OK on success. Any other value means a failure + */ +Retcode_T CChannel_ResendAck(void); + +/** + * @brief Prepares the report corresponding to the message + * + * @param[in] ccmsg The message we are preparing the report for + */ +void CChannel_PrepareReport(CCMsg_T *ccmsg); + +/** + * @brief Sends a Report. Function to be called within the run function implementation + * + * @param[in] result The test result code (0: success / otherwise: failure) + * @param[in] reason 0-terminating string stating a reason. It can be NULL, if no reason should be sent. + * + * @reurn RETCODE_OK on success. Any other value means a failure + */ +Retcode_T CChannel_SendReport(uint8_t result, char *reason); + +/** + * @brief Resends the last report. This is used in case we did not receive an ack for our report + * + * @return RETCODE_OK on success. Any other value means a failure + */ +Retcode_T CChannel_ResendReport(void); + +/** + * @brief Gets a tlv element using the type of the tlvElement input + * + * @param[in] ccmsg - message in which to look for the element + * @param[in, out] tlvElement - to store the element if found + * tlvElement->type used as input to find the element's value + * + * @return RETCODE_OK if found the TLV element, error code otherwise. + */ +Retcode_T CChannel_GetTlvElement(CCMsg_T *ccmsg, TlvElt_T *tlvElement); + +/** + * @brief Checks if the ack we received corresponds to the report we just sent + * + * @param[in] ccack ack message we received + * + * @return true if match, false if no match + */ +bool CChannel_DoesAckMatchReport(CCMsg_T *ccack); + +/** + * @brief Stops the CChannel + * + * @return RETCODE_OK on success. Any other value means a failure + */ +Retcode_T CChannel_Deinitialize(void); + +/** + * @brief Method triggered when a new message in received + * + * @param[in] buffer pointer to the message as a raw buffer + * @param[in] length length of the received message + */ +void CChannel_ReceiveEventHandler(uint8_t *buffer, uint8_t length); + +/** + * @brief Adds the tlv Element to the message + * + * @param[in] ccmsg Message to add the element to + * @param[in] tlvElement TLV element to add + * + * @return RETCODE_OK if successful error code otherwise + */ +Retcode_T CChannel_AddTlvElement(CCMsg_T *ccmsg, TlvElt_T *tlvElement); + +#endif /* KISO_CCHANNEL_H_ */ diff --git a/core/testing/source/protected/SerialCChannel.h b/core/testing/source/protected/SerialCChannel.h new file mode 100644 index 00000000..f4746e44 --- /dev/null +++ b/core/testing/source/protected/SerialCChannel.h @@ -0,0 +1,67 @@ +/******************************************************************************* + * Copyright (c) 2010-2020 Robert Bosch GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Robert Bosch GmbH - initial contribution + * + ******************************************************************************/ + +/** + * @file + * @defgroup + * @ingroup + * @{ + * + * @brief Serial Communication Channel Interface + * @details This header provides APIs to control uart communication line if this one is used as a communication + * channel between the Test executor and the test controller. + */ + +#ifndef SERIALCCHANNEL_H_ +#define SERIALCCHANNEL_H_ + +#include "Kiso_Retcode.h" + +/** + * @brief Initializes the serial interface + * + * @return RETCODE_OK if Initialized successfully error code otherwise. + */ +Retcode_T Serial_Initialize(void); + +/** + * @brief Deinitializes the serial interface + * + * @return RETCODE_OK if Deinitialized successfully error code otherwise. + */ +Retcode_T Serial_Deinitialize(void); + +/** + * @brief Receives data from the serial interface + * + * @param[in] data: A pointer to a data buffer. + * @param[in] len: Number of bytes to be received. + * + * @return RETCODE_OK if Receive successful error code otherwise. + */ +Retcode_T Serial_Receive(void *data, uint32_t len); + +/** + * @brief Sends data to the serial interface + * + * @param[in] data: A pointer to a data buffer. + * @param[in] len: Number of bytes to be sent. + * + * @return RETCODE_OK if Send successful error code otherwise + */ +Retcode_T Serial_Send(void *data, uint32_t len); + +extern volatile uint32_t serialReceivedCnt; + +#endif /* SERIALCCHANNEL_H_ */ diff --git a/core/testing/source/protected/SerialMsgTransceiver.h b/core/testing/source/protected/SerialMsgTransceiver.h new file mode 100644 index 00000000..9aa5fe8c --- /dev/null +++ b/core/testing/source/protected/SerialMsgTransceiver.h @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (c) 2010-2020 Robert Bosch GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Robert Bosch GmbH - initial contribution + * + ******************************************************************************/ + +/** + * @file + * @defgroup + * @ingroup + * @{ + * + * @brief Provides APIs for sending and receiving over serial line + * + */ + +#ifndef SERIALMSGTRANSCEIVER_H_ +#define SERIALMSGTRANSCEIVER_H_ + +#include "Kiso_Retcode.h" + +/** + * @brief Receive a message parses it and forwards it to CChannel for further processing + */ +void SerialMsgTransceiver_Receive(void); + +/** + * @brief Sends a message through the serial interface. + * @details This can be also used for building and sending the messages via other interfaces + * + * @param[in] message A reference to the message buffer to be sent. + * @param[in] length The Length of the message to be sent. + * + * @retval RETCODE_OK if the message transmitted successfully, error code otherwise. + */ +Retcode_T SerialMsgTransceiver_Send(uint8_t *message, uint8_t length); + +#endif /* SERIALMSGTRANSCEIVER_H_ */ + +/** @} */ diff --git a/core/testing/source/protected/TestRegistry.h b/core/testing/source/protected/TestRegistry.h new file mode 100644 index 00000000..ed6def61 --- /dev/null +++ b/core/testing/source/protected/TestRegistry.h @@ -0,0 +1,188 @@ +/******************************************************************************* + * Copyright (c) 2010-2020 Robert Bosch GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Robert Bosch GmbH - initial contribution + * + ******************************************************************************/ + +/** + * @file + * @defgroup + * @ingroup + * @{ + * + * @brief todo mak write brief here + * + */ + +#ifndef TESTREGISTRY_H_ +#define TESTREGISTRY_H_ + +#include "Kiso_Testing.h" + +#ifndef TEST_MAX_NUMBER_OF_TEST_CASES_PER_TEST_SUITE +#warning "config TEST_MAX_NUMBER_OF_TEST_CASES_PER_TEST_SUITE not set. The software will build with default value 1" +#define TEST_MAX_NUMBER_OF_TEST_CASES_PER_TEST_SUITE 1 +#endif + +/** + * @brief Structure for the test case which contains the function pointers for setup, run and tear down. + */ +typedef struct TestCase_S +{ + uint8_t id; + SetupFct_T setup; + RunFct_T run; + TearDownFct_T teardown; +} TstCse_T, TstCse_T; + +/** + * @brief Structure for the test suite which contains the function pointers for setup and tear down. + * It also contains the test case structure with the number of test cases. + */ +typedef struct TestSuite_S +{ + uint8_t id; + uint8_t numTestCases; + SetupFct_T setup; + TearDownFct_T teardown; + TstCse_T testCases[TEST_MAX_NUMBER_OF_TEST_CASES_PER_TEST_SUITE]; +} TstSte_T; + +typedef struct TestEntry_S +{ + uint8_t id; + uint8_t numTestSuites; + SetupFct_T setup; + TearDownFct_T teardown; + TstSte_T testSuites[TEST_MAX_NUMBER_OF_TEST_SUITES]; +} TstEnt_T, TstEnt_T; + +/** + * @brief Initializes the Test Registry. + * @details This function Initializes the Test Registry by filling it with the Test Entry, Test Suites and Test Case arrays. + * @param[in] eId The Identifier of the Test Entry + * @param[in] setup A reference to the setup function of the Test Entry. + * @param[in] teardown A reference to the tear down function of the Test Entry. + * @note setup and tear down functions pointers can be null if nothing has to be done. + */ +void TestRegistry_Initialize(uint8_t eId, SetupFct_T setup, TearDownFct_T teardown); + +/** + * @brief Registers a Test Suite + * @param[in] sId The Identifier of the Test Suite to register + * @param[in] setup A reference to the setup function of the Test Suite + * @param[in] teardown A reference to the tear down function of the Test Suite + * @note setup and tear down functions pointers can be null if nothing has to be done. + * @return RETCODE_OK if the Test Suite registered successfully. + * @return RETCODE_OUT_OF_RESOURCES if Maximum number of Test Suites reached. + * @return RETCODE_TESTING_SUITE_ALREADY_REGISTERED if a test suite with this Id has already been registered. + */ +Retcode_T TestRegistry_RegisterTestSuite(uint8_t sId, SetupFct_T setup, TearDownFct_T teardown); + +/** + * @brief Registers a Test Case + * @param[in] sId Id of the Test Suite of the test to register + * @param[in] cId Id of the Test Case to register + * @param[in] setup A reference to the setup function of the Test Suite + * @param[in] run A reference to the run function of the Test Suite + * @param[in] teardown function pointer to the tear down function of the Test Suite + * @note setup and tear down functions pointers can be null if nothing has to be done. + * @note run method MUST NOT be NULL! + * @return RETCODE_OK The Test Case registered successfully + * @return RETCODE_INVALID_PARAM No Test Suite with the given Id has been found + * @return RETCODE_OUT_OF_RESOURCES Maximum number of Test Cases reached + * @return RETCODE_TESTING_CASE_ALREADY_REGISTERED A Test Case with this Id has already been registered + */ +Retcode_T TestRegistry_RegisterTestCase(uint8_t sId, uint8_t cId, SetupFct_T setup, RunFct_T run, TearDownFct_T teardown); + +/** + * @brief Looks up for a Test Entry by Id + * @param[in] eId Id of the Test Entry to look for. + * @return Reference to the Test Entry on SUCCESS NULL if not found + */ +TstEnt_T *TestRegistry_LookupTestEntry(uint8_t eId); + +/** + * @brief Looks up for a Test Suite + * + * @param[in] eId The identifier of the Test Suite the test to register + * @param[in] sId The identifier of the Test Suite the test to register + * @return Test Suite pointer on SUCCESS NULL if not found. + */ +TstSte_T *TestRegistry_LookupTestSuite(uint8_t eId, uint8_t sId); + +/** + * @brief Looks up for a Test Case + * + * @param[in] eId The identifier of the test entry register to look from + * @param[in] sId The identifier of the test suite register to look from + * @param[in] cId Id of the Test Case to look for. + * @return Test Case pointer on SUCCESS, NULL if not found + */ +TstCse_T *TestRegistry_LookupTestCase(uint8_t eId, uint8_t sId, uint8_t cId); + +/** + * @brief Calls the setup function of the Test Entry + * @param[in] testEntry Test Entry which setup function should be called + * @param[in] ccmsg Message to pass to the Test Entry setup function. + * @return Retcode of the Setup function to use for the Acknowledgment message + */ +Retcode_T TestEntry_Setup(TstEnt_T *testEntry, CCMsg_T *ccmsg); + +/** + * @brief Calls the teardown function of the Test Entry + * @param[in] testEntry Test Entry which teardown function should be called + * @param[in] ccmsg Message to pass to the Test Entry teardown function. + * @return Retcode of the Teardown function to use for the Acknowledgment message. + */ +Retcode_T TestEntry_Teardown(TstEnt_T *testEntry, CCMsg_T *ccmsg); + +/** + * @brief Calls the setup function of the Test Suite + * @param[in] testSuite Test Suite which setup function should be called + * @param[in] ccmsg Message to pass to the Test Suite setup function. * + * @return Retcode of the setup function to use for the Acknowledgment message + */ +Retcode_T TestSuite_Setup(TstSte_T *testSuite, CCMsg_T *ccmsg); + +/** + * @brief Calls the teardown function of the Test Suite + * @param[in] testSuite Test Suite which teardown function should be called + * @param[in] ccmsg Message to pass to the Test Suite teardown function. + * @return Retcode of the Teardown function to use for the Acknowledgment message + */ +Retcode_T TestSuite_Teardown(TstSte_T *testSuite, CCMsg_T *ccmsg); + +/** + * @brief Calls the setup function of the Test Case + * @param[in] testCase Test Case which setup function should be called + * @param[in] ccmsg Message to pass to the Test Case setup function. + * @return Retcode of the setup function to use for the Acknowledgment message + */ +Retcode_T TestCase_Setup(TstCse_T *testCase, CCMsg_T *ccmsg); + +/** + * @brief Calls the run function of the Test Case + * @param[in] testCase Test Case which run function should be called + * @param[in] ccmsg Message to pass to the Test Case run function. + */ +void TestCase_Run(TstCse_T *testCase, CCMsg_T *ccmsg); + +/** + * @brief Calls the teardown function of the Test Case + * + * @param[in] testCase Test Case which teardown function should be called + * @param[in] ccmsg Message to pass to the Test Case teardown function. + * @return Retcode of the Teardown function to use for the Acknowledgment message + */ +Retcode_T TestCase_Teardown(TstCse_T *testCase, CCMsg_T *ccmsg); + +#endif /* TESTREGISTRY_H_ */ diff --git a/core/testing/source/protected/TestRunner.h b/core/testing/source/protected/TestRunner.h new file mode 100644 index 00000000..32544f38 --- /dev/null +++ b/core/testing/source/protected/TestRunner.h @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2010-2020 Robert Bosch GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Robert Bosch GmbH - initial contribution + * + ******************************************************************************/ + +/** + * @file + * @defgroup + * @ingroup + * @{ + * + * @brief todo mak provide description of the module. + * + */ + +#ifndef TESTRUNNER_H_ +#define TESTRUNNER_H_ + +#include "Kiso_Testing.h" +#include "Kiso_Retcode.h" + +/** + * @brief Initializes the Test Runner + * @retval RETCODE_OK in case of success, error code otherwise. + */ +Retcode_T TestRunner_Initialize(void); + +/** + * @brief Processes an incoming message + * @param[in] ccmsg incoming message to process + */ +void TestRunner_ProcessMessage(CCMsg_T *ccmsg); + +/** + * @brief Sends a Report. + * + * @param[in] result test result code (0: success / otherwise: failure) + * @param[in] reason 0-terminating string stating a reason. It can be NULL, if no reason should be sent. + */ +void TestRunner_SendReport(uint8_t result, char *reason); + +#endif /* TESTRUNNER_H_ */ +/** @} */ diff --git a/docs/website/content/Concepts/integration_testing.md b/docs/website/content/Concepts/integration_testing.md new file mode 100644 index 00000000..fb95982a --- /dev/null +++ b/docs/website/content/Concepts/integration_testing.md @@ -0,0 +1,42 @@ +--- +title: "Integration Testing Guide" +weight: 2 +--- + +## Definition + +Integration testing is a form of software testing which combines multiple (sub-)modules of a system into one test environment and verifies the compatibility between the selected system subset. + +## Realization + +In Kiso, integration testing is done **on-target**. This is realized through a special test-app which is flashed onto reference or user hardware and listens to the instructions of a test-coordinator (usually a host PC connected via UART, USB, CAN, or the like). Looking at the hierarchy top-to-bottom (see diagram below), the Kiso integration test framework consists of: + +* Test-App - Flashable/Bootable binary for reference and/or user hardware. + * Test-Executor - Application component of the test-app. Handles basic hardware initialization and connects with test-coordinator. + * Test-Entry - Linkable static library usually declared on a package-level (such as Essentials, Cellular, Utils, etc.), combining different test-suites into a single entity. + * Test-Suite - Collection of test-cases that share a common setup and teardown sequence. Can also be used as logical separation of test-cases. + * Test-Case - Individual test to verify one specific aspect of the (sub-)system. Comprised of setup, run and teardown. +* Test-Coordinator - Python based coordinator application running on a host PC. It communicates with the test-executor running on the device-under-test and decides which test-suites/-cases are to be executed. The python framework is realized as `pykiso` and is available as own executable python module. + +![integration test framework](/images/integration_test.png) + +All Kiso packages share a common test-executor, each package also defines one test-entry. Each test-executor/-entry pair compiles into a standalone binary which can be flashed onto hardware. Users can declare their own test-entries and link them with the provided test-executor, that way users can profit form the existing test framework to run integration tests of user written drivers or applications. + +Test-Entry, test-suite and test-case contain optional hooks for setup and teardown work. + +## Test-App Build System + +As with any target binary in Kiso, building is handled by the CMake toolchain. Two special workspace settings need to be provided during workspace configuration to build a integration test app, targeted towards a specific hardware: + +* `ENABLE_INTEGRATION_TESTING = ON` - Marks this workspace as targeted towards building a integration test app. Note that with this option set, `KISO_APPLICATION_NAME` will be ignored, meaning no user application will be built. +* `KISO_INTEGRATION_TEST_ENTRY_PATH = "path/to/test-entry"` - Path to directory containing a `CMakeLists.txt` used to build the `testentry` static-library. This library will be linked with the test-executor to create the test-app. The `CMakeLists.txt` must declare a static-library target called `testentry`, containing source-files for any test-suites that shall be part of the final test-app. + +The static-library target `testentry` can be created as follows: + +```cmake +add_library(testentry STATIC source/test_suite1.c source/test_suite2.c ...) +# List of additional libs needed +target_link_libraries(testentry testing essentials ${KISO_BOARD_LIBS}) +``` + +Please have a look at the existing CMake build files to get a better idea of what to include in your `CMakeLists.txt`. Test-Entry build files are usually located at `core//test/integration/` diff --git a/docs/website/content/images/integration_test.png b/docs/website/content/images/integration_test.png new file mode 100644 index 00000000..027d7808 Binary files /dev/null and b/docs/website/content/images/integration_test.png differ diff --git a/docs/website/content/images/integration_test.puml b/docs/website/content/images/integration_test.puml new file mode 100644 index 00000000..d458dc59 --- /dev/null +++ b/docs/website/content/images/integration_test.puml @@ -0,0 +1,35 @@ +@startuml + +title Integration Test Framework + +package "Test App" { + component "Test Executor" as testexecutor + package "Kiso Package (e.g. Essentials) or User App" { + component "Test Entriy" as testentry + component "Test Suite 1" as testsuite1 + component "Test Suite 2" as testsuite2 + component "Test Suite ..." as testsuitecont + component "Test Case 1.1" as testcase11 + component "Test Case 1.2" as testcase12 + component "Test Case 2.1" as testcase21 + component "Test Case 2.2" as testcase22 + component "Test Case 2.3" as testcase23 + component "Test Case ..." as testcasecont + } +} + +testexecutor -> testentry : links with + +testentry --> testsuite1 : contains +testentry --> testsuite2 : contains +testentry --> testsuitecont : contains +testsuite1 --> testcase11 : contains +testsuite1 --> testcase12 : contains +testsuite2 --> testcase21 : contains +testsuite2 --> testcase22 : contains +testsuite2 --> testcase23 : contains +testsuitecont --> testcasecont : contains + +footer © Robert Bosch GmbH 2010-2020 | Licensed under EPL-2.0 + +@enduml diff --git a/testing/integration/readme.md b/testing/integration/readme.md new file mode 100644 index 00000000..127ae9a8 --- /dev/null +++ b/testing/integration/readme.md @@ -0,0 +1,8 @@ +# Integration Test Framework + +It is composed of the following packages: + +* Test-scheduler: Called in continuous integration. Defines where the tests should be run. +* Test-coordinator: Coordinate the build and execution of tests. +* Test-auxiliaries: List of elements that support more complex tests (example: communication tests between a cloud service and a device) +* Test-executor: Software that will be flashed on the device under test. diff --git a/testing/integration/test-auxiliary/readme.md b/testing/integration/test-auxiliary/readme.md new file mode 100644 index 00000000..a4822685 --- /dev/null +++ b/testing/integration/test-auxiliary/readme.md @@ -0,0 +1,3 @@ +# Test Auxiliary + +Contains different entities that will support the device under test to execute and verify the tests. diff --git a/testing/integration/test-coordinator/readme.md b/testing/integration/test-coordinator/readme.md new file mode 100644 index 00000000..7171acbf --- /dev/null +++ b/testing/integration/test-coordinator/readme.md @@ -0,0 +1,3 @@ +# Test Coordinator + +TBD diff --git a/testing/integration/test-executor/CMakeLists.txt b/testing/integration/test-executor/CMakeLists.txt new file mode 100644 index 00000000..04476bca --- /dev/null +++ b/testing/integration/test-executor/CMakeLists.txt @@ -0,0 +1,24 @@ +cmake_minimum_required(VERSION 3.6) + +project("Kiso Integration Test Executor" C) + +if(${CMAKE_CROSSCOMPILING}) + set(APP_CONFIG_PATH ${CMAKE_CURRENT_LIST_DIR}/testapp/config PARENT_SCOPE) + + add_executable(TestExecutor + testapp/source/main.c + ) + + target_include_directories(TestExecutor PRIVATE source) + target_link_libraries(TestExecutor bsp essentials testing utils testentry ${KISO_OS_LIB} ${KISO_BOARD_LIBS}) + target_compile_definitions(TestExecutor PRIVATE ${KISO_BOARD_NAME}) + + add_custom_target(TestExecutor.bin ALL + COMMAND ${CMAKE_OBJCOPY} -O binary -R .usrpg $ ${CMAKE_CURRENT_BINARY_DIR}/TestExecutor.bin + COMMENT "Creating flashable binary ${CMAKE_CURRENT_BINARY_DIR}/TestExecutor.bin" + ) + add_dependencies(TestExecutor.bin TestExecutor) + + include(FlashTarget) + CREATE_FLASH_TARGET_JLINK(TestExecutor) +endif() diff --git a/testing/integration/test-executor/readme.md b/testing/integration/test-executor/readme.md new file mode 100644 index 00000000..ecb20e73 --- /dev/null +++ b/testing/integration/test-executor/readme.md @@ -0,0 +1,3 @@ +# Test Executor + +Embedded application linked with integration test fixtures and flashed onto the device under test. diff --git a/testing/integration/test-executor/testapp/config/utils/Kiso_UtilsConfig.h b/testing/integration/test-executor/testapp/config/utils/Kiso_UtilsConfig.h new file mode 100644 index 00000000..9c134ee5 --- /dev/null +++ b/testing/integration/test-executor/testapp/config/utils/Kiso_UtilsConfig.h @@ -0,0 +1,107 @@ +/******************************************************************************** +* Copyright (c) 2010-2019 Robert Bosch GmbH +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* Robert Bosch GmbH - initial contribution +* +********************************************************************************/ + +/** + * @file + * @brief Utils config header. + * + * @details + * Provides configuration interface for the Utils components. + */ + +#ifndef KISO_UTILSCONFIG_H_ +#define KISO_UTILSCONFIG_H_ + +// clang-format off + +#ifndef KISO_FEATURE_CMDLINEDEBUGGER +/** @brief Enable (1) or disable (0) the CmdLineDebugger feature. */ +#define KISO_FEATURE_CMDLINEDEBUGGER 1 +#endif + +#ifndef KISO_FEATURE_CMDPROCESSOR +/** @brief Enable (1) or disable (0) the CmdProcessor feature. */ +#define KISO_FEATURE_CMDPROCESSOR 1 +#endif + +#ifndef KISO_FEATURE_CRC +/** @brief Enable (1) or disable (0) the Crc feature. */ +#define KISO_FEATURE_CRC 1 +#endif + +#ifndef KISO_FEATURE_EVENTHUB +/** @brief Enable (1) or disable (0) the EventHub feature. */ +#define KISO_FEATURE_EVENTHUB 1 +#endif + +#ifndef KISO_FEATURE_GUARDEDTASK +/** @brief Enable (1) or disable (0) the GuardedTask feature. */ +#define KISO_FEATURE_GUARDEDTASK 1 +#endif + +#ifndef KISO_FEATURE_ERRORLOGGER +/** @brief Enable (1) or disable (0) the ErrorLogger feature. */ +#define KISO_FEATURE_ERRORLOGGER 1 +#endif + +#ifndef KISO_FEATURE_LOGGING +/** @brief Enable (1) or disable (0) the Logging feature. */ +#define KISO_FEATURE_LOGGING 1 +#endif + +#ifndef KISO_FEATURE_RINGBUFFER +/** @brief Enable (1) or disable (0) the RingBuffer feature. */ +#define KISO_FEATURE_RINGBUFFER 1 +#endif + +#ifndef KISO_FEATURE_SLEEPCONTROL +/** @brief Enable (1) or disable (0) the SleepControl feature. */ +#define KISO_FEATURE_SLEEPCONTROL 0 +#endif + +#ifndef KISO_FEATURE_TASKMONITOR +/** @brief Enable (1) or disable (0) the TaskMonitor feature. */ +#define KISO_FEATURE_TASKMONITOR 1 +#endif + +#if KISO_FEATURE_TASKMONITOR + #ifndef KISO_TASKMONITOR_MAX_TASKS + /** @brief Maximum number of TaskMonitor tickets to reserve for the system. */ + #define KISO_TASKMONITOR_MAX_TASKS 10 + #endif +#endif /* if KISO_FEATURE_TASKMONITOR */ + +#ifndef KISO_FEATURE_UARTTRANSCEIVER +/** @brief Enable (1) or disable (0) the UartTransceiver feature. */ +#define KISO_FEATURE_UARTTRANSCEIVER 1 +#endif + +#ifndef KISO_FEATURE_I2CTRANSCEIVER +/** @brief Enable (1) or disable (0) the I2CTransceiver feature. */ +#define KISO_FEATURE_I2CTRANSCEIVER 1 +#endif + +#ifndef KISO_FEATURE_XPROTOCOL +/** @brief Enable (1) or disable (0) the XProtocol feature. */ +#define KISO_FEATURE_XPROTOCOL 1 +#endif + +#ifndef KISO_FEATURE_PIPEANDFILTER +/** @brief Enable (1) or disable (0) the pipe & filter pattern feature. */ +#define KISO_FEATURE_PIPEANDFILTER 1 +#endif + +// clang-format on + +#endif /* KISO_UTILSCONFIG_H_ */ diff --git a/testing/integration/test-executor/testapp/source/AppModules.h b/testing/integration/test-executor/testapp/source/AppModules.h new file mode 100644 index 00000000..cbd2681a --- /dev/null +++ b/testing/integration/test-executor/testapp/source/AppModules.h @@ -0,0 +1,17 @@ + +#ifndef APPMODULES_H_ +#define APPMODULES_H_ + +/** + * @brief Enumerates application modules which are reporting error codes according to RETCODE specification. + * @info usage: + * #undef KISO_APP_MODULE_ID + * #define KISO_APP_MODULE_ID APP_MODULE_ID_xxx + */ +enum App_ModuleID_E +{ + APP_MODULE_ID_MAIN = 1, + /* Define next module ID here and assign a value to it! */ +}; + +#endif diff --git a/testing/integration/test-executor/testapp/source/BSP_Proxy.h b/testing/integration/test-executor/testapp/source/BSP_Proxy.h new file mode 100644 index 00000000..08994ffc --- /dev/null +++ b/testing/integration/test-executor/testapp/source/BSP_Proxy.h @@ -0,0 +1,53 @@ +/******************************************************************************* + * Copyright (c) 2010-2020 Robert Bosch GmbH + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Robert Bosch GmbH - initial contribution + * + ******************************************************************************/ + +/** + * @file + * @defgroup + * @ingroup + * @{ + * + * @brief Provides conversion from the bsp board interface to the testapp + * expected interface. + */ + +#if defined(CommonGateway) +#include "BSP_CommonGateway.h" +#elif defined(NucleoF767) +#include "BSP_NucleoF767.h" +#endif + +#if defined(CommonGateway) + +#define TEST_BOARD_LED_ALL COMMONGATEWAY_LED_ALL +#define TEST_BOARD_LED_PASS COMMONGATEWAY_LED_GREEN_ID +#define TEST_BOARD_LED_FAIL COMMONGATEWAY_LED_BLUE_ID +#define TEST_BOARD_LED_PANIC COMMONGATEWAY_LED_RED_ID +#define TEST_BOARD_LED_COMMAND_ON COMMONGATEWAY_LED_COMMAND_ON +#define TEST_BOARD_LED_COMMAND_OFF COMMONGATEWAY_LED_COMMAND_OFF +#define TEST_BOARD_LED_COMMAND_TOGGLE COMMONGATEWAY_LED_COMMAND_TOGGLE + +#elif defined(NucleoF767) + +#define TEST_BOARD_LED_ALL NUCLEOF767_LED_ALL +#define TEST_BOARD_LED_PASS NUCLEOF767_LED_GREEN_ID +#define TEST_BOARD_LED_FAIL NUCLEOF767_LED_BLUE_ID +#define TEST_BOARD_LED_PANIC NUCLEOF767_LED_RED_ID +#define TEST_BOARD_LED_COMMAND_ON NUCLEOF767_LED_COMMAND_ON +#define TEST_BOARD_LED_COMMAND_OFF NUCLEOF767_LED_COMMAND_OFF +#define TEST_BOARD_LED_COMMAND_TOGGLE NUCLEOF767_LED_COMMAND_TOGGLE + +#endif + +/** @} */ diff --git a/testing/integration/test-executor/testapp/source/main.c b/testing/integration/test-executor/testapp/source/main.c new file mode 100644 index 00000000..acad8f35 --- /dev/null +++ b/testing/integration/test-executor/testapp/source/main.c @@ -0,0 +1,260 @@ +/******************************************************************************* +* Copyright (c) 2010-2020 Robert Bosch GmbH +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* Robert Bosch GmbH - initial contribution +* +*******************************************************************************/ + +/** + * @brief This file implements the #main() of the test executor software. After + * initializing the test board and starting the OS scheduler, it shall call the + * function in the feature integration tests responsible for initializing and + * starting the tests (e.g. #TestEntry_Initialize() under + * core/essentials/test/integration/source/TestEntry.c) + * + * @file + */ + +#include "AppModules.h" +#include "BSP_Proxy.h" +#include "Kiso_Retcode.h" +#include "Kiso_BSP_LED.h" +#include "Kiso_BSP_Board.h" +#include "Kiso_Basics.h" +#include "Kiso_CmdProcessor.h" +#include "FreeRTOS.h" +#include "task.h" +#include + +#undef KISO_MODULE_ID +#define KISO_MODULE_ID KISO_APP_MODULE_MAIN + +#define TASK_PRIO_MAIN_CMD_PROCESSOR (UINT32_C(1)) +#define TASK_STACK_SIZE_MAIN_CMD_PROCESSOR (UINT16_C(700)) +#define TASK_Q_LEN_MAIN_CMD_PROCESSOR (UINT32_C(10)) + +extern void xPortSysTickHandler(void); /*link-time function to be provided by the freertos library */ +extern Retcode_T TestEntry_Initialize(void *param1, uint32_t param2); /* link-time function to be provided by the library implementing the feature integration tests*/ + +static Retcode_T systemStartup(void); +static void systemInit(void *param1, uint32_t param2); +static void ErrorHandler(Retcode_T error, bool isfromIsr); +static void assertIndicationMapping(const unsigned long line, const unsigned char *const file); +static void SysTickPreCallback(void); + +#if configSUPPORT_STATIC_ALLOCATION +void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer, + StackType_t **ppxIdleTaskStackBuffer, + uint32_t *pulIdleTaskStackSize); +#endif +#if configSUPPORT_STATIC_ALLOCATION && configUSE_TIMERS +void vApplicationGetTimerTaskMemory(StaticTask_t **ppxTimerTaskTCBBuffer, + StackType_t **ppxTimerTaskStackBuffer, + uint32_t *pulTimerTaskStackSize); +#endif + +static CmdProcessor_T MainCmdProcessor; + +int main(void) +{ + /* Mapping Default Error Handling function */ + Retcode_T retcode = Retcode_Initialize(ErrorHandler); + +#ifndef NDEBUG + if (RETCODE_OK == retcode) + { + retcode = Assert_Initialize(assertIndicationMapping); + } +#endif /*NDEBUG*/ + if (RETCODE_OK == retcode) + { + retcode = systemStartup(); + } + if (RETCODE_OK == retcode) + { + retcode = CmdProcessor_Initialize(&MainCmdProcessor, + (char *)"MainCmdProcessor", + TASK_PRIO_MAIN_CMD_PROCESSOR, + TASK_STACK_SIZE_MAIN_CMD_PROCESSOR, + TASK_Q_LEN_MAIN_CMD_PROCESSOR); + } + if (RETCODE_OK == retcode) + { + /* Here we enqueue the application initialization into the command + * processor, such that the initialization function will be invoked + * once the RTOS scheduler is started below. + */ + retcode = CmdProcessor_Enqueue(&MainCmdProcessor, + systemInit, + &MainCmdProcessor, + UINT32_C(0)); + } + + if (RETCODE_OK != retcode) + { + printf("System Startup failed"); + assert(false); + } + /* start scheduler */ + vTaskStartScheduler(); +} + +/** + * @brief Starts the system up. + * @details This function will execute before the scheduler starts and it is + * intended to make the target board ready for operation. + * @return Returns RETCODE_OK in case of success, error code otherwise. + */ +Retcode_T systemStartup(void) +{ + Retcode_T retcode = RETCODE_OK; + uint32_t param1 = 0; + void *param2 = NULL; + + /* Initialize the callbacks for the system tick */ + BSP_Board_OSTickInitialize(SysTickPreCallback, NULL); + retcode = BSP_Board_Initialize(param1, param2); + if (RETCODE_OK == retcode) + { + retcode = BSP_LED_Connect(); + } + if (RETCODE_OK == retcode) + { + retcode = BSP_LED_Enable(TEST_BOARD_LED_ALL); + } + if (RETCODE_OK == retcode) + { + retcode = BSP_LED_Switch(TEST_BOARD_LED_ALL, TEST_BOARD_LED_COMMAND_ON); + } + return retcode; +} + +void systemInit(void *param1, uint32_t param2) +{ + KISO_UNUSED(param1); + KISO_UNUSED(param2); + Retcode_T retcode = TestEntry_Initialize(NULL, 0U); + if (RETCODE_OK != retcode) + { + Retcode_RaiseError(retcode); + } +} + +/** + * @brief Error handler function. + * @details This function is called when Retcode_RaiseError() function is + * invoked, it is used to report the error to the user. + * @param[in] error Error code raised. + * @param[in] isfromIsr if true then the ErrorHandler is being executed from an + * ISR context and not all the services are available. + */ +void ErrorHandler(Retcode_T error, bool isfromIsr) +{ + if (!isfromIsr) + { + /** \todo: ERROR HANDLING SHOULD BE DONE FOR THE ERRORS RAISED FROM PLATFORM */ + uint32_t PackageID = Retcode_GetPackage(error); + uint32_t ErrorCode = Retcode_GetCode(error); + uint32_t ModuleID = Retcode_GetModuleId(error); + Retcode_Severity_T SeverityCode = Retcode_GetSeverity(error); + + if (RETCODE_SEVERITY_FATAL == SeverityCode) + { + printf("Fatal Error:[%u] from Module:[%u] in Package:[%u]\r\n", + (unsigned int)ErrorCode, + (unsigned int)ModuleID, + (unsigned int)PackageID); + } + else if (RETCODE_SEVERITY_ERROR == SeverityCode) + { + printf("Severe Error:[%u] from Module:[%u] in Package:[%u]\r\n", + (unsigned int)ErrorCode, + (unsigned int)ModuleID, + (unsigned int)PackageID); + } + BSP_LED_Switch(TEST_BOARD_LED_ALL, TEST_BOARD_LED_COMMAND_OFF); + BSP_LED_Switch(TEST_BOARD_LED_PANIC, TEST_BOARD_LED_COMMAND_ON); + } + else + { + + BSP_LED_Switch(TEST_BOARD_LED_ALL, TEST_BOARD_LED_COMMAND_OFF); + BSP_LED_Switch(TEST_BOARD_LED_PANIC, TEST_BOARD_LED_COMMAND_ON); + } +} + +#ifndef NDEBUG +/** + * @brief This API is called when function enters an assert + * @param[in] line line number where asserted. + * @param[in] file file name which is asserted. + */ +void assertIndicationMapping(const unsigned long line, const unsigned char *const file) +{ + (void)BSP_LED_Switch(TEST_BOARD_LED_ALL, TEST_BOARD_LED_COMMAND_ON); + printf("asserted at Filename %s , line no %ld \n\r", file, line); +} +#endif /* NDEBUG */ + +/** + * @brief This function is a hook from FreeRTOS to systick. + * @details This function is called when ever the Systick IRQ is hit. This is a + * temporary implementation where the #SysTick_Handler() is not directly mapped + * to #xPortSysTickHandler(). Instead it is only called if the scheduler has + * started. + */ +static void SysTickPreCallback(void) +{ + if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) + { + xPortSysTickHandler(); + } +} + +#if configSUPPORT_STATIC_ALLOCATION + +static StaticTask_t xIdleTaskTCBBuffer; +static StackType_t xIdleStack[IDLE_TASK_SIZE]; + +/** + * @brief If static allocation is supported then the application must provide + * the following callback function + * @details Enables the application to optionally provide the memory that will + * be used by the idle task as the task's stack and TCB. + */ +void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer, + StackType_t **ppxIdleTaskStackBuffer, + uint32_t *pulIdleTaskStackSize) +{ + *ppxIdleTaskTCBBuffer = &xIdleTaskTCBBuffer; + *ppxIdleTaskStackBuffer = &xIdleStack[0]; + *pulIdleTaskStackSize = IDLE_TASK_SIZE; +} +#endif + +#if configSUPPORT_STATIC_ALLOCATION && configUSE_TIMERS +static StaticTask_t xTimerTaskTCBBuffer; +static StackType_t xTimerStack[configTIMER_TASK_STACK_DEPTH]; + +/** + * @brief If static allocation and timers are supported then the application + * must provide the following callback function + * @details Enables the application to optionally provide the memory that will + * be used by the timer task as the task's stack and TCB. + */ +void vApplicationGetTimerTaskMemory(StaticTask_t **ppxTimerTaskTCBBuffer, + StackType_t **ppxTimerTaskStackBuffer, + uint32_t *pulTimerTaskStackSize) +{ + *ppxTimerTaskTCBBuffer = &xTimerTaskTCBBuffer; + *ppxTimerTaskStackBuffer = &xTimerStack[0]; + *pulTimerTaskStackSize = configTIMER_TASK_STACK_DEPTH; +} +#endif diff --git a/testing/integration/test-scheduler/readme.md b/testing/integration/test-scheduler/readme.md new file mode 100644 index 00000000..d049eab1 --- /dev/null +++ b/testing/integration/test-scheduler/readme.md @@ -0,0 +1,3 @@ +# Test Scheduler + +Schedules which tests should be executed where. Represents the link to the continuous integration setup on Jenkins.