1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

Merge pull request #15950 from MrKevinWeiss/pr/turo/initial

sys/test_utils/result_output: Initial implementations and API of turo
This commit is contained in:
Martine Lenders 2021-03-15 11:35:01 +01:00 committed by GitHub
commit fd36c626a3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 1643 additions and 0 deletions

View File

@ -10,5 +10,9 @@ sys/riotboot/.*\.h
sys/riotboot/.*\.c
sys/congure.*\.c
sys/include/congure.*\.h
sys/test_utils/result_output/.*\c
sys/test_utils/result_output/*/.*\c
sys/test_utils/result_output/*/.*\h
sys/test_utils/include/result_output.h
sys/ztimer/.*\.c
sys/include/ztimer.*\.h

View File

@ -173,6 +173,9 @@ endif
ifneq (,$(filter test_utils_interactive_sync,$(USEMODULE)))
DIRS += test_utils/interactive_sync
endif
ifneq (,$(filter test_utils_result_output,$(USEMODULE)))
DIRS += test_utils/result_output
endif
ifneq (,$(filter udp,$(USEMODULE)))
DIRS += net/transport_layer/udp
endif

View File

@ -1083,6 +1083,10 @@ ifneq (,$(filter suit_%,$(USEMODULE)))
USEMODULE += suit
endif
ifneq (,$(filter test_utils_result_output_%,$(USEMODULE)))
USEMODULE += test_utils_result_output
endif
# include ztimer dependencies
ifneq (,$(filter ztimer% %ztimer,$(USEMODULE)))
include $(RIOTBASE)/sys/ztimer/Makefile.dep

View File

@ -129,6 +129,10 @@ ifneq (,$(filter zptr,$(USEMODULE)))
include $(RIOTBASE)/sys/zptr/Makefile.include
endif
ifneq (,$(filter test_utils_result_output,$(USEMODULE)))
include $(RIOTBASE)/sys/test_utils/result_output/Makefile.include
endif
# Convert xtimer into a pseudo module if its API is already implemented by
# ztimer's compatibility wrapper
ifneq (,$(filter ztimer_xtimer_compat,$(USEMODULE)))

View File

