Merge branch 'bugfix/stdatomic_64bit' into 'master'

newlib: define 64-bit stdatomic operations on 32-bit SMP SoCs

Closes IDFGH-703

See merge request espressif/esp-idf!13463
This commit is contained in:
Angus Gratton 2021-05-12 23:44:11 +00:00
commit 3ce98690e6
12 changed files with 379 additions and 79 deletions

View File

@ -395,12 +395,34 @@ test_app_test_esp32_generic:
- .component_ut_template - .component_ut_template
- .rules:test:component_ut-esp32 - .rules:test:component_ut-esp32
.component_ut_esp32s2_template:
extends:
- .component_ut_template
- .rules:test:component_ut-esp32s2
.component_ut_esp32c3_template:
extends:
- .component_ut_template
- .rules:test:component_ut-esp32c3
component_ut_test_001: component_ut_test_001:
extends: .component_ut_esp32_template extends: .component_ut_esp32_template
tags: tags:
- ESP32 - ESP32
- COMPONENT_UT_GENERIC - COMPONENT_UT_GENERIC
component_ut_test_esp32s2:
extends: .component_ut_esp32s2_template
tags:
- ESP32S2
- COMPONENT_UT_GENERIC
component_ut_test_esp32c3:
extends: .component_ut_esp32c3_template
tags:
- ESP32C3
- COMPONENT_UT_GENERIC
.unit_test_template: .unit_test_template:
extends: .target_test_job_template extends: .target_test_job_template
variables: variables:

View File

