diff --git a/.gitignore b/.gitignore index 48b7619e..61325994 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,6 @@ /debug build builddir-* -.vscode \ No newline at end of file +.vscode +__pycache__/ +testing/integration/test-coordinator/kiso-testing/ 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 36499919..61876f13 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -7,7 +7,7 @@ pipeline containerTemplate { name 'kiso-build-env' - image 'eclipse/kiso-build-env:v0.0.7' + image 'eclipse/kiso-build-env:v0.0.8' ttyEnabled true resourceRequestCpu '2' resourceLimitCpu '2' @@ -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' } } } @@ -107,6 +109,7 @@ pipeline script { echo "Generate Hugo Website" + sh 'rm -rf docs/website/themes/learn/' sh 'git clone --depth 1 --branch 2.5.0 https://github.com/matcornic/hugo-theme-learn.git docs/website/themes/learn/' sh 'hugo -s docs/website' } 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/docker/Readme.md b/ci/docker/Readme.md index 0b760cb6..e601156b 100644 --- a/ci/docker/Readme.md +++ b/ci/docker/Readme.md @@ -1,23 +1,33 @@ # Docker Image Creation For Jenkins ## Introduction -The Dockerfile file is the source of truth for the docker image used in our continuous integration. -This means, modifying it will affect all builds. + +These `Dockerfile`s are the source of truth for the docker image used in our continuous integration. This means, modifying it will affect all builds. + +* `development` contains the description for the `eclipse/kiso-build-env` image used by developers and CI/CD alike, to build and unit-test Kiso packages. +* `agent` contains the description for the `eclipse/kiso-integration-test-agent` image used by CI/CD for integration-testing. It adds a Jenkins agent to `eclipse/kiso-build-env`. Building it requires a build argument to indicate whether or not to install JLink. Use `docker build ... --build-arg ACCEPT_JLINK_TERMS_OF_USE='accepted'` if you have read and accepted the SEGGER's Terms of Use for this software. Alternatively, set `ACCEPT_JLINK_TERMS_OF_USE='not accepted'` to skip JLink installation. \ +**NOTE: The image must not be re-distributed, if built with JLink enabled! This option is intended for your local setup only.** ## Installation of docker -Please check the following install manuals: + +Please check the following installation manuals: + * [For Windows 10](https://runnable.com/docker/install-docker-on-windows-10) * [For Linux](https://runnable.com/docker/install-docker-on-linux) ## Creation of the image -In this folder, open a Terminal and -* to build the image: `docker build --tag= .` + +In `development` directory, open a terminal and type `docker build . --tag=` to build the image. ## Verification of the image + You can run the image in a container with `docker run -it bash` -## Commit of the image -open a Terminal and -* to commit the image into the registry: `docker tag :` -* to log into the registry: `docker login ` -* to push the image into the registry: `docker push :` +## Deploy an updated image + +To deploy a modified image open a terminal and enter the following: + +```sh +docker login # username and password entered interactively +docker push /: +``` diff --git a/ci/docker/agent/Dockerfile b/ci/docker/agent/Dockerfile new file mode 100644 index 00000000..611270cb --- /dev/null +++ b/ci/docker/agent/Dockerfile @@ -0,0 +1,64 @@ +FROM eclipse/kiso-build-env:v0.0.8 + +LABEL Description="Eclipse Kiso Jenkins Agent container, used to execute hardware integation tests" + +ARG VERSION=4.3 +ARG user=jenkins +ARG group=jenkins +ARG uid=1000 +ARG gid=1000 +ARG AGENT_WORKDIR=/home/${user}/agent + +# Change to 'accepted' if you read and agreed to the SEGGER Terms of Use +# available at https://www.segger.com/downloads/jlink/#J-LinkSoftwareAndDocumentationPack +# Otherwise set to 'not accepted' to skip JLink installation. +ARG ACCEPT_JLINK_TERMS_OF_USE= + +RUN groupadd -g ${gid} ${group} +RUN useradd -c "Jenkins Agent user" -d /home/${user} -u ${uid} -g ${gid} -m ${user} + +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + curl \ + docker.io \ + git-lfs \ + libncurses5 \ + && apt-get clean + +RUN JLINK_ARCH=`([ $(uname -m) = 'x86_64' ] && echo 'x86_64') || ([ $(uname -m) = 'armv7l' ] && echo 'arm') || ([ $(uname -m) = 'aarch64' ] && echo 'arm64')` \ + && [ -z "$ACCEPT_JLINK_TERMS_OF_USE" ] \ + && (echo "\n################################################################################\n" \ + "\nBuild argument ACCEPT_JLINK_TERMS_OF_USE not set. Please read the below stated SEGGER Terms of Use and set docker build argument ACCEPT_JLINK_TERMS_OF_USE to 'accepted' (to install JLink) or 'not accepted' (to skip installation).\n" \ + "\n################################################################################\n" \ + ; curl -fsSL "https://www.segger.com/downloads/jlink/JLink_Linux_${JLINK_ARCH}.deb" \ + | xmllint --html --xpath "//body/div[@id='page']/main/div[@id='productdetails']//textarea/text()" - 2>/dev/null \ + && echo '\n\n' \ + ; exit 123) \ + || [ "$ACCEPT_JLINK_TERMS_OF_USE" = 'accepted' -o "$ACCEPT_JLINK_TERMS_OF_USE" = 'not accepted' ] \ + && true \ + || (echo "Invalid build argument value ACCEPT_JLINK_TERMS_OF_USE='${ACCEPT_JLINK_TERMS_OF_USE}'"; exit 124); + +# Workaround for new buggy update-ca-certificates in Debian Buster on ARM 32-bit +# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=923479 +RUN ([ ! $(uname -m) = 'armv7l' ] && true) || ([ $(uname -m) = 'armv7l' ] && c_rehash) + +RUN curl --create-dirs -fsSLo /usr/share/jenkins/agent.jar https://repo.jenkins-ci.org/public/org/jenkins-ci/main/remoting/${VERSION}/remoting-${VERSION}.jar \ + && chmod 755 /usr/share/jenkins \ + && chmod 644 /usr/share/jenkins/agent.jar \ + && ln -sf /usr/share/jenkins/agent.jar /usr/share/jenkins/slave.jar +RUN JLINK_ARCH=`([ $(uname -m) = 'x86_64' ] && echo 'x86_64') || ([ $(uname -m) = 'armv7l' ] && echo 'arm') || ([ $(uname -m) = 'aarch64' ] && echo 'arm64')` \ + && ([ "$ACCEPT_JLINK_TERMS_OF_USE" = 'not accepted' ] \ + && true \ + || (TEMP_DEB="$(mktemp)" \ + && curl -fsSL -X POST -F 'accept_license_agreement=accepted' "https://www.segger.com/downloads/jlink/JLink_Linux_${JLINK_ARCH}.deb" -o "$TEMP_DEB" \ + && dpkg -i "$TEMP_DEB" \ + && rm -f "$TEMP_DEB")) + +USER ${user} +ENV AGENT_WORKDIR=${AGENT_WORKDIR} +RUN mkdir /home/${user}/.jenkins && mkdir -p ${AGENT_WORKDIR} + +VOLUME /home/${user}/.jenkins +VOLUME ${AGENT_WORKDIR} + +WORKDIR /home/${user} diff --git a/ci/docker/Dockerfile b/ci/docker/development/Dockerfile similarity index 96% rename from ci/docker/Dockerfile rename to ci/docker/development/Dockerfile index 76544dd2..a077ba91 100644 --- a/ci/docker/Dockerfile +++ b/ci/docker/development/Dockerfile @@ -22,6 +22,7 @@ RUN apt-get update && apt-get install -y \ hugo \ lcov \ libgtest-dev \ + libncurses5 \ libxml2-utils \ ninja-build \ plantuml \ 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/FlashTarget.cmake b/cmake/FlashTarget.cmake index 48f16ad1..c59f6e61 100644 --- a/cmake/FlashTarget.cmake +++ b/cmake/FlashTarget.cmake @@ -67,6 +67,9 @@ function(CREATE_FLASH_TARGET_JLINK ELF_TARGET) # \todo: This can be exported to file and replaced with configure_file file(WRITE ${SCRIPT_PATH} "exitonerror 1\n") + if(JLINK_REMOTE) + file(APPEND ${SCRIPT_PATH} "ip ${JLINK_REMOTE}\n") + endif() file(APPEND ${SCRIPT_PATH} "device ${KISO_MCU_DEVICE}\n") file(APPEND ${SCRIPT_PATH} "if swd\n") file(APPEND ${SCRIPT_PATH} "speed 4000\n") 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/coordinator/test_config.yaml b/core/essentials/test/integration/coordinator/test_config.yaml new file mode 100644 index 00000000..a358c9f5 --- /dev/null +++ b/core/essentials/test/integration/coordinator/test_config.yaml @@ -0,0 +1,16 @@ +auxiliaries: + dut: + connectors: + com: uart + type: pykiso.lib.auxiliaries.communication_auxiliary:CommunicationAuxiliary +connectors: + uart: + config: + serialPort: /dev/ttyACM0 + baudrate: 115200 + type: pykiso.lib.connectors.cc_uart:CCUart +test_suite_list: + - suite_dir: uart_test_suite + test_filter_pattern: "*.py" + test_suite_id: 1 + test_entry_id: 1 diff --git a/core/essentials/test/integration/coordinator/uart_test_suite/uart_test_suite.py b/core/essentials/test/integration/coordinator/uart_test_suite/uart_test_suite.py new file mode 100644 index 00000000..0f7cd50e --- /dev/null +++ b/core/essentials/test/integration/coordinator/uart_test_suite/uart_test_suite.py @@ -0,0 +1,78 @@ +import pykiso +import logging + +from pykiso.auxiliaries import dut + +from pykiso import message + + +@pykiso.define_test_parameters(entry_id=1, suite_id=1, aux_list=[dut]) +class SuiteSetup(pykiso.BasicTestSuiteSetup): + pass + + +@pykiso.define_test_parameters(entry_id=1, suite_id=1, aux_list=[dut]) +class SuiteTearDown(pykiso.BasicTestSuiteTeardown): + pass + + +@pykiso.define_test_parameters(entry_id=1, suite_id=1, case_id=1, aux_list=[dut]) +class MyTest(pykiso.BasicTest): + def setUp(self): + logging.info("### SETUP") + + msg = message.Message( + msg_type=message.MessageType.COMMAND, + sub_type=message.MessageCommandType.TEST_CASE_SETUP, + test_entry=1, + test_suite=1, + test_case=1, + ) + msg_sent = dut.send_message(msg) + self.assertTrue(msg_sent) + + ack = dut.receive_message() + self.assertEqual(message.MessageType.ACK, ack.msg_type) + self.assertEqual(message.MessageAckType.ACK, ack.sub_type) + + super().setUp() + + def test_run(self): + logging.info("### RUN") + + msg = message.Message( + msg_type=message.MessageType.COMMAND, + sub_type=message.MessageCommandType.TEST_CASE_RUN, + test_entry=1, + test_suite=1, + test_case=1, + ) + msg_sent = dut.send_message(msg) + self.assertTrue(msg_sent) + + ack = dut.receive_message() + self.assertEqual(message.MessageType.ACK, ack.msg_type) + self.assertEqual(message.MessageAckType.ACK, ack.sub_type) + + logging.info("### Executing test case on hardware...") + + super().test_run() + + def tearDown(self): + logging.info("### TEARDOWN") + + msg = message.Message( + msg_type=message.MessageType.COMMAND, + sub_type=message.MessageCommandType.TEST_CASE_TEARDOWN, + test_entry=1, + test_suite=1, + test_case=1, + ) + msg_sent = dut.send_message(msg) + self.assertTrue(msg_sent) + + ack = dut.receive_message() + self.assertEqual(message.MessageType.ACK, ack.msg_type) + self.assertEqual(message.MessageAckType.ACK, ack.sub_type) + + super().tearDown() 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..827369ee --- /dev/null +++ b/core/essentials/test/integration/source/TestSuiteUart.c @@ -0,0 +1,276 @@ +/******************************************************************************* + * 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, NULL, NULL); + + 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; + uint8_t dummy[0]; + + /* cancel receive */ + (void)MCU_UART_Receive(UartHdl, dummy, 0); + + 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 (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 (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 (event.TxError || event.RxError) + { + 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..c8e06f75 --- /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 physically connected in loopback mode, i.e. jumper wire between UART Tx <-> Rx. +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..a419751e --- /dev/null +++ b/core/testing/source/CChannel.c @@ -0,0 +1,314 @@ +/******************************************************************************* + * 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.messageType = result == 0 ? 0 : 1; + 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 = 0; + 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; + reportMessage.header.messageType = result == 0 ? 0 : 1; + + 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..375b3dac --- /dev/null +++ b/docs/website/content/Concepts/integration_testing.md @@ -0,0 +1,42 @@ +--- +title: "Integration Testing" +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.svg) + +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/User guide/integration_testing_setup_guide.md b/docs/website/content/User guide/integration_testing_setup_guide.md new file mode 100644 index 00000000..4b369019 --- /dev/null +++ b/docs/website/content/User guide/integration_testing_setup_guide.md @@ -0,0 +1,130 @@ +--- +title: "Integration Testing - Setup Guide" +weight: 3 +--- + +This advanced guide will teach you how to set up your own integration testing CI/CD infrastructure. The concept behind Kiso's integration tests is explained in [Integration Testing]({{< ref "../Concepts/integration_testing.md" >}}). + +While integration tests can of course be executed manually by each developer, an automated CI/CD setup, in which integration tests are run with every commit, has proved to be of great value. However, the reliance on hardware of Kiso's integration tests present some challenges. For starters, the default [Eclipse/Kiso](https://github.com/eclipse/kiso) CI/CD setup runs in a fully virtualized Kubernetes environment. Thus it can only run non-hardware-reliant checks, such as compilation, static code analysis, format checks, unit-tests, etc. To enable physical access, remote agents need to be created, equipped with hardware to flash and communicate with the Device-under-Test (DuT) and acting as a proxy between Jenkins the DuT. Agents may also be able to physically interact with the DuT during a test (such as actuating a button electronically via GPIO toggle). Each agent identifies its capabilities via labels, following a directory-like naming-scheme `kiso/itf/...` (`itf` stands for "Integration Test Framework"). A separate `Jenkinsfile` located in `/testing/integration` controls the build, flash and execution of integration tests. + +Kiso's Integration Test Infrastructure follows a decentralized approach. You as a developer may set up **your own** test infrastructure, and either run automated tests for your private fork during development, or offer access to it to the community. + +## Setting up Your own Agent + +### Requirements + +- **Device-under-Test** +- **Jenkins master** +- Linux based **Jenkins agent** computer (e.g. Raspberry Pi) +- **Flashing equipment** (if not built into DuT) +- Auxiliaries + - **Communication channel to DuT to control integration test sequence** + - _Additional hardware needed as per test specification (e.g. GSM simulator, shielding box, web-cam, etc.)_ + +(Jenkins master and agent may be combined on the same physical computer) + +### Architecture + +![integration test infrastructure](/images/integration_test_infrastructure_architecture.svg) + +In this guide we will set up a Kiso Integration Test Agent, as depicted in the figure above. The setup consists of a Raspberry Pi 3 and a DuT -- in this case a STM32 Nucleo-F767ZI. We will not cover, how to set up a reverse proxy for your Jenkins setup and make it accessible to the public. In its simplest form, Jenkins just polls the GitHub repository every couple of minutes and starts a new build if new commits are found. Then, once all tests have ran, the build status is posted to GitHub. Internally the Jenkins master spawns a docker-based Jenkins agent with access to the host's devices (via `docker run --device /dev/... ...`). This way we can control the toolchain via `Dockerfiles` and ensure our builds are consistent with Eclipse's Jenkins Master, while at the same time being able to interact with the hardware. Access to JLink flash hardware is provided via a JLink Remote Server, running parallel to the Jenkins Master. The agent then uses an IP-based connection to the JLink Remote Server to flash and reset the hardware. + +### Step by Step + +Start with a fresh Debian-based Raspberry Pi OS (the "Lite" version should suffice). The SD card should be sized adequately to store several GB of tooling. Anything below 32 GB may quickly become a hassle, as different toolchain versions may persist in the form of docker images. Follow the official [Getting Started Guide](https://projects.raspberrypi.org/en/projects/raspberry-pi-getting-started) and set up a SSH connection from your dev-PC to access it remotely. + +#### Installing the Tools + +Once on your Pi you need to install a few tools: + +- Jenkins - Follow [this guide](https://pkg.jenkins.io/debian-stable/) for Debian releases. As Jenkins may not list it as a dependency you may have to manually install a JRE: + ```sh + sudo apt install openjdk-8-jre # use 'apt list openjdk-*-jre' to see what versions are available + ``` +- Docker - Can be installed directly via `apt`: + ```sh + sudo apt install docker.io + ``` +- JLink - Download the `.deb` file for 32-bit ARM systems from [SEGGER](https://www.segger.com/downloads/jlink/#J-LinkSoftwareAndDocumentationPack) and install it via `dpkg`: + ```sh + sudo apt install libncurses5 # requirement for JLink + sudo dpkg -i JLink_Linux_arm.deb + ``` +- Some more recommended tools you maybe want to install are: `vim`, `unattended-upgrades`, and `winbind`. + +#### JLink Remote Service + +We assume your Nucleo's on-chip [ST-Link is flashed with JLink firmware](https://www.segger.com/products/debug-probes/j-link/models/other-j-links/st-link-on-board/). This allows us to use JLink's excellent JLink Remote Server to access the hardware via TCP/IP-connection. To ensure the server is always running we will create a [systemd service](https://www.freedesktop.org/software/systemd/man/systemd.service.html). + +Create a new service unit `/etc/systemd/system/jlinkremote.service`. Paste the following unit description into the newly created file: + +```systemd +[Unit] +Description=JLink Remote Server allowing remote JLink Clients to access local hardware +PartOf=jenkins.service + +[Service] +type=simple +ExecStart=/usr/bin/JLinkRemoteServer + +[Install] +WantedBy=jenkins.service +``` + +Note that we specified this service to be `WantedBy=jenkins.service` and `PartOf=jenkins.service`. Thus if Jenkins is started or stopped JLink Remote will follow in turn. + +Enable and start the service initially via: + +```sh +sudo systemctl enable jlinkremote.service +sudo systemctl start jlinkremote.service +``` + +#### Jenkins Master & Agent + +Make sure Jenkins is running: + +```sh +systemctl status jenkins.service # will report if Jenkins is running or not +# ... +systemctl status jenkins.service # to start Jenkins +``` + +Open Jenkins' web-service in a browser (IP depends on your network, port 8080), and follow the initial setup instructions. We don't need any special plugins so the default selection should be ok. + +Create a GitHub Organization job via "New Item" > give it a name > select "GitHub Organization" > "OK". In the following configuration screen provide your GitHub credentials (use access tokens!) and create a repository filter for Kiso. For token-based authentication make sure the token has the `repo:status` flag set in [GitHub Developer Settings](https://github.com/settings/apps) settings. Under "Project Recognizers" change the "Script Path" to `testing/integration/Jenkinsfile`. Change the scan interval to something reasonable, like every 5 or 10 minutes. All other settings can be left as default or tweaked at a later time. + +The final piece in our setup is the Jenkins agent. As described earlier we use a docker-based agent, spawned by the Jenkins master. We can create such an agent by navigating to the Jenkins dashboard and hitting "Manage Jenkins" > "Manage Nodes and Clouds" > "New Node" > give it a name (e.g. "goofy") > select "Permanent Agent" > "OK". + +![agent configuration](/images/integration_test_agent_config.png) + +In the configuration screen set "Remote root directory" to `/home/jenkins`, set the appropriate labels (more on that below) and select launch method "Launch agent via execution of command on the master". Set "Launch command" to: + +```sh +docker run -i --rm --name jenkins-agent-kiso --device /dev/ttyACM0 -e JLINK_REMOTE="$JLINK_REMOTE" --init eclipse/kiso-integration-test-agent java -jar /usr/share/jenkins/agent.jar`. +``` + +Check "Environment variables" and add `JLINK_REMOTE` with value `172.17.0.1`. This should be the IP address of the docker host as seen from the docker network the agent is part of. + +Note the use of `--device /dev/ttyACM0` in the launch command. This should be the device-file representing the USB serial COM-port of the Nucleo-F767. If this device does not exist, the agent will not be able to start. Connect the Nucleo-F767 and check the presence of the COM-port via `ls -l /dev/ttyACM0`. + +Save the node and hit "Launch agent". If everything is set up properly, the agent should boot and now (provided the correct labels are set) be ready for integration tests. + +### Agent Labeling + +Agents publish their capabilities via labels. The labels follow a directory-like naming-scheme. The following predefined categories exist as of now: + +- `kiso/` - Identifies that label is part of Kiso project + - `itf/` - Identifies that label is part of Kiso's Integration Test Framework + - `connector/` - Available connectors + - `jlink-via-local` - JLink flasher locally available. + - `jlink-via-remote` - JLink flasher accessible via IP, `JLINK_REMOTE` environment variable must contain IP-address pointing to JLink Remote Server instance. + - `uart` - COM-port available. + - `dut/` - Available Device(s)-under-Test + - `nucleof767` - STM32 Nucleo-F767 + +You may add your own labels as needed. A typical agent may have the following labels: + +- `kiso/itf/connector/jlink-via-remote` +- `kiso/itf/connector/uart` +- `kiso/itf/dut/nucleof767` 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/docs/website/content/images/integration_test.svg b/docs/website/content/images/integration_test.svg new file mode 100644 index 00000000..4127bb57 --- /dev/null +++ b/docs/website/content/images/integration_test.svg @@ -0,0 +1,70 @@ +Integration Test FrameworkTest AppKiso Package (e.g. Essentials) or User AppTest ExecutorTest EntriyTest Suite 1Test Suite 2Test Suite ...Test Case 1.1Test Case 1.2Test Case 2.1Test Case 2.2Test Case 2.3Test Case ...links withcontainscontainscontainscontainscontainscontainscontainscontainscontains© Robert Bosch GmbH 2010-2020 | Licensed under EPL-2.0 \ No newline at end of file diff --git a/docs/website/content/images/integration_test_agent_config.png b/docs/website/content/images/integration_test_agent_config.png new file mode 100644 index 00000000..1cebf1a6 Binary files /dev/null and b/docs/website/content/images/integration_test_agent_config.png differ diff --git a/docs/website/content/images/integration_test_infrastructure_architecture.puml b/docs/website/content/images/integration_test_infrastructure_architecture.puml new file mode 100644 index 00000000..cb8a7daa --- /dev/null +++ b/docs/website/content/images/integration_test_infrastructure_architecture.puml @@ -0,0 +1,29 @@ +@startuml + +title Integration Testing - Example Setup + +node "Nucleo-F767" { + component "Test App" as testapp +} + +node "Raspberry Pi 3" { + component "Jenkins Master" as jenkins_master + component "Jenkins Agent" as jenkins_agent +} + +node "GitHub" { + component "/kiso" as fork +} + +actor "Developer" as dev + +dev --up--> fork : "(1) push commit" +jenkins_master --left--> fork : "(2) poll latest commit" +jenkins_master --left--> fork : "(6) notify build status" +jenkins_master -down-> jenkins_agent : "(3) spawn" +jenkins_master <-down- jenkins_agent : "(5) report test result" +jenkins_agent <-down-> testapp : "(4) interact" + +footer © Robert Bosch GmbH 2010-2020 | Licensed under EPL-2.0 + +@enduml diff --git a/docs/website/content/images/integration_test_infrastructure_architecture.svg b/docs/website/content/images/integration_test_infrastructure_architecture.svg new file mode 100644 index 00000000..fb278936 --- /dev/null +++ b/docs/website/content/images/integration_test_infrastructure_architecture.svg @@ -0,0 +1,55 @@ +Integration Testing - Example SetupNucleo-F767Raspberry Pi 3GitHubTest AppJenkins MasterJenkins Agent<your-fork>/kisoDeveloper(1) push commit(2) poll latest commit(6) notify build status(3) spawn(5) report test result(4) interact© Robert Bosch GmbH 2010-2020 | Licensed under EPL-2.0 \ No newline at end of file diff --git a/testing/integration/Jenkinsfile b/testing/integration/Jenkinsfile new file mode 100644 index 00000000..19d51409 --- /dev/null +++ b/testing/integration/Jenkinsfile @@ -0,0 +1,43 @@ +pipeline +{ + /* NOTE: don't use agent declaration in both global and stage scope: + * https://issues.jenkins-ci.org/browse/JENKINS-63449 + * Follow-up once above issue resolved. */ + agent none + stages + { + stage('Run Integration Tests') + { + agent + { + node + { + label 'kiso/itf/dut/nucleof767 && kiso/itf/connector/uart && (kiso/itf/connector/jlink-via-remote || kiso/itf/connector/jlink-via-local)' + } + } + environment + { + WORKON_HOME = "/tmp/.venvs" + PIPENV_CACHE_DIR = "/tmp/.cache" + } + steps + { + script + { + 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' -DJLINK_REMOTE=\"${env.JLINK_REMOTE}\"" + sh 'cmake --build builddir-integration --target flash' + + sh 'rm -rf testing/integration/test-coordinator/kiso-testing' + sh 'git clone --depth 1 --branch prototype/integrate-kiso-demo https://github.com/ChiefGokhlayehBosch/kiso-testing.git testing/integration/test-coordinator/kiso-testing' + dir('testing/integration/test-coordinator/kiso-testing') + { + sh 'pipenv --python 3' + sh 'pipenv install' + sh 'pipenv run python -m pykiso -c ../../../../core/essentials/test/integration/coordinator/test_config.yaml' + } + } + } + } // stage('Run Integration Tests') + } // stages +} // pipeline diff --git a/testing/integration/readme.md b/testing/integration/readme.md new file mode 100644 index 00000000..30728422 --- /dev/null +++ b/testing/integration/readme.md @@ -0,0 +1,14 @@ +# Integration Test Framework + +The ITF is composed of the following packages: + +- Test-auxiliaries: List of elements that support more complex tests (example: utilities to control a bench power supply or a database server the device-under-test can access during the test). +- Test-coordinator: Coordinate the build and execution of tests. +- Test-executor: Software that will be flashed on the device under test. +- Test-scheduler: Called in continuous integration. Defines where the tests should be run. + +## CI/CD Configuration + +This directory contains a special `Jenkinsfile` designed for Jenkins agents that act as Kiso's Integration Test hosts. These Agents must have physical access to test-hardware on which to flash and execute the integration tests. Additionally, Jenkins master must be configured to source the job's `Jenkinsfile` from this directory. This can be done in the configuration view of any GitHub or Pipeline job. + +Agents participating in the ITF setup must be marked by labels. Check out the [integration testing guide](http://docs.eclipsekiso.de:1313/user-guide/integration_testing_setup_guide.html) for more details. diff --git a/testing/integration/test-auxiliaries/readme.md b/testing/integration/test-auxiliaries/readme.md new file mode 100644 index 00000000..a4822685 --- /dev/null +++ b/testing/integration/test-auxiliaries/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.