@ -0,0 +1,357 @@
/*
* Copyright (C) 2021 HAW Hamburg
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @defgroup test_utils_result_output Test result output
* @ingroup sys
* @brief Utility function for abstraction of test result output format
*
* The TURO module provides an abstraction layer allowing salient data to be
* provided for tests independent of format or medium. The intention is to have
* a test that expects some data, for example, reading some registers,
* output the results in a know way, for example json. This should help
* keeping the test results stable and not lock anyone into a perticular
* format. If JSON is too heavy all tests using this can be swapped out for
* something lighter, for example CBOR. Then the tests should not have to be
* adapted. There can also be python layers that coordinate the output results,
* ideally done with riotctrl.
*
* Only one implementation should be selected, for example,
* test_utils_result_output_json.
*
* Some of the design decisions include:
* - ability to flush immediately to prevent the need for large buffers
* - selectable output format based on `USEMODULE`:
* + `test_utils_result_output_check`: @ref test_utils_result_output_check
* + `test_utils_result_output_json`: @ref test_utils_result_output_json
* + `test_utils_result_output_txt`: @ref test_utils_result_output_txt
* - exit status similar to a linux exit status.
* - readable raw output used in the CI to assist with reproducing errors
* - structure doesn't need to be enforced in every implementation to save
* bytes, see test_utils_result_output_check for structure assertions
*
* Some limitations are:
* - Only one type of result output implementation can be used at a time
* - Errors may be caused by the specific result output implementation making
* it difficult to debug
*
* @{
* @file
* @brief Provides abstraction and convention for output of test results
*
* @author Kevin Weiss <kevin.weiss@haw-hamburg.de>
*/
#ifndef TEST_UTILS_RESULT_OUTPUT_H
#define TEST_UTILS_RESULT_OUTPUT_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include "result_output_types.h"
/** @defgroup turo_API Test Utils Result Output Implementation API
* @brief The turo API that must have an implementation.
* @{
*/
/**
* @brief Type for a TURO object
*
* @note API implementors: `struct turo` needs to be defined by
* implementation-specific `result_output_types.h`.
*/
typedef struct turo turo_t;
/**
* @brief Provides initial values for the turo context.
*
* @param[out] ctx The implementation specific turo context.
*/
void turo_init(turo_t *ctx);
/**
* @brief Outputs a container open.
*
* Must be used when starting formatted result output.
*
* @param[in] ctx The implementation specific turo context.
*/
void turo_container_open(turo_t *ctx);
/**
* @brief Outputs a signed 32 bit integer.
*
* @pre `turo_container_open` called
*
* @param[in] ctx The implementation specific turo context.
* @param[in] val The value to output.
*/
void turo_s32(turo_t *ctx, int32_t val);
/**
* @brief Outputs an unsigned 32 bit integer.
*
* @pre `turo_container_open` called
*
* @param[in] ctx The implementation specific turo context.
* @param[in] val The value to output.
*/
void turo_u32(turo_t *ctx, uint32_t val);
/**
* @brief Outputs a signed 64 bit integer.
*
* @pre `turo_container_open` called
*
* @param[in] ctx The implementation specific turo context.
* @param[in] val The value to output.
*/
void turo_s64(turo_t *ctx, int64_t val);
/**
* @brief Outputs a formatted result unsigned 64 bit integer.
*
* @pre `turo_container_open` called
*
* @param[in] ctx The implementation specific turo context.
* @param[in] val The value to output.
*/
void turo_u64(turo_t *ctx, uint64_t val);
/**
* @brief Outputs a formatted float result of varied precision.
*
* @pre `turo_container_open` called
*
* @param[in] ctx The implementation specific turo context.
* @param[in] val The value to output.
*/
void turo_float(turo_t *ctx, float val);
/**
* @brief Outputs a formatted string string.
*
* @pre `turo_container_open` called
*
* @param[in] ctx The implementation specific turo context.
* @param[in] str The string to output.
*/
void turo_string(turo_t *ctx, const char *str);
/**
* @brief Outputs a formatted boolean result.
*
* @pre `turo_container_open` called
*
* @param[in] ctx The implementation specific turo context.
* @param[in] val The value to output.
*/
void turo_bool(turo_t *ctx, bool val);
/**
* @brief Outputs a formatted open of a dictionary result.
*
* A `turo_dict_close` must match.
*
* @pre `turo_container_open` called
*
* @param[in] ctx The implementation specific turo context.
*/
void turo_dict_open(turo_t *ctx);
/**
* @brief Outputs a formatted open of a dictionary result.
*
* A turo value must follow.
*
* @pre `turo_container_open` called
*
* @param[in] ctx The implementation specific turo context.
* @param[in] key The key of the dictionary.
*/
void turo_dict_key(turo_t *ctx, const char *key);
/**
* @brief Outputs a formatted close of a dictionary result.
*
* @pre `turo_container_open` called
* @pre `turo_dict_open` called
*
* @param[in] ctx The implementation specific turo context.
*/
void turo_dict_close(turo_t *ctx);
/**
* @brief Outputs a formatted open of an array result.
*
* A `turo_array_close` must match.
*
* @pre `turo_container_open` called
*
* @param[in] ctx The implementation specific turo context.
*/
void turo_array_open(turo_t *ctx);
/**
* @brief Outputs a formatted close of an array result.
*
* @pre `turo_container_open` called
* @pre `turo_array_open` called
*
* @param[in] ctx The implementation specific turo context.
*/
void turo_array_close(turo_t *ctx);
/**
* @brief Outputs a formatted close of a container result.
*
* @pre `turo_container_open` called
*
* @param[in] ctx The implementation specific turo context.
* @param[in] exit_status Exit status code for the result, 0 is success.
*/
void turo_container_close(turo_t *ctx, int exit_status);
/** @} */
/** @defgroup turo_helpers Test Utils Result Output Helpers
* @brief Common functions and helpers that all implementations can use.
* @{
*/
/**
* @brief Outputs a formatted uint8 array result.
*
* @pre `turo_container_open` called
*
* @param[in] ctx The implementation specific turo context.
* @param[in] vals A buffer of data to output.
* @param[in] size The amount of elements to output.
*/
void turo_array_u8(turo_t *ctx, uint8_t *vals, size_t size);
/**
* @brief Outputs a int32 array result.
*
* @pre `turo_container_open` called
*
* @param[in] ctx The implementation specific turo context.
* @param[in] vals A buffer of data to output.
* @param[in] size The amount of elements to output.
*/
void turo_array_s32(turo_t *ctx, int32_t *vals, size_t size);
/**
* @brief Outputs a dict with string data.
*
* @pre `turo_container_open` called
*
* @param[in] ctx The implementation specific turo context.
* @param[in] key A dictionary key.
* @param[in] val A string value of the dictionary
*/
void turo_dict_string(turo_t *ctx, const char *key, const char *val);
/**
* @brief Outputs a dict with integer data.
*
* @pre `turo_container_open` called
*
* @param[in] ctx The implementation specific turo context.
* @param[in] key A dictionary key.
* @param[in] val The integer value of the dictionary.
*/
void turo_dict_s32(turo_t *ctx, const char *key, int32_t val);
/**
* @brief Outputs a full successful int32 turo result.
*
* This includes all opening and closing of turo elements.
*
* @pre turo ctx initialized
*
* @param[in] ctx The implementation specific turo context.
* @param[in] val The value to output.
*/
void turo_simple_s32(turo_t *ctx, int32_t val);
/**
* @brief Outputs a full successful uint8 array turo result.
*
* This includes all opening and closing of turo elements.
*
* @pre turo ctx initialized
*
* @param[in] ctx The implementation specific turo context.
* @param[in] vals The buffer of the integers.
* @param[in] size Number of elements in the array.
*/
void turo_simple_array_u8(turo_t *ctx, uint8_t *vals, size_t size);
/**
* @brief Outputs a full successful int32 array turo result.
*
* This includes all opening and closing of turo elements.
*
* @pre turo ctx initialized
*
* @param[in] ctx The implementation specific turo context.
* @param[in] vals The buffer of the integers.
* @param[in] size Number of elements in the array.
*/
void turo_simple_array_s32(turo_t *ctx, int32_t *vals, size_t size);
/**
* @brief Outputs a full successful dict with string turo result.
*
* This includes all opening and closing of turo elements.
*
* @pre turo ctx initialized
*
* @param[in] ctx The implementation specific turo context.
* @param[in] key The dictionary key.
* @param[in] val The string value.
*/
void turo_simple_dict_string(turo_t *ctx, const char *key, const char *val);
/**
* @brief Outputs a full successful dict with an integer turo result.
*
* This includes all opening and closing of turo elements.
*
* @pre turo ctx initialized
*
* @param[in] ctx The implementation specific turo context.
* @param[in] key The dictionary key.
* @param[in] val The integer value.
*/
void turo_simple_dict_s32(turo_t *ctx, const char *key, int32_t val);
/**
* @brief Outputs a full turo result with exit code.
*
* This includes all opening and closing of turo elements.
*
* @pre turo ctx initialized
*
* @param[in] ctx The implementation specific turo context.
* @param[in] exit_status The exit status to output.
*/
void turo_simple_exit_status(turo_t *ctx, int exit_status);
/** @} */
#ifdef __cplusplus
}
#endif
#endif /* TEST_UTILS_RESULT_OUTPUT_H */
/** @} */