@ -1,4 +1,4 @@
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD // Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@ -17,61 +17,78 @@
#include "sdkconfig.h" #include "sdkconfig.h"
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include "soc/soc_caps.h"
#include "freertos/FreeRTOS.h"
#ifdef __XTENSA__ #ifdef __XTENSA__
#include "xtensa/config/core-isa.h" #include "xtensa/config/core-isa.h"
#include "xtensa/xtruntime.h"
// This allows nested interrupts disabling and restoring via local registers or stack.
// They can be called from interrupts too.
// WARNING: Only applies to current CPU.
#define _ATOMIC_ENTER_CRITICAL(void) ({ \
unsigned state = XTOS_SET_INTLEVEL(XCHAL_EXCM_LEVEL); \
atomic_benchmark_intr_disable(); \
state; \
})
#define _ATOMIC_EXIT_CRITICAL(state) do { \
atomic_benchmark_intr_restore(state); \
XTOS_RESTORE_JUST_INTLEVEL(state); \
} while (0)
#ifndef XCHAL_HAVE_S32C1I #ifndef XCHAL_HAVE_S32C1I
#error "XCHAL_HAVE_S32C1I not defined, include correct header!" #error "XCHAL_HAVE_S32C1I not defined, include correct header!"
#endif #endif
#define NO_ATOMICS_SUPPORT (XCHAL_HAVE_S32C1I == 0) #define HAS_ATOMICS_32 (XCHAL_HAVE_S32C1I == 1)
// no 64-bit atomics on Xtensa
#define HAS_ATOMICS_64 0
#else // RISCV #else // RISCV
#include "freertos/portmacro.h" // GCC toolchain will define this pre-processor if "A" extension is supported
#ifndef __riscv_atomic
#define __riscv_atomic 0
#endif
// This allows nested interrupts disabling and restoring via local registers or stack. #define HAS_ATOMICS_32 (__riscv_atomic == 1)
// They can be called from interrupts too. #define HAS_ATOMICS_64 ((__riscv_atomic == 1) && (__riscv_xlen == 64))
// WARNING: Only applies to current CPU. #endif // (__XTENSA__, __riscv)
#define _ATOMIC_ENTER_CRITICAL(void) ({ \
#if SOC_CPU_CORES_NUM == 1
// Single core SoC: atomics can be implemented using portENTER_CRITICAL_NESTED
// and portEXIT_CRITICAL_NESTED, which disable and enable interrupts.
#define _ATOMIC_ENTER_CRITICAL() ({ \
unsigned state = portENTER_CRITICAL_NESTED(); \ unsigned state = portENTER_CRITICAL_NESTED(); \
atomic_benchmark_intr_disable(); \
state; \ state; \
}) })
#define _ATOMIC_EXIT_CRITICAL(state) do { \ #define _ATOMIC_EXIT_CRITICAL(state) do { \
atomic_benchmark_intr_restore(state); \
portEXIT_CRITICAL_NESTED(state); \ portEXIT_CRITICAL_NESTED(state); \
} while (0) } while (0)
#ifndef __riscv_atomic // GCC toolchain will define this pre-processor if "A" extension is supported #else // SOC_CPU_CORES_NUM
#define __riscv_atomic 0
#endif
#define NO_ATOMICS_SUPPORT (__riscv_atomic == 0) _Static_assert(HAS_ATOMICS_32, "32-bit atomics should be supported if SOC_CPU_CORES_NUM > 1");
// Only need to implement 64-bit atomics here. Use a single global portMUX_TYPE spinlock
// to emulate the atomics.
static portMUX_TYPE s_atomic_lock = portMUX_INITIALIZER_UNLOCKED;
#endif // Return value is not used but kept for compatibility with the single-core version above.
#define _ATOMIC_ENTER_CRITICAL() ({ \
portENTER_CRITICAL_SAFE(&s_atomic_lock); \
0; \
})
#define _ATOMIC_EXIT_CRITICAL(state) do { \
(void) (state); \
portEXIT_CRITICAL_SAFE(&s_atomic_lock); \
} while(0)
//reserved to measure atomic operation time #endif // SOC_CPU_CORES_NUM
#define atomic_benchmark_intr_disable()
#define atomic_benchmark_intr_restore(STATE) #define ATOMIC_LOAD(n, type) type __atomic_load_ ## n (const type* mem, int memorder) \
{ \
unsigned state = _ATOMIC_ENTER_CRITICAL(); \
type ret = *mem; \
_ATOMIC_EXIT_CRITICAL(state); \
return ret; \
}
#define ATOMIC_STORE(n, type) void __atomic_store_ ## n (type* mem, type val, int memorder) \
{ \
unsigned state = _ATOMIC_ENTER_CRITICAL(); \
*mem = val; \
_ATOMIC_EXIT_CRITICAL(state); \
}
#define ATOMIC_EXCHANGE(n, type) type __atomic_exchange_ ## n (type* mem, type val, int memorder) \ #define ATOMIC_EXCHANGE(n, type) type __atomic_exchange_ ## n (type* mem, type val, int memorder) \
{ \ { \
@ -82,7 +99,7 @@
return ret; \ return ret; \
} }
#define CMP_EXCHANGE(n, type) bool __atomic_compare_exchange_ ## n (type* mem, type* expect, type desired, int success, int failure) \ #define CMP_EXCHANGE(n, type) bool __atomic_compare_exchange_ ## n (type* mem, type* expect, type desired, bool weak, int success, int failure) \
{ \ { \
bool ret = false; \ bool ret = false; \
unsigned state = _ATOMIC_ENTER_CRITICAL(); \ unsigned state = _ATOMIC_ENTER_CRITICAL(); \
@ -141,12 +158,12 @@
return ret; \ return ret; \
} }
#define SYNC_FETCH_OP(op, n, type) type __sync_fetch_and_ ## op ##_ ## n (type* ptr, type value, ...) \ #define SYNC_FETCH_OP(op, n, type) type __sync_fetch_and_ ## op ##_ ## n (type* ptr, type value) \
{ \ { \
return __atomic_fetch_ ## op ##_ ## n (ptr, value, __ATOMIC_SEQ_CST); \ return __atomic_fetch_ ## op ##_ ## n (ptr, value, __ATOMIC_SEQ_CST); \
} }
#define SYNC_BOOL_CMP_EXCHANGE(n, type) bool __sync_bool_compare_and_swap_ ## n (type *ptr, type oldval, type newval, ...) \ #define SYNC_BOOL_CMP_EXCHANGE(n, type) bool __sync_bool_compare_and_swap_ ## n (type *ptr, type oldval, type newval) \
{ \ { \
bool ret = false; \ bool ret = false; \
unsigned state = _ATOMIC_ENTER_CRITICAL(); \ unsigned state = _ATOMIC_ENTER_CRITICAL(); \
@ -158,7 +175,7 @@
return ret; \ return ret; \
} }
#define SYNC_VAL_CMP_EXCHANGE(n, type) type __sync_val_compare_and_swap_ ## n (type *ptr, type oldval, type newval, ...) \ #define SYNC_VAL_CMP_EXCHANGE(n, type) type __sync_val_compare_and_swap_ ## n (type *ptr, type oldval, type newval) \
{ \ { \
unsigned state = _ATOMIC_ENTER_CRITICAL(); \ unsigned state = _ATOMIC_ENTER_CRITICAL(); \
type ret = *ptr; \ type ret = *ptr; \
@ -169,78 +186,98 @@
return ret; \ return ret; \
} }
#pragma GCC diagnostic ignored "-Wbuiltin-declaration-mismatch" #if !HAS_ATOMICS_32
#if NO_ATOMICS_SUPPORT
ATOMIC_EXCHANGE(1, uint8_t) ATOMIC_EXCHANGE(1, uint8_t)
ATOMIC_EXCHANGE(2, uint16_t) ATOMIC_EXCHANGE(2, uint16_t)
ATOMIC_EXCHANGE(4, uint32_t) ATOMIC_EXCHANGE(4, uint32_t)
ATOMIC_EXCHANGE(8, uint64_t)
CMP_EXCHANGE(1, uint8_t) CMP_EXCHANGE(1, uint8_t)
CMP_EXCHANGE(2, uint16_t) CMP_EXCHANGE(2, uint16_t)
CMP_EXCHANGE(4, uint32_t) CMP_EXCHANGE(4, uint32_t)
CMP_EXCHANGE(8, uint64_t)
FETCH_ADD(1, uint8_t) FETCH_ADD(1, uint8_t)
FETCH_ADD(2, uint16_t) FETCH_ADD(2, uint16_t)
FETCH_ADD(4, uint32_t) FETCH_ADD(4, uint32_t)
FETCH_ADD(8, uint64_t)
FETCH_SUB(1, uint8_t) FETCH_SUB(1, uint8_t)
FETCH_SUB(2, uint16_t) FETCH_SUB(2, uint16_t)
FETCH_SUB(4, uint32_t) FETCH_SUB(4, uint32_t)
FETCH_SUB(8, uint64_t)
FETCH_AND(1, uint8_t) FETCH_AND(1, uint8_t)
FETCH_AND(2, uint16_t) FETCH_AND(2, uint16_t)
FETCH_AND(4, uint32_t) FETCH_AND(4, uint32_t)
FETCH_AND(8, uint64_t)
FETCH_OR(1, uint8_t) FETCH_OR(1, uint8_t)
FETCH_OR(2, uint16_t) FETCH_OR(2, uint16_t)
FETCH_OR(4, uint32_t) FETCH_OR(4, uint32_t)
FETCH_OR(8, uint64_t)
FETCH_XOR(1, uint8_t) FETCH_XOR(1, uint8_t)
FETCH_XOR(2, uint16_t) FETCH_XOR(2, uint16_t)
FETCH_XOR(4, uint32_t) FETCH_XOR(4, uint32_t)
FETCH_XOR(8, uint64_t)
SYNC_FETCH_OP(add, 1, uint8_t) SYNC_FETCH_OP(add, 1, uint8_t)
SYNC_FETCH_OP(add, 2, uint16_t) SYNC_FETCH_OP(add, 2, uint16_t)
SYNC_FETCH_OP(add, 4, uint32_t) SYNC_FETCH_OP(add, 4, uint32_t)
SYNC_FETCH_OP(add, 8, uint64_t)
SYNC_FETCH_OP(sub, 1, uint8_t) SYNC_FETCH_OP(sub, 1, uint8_t)
SYNC_FETCH_OP(sub, 2, uint16_t) SYNC_FETCH_OP(sub, 2, uint16_t)
SYNC_FETCH_OP(sub, 4, uint32_t) SYNC_FETCH_OP(sub, 4, uint32_t)
SYNC_FETCH_OP(sub, 8, uint64_t)
SYNC_FETCH_OP(and, 1, uint8_t) SYNC_FETCH_OP(and, 1, uint8_t)
SYNC_FETCH_OP(and, 2, uint16_t) SYNC_FETCH_OP(and, 2, uint16_t)
SYNC_FETCH_OP(and, 4, uint32_t) SYNC_FETCH_OP(and, 4, uint32_t)
SYNC_FETCH_OP(and, 8, uint64_t)
SYNC_FETCH_OP(or, 1, uint8_t) SYNC_FETCH_OP(or, 1, uint8_t)
SYNC_FETCH_OP(or, 2, uint16_t) SYNC_FETCH_OP(or, 2, uint16_t)
SYNC_FETCH_OP(or, 4, uint32_t) SYNC_FETCH_OP(or, 4, uint32_t)
SYNC_FETCH_OP(or, 8, uint64_t)
SYNC_FETCH_OP(xor, 1, uint8_t) SYNC_FETCH_OP(xor, 1, uint8_t)
SYNC_FETCH_OP(xor, 2, uint16_t) SYNC_FETCH_OP(xor, 2, uint16_t)
SYNC_FETCH_OP(xor, 4, uint32_t) SYNC_FETCH_OP(xor, 4, uint32_t)
SYNC_FETCH_OP(xor, 8, uint64_t)
SYNC_BOOL_CMP_EXCHANGE(1, uint8_t) SYNC_BOOL_CMP_EXCHANGE(1, uint8_t)
SYNC_BOOL_CMP_EXCHANGE(2, uint16_t) SYNC_BOOL_CMP_EXCHANGE(2, uint16_t)
SYNC_BOOL_CMP_EXCHANGE(4, uint32_t) SYNC_BOOL_CMP_EXCHANGE(4, uint32_t)
SYNC_BOOL_CMP_EXCHANGE(8, uint64_t)
SYNC_VAL_CMP_EXCHANGE(1, uint8_t) SYNC_VAL_CMP_EXCHANGE(1, uint8_t)
SYNC_VAL_CMP_EXCHANGE(2, uint16_t) SYNC_VAL_CMP_EXCHANGE(2, uint16_t)
SYNC_VAL_CMP_EXCHANGE(4, uint32_t) SYNC_VAL_CMP_EXCHANGE(4, uint32_t)
#endif // !HAS_ATOMICS_32
#if !HAS_ATOMICS_64
ATOMIC_LOAD(8, uint64_t)
ATOMIC_STORE(8, uint64_t)
ATOMIC_EXCHANGE(8, uint64_t)
CMP_EXCHANGE(8, uint64_t)
FETCH_ADD(8, uint64_t)
FETCH_SUB(8, uint64_t)
FETCH_AND(8, uint64_t)
FETCH_OR(8, uint64_t)
FETCH_XOR(8, uint64_t)
SYNC_FETCH_OP(add, 8, uint64_t)
SYNC_FETCH_OP(sub, 8, uint64_t)
SYNC_FETCH_OP(and, 8, uint64_t)
SYNC_FETCH_OP(or, 8, uint64_t)
SYNC_FETCH_OP(xor, 8, uint64_t)
SYNC_BOOL_CMP_EXCHANGE(8, uint64_t)
SYNC_VAL_CMP_EXCHANGE(8, uint64_t) SYNC_VAL_CMP_EXCHANGE(8, uint64_t)
#endif #endif // !HAS_ATOMICS_64