View File

@ -10,4 +10,5 @@ menu "Test utilities"
rsource "dummy_thread/Kconfig"
rsource "interactive_sync/Kconfig"
rsource "result_output/Kconfig"
endmenu # Test utilities

View File

@ -1,3 +1,6 @@
ifneq (,$(filter test_utils_interactive_sync,$(USEMODULE)))
USEMODULE += stdin
endif
ifneq (,$(filter test_utils_result_output_%,$(USEMODULE)))
USEMODULE += fmt
endif

View File

@ -0,0 +1,44 @@
# Copyright (c) 2021 HAW Hamburg
#
# This file is subject to the terms and conditions of the GNU Lesser
# General Public License v2.1. See the file LICENSE in the top level
# directory for more details.
#
choice
bool "Test utils result output"
depends on TEST_KCONFIG
optional
default MODULE_TEST_UTILS_RESULT_OUTPUT_JSON
help
A common API that can format result output depending on the module
used.
config MODULE_TEST_UTILS_RESULT_OUTPUT_TXT
bool "Text"
select MODULE_FMT
select MODULE_TEST_UTILS_RESULT_OUTPUT
help
Output results in plain text. Intended for developer friendly console
output.
config MODULE_TEST_UTILS_RESULT_OUTPUT_JSON
bool "JSON"
select MODULE_FMT
select MODULE_TEST_UTILS_RESULT_OUTPUT
help
Output results json formatted results. This allows generic json parsers
to be used. Trailing commas may be present.
config MODULE_TEST_UTILS_RESULT_OUTPUT_CHECK
bool "Check"
select MODULE_TEST_UTILS_RESULT_OUTPUT
help
Asserts that the structure of the result output are correct. No output
is given.
endchoice
config MODULE_TEST_UTILS_RESULT_OUTPUT
bool

View File

@ -0,0 +1,15 @@
MODULE = test_utils_result_output
ifneq (,$(filter test_utils_result_output_json,$(USEMODULE)))
DIRS += json
endif
ifneq (,$(filter test_utils_result_output_txt,$(USEMODULE)))
DIRS += txt
endif
ifneq (,$(filter test_utils_result_output_check,$(USEMODULE)))
DIRS += check
endif
include $(RIOTBASE)/Makefile.base

View File

@ -0,0 +1,9 @@
ifneq (,$(filter test_utils_result_output_json,$(USEMODULE)))
INCLUDES += -I$(RIOTBASE)/sys/test_utils/result_output/json
endif
ifneq (,$(filter test_utils_result_output_txt,$(USEMODULE)))
INCLUDES += -I$(RIOTBASE)/sys/test_utils/result_output/txt
endif
ifneq (,$(filter test_utils_result_output_check,$(USEMODULE)))
INCLUDES += -I$(RIOTBASE)/sys/test_utils/result_output/check
endif

View File

@ -0,0 +1,3 @@
MODULE = test_utils_result_output_check
include $(RIOTBASE)/Makefile.base

View File

@ -0,0 +1,13 @@
/**
* @defgroup test_utils_result_output_check Test result output structure
* @brief Enable this module to check the result structure
* @ingroup test_utils_result_output
* @{
*
* @file
* @brief Result output assert check implementation
*
*
* @author Kevin Weiss <kevin.weiss@haw-hamburg.de>
* @}
*/

View File

@ -0,0 +1,126 @@
/*
* Copyright (C) 2021 HAW Hamburg
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
#include <stdint.h>
#include <stdbool.h>
#include <assert.h>
#include "test_utils/result_output.h"
void turo_init(turo_t *ctx)
{
ctx->idx = 0;
}
void turo_container_open(turo_t *ctx)
{
assert(ctx->idx == 0);
assert(ctx->idx < CONFIG_TURO_MAX_NESTING_LEVELS);
ctx->states[ctx->idx++] = TURO_STATE_CONTAINER;
ctx->states[ctx->idx] = TURO_STATE_READY;
}
static void _val_check(turo_t *ctx)
{
assert(ctx->idx > 0);
assert(ctx->states[ctx->idx] != TURO_STATE_DICT_OPENED);
ctx->states[ctx->idx] = TURO_STATE_READY;
}
void turo_s32(turo_t *ctx, int32_t val)
{
(void)val;
_val_check(ctx);
}
void turo_u32(turo_t *ctx, uint32_t val)
{
(void)val;
_val_check(ctx);
}
void turo_s64(turo_t *ctx, int64_t val)
{
(void)val;
_val_check(ctx);
}
void turo_u64(turo_t *ctx, uint64_t val)
{
(void)val;
_val_check(ctx);
}
void turo_float(turo_t *ctx, float val)
{
(void)val;
_val_check(ctx);
}
void turo_string(turo_t *ctx, const char *str)
{
(void)str;
_val_check(ctx);
}
void turo_bool(turo_t *ctx, bool val)
{
(void)val;
_val_check(ctx);
}
void turo_dict_open(turo_t *ctx)
{
assert(ctx->idx > 0);
assert(ctx->idx < CONFIG_TURO_MAX_NESTING_LEVELS);
ctx->states[ctx->idx++] = TURO_STATE_DICT_OPENED;
ctx->states[ctx->idx] = TURO_STATE_DICT_OPENED;
}
void turo_dict_key(turo_t *ctx, const char *key)
{
(void)key;
assert(ctx->idx > 0);
assert(ctx->states[ctx->idx - 1] == TURO_STATE_DICT_OPENED);
assert(ctx->states[ctx->idx] == TURO_STATE_READY ||
ctx->states[ctx->idx] == TURO_STATE_DICT_OPENED);
ctx->states[ctx->idx] = TURO_STATE_READY;
}
void turo_dict_close(turo_t *ctx)
{
assert(ctx->idx > 0);
assert(ctx->states[ctx->idx] == TURO_STATE_READY);
assert(ctx->states[--ctx->idx] == TURO_STATE_DICT_OPENED);
ctx->states[ctx->idx] = TURO_STATE_READY;
}
void turo_array_open(turo_t *ctx)
{
assert(ctx->idx > 0);
assert(ctx->idx < CONFIG_TURO_MAX_NESTING_LEVELS);
ctx->states[ctx->idx++] = TURO_STATE_ARRAY_OPENED;
ctx->states[ctx->idx] = TURO_STATE_READY;
}
void turo_array_close(turo_t *ctx)
{
assert(ctx->idx > 0);
assert(ctx->states[ctx->idx] == TURO_STATE_READY);
assert(ctx->states[--ctx->idx] == TURO_STATE_ARRAY_OPENED);
ctx->states[ctx->idx] = TURO_STATE_READY;
}
void turo_container_close(turo_t *ctx, int exit_status)
{
(void)exit_status;
assert(ctx->idx == 1);
assert(ctx->states[ctx->idx] == TURO_STATE_READY);
assert(ctx->states[0] == TURO_STATE_CONTAINER);
ctx->idx--;
}

View File

@ -0,0 +1,45 @@
/*
* Copyright (C) 2021 HAW Hamburg
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
#ifndef RESULT_OUTPUT_TYPES_H
#define RESULT_OUTPUT_TYPES_H
#ifdef __cplusplus
extern "C" {
#endif
#ifndef CONFIG_TURO_MAX_NESTING_LEVELS
#define CONFIG_TURO_MAX_NESTING_LEVELS 32 /**< max level of state nesting */
#endif
/**
* @brief States of the TURO container
* @{
*/
typedef enum {
TURO_STATE_UNKNOWN, /**< unknown state */
TURO_STATE_READY, /**< state ready */
TURO_STATE_CONTAINER, /**< container open or closing */
TURO_STATE_DICT_OPENED, /**< dictionary opened */
TURO_STATE_ARRAY_OPENED /**< array opening */
} turo_state_t;
/** @} */
/**
* @brief turo type
* @internal
*/
struct turo {
size_t idx; /**< index for states */
turo_state_t states[CONFIG_TURO_MAX_NESTING_LEVELS]; /**< state buffer */
};
#ifdef __cplusplus
}
#endif
#endif /* RESULT_OUTPUT_TYPES_H */

View File

@ -0,0 +1,3 @@
MODULE = test_utils_result_output_json
include $(RIOTBASE)/Makefile.base

View File

@ -0,0 +1,13 @@
/**
* @defgroup test_utils_result_output_json Test result output with JSON
* @brief Enable this module to have results output as json
* @ingroup test_utils_result_output
* @{
*
* @file
* @brief Result output in json
*
*
* @author Kevin Weiss <kevin.weiss@haw-hamburg.de>
* @}
*/

View File

@ -0,0 +1,122 @@
/*
* Copyright (C) 2020 HAW Hamburg
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
#include <stdint.h>
#include <stdbool.h>
#include "fmt.h"
#include "test_utils/result_output.h"
static void _print_comma(turo_t *ctx, turo_state_t state)
{
if (ctx->state == TURO_STATE_NEED_COMMA) {
print_str(",");
}
ctx->state = state;
}
void turo_init(turo_t *ctx)
{
ctx->state = TURO_STATE_READY;
}
void turo_container_open(turo_t *ctx)
{
(void)ctx;
print_str("[");
}
void turo_s32(turo_t *ctx, int32_t val)
{
_print_comma(ctx, TURO_STATE_NEED_COMMA);
print_s32_dec(val);
}
void turo_u32(turo_t *ctx, uint32_t val)
{
_print_comma(ctx, TURO_STATE_NEED_COMMA);
print_u32_dec(val);
}
void turo_s64(turo_t *ctx, int64_t val)
{
_print_comma(ctx, TURO_STATE_NEED_COMMA);
print_s64_dec(val);
}
void turo_u64(turo_t *ctx, uint64_t val)
{
_print_comma(ctx, TURO_STATE_NEED_COMMA);
print_u64_dec(val);
}
void turo_float(turo_t *ctx, float val)
{
_print_comma(ctx, TURO_STATE_NEED_COMMA);
print_float(val, 8);
}
void turo_string(turo_t *ctx, const char *str)
{
_print_comma(ctx, TURO_STATE_NEED_COMMA);
print_str("\"");
print_str(str);
print_str("\"");
}
void turo_bool(turo_t *ctx, bool val)
{
_print_comma(ctx, TURO_STATE_NEED_COMMA);
if (val) {
print_str("true");
}
else {
print_str("false");
}
}
void turo_dict_open(turo_t *ctx)
{
_print_comma(ctx, TURO_STATE_READY);
print_str("{");
}
void turo_dict_key(turo_t *ctx, const char *key)
{
_print_comma(ctx, TURO_STATE_READY);
print_str("\"");
print_str(key);
print_str("\": ");
}
void turo_dict_close(turo_t *ctx)
{
ctx->state = TURO_STATE_NEED_COMMA;
print_str("}");
}
void turo_array_open(turo_t *ctx)
{
_print_comma(ctx, TURO_STATE_READY);
print_str("[");
}
void turo_array_close(turo_t *ctx)
{
ctx->state = TURO_STATE_NEED_COMMA;
print_str("]");
}
void turo_container_close(turo_t *ctx, int exit_status)
{
_print_comma(ctx, TURO_STATE_READY);
print_str("{\"exit_status\": ");
print_s32_dec((int32_t)exit_status);
print_str("}]\n");
}

View File

@ -0,0 +1,37 @@
/*
* Copyright (C) 2021 HAW Hamburg
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
#ifndef RESULT_OUTPUT_TYPES_H
#define RESULT_OUTPUT_TYPES_H
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief States of the TURO container
* @{
*/
typedef enum {
TURO_STATE_READY, /**< state ready */
TURO_STATE_NEED_COMMA, /**< next entry will need a comma */
} turo_state_t;
/** @} */
/**
* @brief turo type
* @internal
*/
struct turo {
turo_state_t state; /**< current state */
};
#ifdef __cplusplus
}
#endif
#endif /* RESULT_OUTPUT_TYPES_H */