View File

@ -0,0 +1,8 @@
# This is the project CMakeLists.txt file for the test subproject
cmake_minimum_required(VERSION 3.5)
set(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components")
set(COMPONENTS main)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(newlib_test)

View File

@ -0,0 +1,19 @@
import tiny_test_fw # noqa: F401 # pylint: disable=unused-import
import ttfw_idf
try:
import typing # noqa: F401 # pylint: disable=unused-import
except ImportError:
pass
@ttfw_idf.idf_component_unit_test(env_tag='COMPONENT_UT_GENERIC', target=['esp32', 'esp32s2', 'esp32c3'])
def test_component_ut_newlib(env, _): # type: (tiny_test_fw.Env, typing.Any) -> None
dut = env.get_dut('newlib', 'components/newlib/test_apps')
dut.start_app()
stdout = dut.expect('Tests finished, rc=0', full_stdout=True)
ttfw_idf.ComponentUTResult.parse_result(stdout)
if __name__ == '__main__':
test_component_ut_newlib()

View File

@ -0,0 +1,5 @@
idf_component_register(SRCS
"test_newlib_main.c"
"test_stdatomic.c"
REQUIRES test_utils
PRIV_REQUIRES unity)

View File

@ -0,0 +1,12 @@
#include "unity.h"
#include "unity_fixture.h"
static void run_all_tests(void)
{
RUN_TEST_GROUP(stdatomic);
}
void app_main(void)
{
UNITY_MAIN_FUNC(run_all_tests);
}

View File

@ -0,0 +1,131 @@
#include <assert.h>
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <stdatomic.h>
#include <stdio.h>
#include <pthread.h>
#include "unity.h"
#include "unity_fixture.h"
/* non-static to prevent optimization */
atomic_ullong g_atomic64;
atomic_uint g_atomic32;
atomic_ushort g_atomic16;
atomic_uchar g_atomic8;
TEST_GROUP(stdatomic);
TEST_SETUP(stdatomic)
{
}
TEST_TEAR_DOWN(stdatomic)
{
}
TEST(stdatomic, test_64bit_atomics)
{
unsigned long long x64 = 0;
g_atomic64 = 0; // calls atomic_store
x64 += atomic_fetch_or (&g_atomic64, 0x1111111111111111ULL);
x64 += atomic_fetch_xor(&g_atomic64, 0x3333333333333333ULL);
x64 += atomic_fetch_and(&g_atomic64, 0xf0f0f0f0f0f0f0f0ULL);
x64 += atomic_fetch_sub(&g_atomic64, 0x0f0f0f0f0f0f0f0fULL);
x64 += atomic_fetch_add(&g_atomic64, 0x2222222222222222ULL);
TEST_ASSERT_EQUAL_HEX64(0x6464646464646464ULL, x64);
TEST_ASSERT_EQUAL_HEX64(0x3333333333333333ULL, g_atomic64); // calls atomic_load
}
TEST(stdatomic, test_32bit_atomics)
{
unsigned int x32 = 0;
g_atomic32 = 0;
x32 += atomic_fetch_or (&g_atomic32, 0x11111111U);
x32 += atomic_fetch_xor(&g_atomic32, 0x33333333U);
x32 += atomic_fetch_and(&g_atomic32, 0xf0f0f0f0U);
x32 += atomic_fetch_sub(&g_atomic32, 0x0f0f0f0fU);
x32 += atomic_fetch_add(&g_atomic32, 0x22222222U);
TEST_ASSERT_EQUAL_HEX32(0x64646464, x32);
TEST_ASSERT_EQUAL_HEX32(0x33333333, g_atomic32);
}
TEST(stdatomic, test_16bit_atomics)
{
unsigned int x16 = 0;
g_atomic16 = 0;
x16 += atomic_fetch_or (&g_atomic16, 0x1111);
x16 += atomic_fetch_xor(&g_atomic16, 0x3333);
x16 += atomic_fetch_and(&g_atomic16, 0xf0f0);
x16 += atomic_fetch_sub(&g_atomic16, 0x0f0f);
x16 += atomic_fetch_add(&g_atomic16, 0x2222);
TEST_ASSERT_EQUAL_HEX16(0x6464, x16);
TEST_ASSERT_EQUAL_HEX16(0x3333, g_atomic16);
}
TEST(stdatomic, test_8bit_atomics)
{
unsigned int x8 = 0;
g_atomic8 = 0;
x8 += atomic_fetch_or (&g_atomic8, 0x11);
x8 += atomic_fetch_xor(&g_atomic8, 0x33);
x8 += atomic_fetch_and(&g_atomic8, 0xf0);
x8 += atomic_fetch_sub(&g_atomic8, 0x0f);
x8 += atomic_fetch_add(&g_atomic8, 0x22);
TEST_ASSERT_EQUAL_HEX8(0x64, x8);
TEST_ASSERT_EQUAL_HEX16(0x33, g_atomic8);
}
static void *exclusion_test_task(void *arg);
TEST(stdatomic, test_exclusion)
{
/* Check 64-bit atomics for exclusion.
* Only atomic_fetch_add/sub are checked, since all 64-bit atomics use the
* same locking implementation.
*/
g_atomic64 = 0;
pthread_t thread1;
pthread_t thread2;
pthread_create(&thread1, NULL, exclusion_test_task, (void*) 1);
pthread_create(&thread2, NULL, exclusion_test_task, (void*) 0);
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
TEST_ASSERT_EQUAL(0, g_atomic64);
}
/* Two threads run in parallel, incrementing and decrementing
* a single 64-bit variable. In the end the variable should
* have the same value as at the start.
*/
static void* exclusion_test_task(void *varg)
{
int arg = (int) varg;
for (int i = 0; i < 1000000; ++i) {
if (arg == 0) {
atomic_fetch_add(&g_atomic64, 1ULL);
} else {
atomic_fetch_sub(&g_atomic64, 1ULL);
}
}
return NULL;
}
TEST_GROUP_RUNNER(stdatomic)
{
RUN_TEST_CASE(stdatomic, test_64bit_atomics)
RUN_TEST_CASE(stdatomic, test_32bit_atomics)
RUN_TEST_CASE(stdatomic, test_16bit_atomics)
RUN_TEST_CASE(stdatomic, test_8bit_atomics)
RUN_TEST_CASE(stdatomic, test_exclusion)
}

View File

@ -0,0 +1,3 @@
CONFIG_UNITY_ENABLE_FIXTURE=y
CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=n
CONFIG_UNITY_ENABLE_64BIT=y

View File

@ -13,6 +13,14 @@ menu "Unity unit testing library"
help help
If not set, assertions on double arguments will not be available. If not set, assertions on double arguments will not be available.
config UNITY_ENABLE_64BIT
bool "Support for 64-bit integer types"
default n
help
If not set, assertions on 64-bit integer types will always fail.
If this feature is enabled, take care not to pass pointers (which are 32 bit)
to UNITY_ASSERT_EQUAL, as that will cause pointer-to-int-cast warnings.
config UNITY_ENABLE_COLOR config UNITY_ENABLE_COLOR
bool "Colorize test output" bool "Colorize test output"
default n default n

View File

@ -21,13 +21,16 @@
#define UNITY_EXCLUDE_DOUBLE #define UNITY_EXCLUDE_DOUBLE
#endif //CONFIG_UNITY_ENABLE_DOUBLE #endif //CONFIG_UNITY_ENABLE_DOUBLE
#ifdef CONFIG_UNITY_ENABLE_64BIT
#define UNITY_SUPPORT_64
#endif
#ifdef CONFIG_UNITY_ENABLE_COLOR #ifdef CONFIG_UNITY_ENABLE_COLOR
#define UNITY_OUTPUT_COLOR #define UNITY_OUTPUT_COLOR
#endif #endif
#define UNITY_EXCLUDE_TIME_H #define UNITY_EXCLUDE_TIME_H
void unity_flush(void); void unity_flush(void);
void unity_putc(int c); void unity_putc(int c);
void unity_gets(char* dst, size_t len); void unity_gets(char* dst, size_t len);

View File

@ -1,25 +1,78 @@
/* IDF-specific additions to "Unity Fixture" */ // Copyright 2016-2021 Espressif Systems (Shanghai) Co. Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/* IDF-specific additions to "Unity Fixture".
* This file doesn't need to be included directly, it gets included into unity.h
* through unity_config.h.
*/
#pragma once #pragma once
#ifndef CONFIG_IDF_TARGET #include "sdkconfig.h"
/* A shorthand for running one test group from the main function */ #ifdef __cplusplus
#define UNITY_MAIN(group_) do { \ extern "C" {
#endif
#if !defined(CONFIG_IDF_TARGET) || defined(CONFIG_IDF_TARGET_LINUX)
#define UNITY_MAYBE_EXIT(rc) do { exit(rc); } while(0)
#else
#define UNITY_MAYBE_EXIT(rc) do { (void) rc; } while(0)
#endif
/* A shorthand for running all tests called from one function "func_", from the app_main function.
* Use this when there is more than one test group.
*
* Example:
*
* #include "unity.h"
* #include "unity_fixture.h"
*
* static_void run_all_tests(void) {
* RUN_TEST_GROUP(group1); // test group defined in another file, e.g. test_group1.c
* RUN_TEST_GROUP(group2); // test group defined in another file, e.g. test_group2.c
* }
*
* void app_main(void) {
* UNITY_MAIN_FUNC(run_all_tests);
* }
*/
#define UNITY_MAIN_FUNC(func_) do { \
const char* argv[] = { "test", "-v" }; \ const char* argv[] = { "test", "-v" }; \
const int argc = sizeof(argv)/sizeof(argv[0]); \ const int argc = sizeof(argv)/sizeof(argv[0]); \
int rc = UnityMain(argc, argv, TEST_ ## group_ ## _GROUP_RUNNER); \ int rc = UnityMain(argc, argv, func_); \
printf("\nTests finished, rc=%d\n", rc); \ printf("\nTests finished, rc=%d\n", rc); \
exit(rc); \ UNITY_MAYBE_EXIT(rc); \
} while(0) } while(0)
#else // CONFIG_IDF_TARGET /* A shorthand for running one test group from the app_main function, when there is only
* one test group and it is defined in the same file.
*
* Example:
*
* #include "unity.h"
* #include "unity_fixture.h"
*
* TEST_GROUP(my_feature);
* // also define TEST_SETUP, TEST_TEARDOWN, TESTs, TEST_GROUP_RUNNER
*
* void app_main(void) {
* UNITY_MAIN(my_feature);
* }
*/
#define UNITY_MAIN(group_) UNITY_MAIN_FUNC(TEST_ ## group_ ## _GROUP_RUNNER)
/* A shorthand for running one test group from the main function */ #ifdef __cplusplus
#define UNITY_MAIN(group_) do { \ }
const char* argv[] = { "test", "-v" }; \ #endif
const int argc = sizeof(argv)/sizeof(argv[0]); \
int rc = UnityMain(argc, argv, TEST_ ## group_ ## _GROUP_RUNNER); \
printf("\nTests finished, rc=%d\n", rc); \
} while(0)
#endif // CONFIG_IDF_TARGET

View File

@ -82,7 +82,6 @@ components/spiffs/include/spiffs_config.h
components/unity/unity/src/unity_internals.h components/unity/unity/src/unity_internals.h
components/unity/unity/extras/ components/unity/unity/extras/
components/unity/include/unity_fixture_extras.h
components/unity/include/unity_config.h components/unity/include/unity_config.h
components/unity/include/unity_test_runner.h components/unity/include/unity_test_runner.h