View File

@ -0,0 +1,113 @@
/*
* Copyright (C) 2021 HAW Hamburg
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup sys
* @{
*
* @file
* @brief Result output common implementation
*
*
* @author Kevin Weiss <kevin.weiss@haw-hamburg.de>
* @}
*/
#include <stdint.h>
#include <stddef.h>
#include "test_utils/result_output.h"
void __attribute__((weak)) turo_init(turo_t *ctx)
{
(void)ctx;
}
void turo_array_u8(turo_t *ctx, uint8_t *vals, size_t size)
{
turo_array_open(ctx);
while (size > 0) {
turo_u32(ctx, (uint32_t)*vals);
vals++;
size--;
}
turo_array_close(ctx);
}
void turo_array_s32(turo_t *ctx, int32_t *vals, size_t size)
{
turo_array_open(ctx);
while (size-- > 0) {
turo_s32(ctx, *vals);
vals++;
}
turo_array_close(ctx);
}
void turo_dict_string(turo_t *ctx, const char *key, const char *val)
{
turo_dict_open(ctx);
turo_dict_key(ctx, key);
turo_string(ctx, val);
turo_dict_close(ctx);
}
void turo_dict_s32(turo_t *ctx, const char *key, int32_t val)
{
turo_dict_open(ctx);
turo_dict_key(ctx, key);
turo_s32(ctx, val);
turo_dict_close(ctx);
}
void turo_simple_s32(turo_t *ctx, int32_t val)
{
turo_container_open(ctx);
turo_s32(ctx, val);
turo_container_close(ctx, 0);
}
void turo_simple_array_s32(turo_t *ctx, int32_t *vals, size_t size)
{
turo_container_open(ctx);
turo_array_s32(ctx, vals, size);
turo_container_close(ctx, 0);
}
void turo_simple_array_u8(turo_t *ctx, uint8_t *vals, size_t size)
{
turo_container_open(ctx);
turo_array_u8(ctx, vals, size);
turo_container_close(ctx, 0);
}
void turo_simple_dict_string(turo_t *ctx, const char *key, const char *val)
{
turo_container_open(ctx);
turo_dict_open(ctx);
turo_dict_key(ctx, key);
turo_string(ctx, val);
turo_dict_close(ctx);
turo_container_close(ctx, 0);
}
void turo_simple_dict_s32(turo_t *ctx, const char *key, int32_t val)
{
turo_container_open(ctx);
turo_dict_open(ctx);
turo_dict_key(ctx, key);
turo_s32(ctx, val);
turo_dict_close(ctx);
turo_container_close(ctx, 0);
}
void turo_simple_exit_status(turo_t *ctx, int exit_status)
{
turo_container_open(ctx);
turo_container_close(ctx, exit_status);
}

View File

@ -0,0 +1,3 @@
MODULE = test_utils_result_output_txt
include $(RIOTBASE)/Makefile.base

View File

@ -0,0 +1,13 @@
/**
* @defgroup test_utils_result_output_txt Test result output plain text
* @brief Enable this module to have results output as text
* @ingroup test_utils_result_output
* @{
*
* @file
* @brief Result output plain text.
*
*
* @author Kevin Weiss <kevin.weiss@haw-hamburg.de>
* @}
*/

View File

@ -0,0 +1,115 @@
/*
* Copyright (C) 2020 HAW Hamburg
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
#include <stdint.h>
#include <stdbool.h>
#include "fmt.h"
#include "test_utils/result_output.h"
void turo_container_open(turo_t *ctx)
{
(void)ctx;
}
void turo_s32(turo_t *ctx, int32_t val)
{
(void)ctx;
print_s32_dec(val);
print_str(" ");
}
void turo_u32(turo_t *ctx, uint32_t val)
{
(void)ctx;
print_u32_dec(val);
print_str(" ");
}
void turo_s64(turo_t *ctx, int64_t val)
{
(void)ctx;
print_s64_dec(val);
print_str(" ");
}
void turo_u64(turo_t *ctx, uint64_t val)
{
(void)ctx;
print_u64_dec(val);
print_str(" ");
}
void turo_float(turo_t *ctx, float val)
{
(void)ctx;
print_float(val, 8);
print_str(" ");
}
void turo_string(turo_t *ctx, const char *str)
{
(void)ctx;
print_str(str);
print_str(" ");
}
void turo_bool(turo_t *ctx, bool val)
{
(void)ctx;
if (val) {
print_str("True");
}
else {
print_str("False");
}
print_str(" ");
}
void turo_dict_open(turo_t *ctx)
{
(void)ctx;
}
void turo_dict_key(turo_t *ctx, const char *key)
{
(void)ctx;
print_str(key);
print_str(": ");
}
void turo_dict_close(turo_t *ctx)
{
(void)ctx;
print_str("\n");
}
void turo_array_open(turo_t *ctx)
{
(void)ctx;
}
void turo_array_close(turo_t *ctx)
{
(void)ctx;
print_str("\n");
}
void turo_container_close(turo_t *ctx, int exit_status)
{
(void)ctx;
(void)exit_status;
if (exit_status) {
print_str("\nError\n");
}
else {
print_str("\nSuccess\n");
}
}

View File

@ -0,0 +1,27 @@
/*
* Copyright (C) 2021 HAW Hamburg
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
#ifndef RESULT_OUTPUT_TYPES_H
#define RESULT_OUTPUT_TYPES_H
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief turo type
* @internal
*/
struct turo {
void *empty; /**< Empty type needed due to C11-§6.7.2.1/5 */
};
#ifdef __cplusplus
}
#endif
#endif /* RESULT_OUTPUT_TYPES_H */

15
tests/turo/Makefile Normal file
View File

@ -0,0 +1,15 @@
include ../Makefile.tests_common
OUTPUT_FORMAT ?= json
USEMODULE += test_utils_result_output_${OUTPUT_FORMAT}
USEMODULE += shell
USEMODULE += fmt
# Use a terminal that does not introduce extra characters into the stream.
RIOT_TERMINAL ?= socat
ifndef CONFIG_SHELL_NO_ECHO
CFLAGS += -DCONFIG_SHELL_NO_ECHO=1
endif
include $(RIOTBASE)/Makefile.include

14
tests/turo/README.md Normal file
View File

@ -0,0 +1,14 @@
# TURO (Test Utils Result Output) Test
This shows a non-trival example of how to use the TURO module as a
testing abstraction layer.
The test is written with only TURO commands allowing the underling output to
be changed as needed depending on the interpreter. This means that the test
will not need to be changed if output is changed. If the test results are
output as json and the binary is too large, the TURO can be switched to CBOR
to save space. The interpreter should also switch to a CBOR parser and the
test should not need to be changed.
This should keep tests more stable, which is particularly useful for automated
tests.

View File

@ -0,0 +1,3 @@
CONFIG_MODULE_FMT=y
CONFIG_MODULE_SHELL=y
CONFIG_MODULE_TEST_UTILS_RESULT_OUTPUT_JSON=y

292
tests/turo/main.c Normal file
View File

@ -0,0 +1,292 @@
/*
* Copyright (C) 2021 HAW Hamburg
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup tests
* @{
*
* @file
* @brief Test Utils Result Output test application
*
* @author Kevin Weiss <kevin.weiss@haw-hamburg.de>
*
* @}
*/
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <errno.h>
#include "kernel_defines.h"
#include "test_utils/result_output.h"
#include "shell.h"
#define _BUF_COUNT 4
turo_t ctx;
static int _sc_arg2long(const char *arg, long *val)
{
errno = 0;
char *end;
long res = strtol(arg, &end, 0);
if ((*end != '\0') || ((res == LONG_MIN || res == LONG_MAX) && errno == ERANGE)) {
return -1;
}
*val = res;
return 0;
}
/* We need to disable warning since long can mean different things */
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wtype-limits"
static int _sc_arg2s32(const char *arg, int32_t *val)
{
long lval;
int res = _sc_arg2long(arg, &lval);
if (res == 0) {
if (lval <= 2147483647 && lval >= -2147483648) {
*val = (int32_t)lval;
}
else {
res = -1;
}
}
return res;
}
#pragma GCC diagnostic pop
static int _sc_arg2u8(const char *arg, uint8_t *val)
{
long lval;
int res = _sc_arg2long(arg, &lval);
if (res == 0) {
if (lval <= 255 && lval >= 0) {
*val = (uint8_t)lval;
}
else {
res = -1;
}
}
return res;
}
static void _netif_list(turo_t *ctx, int32_t netif_num)
{
uint8_t buf8[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
turo_dict_open(ctx);
turo_dict_key(ctx, "netif");
turo_dict_open(ctx);
turo_dict_key(ctx, "num");
turo_s32(ctx, netif_num);
turo_dict_key(ctx, "HWaddr");
turo_array_u8(ctx, buf8, ARRAY_SIZE(buf8));
turo_dict_key(ctx, "inet6 addr");
turo_dict_open(ctx);
turo_dict_key(ctx, "addr");
turo_string(ctx, "fe80::2445:7fff:fe5a:6fd9");
turo_dict_key(ctx, "scope");
turo_string(ctx, "link");
turo_dict_key(ctx, "flags");
turo_array_open(ctx);
turo_string(ctx, "VAL");
turo_array_close(ctx);
turo_dict_close(ctx);
turo_dict_key(ctx, "inet6 group");
turo_array_open(ctx);
turo_string(ctx, "ff02::2");
turo_string(ctx, "ff02::1");
turo_string(ctx, "ff02::1:ff5a:6fd9");
turo_array_close(ctx);
turo_dict_key(ctx, "flags");
turo_array_open(ctx);
turo_dict_s32(ctx, "L2-PDU", 1500);
turo_dict_s32(ctx, "MTU", 1500);
turo_dict_s32(ctx, "HL", 64);
turo_string(ctx, "RTR");
turo_string(ctx, "RTR_ADV");
turo_dict_s32(ctx, "Source address length", 6);
turo_array_close(ctx);
turo_dict_close(ctx);
turo_dict_close(ctx);
}
static int cmd_turo_simple_s32(int argc, char **argv)
{
int32_t s32 = 0;
if (argc != 2) {
turo_simple_exit_status(&ctx, -2);
return 1;
}
if (_sc_arg2s32(argv[1], &s32) != 0) {
turo_simple_exit_status(&ctx, -3);
return 1;
}
turo_simple_s32(&ctx, s32);
return 0;
}
static int cmd_turo_simple_array_u8(int argc, char **argv)
{
uint8_t buf8[_BUF_COUNT];
if (argc == 1) {
turo_simple_exit_status(&ctx, -4);
return 1;
}
if (argc > _BUF_COUNT + 1) {
turo_simple_exit_status(&ctx, -5);
return 1;
}
for (int i = 0; i < argc - 1; i++) {
if (_sc_arg2u8(argv[i + 1], &buf8[i]) != 0) {
turo_simple_exit_status(&ctx, -6);
return 1;
}
}
turo_simple_array_u8(&ctx, buf8, argc - 1);
return 0;
}
static int cmd_turo_simple_array_s32(int argc, char **argv)
{
int32_t buf32[_BUF_COUNT];
if (argc == 1) {
turo_simple_exit_status(&ctx, -7);
return 1;
}
if (argc > _BUF_COUNT + 1) {
turo_simple_exit_status(&ctx, -8);
return 1;
}
for (int i = 0; i < argc - 1; i++) {
if (_sc_arg2s32(argv[i + 1], &buf32[i]) != 0) {
turo_simple_exit_status(&ctx, -9);
return 1;
}
}
turo_simple_array_s32(&ctx, buf32, argc - 1);
return 0;
}
static int cmd_turo_simple_dict_string(int argc, char **argv)
{
if (argc != 3) {
turo_simple_exit_status(&ctx, -10);
return 1;
}
turo_simple_dict_string(&ctx, argv[1], argv[2]);
return 0;
}
static int cmd_turo_simple_dict_s32(int argc, char **argv)
{
int32_t s32 = 0;
if (argc != 3) {
turo_simple_exit_status(&ctx, -11);
return 1;
}
if (_sc_arg2s32(argv[2], &s32) != 0) {
turo_simple_exit_status(&ctx, -12);
return 1;
}
turo_simple_dict_s32(&ctx, argv[1], s32);
return 0;
}
static int cmd_turo_simple_exit_status(int argc, char **argv)
{
int32_t s32 = 0;
if (argc != 2) {
turo_simple_exit_status(&ctx, -13);
return 1;
}
if (_sc_arg2s32(argv[1], &s32) != 0) {
turo_simple_exit_status(&ctx, -14);
return 1;
}
turo_simple_exit_status(&ctx, (int)s32);
return 0;
}
static int cmd_test_multi_element_dict(int argc, char **argv)
{
if (argc != 5) {
turo_simple_exit_status(&ctx, -15);
return 1;
}
turo_container_open(&ctx);
turo_dict_open(&ctx);
turo_dict_key(&ctx, argv[1]);
turo_string(&ctx, argv[2]);
turo_dict_key(&ctx, argv[3]);
turo_string(&ctx, argv[4]);
turo_dict_close(&ctx);
turo_container_close(&ctx, 0);
return 0;
}
static int cmd_test_netif(int argc, char **argv)
{
(void) argc;
(void) argv;
turo_container_open(&ctx);
_netif_list(&ctx, 5);
_netif_list(&ctx, 6);
turo_container_close(&ctx, 0);
return 0;
}
static const shell_command_t shell_commands[] = {
{ "turo_simple_s32", "", cmd_turo_simple_s32 },
{ "turo_simple_array_u8", "", cmd_turo_simple_array_u8 },
{ "turo_simple_array_s32", "", cmd_turo_simple_array_s32 },
{ "turo_simple_dict_string", "", cmd_turo_simple_dict_string },
{ "turo_simple_dict_s32", "", cmd_turo_simple_dict_s32 },
{ "turo_simple_exit_status", "", cmd_turo_simple_exit_status },
{ "test_multi_element_dict", "", cmd_test_multi_element_dict },
{ "test_netif", "", cmd_test_netif },
{ NULL, NULL, NULL }
};
int main(void)
{
char line_buf[SHELL_DEFAULT_BUFSIZE];
puts("Test for the test utilities result output");
turo_init(&ctx);
shell_run(shell_commands, line_buf, SHELL_DEFAULT_BUFSIZE);
return 0;
}

151
tests/turo/tests/01-run.py Executable file
View File

@ -0,0 +1,151 @@
#! /usr/bin/env python3
# Copyright (C) 2021 HAW Hamburg
#
# This file is subject to the terms and conditions of the GNU Lesser
# General Public License v2.1. See the file LICENSE in the top level
# directory for more details.
import logging
import sys
import unittest
from riotctrl.ctrl import RIOTCtrl
from riotctrl.shell import ShellInteraction
from riotctrl.shell.json import RapidJSONShellInteractionParser
class TestTuroBase(unittest.TestCase):
DEBUG = False
@classmethod
def setUpClass(cls):
cls.ctrl = RIOTCtrl()
cls.ctrl.start_term()
if cls.DEBUG:
cls.ctrl.term.logfile = sys.stdout
cls.ctrl.reset()
cls.shell = ShellInteraction(cls.ctrl)
cls.json_parser = RapidJSONShellInteractionParser()
cls.logger = logging.getLogger(cls.__name__)
if cls.DEBUG:
cls.logger.setLevel(logging.DEBUG)
@classmethod
def tearDownClass(cls):
cls.ctrl.stop_term()
def exec_turo_cmd(self, cmd, timeout=-1, async_=False):
resp = self.shell.cmd(cmd, timeout, async_)
self.logger.debug(repr(resp))
return self._parse(resp)
def _parse(self, resp):
resp = self.json_parser.parse(resp)
if resp[-1]['exit_status'] != 0:
raise RuntimeError("{}".format(resp[-1]))
if len(resp) == 1:
return None
elif len(resp) == 2:
return resp[0]
return resp[:-1]
class TestTuro(TestTuroBase):
def test_turo_simple_s32(self):
vals = [0, -1, -2147483648, 2147483647]
for val in vals:
resp = self.exec_turo_cmd('turo_simple_s32 '
'{}'.format(val))
assert resp == val
def test_turo_simple_s32_fail(self):
vals = ["foo", -2147483649, 2147483648]
for val in vals:
with self.assertRaises(RuntimeError):
self.exec_turo_cmd('turo_simple_s32 '
'{}'.format(val))
def test_turo_simple_array_u8(self):
vals = [255, 0, 1]
cmd = 'turo_simple_array_u8 ' + ' '.join(map(str, vals))
resp = self.exec_turo_cmd(cmd)
self.assertCountEqual(resp, vals)
def test_turo_simple_array_u8_fail(self):
vals = ["foo", -1, 256]
for val in vals:
with self.assertRaises(RuntimeError):
self.exec_turo_cmd('turo_simple_array_u8 '
'{}'.format(val))
def test_turo_simple_array_s32(self):
vals = [0, -1, -2147483648, 2147483647]
cmd = 'turo_simple_array_s32 ' + ' '.join(map(str, vals))
resp = self.exec_turo_cmd(cmd)
self.assertCountEqual(resp, vals)
def test_turo_simple_array_s32_fail(self):
vals = ["foo", -2147483649, 2147483648]
for val in vals:
with self.assertRaises(RuntimeError):
self.exec_turo_cmd('turo_simple_array_s32 '
'{}'.format(val))
def test_turo_simple_dict_string(self):
test_dict = {'foo': 'bar', 'strnum': '42'}
for key, val in test_dict.items():
cmd = 'turo_simple_dict_string {} {}'.format(key, val)
resp = self.exec_turo_cmd(cmd)
assert resp[key] == val
def test_turo_simple_dict_string_fail(self):
pass
def test_turo_simple_dict_s32(self):
test_dict = {'foo': -1, 'bar': 2147483647}
for key, val in test_dict.items():
cmd = 'turo_simple_dict_s32 {} {}'.format(key, val)
resp = self.exec_turo_cmd(cmd)
assert resp[key] == val
def test_turo_simple_dict_s32_fail(self):
with self.assertRaises(RuntimeError):
self.exec_turo_cmd('turo_simple_dict_s32 foo bar')
def test_turo_simple_exit_status(self):
self.exec_turo_cmd('turo_simple_exit_status 0')
with self.assertRaises(RuntimeError):
self.exec_turo_cmd('turo_simple_exit_status -1')
def test_test_multi_element_dict(self):
test_dict = {'foo': 'bar', 'strnum': '42'}
cmd = 'test_multi_element_dict'
for key, val in test_dict.items():
cmd += ' {} {}'.format(key, val)
resp = self.exec_turo_cmd(cmd)
self.assertDictEqual(resp, test_dict)
def test_test_netif(self):
resp = self.exec_turo_cmd("test_netif")
assert resp[1]['netif']['num'] == 6
assert resp[0]['netif']['num'] == 5
addr = resp[0]['netif']['inet6 addr']['addr']
assert addr == 'fe80::2445:7fff:fe5a:6fd9'
scope = resp[0]['netif']['inet6 addr']['scope']
assert scope == 'link'
flags = resp[0]['netif']['flags']
for flag in flags:
if isinstance(flag, dict):
if "MTU" in flag.keys():
assert flag['MTU'] == 1500
break
else:
assert False, "MTU flag does not exist"
if __name__ == '__main__':
unittest.main()

View File

@ -0,0 +1,4 @@
# Since the asserts are a part of the check we need to enable them.
DEVELHELP=1
include $(RIOTBASE)/Makefile.base

View File

@ -0,0 +1 @@
USEMODULE += test_utils_result_output_check

View File

@ -0,0 +1,86 @@
/*
* Copyright (C) 2021 HAW Hamburg
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup tests
* @{
*
* @file
* @brief Unit tests for test_utils_result_output module
*
* @author Kevin Weiss <kevin.weiss@haw-hamburg.de>
*/
#include "embUnit.h"
#include "kernel_defines.h"
#include "test_utils/result_output.h"
void test_turo_impl(void)
{
turo_t ctx;
turo_init(&ctx);
turo_container_open(&ctx);
turo_s32(&ctx, -1);
turo_u32(&ctx, 0xFFFFFFFF);
turo_s64(&ctx, -1);
turo_u64(&ctx, 0xFFFFFFFFFFFFFFFF);
turo_float(&ctx, 0.00001);
turo_string(&ctx, "foo");
turo_bool(&ctx, true);
turo_dict_open(&ctx);
turo_dict_key(&ctx, "bar");
turo_u32(&ctx, 0);
turo_dict_close(&ctx);
turo_array_open(&ctx);
turo_u32(&ctx, 0);
turo_array_close(&ctx);
turo_container_close(&ctx, 0);
}
void test_turo_helpers(void)
{
turo_t ctx;
turo_init(&ctx);
turo_container_open(&ctx);
uint8_t buf8[] = {1, 2, 3};
int32_t buf32[] = {-1, 1, 0x7FFFFFFF};
turo_array_u8(&ctx, buf8, sizeof(buf8));
turo_array_s32(&ctx, buf32, ARRAY_SIZE(buf32));
turo_dict_string(&ctx, "foo", "bar");
turo_dict_s32(&ctx, "baz", 42);
turo_container_close(&ctx, 0);
}
void test_turo_simple(void)
{
turo_t ctx;
turo_init(&ctx);
turo_simple_s32(&ctx, 42);
uint8_t buf8[] = {1, 2, 3};
int32_t buf32[] = {-1, 1, 0x7FFFFFFF};
turo_simple_array_u8(&ctx, buf8, sizeof(buf8));
turo_simple_array_s32(&ctx, buf32, ARRAY_SIZE(buf32));
turo_simple_dict_string(&ctx, "foo", "bar");
turo_simple_dict_s32(&ctx, "baz", 42);
turo_simple_exit_status(&ctx, -1);
}
Test *tests_turo_all(void)
{
EMB_UNIT_TESTFIXTURES(fixtures) {
new_TestFixture(test_turo_impl),
new_TestFixture(test_turo_helpers),
new_TestFixture(test_turo_simple),
};
EMB_UNIT_TESTCALLER(turo_tests, NULL, NULL, fixtures);
return (Test *)&turo_tests;
}
void tests_turo(void)
{
TESTS_RUN(tests_turo_all());
}