mirror of
https://github.com/eclipse-threadx/threadx.git
synced 2026-10-06 06:59:08 +08:00
Added a regression suite for the FreeRTOS compatibility layer (#583)
* Fixed the resource leaks on the xQueueCreate error paths xQueueCreate() allocated the queue descriptor and its backing memory, then created two ThreadX semaphores, and returned NULL on either semaphore failure without releasing anything. Since no handle reached the caller, vQueueDelete() could not be used to recover, so both allocations were lost. A failure on the second semaphore additionally abandoned the read semaphore it had already created, leaving a live ThreadX control block inside freed memory. Release the backing memory and the descriptor on both paths, and delete the read semaphore before returning when the write semaphore cannot be created. This is the teardown order vQueueDelete() already uses, and it matches the cleanup xTaskCreate() performs on its own error paths. Verified with a fault injection harness that intercepts the ThreadX byte pool and semaphore entry points to force tx_semaphore_create() to fail on a chosen call. On a read semaphore failure the layer previously performed 2 allocations and 0 releases, and on a write semaphore failure 2 allocations, 0 releases and 0 semaphore deletions. It now performs 2 releases in both cases and deletes the read semaphore in the second, with the byte pool restored to its prior state. Fixes https://github.com/eclipse-threadx/threadx/issues/570 Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com> * Added a regression suite for the FreeRTOS compatibility layer The compatibility layer had no tests in this repository, which is awkward for its creation functions in particular. Each of them takes one or two byte pool allocations for its bookkeeping and then creates ThreadX kernel objects, and each returns NULL when a kernel object cannot be created. The caller is left without a handle, so it cannot call the matching delete function, and anything the layer failed to release is gone until the system restarts. A leaking version and a correct version are indistinguishable from the outside, which is how the leak in issue 570 went unnoticed. Add a suite that counts what the layer takes and gives back. A test asks the harness to fail a chosen kernel creation call, then checks the number of byte pool allocations, releases, object creations and object deletions performed. The ThreadX entry points are intercepted with the linker's --wrap so that tx_freertos.c is compiled exactly as it ships, with no test hooks in it. Note that tx_api.h maps the public API onto the error checking entry points, so the _txe_ symbols are the ones wrapped. Coverage is the creation and teardown paths of queues, tasks, semaphores, mutexes, event groups and timers, including a regression test for the two paths fixed for issue 570. The suite follows the layout of the existing ThreadX and SMP suites, is registered with ctest, and runs in CI through the shared regression template. It is built 32 bit because the Linux port defines ULONG as unsigned int on x86_64 while the layer passes pointers through ULONG arguments, so a 64 bit build truncates them. It is Linux only because --wrap has no MSVC equivalent, and the CMake configuration says so rather than failing at link time. Validated by building the suite against the layer as it stands before the issue 570 fix, where the two expected checks fail with the leaked counts, and against the fixed layer, where all three tests pass. Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>
This commit is contained in:
@@ -41,6 +41,24 @@ jobs:
|
||||
cmake_path: ./test/smp/cmake
|
||||
result_affix: SMP
|
||||
skip_deploy: true
|
||||
freertos:
|
||||
permissions:
|
||||
contents: read
|
||||
issues: read
|
||||
checks: write
|
||||
pull-requests: write
|
||||
pages: write
|
||||
id-token: write
|
||||
uses: ./.github/workflows/regression_template.yml
|
||||
with:
|
||||
build_script: ./scripts/build_freertos.sh
|
||||
test_script: ./scripts/test_freertos.sh
|
||||
cmake_path: ./test/freertos/cmake
|
||||
result_affix: FreeRTOS
|
||||
skip_deploy: true
|
||||
# No coverage build configuration yet; the suite covers the creation
|
||||
# paths of the layer rather than all of it.
|
||||
skip_coverage: true
|
||||
# riscv: disabled — re-enable when RISC-V CI is ready
|
||||
# riscv:
|
||||
# permissions:
|
||||
|
||||
Executable
+17
@@ -0,0 +1,17 @@
|
||||
#!/bin/bash
|
||||
##############################################################################
|
||||
# Copyright (C) 2026 Eclipse ThreadX contributors
|
||||
#
|
||||
# This program and the accompanying materials are made available under the
|
||||
# terms of the MIT License which is available at
|
||||
# https://opensource.org/licenses/MIT.
|
||||
#
|
||||
# AI Disclosure: This file was largely AI-generated by Claude Code (Opus 5).
|
||||
# The AI-generated portions may be considered public domain (CC0-1.0)
|
||||
# and not subject to the project's licence. The human contributor has
|
||||
# reviewed and verified that the code is correct.
|
||||
#
|
||||
# SPDX-License-Identifier: MIT and CC0-1.0
|
||||
##############################################################################
|
||||
|
||||
$(dirname `realpath $0`)/../test/freertos/cmake/run.sh build all
|
||||
Executable
+17
@@ -0,0 +1,17 @@
|
||||
#!/bin/bash
|
||||
##############################################################################
|
||||
# Copyright (C) 2026 Eclipse ThreadX contributors
|
||||
#
|
||||
# This program and the accompanying materials are made available under the
|
||||
# terms of the MIT License which is available at
|
||||
# https://opensource.org/licenses/MIT.
|
||||
#
|
||||
# AI Disclosure: This file was largely AI-generated by Claude Code (Opus 5).
|
||||
# The AI-generated portions may be considered public domain (CC0-1.0)
|
||||
# and not subject to the project's licence. The human contributor has
|
||||
# reviewed and verified that the code is correct.
|
||||
#
|
||||
# SPDX-License-Identifier: MIT and CC0-1.0
|
||||
##############################################################################
|
||||
|
||||
CTEST_PARALLEL_LEVEL=4 $(dirname `realpath $0`)/../test/freertos/cmake/run.sh test all
|
||||
@@ -0,0 +1,78 @@
|
||||
##############################################################################
|
||||
# Copyright (C) 2026 Eclipse ThreadX contributors
|
||||
#
|
||||
# This program and the accompanying materials are made available under the
|
||||
# terms of the MIT License which is available at
|
||||
# https://opensource.org/licenses/MIT.
|
||||
#
|
||||
# AI Disclosure: This file was largely AI-generated by Claude Code (Opus 5).
|
||||
# The AI-generated portions may be considered public domain (CC0-1.0)
|
||||
# and not subject to the project's licence. The human contributor has
|
||||
# reviewed and verified that the code is correct.
|
||||
#
|
||||
# SPDX-License-Identifier: MIT and CC0-1.0
|
||||
##############################################################################
|
||||
|
||||
cmake_minimum_required(VERSION 3.13 FATAL_ERROR)
|
||||
cmake_policy(SET CMP0054 NEW)
|
||||
cmake_policy(SET CMP0057 NEW)
|
||||
|
||||
project(threadx_freertos_test LANGUAGES C)
|
||||
|
||||
set(CMAKE_C_STANDARD 99)
|
||||
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_C_EXTENSIONS OFF)
|
||||
|
||||
# The suite intercepts the ThreadX entry points with the linker's --wrap, which
|
||||
# is a GNU ld and lld feature. There is no MSVC equivalent, so the suite is
|
||||
# built on Linux only and the build stops early elsewhere rather than failing
|
||||
# later with confusing link errors.
|
||||
if(NOT CMAKE_C_COMPILER_ID MATCHES "GNU|Clang")
|
||||
message(FATAL_ERROR
|
||||
"The FreeRTOS compatibility layer tests require GNU ld or lld for "
|
||||
"--wrap support and are supported on Linux only.")
|
||||
endif()
|
||||
|
||||
set(BUILD_CONFIGURATIONS default_build)
|
||||
set(CMAKE_CONFIGURATION_TYPES
|
||||
${BUILD_CONFIGURATIONS}
|
||||
CACHE STRING "list of supported configuration types" FORCE)
|
||||
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
|
||||
${CMAKE_CONFIGURATION_TYPES})
|
||||
list(GET CMAKE_CONFIGURATION_TYPES 0 BUILD_TYPE)
|
||||
if((NOT CMAKE_BUILD_TYPE) OR (NOT ("${CMAKE_BUILD_TYPE}" IN_LIST
|
||||
CMAKE_CONFIGURATION_TYPES)))
|
||||
set(CMAKE_BUILD_TYPE
|
||||
"${BUILD_TYPE}"
|
||||
CACHE STRING "Build Type of the project" FORCE)
|
||||
endif()
|
||||
|
||||
message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
|
||||
message(STATUS "Using toolchain file: ${CMAKE_TOOLCHAIN_FILE}.")
|
||||
|
||||
# Built 32 bit, as the ThreadX and SMP suites are. This is not incidental for
|
||||
# this suite: the Linux port defines ULONG as unsigned int on x86_64, while the
|
||||
# compatibility layer passes pointers through ULONG arguments, for instance the
|
||||
# task argument and the timer identifier. A 64 bit build therefore truncates
|
||||
# those pointers, which the compiler reports as -Wpointer-to-int-cast and which
|
||||
# would crash the timer callback wrapper.
|
||||
add_compile_options(-m32)
|
||||
add_link_options(-m32)
|
||||
|
||||
# Applied to everything, including the ThreadX build pulled in below. The
|
||||
# strict warning set is applied to the test code alone, in the regression
|
||||
# CMakeLists, since neither ThreadX nor the layer under test builds clean
|
||||
# under -Wextra today and that is not this suite's to fix.
|
||||
add_compile_options(-ggdb -g3 -fdiagnostics-color)
|
||||
|
||||
enable_testing()
|
||||
|
||||
# The compatibility layer stores a back pointer in each thread control block,
|
||||
# so ThreadX has to be built with the matching user extension.
|
||||
set(TX_USER_FILE
|
||||
${CMAKE_CURRENT_LIST_DIR}/../fixtures/tx_user.h
|
||||
CACHE FILEPATH "tx_user.h used for the FreeRTOS layer tests" FORCE)
|
||||
|
||||
add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/../../.. threadx)
|
||||
|
||||
add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/regression)
|
||||
@@ -0,0 +1,83 @@
|
||||
##############################################################################
|
||||
# Copyright (C) 2026 Eclipse ThreadX contributors
|
||||
#
|
||||
# This program and the accompanying materials are made available under the
|
||||
# terms of the MIT License which is available at
|
||||
# https://opensource.org/licenses/MIT.
|
||||
#
|
||||
# AI Disclosure: This file was largely AI-generated by Claude Code (Opus 5).
|
||||
# The AI-generated portions may be considered public domain (CC0-1.0)
|
||||
# and not subject to the project's licence. The human contributor has
|
||||
# reviewed and verified that the code is correct.
|
||||
#
|
||||
# SPDX-License-Identifier: MIT and CC0-1.0
|
||||
##############################################################################
|
||||
|
||||
cmake_minimum_required(VERSION 3.13 FATAL_ERROR)
|
||||
cmake_policy(SET CMP0057 NEW)
|
||||
|
||||
project(freertos_regression_test LANGUAGES C)
|
||||
|
||||
set(SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/../../regression)
|
||||
set(FIXTURE_DIR ${CMAKE_CURRENT_LIST_DIR}/../../fixtures)
|
||||
set(REPO_ROOT ${CMAKE_CURRENT_LIST_DIR}/../../../..)
|
||||
set(LAYER_SOURCE ${REPO_ROOT}/utility/rtos_compatibility_layers/FreeRTOS/tx_freertos.c)
|
||||
set(LAYER_INCLUDE_DIR ${REPO_ROOT}/utility/rtos_compatibility_layers/FreeRTOS)
|
||||
|
||||
set(freertos_test_cases
|
||||
${SOURCE_DIR}/txfr_queue_create_test.c
|
||||
${SOURCE_DIR}/txfr_task_create_test.c
|
||||
${SOURCE_DIR}/txfr_sync_create_test.c)
|
||||
|
||||
# tx_api.h maps the public API onto the error checking entry points, so those
|
||||
# are the symbols that exist at link time and the ones the harness wraps. A
|
||||
# name that does not resolve is silently ignored by the linker, so this list
|
||||
# must be kept in step with the __wrap_ functions in txfr_test_harness.c.
|
||||
set(wrapped_symbols
|
||||
_txe_byte_allocate
|
||||
_txe_byte_release
|
||||
_txe_semaphore_create
|
||||
_txe_semaphore_delete
|
||||
_txe_mutex_create
|
||||
_txe_mutex_delete
|
||||
_txe_event_flags_create
|
||||
_txe_event_flags_delete
|
||||
_txe_timer_create
|
||||
_txe_timer_delete
|
||||
_txe_thread_create
|
||||
_txe_thread_delete)
|
||||
|
||||
set(wrap_link_options "")
|
||||
foreach(symbol ${wrapped_symbols})
|
||||
list(APPEND wrap_link_options "-Wl,--wrap=${symbol}")
|
||||
endforeach()
|
||||
|
||||
# The layer is compiled from source into each test rather than linked from the
|
||||
# freertos-threadx target, so that the test's FreeRTOSConfig.h applies.
|
||||
add_library(freertos_layer_under_test OBJECT ${LAYER_SOURCE})
|
||||
target_include_directories(freertos_layer_under_test
|
||||
PUBLIC ${LAYER_INCLUDE_DIR} ${FIXTURE_DIR})
|
||||
target_link_libraries(freertos_layer_under_test PUBLIC azrtos::threadx)
|
||||
|
||||
set(test_warning_options -Wall -Wextra -Werror)
|
||||
|
||||
add_library(freertos_test_harness OBJECT ${SOURCE_DIR}/txfr_test_harness.c)
|
||||
target_include_directories(freertos_test_harness
|
||||
PUBLIC ${SOURCE_DIR} ${LAYER_INCLUDE_DIR} ${FIXTURE_DIR})
|
||||
target_link_libraries(freertos_test_harness PUBLIC azrtos::threadx)
|
||||
target_compile_options(freertos_test_harness PRIVATE ${test_warning_options})
|
||||
|
||||
foreach(test_case ${freertos_test_cases})
|
||||
get_filename_component(test_name ${test_case} NAME_WE)
|
||||
|
||||
add_executable(${test_name} ${test_case}
|
||||
$<TARGET_OBJECTS:freertos_test_harness>
|
||||
$<TARGET_OBJECTS:freertos_layer_under_test>)
|
||||
target_include_directories(${test_name}
|
||||
PRIVATE ${SOURCE_DIR} ${LAYER_INCLUDE_DIR} ${FIXTURE_DIR})
|
||||
target_link_libraries(${test_name} PRIVATE azrtos::threadx)
|
||||
target_link_options(${test_name} PRIVATE ${wrap_link_options})
|
||||
target_compile_options(${test_name} PRIVATE ${test_warning_options})
|
||||
|
||||
add_test(${CMAKE_BUILD_TYPE}::${test_name} ${test_name})
|
||||
endforeach()
|
||||
Executable
+112
@@ -0,0 +1,112 @@
|
||||
#!/bin/bash
|
||||
##############################################################################
|
||||
# Copyright (C) 2026 Eclipse ThreadX contributors
|
||||
#
|
||||
# This program and the accompanying materials are made available under the
|
||||
# terms of the MIT License which is available at
|
||||
# https://opensource.org/licenses/MIT.
|
||||
#
|
||||
# AI Disclosure: This file was largely AI-generated by Claude Code (Opus 5).
|
||||
# The AI-generated portions may be considered public domain (CC0-1.0)
|
||||
# and not subject to the project's licence. The human contributor has
|
||||
# reviewed and verified that the code is correct.
|
||||
#
|
||||
# SPDX-License-Identifier: MIT and CC0-1.0
|
||||
##############################################################################
|
||||
|
||||
# Builds and runs the FreeRTOS compatibility layer regression suite. The
|
||||
# interface matches test/tx/cmake/run.sh so that the shared CI template can
|
||||
# drive it: "run.sh build all" then "run.sh test all".
|
||||
|
||||
set -e
|
||||
|
||||
function help() {
|
||||
echo "Usage: $0 [build|test] [all|<build_configuration> <build_configuration>...]"
|
||||
echo "Available build_configuration:"
|
||||
for build in ${build_configurations[*]}; do
|
||||
echo " $build"
|
||||
done
|
||||
exit 1
|
||||
}
|
||||
|
||||
function validate() {
|
||||
for build in ${build_configurations[*]}; do
|
||||
if [ "$1" == "$build" ]; then
|
||||
return
|
||||
fi
|
||||
done
|
||||
help
|
||||
}
|
||||
|
||||
function generate() {
|
||||
build=$1
|
||||
cmake -Bbuild/$build -GNinja -DCMAKE_TOOLCHAIN_FILE=$(dirname $(realpath $0))/../../../cmake/linux.cmake -DCMAKE_BUILD_TYPE=$build .
|
||||
}
|
||||
|
||||
function build() {
|
||||
cmake --build build/$1
|
||||
}
|
||||
|
||||
function test() {
|
||||
pushd build/$1
|
||||
[ -z "${CTEST_PARALLEL_LEVEL}" ] && parallel="-j$2"
|
||||
if [ -z "${CTEST_REPEAT_FAIL}" ];
|
||||
then
|
||||
repeat_fail=2
|
||||
else
|
||||
repeat_fail=${CTEST_REPEAT_FAIL}
|
||||
fi
|
||||
ctest $parallel --timeout 300 -O $1.txt -T test --no-compress-output --test-output-size-passed 4194304 --test-output-size-failed 4194304 --output-on-failure --repeat until-pass:${repeat_fail} --output-junit $1.xml
|
||||
popd
|
||||
grep -E "^(\s*[0-9]+|Total)" build/$1/$1.txt >build/$1.txt
|
||||
sed -i "s/\x1B\[[0-9;]*[JKmsu]//g" build/$1.txt
|
||||
}
|
||||
|
||||
cd $(dirname $0)
|
||||
|
||||
result=$(sed -n "/(BUILD_CONFIGURATIONS/,/)/p" CMakeLists.txt|sed ':label;N;s/\n/ /;b label'|grep -Pzo "[a-zA-Z0-9_]*build[a-zA-Z0-9_]*\s*"| tr -d '\0')
|
||||
IFS=' '
|
||||
read -ra build_configurations <<< "$result"
|
||||
|
||||
if [ $# -lt 1 ]; then
|
||||
help
|
||||
fi
|
||||
|
||||
command=$1
|
||||
shift
|
||||
|
||||
if [ "$#" == "0" ]; then
|
||||
builds=${build_configurations[0]}
|
||||
elif [ "$*" == "all" ]; then
|
||||
builds=${build_configurations[@]}
|
||||
else
|
||||
for item in $*; do
|
||||
validate $item
|
||||
done
|
||||
builds=$*
|
||||
fi
|
||||
|
||||
if [ "$command" == "build" ]; then
|
||||
for item in $builds; do
|
||||
generate $item
|
||||
echo ""
|
||||
done
|
||||
|
||||
for item in $builds; do
|
||||
echo "Building $item"
|
||||
build $item
|
||||
echo ""
|
||||
done
|
||||
elif [ "$command" == "test" ]; then
|
||||
cores=$(nproc)
|
||||
if [ -z "${CTEST_PARALLEL_LEVEL}" ];
|
||||
then
|
||||
parallel_jobs=$(($cores + 2))
|
||||
fi
|
||||
for item in $builds; do
|
||||
echo "Testing $item"
|
||||
test $item $parallel_jobs
|
||||
done
|
||||
else
|
||||
help
|
||||
fi
|
||||
@@ -0,0 +1,66 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2026 Eclipse ThreadX contributors
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the MIT License which is available at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*
|
||||
* AI Disclosure: This file was largely AI-generated by Claude Code (Opus 5).
|
||||
* The AI-generated portions may be considered public domain (CC0-1.0)
|
||||
* and not subject to the project's licence. The human contributor has
|
||||
* reviewed and verified that the code is correct.
|
||||
*
|
||||
* SPDX-License-Identifier: MIT and CC0-1.0
|
||||
**************************************************************************/
|
||||
|
||||
/**************************************************************************/
|
||||
/** */
|
||||
/** ThreadX Component */
|
||||
/** */
|
||||
/** FreeRTOS compatibility layer regression test configuration */
|
||||
/** */
|
||||
/**************************************************************************/
|
||||
|
||||
/* Configuration used to build the compatibility layer under test. It follows
|
||||
the shipped template in utility/rtos_compatibility_layers/FreeRTOS/
|
||||
config_template, with the choices a test build needs called out below. */
|
||||
|
||||
#ifndef FREERTOS_CONFIG_H
|
||||
#define FREERTOS_CONFIG_H
|
||||
|
||||
#define configTICK_RATE_HZ (1000u)
|
||||
#define configMAX_PRIORITIES (32u)
|
||||
#define configMINIMAL_STACK_SIZE (512u)
|
||||
|
||||
/* Large enough that a handful of leaked descriptors cannot exhaust it. The
|
||||
tests therefore never infer a leak from an allocation failure; they count
|
||||
the byte pool operations directly. */
|
||||
#define configTOTAL_HEAP_SIZE (1024u * 128u)
|
||||
|
||||
#define configUSE_16_BIT_TICKS 0
|
||||
#define configSTACK_DEPTH_TYPE uint32_t
|
||||
|
||||
/* Task deletion is included so that the idle task, and with it the teardown
|
||||
path the tests exercise, is built. */
|
||||
#define INCLUDE_vTaskDelete 1
|
||||
|
||||
/* The tests drive the error paths deliberately, so neither assertion may halt
|
||||
the run. A failed expectation is reported by the harness instead. */
|
||||
#define configASSERT(x)
|
||||
#define TX_FREERTOS_ASSERT_FAIL()
|
||||
|
||||
/* The harness calls tx_freertos_init() from tx_application_define(), so the
|
||||
automatic initialization path is not wanted here. */
|
||||
#define TX_FREERTOS_AUTO_INIT 0
|
||||
|
||||
/* FreeRTOS.h selects portDISABLE_INTERRUPTS() by compiler rather than by
|
||||
target, so a GNU build resolves it to the bare metal __disable_interrupts()
|
||||
intrinsic, which does not exist when the layer is hosted on Linux. Both
|
||||
macros are guarded with #ifndef, so the definitions below pre-empt that
|
||||
choice with the ThreadX primitives the header's own fallback branch uses. */
|
||||
UINT _tx_thread_interrupt_disable(VOID);
|
||||
VOID _tx_thread_interrupt_restore(UINT previous_posture);
|
||||
#define portDISABLE_INTERRUPTS() _tx_thread_interrupt_disable()
|
||||
#define portENABLE_INTERRUPTS() _tx_thread_interrupt_restore(TX_INT_ENABLE)
|
||||
|
||||
#endif /* #ifndef FREERTOS_CONFIG_H */
|
||||
@@ -0,0 +1,35 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2026 Eclipse ThreadX contributors
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the MIT License which is available at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*
|
||||
* AI Disclosure: This file was largely AI-generated by Claude Code (Opus 5).
|
||||
* The AI-generated portions may be considered public domain (CC0-1.0)
|
||||
* and not subject to the project's licence. The human contributor has
|
||||
* reviewed and verified that the code is correct.
|
||||
*
|
||||
* SPDX-License-Identifier: MIT and CC0-1.0
|
||||
**************************************************************************/
|
||||
|
||||
/**************************************************************************/
|
||||
/** */
|
||||
/** ThreadX Component */
|
||||
/** */
|
||||
/** FreeRTOS compatibility layer regression test ThreadX settings */
|
||||
/** */
|
||||
/**************************************************************************/
|
||||
|
||||
/* Every other ThreadX setting is left at its default; only the extension the
|
||||
compatibility layer requires is defined here. */
|
||||
|
||||
#ifndef TX_USER_H
|
||||
#define TX_USER_H
|
||||
|
||||
/* The layer stores a back pointer to its own task structure in each thread
|
||||
control block, as documented in the layer's readme.md. Without this the
|
||||
layer does not compile. */
|
||||
#define TX_THREAD_USER_EXTENSION VOID *txfr_thread_ptr;
|
||||
|
||||
#endif /* #ifndef TX_USER_H */
|
||||
@@ -0,0 +1,86 @@
|
||||
# FreeRTOS compatibility layer regression tests
|
||||
|
||||
Regression tests for `utility/rtos_compatibility_layers/FreeRTOS/tx_freertos.c`,
|
||||
built against the real ThreadX Linux port.
|
||||
|
||||
## Running them
|
||||
|
||||
```bash
|
||||
./scripts/build_freertos.sh
|
||||
./scripts/test_freertos.sh
|
||||
```
|
||||
|
||||
Or directly, which is the same thing:
|
||||
|
||||
```bash
|
||||
test/freertos/cmake/run.sh build all
|
||||
test/freertos/cmake/run.sh test all
|
||||
```
|
||||
|
||||
CI runs both scripts through `.github/workflows/regression_test.yml`.
|
||||
|
||||
## What they cover
|
||||
|
||||
Every function in the layer that creates an object takes one or two byte pool
|
||||
allocations for its bookkeeping and then creates one or more ThreadX kernel
|
||||
objects. When one of those kernel objects cannot be created, the function
|
||||
returns `NULL`, or `pdFAIL` for `xTaskCreate()`, and the caller is left with no
|
||||
handle and therefore no way to call the matching delete function. Anything the
|
||||
layer fails to release on the way out is lost until the system restarts.
|
||||
|
||||
That makes these error paths invisible from the outside: a leaking version and
|
||||
a correct version return exactly the same thing to the caller. The suite
|
||||
therefore counts the ThreadX primitives the layer reaches for, and checks that
|
||||
each error path gives back precisely what it took.
|
||||
|
||||
| Test | Covers |
|
||||
| --- | --- |
|
||||
| `txfr_queue_create_test` | `xQueueCreate`, `xQueueCreateStatic`, `vQueueDelete` |
|
||||
| `txfr_task_create_test` | `xTaskCreate`, `xTaskCreateStatic` |
|
||||
| `txfr_sync_create_test` | semaphores, mutexes, event groups and timers |
|
||||
|
||||
## How the fault injection works
|
||||
|
||||
A test asks the harness to fail a chosen kernel creation call, then reads back
|
||||
how many allocations, releases, object creations and object deletions the layer
|
||||
performed. The interception is done with the linker's `--wrap` option, so
|
||||
`tx_freertos.c` is compiled exactly as it ships, with no test hooks in it.
|
||||
|
||||
Two things are worth knowing before adding tests:
|
||||
|
||||
- `tx_api.h` maps the public API onto the error checking entry points, so the
|
||||
symbols that exist at link time are the `_txe_` variants, and those are what
|
||||
the wrap list in `cmake/regression/CMakeLists.txt` names. A `--wrap` for a
|
||||
name that does not resolve is silently ignored, so a typo there produces a
|
||||
test that quietly never injects anything.
|
||||
- `txfr_malloc()` and `txfr_free()` cannot be wrapped, because they are defined
|
||||
in `tx_freertos.c` and called from within it, so the compiler resolves those
|
||||
calls internally. The byte pool counts stand in for them.
|
||||
|
||||
Because `--wrap` is a GNU ld and lld feature with no MSVC equivalent, this
|
||||
suite is Linux only. The CMake configuration stops with a clear message rather
|
||||
than failing later with confusing link errors.
|
||||
|
||||
## Fixtures
|
||||
|
||||
`fixtures/FreeRTOSConfig.h` configures the layer for the tests. Two of its
|
||||
settings are not arbitrary:
|
||||
|
||||
- `configASSERT()` and `TX_FREERTOS_ASSERT_FAIL()` are empty, since the tests
|
||||
drive error paths deliberately and neither may halt the run.
|
||||
- `portDISABLE_INTERRUPTS()` and `portENABLE_INTERRUPTS()` are defined up front.
|
||||
`FreeRTOS.h` picks those by compiler rather than by target, so a GNU build
|
||||
otherwise resolves them to the bare metal `__disable_interrupts()` intrinsic,
|
||||
which does not exist when the layer is hosted on Linux.
|
||||
|
||||
`fixtures/tx_user.h` supplies `TX_THREAD_USER_EXTENSION`, which the layer
|
||||
requires, as documented in the layer's own `readme.md`.
|
||||
|
||||
## Why the build is 32 bit
|
||||
|
||||
The Linux port defines `ULONG` as `unsigned int` on x86_64, while the layer
|
||||
passes pointers through `ULONG` arguments, such as the task argument and the
|
||||
timer identifier. A 64 bit build truncates those pointers, which the compiler
|
||||
reports as `-Wpointer-to-int-cast` and which crashes the timer callback
|
||||
wrapper. The ThreadX and SMP suites build 32 bit for their own reasons; this
|
||||
suite has to.
|
||||
@@ -0,0 +1,116 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2026 Eclipse ThreadX contributors
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the MIT License which is available at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*
|
||||
* AI Disclosure: This file was largely AI-generated by Claude Code (Opus 5).
|
||||
* The AI-generated portions may be considered public domain (CC0-1.0)
|
||||
* and not subject to the project's licence. The human contributor has
|
||||
* reviewed and verified that the code is correct.
|
||||
*
|
||||
* SPDX-License-Identifier: MIT and CC0-1.0
|
||||
**************************************************************************/
|
||||
|
||||
/**************************************************************************/
|
||||
/** */
|
||||
/** ThreadX Component */
|
||||
/** */
|
||||
/** FreeRTOS compatibility layer, queue creation resource accounting */
|
||||
/** */
|
||||
/**************************************************************************/
|
||||
|
||||
/* xQueueCreate() takes two byte pool allocations, the descriptor and the
|
||||
backing storage, and then creates two semaphores. Since a failure returns
|
||||
NULL, the caller has no handle and cannot call vQueueDelete(), so anything
|
||||
the layer fails to undo on the way out is lost for good. These checks pin
|
||||
down the accounting on each of those paths.
|
||||
|
||||
The failure paths here are the ones reported in issue 570. */
|
||||
|
||||
#include "txfr_test_harness.h"
|
||||
|
||||
#include "queue.h"
|
||||
|
||||
#define QUEUE_LENGTH 8u
|
||||
#define QUEUE_ITEM_SIZE 32u
|
||||
|
||||
/* Storage for the static variant. */
|
||||
static StaticQueue_t queue_control_block;
|
||||
static uint8_t queue_storage[QUEUE_LENGTH * QUEUE_ITEM_SIZE];
|
||||
|
||||
|
||||
void txfr_test_body(void)
|
||||
{
|
||||
QueueHandle_t queue;
|
||||
TXFR_COUNTERS counters;
|
||||
int i;
|
||||
int cycles;
|
||||
|
||||
/* A successful creation takes both allocations and creates both
|
||||
semaphores, and releases nothing. */
|
||||
txfr_test_account_start(TXFR_INJECT_NONE, 0);
|
||||
queue = xQueueCreate(QUEUE_LENGTH, QUEUE_ITEM_SIZE);
|
||||
txfr_test_account_stop(&counters);
|
||||
txfr_test_check("xQueueCreate returns a handle", queue != NULL);
|
||||
txfr_test_check_counts("xQueueCreate accounting", &counters, 2, 0, 2, 0);
|
||||
|
||||
/* Deleting it gives both allocations back and deletes both semaphores. */
|
||||
if(queue != NULL)
|
||||
{
|
||||
txfr_test_account_start(TXFR_INJECT_NONE, 0);
|
||||
vQueueDelete(queue);
|
||||
txfr_test_account_stop(&counters);
|
||||
txfr_test_check_counts("vQueueDelete accounting", &counters, 0, 2, 0, 2);
|
||||
}
|
||||
|
||||
/* The read semaphore fails. Nothing was created that needs deleting, but
|
||||
both allocations must be released. */
|
||||
txfr_test_account_start(TXFR_INJECT_SEMAPHORE_CREATE, 1);
|
||||
queue = xQueueCreate(QUEUE_LENGTH, QUEUE_ITEM_SIZE);
|
||||
txfr_test_account_stop(&counters);
|
||||
txfr_test_check("xQueueCreate returns NULL on read_sem failure", queue == NULL);
|
||||
txfr_test_check_counts("read_sem failure releases both allocations", &counters, 2, 2, 1, 0);
|
||||
|
||||
/* The write semaphore fails. The read semaphore already exists by then, so
|
||||
it must be deleted as well as both allocations released. */
|
||||
txfr_test_account_start(TXFR_INJECT_SEMAPHORE_CREATE, 2);
|
||||
queue = xQueueCreate(QUEUE_LENGTH, QUEUE_ITEM_SIZE);
|
||||
txfr_test_account_stop(&counters);
|
||||
txfr_test_check("xQueueCreate returns NULL on write_sem failure", queue == NULL);
|
||||
txfr_test_check_counts("write_sem failure unwinds read_sem and memory", &counters, 2, 2, 2, 1);
|
||||
|
||||
/* The static variant takes no allocations and creates both semaphores. */
|
||||
txfr_test_account_start(TXFR_INJECT_NONE, 0);
|
||||
queue = xQueueCreateStatic(QUEUE_LENGTH, QUEUE_ITEM_SIZE, queue_storage, &queue_control_block);
|
||||
txfr_test_account_stop(&counters);
|
||||
txfr_test_check("xQueueCreateStatic returns a handle", queue != NULL);
|
||||
txfr_test_check_counts("xQueueCreateStatic accounting", &counters, 0, 0, 2, 0);
|
||||
|
||||
if(queue != NULL)
|
||||
{
|
||||
txfr_test_account_start(TXFR_INJECT_NONE, 0);
|
||||
vQueueDelete(queue);
|
||||
txfr_test_account_stop(&counters);
|
||||
txfr_test_check_counts("vQueueDelete on static queue", &counters, 0, 0, 0, 2);
|
||||
}
|
||||
|
||||
/* Repeated create and delete cycles must leave the pool able to serve the
|
||||
same request. This would not catch a small leak on its own, which is why
|
||||
the counts above are checked directly, but it does catch a descriptor
|
||||
that is released to the wrong pool or released twice. */
|
||||
cycles = 0;
|
||||
for(i = 0; i < 64; i++)
|
||||
{
|
||||
queue = xQueueCreate(QUEUE_LENGTH, QUEUE_ITEM_SIZE);
|
||||
if(queue == NULL)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
cycles++;
|
||||
vQueueDelete(queue);
|
||||
}
|
||||
txfr_test_check("64 create and delete cycles succeed", cycles == 64);
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2026 Eclipse ThreadX contributors
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the MIT License which is available at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*
|
||||
* AI Disclosure: This file was largely AI-generated by Claude Code (Opus 5).
|
||||
* The AI-generated portions may be considered public domain (CC0-1.0)
|
||||
* and not subject to the project's licence. The human contributor has
|
||||
* reviewed and verified that the code is correct.
|
||||
*
|
||||
* SPDX-License-Identifier: MIT and CC0-1.0
|
||||
**************************************************************************/
|
||||
|
||||
/**************************************************************************/
|
||||
/** */
|
||||
/** ThreadX Component */
|
||||
/** */
|
||||
/** FreeRTOS compatibility layer, synchronisation object accounting */
|
||||
/** */
|
||||
/**************************************************************************/
|
||||
|
||||
/* Semaphores, mutexes, event groups and timers each take a single descriptor
|
||||
allocation and then create one kernel object. Every one of them must give
|
||||
the descriptor back when that kernel object cannot be created. */
|
||||
|
||||
#include "txfr_test_harness.h"
|
||||
|
||||
#include "semphr.h"
|
||||
#include "event_groups.h"
|
||||
#include "timers.h"
|
||||
|
||||
|
||||
static void test_timer_callback(TimerHandle_t timer)
|
||||
{
|
||||
(void)timer;
|
||||
}
|
||||
|
||||
|
||||
void txfr_test_body(void)
|
||||
{
|
||||
SemaphoreHandle_t semaphore;
|
||||
EventGroupHandle_t event_group;
|
||||
TimerHandle_t timer;
|
||||
TXFR_COUNTERS counters;
|
||||
|
||||
/* Counting semaphore. */
|
||||
txfr_test_account_start(TXFR_INJECT_NONE, 0);
|
||||
semaphore = xSemaphoreCreateCounting(4u, 0u);
|
||||
txfr_test_account_stop(&counters);
|
||||
txfr_test_check("xSemaphoreCreateCounting returns a handle", semaphore != NULL);
|
||||
txfr_test_check_counts("xSemaphoreCreateCounting accounting", &counters, 1, 0, 1, 0);
|
||||
if(semaphore != NULL)
|
||||
{
|
||||
vSemaphoreDelete(semaphore);
|
||||
}
|
||||
|
||||
txfr_test_account_start(TXFR_INJECT_SEMAPHORE_CREATE, 1);
|
||||
semaphore = xSemaphoreCreateCounting(4u, 0u);
|
||||
txfr_test_account_stop(&counters);
|
||||
txfr_test_check("xSemaphoreCreateCounting returns NULL on failure", semaphore == NULL);
|
||||
txfr_test_check_counts("counting semaphore failure releases descriptor", &counters, 1, 1, 1, 0);
|
||||
|
||||
/* Binary semaphore, which shares the same descriptor path. */
|
||||
txfr_test_account_start(TXFR_INJECT_SEMAPHORE_CREATE, 1);
|
||||
semaphore = xSemaphoreCreateBinary();
|
||||
txfr_test_account_stop(&counters);
|
||||
txfr_test_check("xSemaphoreCreateBinary returns NULL on failure", semaphore == NULL);
|
||||
txfr_test_check_counts("binary semaphore failure releases descriptor", &counters, 1, 1, 1, 0);
|
||||
|
||||
/* Mutex. */
|
||||
txfr_test_account_start(TXFR_INJECT_NONE, 0);
|
||||
semaphore = xSemaphoreCreateMutex();
|
||||
txfr_test_account_stop(&counters);
|
||||
txfr_test_check("xSemaphoreCreateMutex returns a handle", semaphore != NULL);
|
||||
txfr_test_check_counts("xSemaphoreCreateMutex accounting", &counters, 1, 0, 1, 0);
|
||||
if(semaphore != NULL)
|
||||
{
|
||||
vSemaphoreDelete(semaphore);
|
||||
}
|
||||
|
||||
txfr_test_account_start(TXFR_INJECT_MUTEX_CREATE, 1);
|
||||
semaphore = xSemaphoreCreateMutex();
|
||||
txfr_test_account_stop(&counters);
|
||||
txfr_test_check("xSemaphoreCreateMutex returns NULL on failure", semaphore == NULL);
|
||||
txfr_test_check_counts("mutex failure releases descriptor", &counters, 1, 1, 1, 0);
|
||||
|
||||
/* Recursive mutex, which differs only in the inheritance argument. */
|
||||
txfr_test_account_start(TXFR_INJECT_MUTEX_CREATE, 1);
|
||||
semaphore = xSemaphoreCreateRecursiveMutex();
|
||||
txfr_test_account_stop(&counters);
|
||||
txfr_test_check("xSemaphoreCreateRecursiveMutex returns NULL on failure", semaphore == NULL);
|
||||
txfr_test_check_counts("recursive mutex failure releases descriptor", &counters, 1, 1, 1, 0);
|
||||
|
||||
/* Event group. */
|
||||
txfr_test_account_start(TXFR_INJECT_NONE, 0);
|
||||
event_group = xEventGroupCreate();
|
||||
txfr_test_account_stop(&counters);
|
||||
txfr_test_check("xEventGroupCreate returns a handle", event_group != NULL);
|
||||
txfr_test_check_counts("xEventGroupCreate accounting", &counters, 1, 0, 1, 0);
|
||||
if(event_group != NULL)
|
||||
{
|
||||
vEventGroupDelete(event_group);
|
||||
}
|
||||
|
||||
txfr_test_account_start(TXFR_INJECT_EVENT_FLAGS_CREATE, 1);
|
||||
event_group = xEventGroupCreate();
|
||||
txfr_test_account_stop(&counters);
|
||||
txfr_test_check("xEventGroupCreate returns NULL on failure", event_group == NULL);
|
||||
txfr_test_check_counts("event group failure releases descriptor", &counters, 1, 1, 1, 0);
|
||||
|
||||
/* Timer. */
|
||||
txfr_test_account_start(TXFR_INJECT_NONE, 0);
|
||||
timer = xTimerCreate("t", 10u, pdFALSE, NULL, test_timer_callback);
|
||||
txfr_test_account_stop(&counters);
|
||||
txfr_test_check("xTimerCreate returns a handle", timer != NULL);
|
||||
txfr_test_check_counts("xTimerCreate accounting", &counters, 1, 0, 1, 0);
|
||||
if(timer != NULL)
|
||||
{
|
||||
(void)xTimerDelete(timer, 0u);
|
||||
}
|
||||
|
||||
txfr_test_account_start(TXFR_INJECT_TIMER_CREATE, 1);
|
||||
timer = xTimerCreate("t", 10u, pdFALSE, NULL, test_timer_callback);
|
||||
txfr_test_account_stop(&counters);
|
||||
txfr_test_check("xTimerCreate returns NULL on failure", timer == NULL);
|
||||
txfr_test_check_counts("timer failure releases descriptor", &counters, 1, 1, 1, 0);
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2026 Eclipse ThreadX contributors
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the MIT License which is available at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*
|
||||
* AI Disclosure: This file was largely AI-generated by Claude Code (Opus 5).
|
||||
* The AI-generated portions may be considered public domain (CC0-1.0)
|
||||
* and not subject to the project's licence. The human contributor has
|
||||
* reviewed and verified that the code is correct.
|
||||
*
|
||||
* SPDX-License-Identifier: MIT and CC0-1.0
|
||||
**************************************************************************/
|
||||
|
||||
/**************************************************************************/
|
||||
/** */
|
||||
/** ThreadX Component */
|
||||
/** */
|
||||
/** FreeRTOS compatibility layer, task creation resource accounting */
|
||||
/** */
|
||||
/**************************************************************************/
|
||||
|
||||
/* xTaskCreate() takes two byte pool allocations, the stack and the task
|
||||
structure, then creates a notification semaphore and a thread. It is the
|
||||
function the rest of the layer's error handling is modelled on, so these
|
||||
checks also serve as the reference for what correct unwinding looks like. */
|
||||
|
||||
#include "txfr_test_harness.h"
|
||||
|
||||
#include "task.h"
|
||||
|
||||
#define TASK_STACK_DEPTH (configMINIMAL_STACK_SIZE)
|
||||
#define TASK_PRIORITY (3u)
|
||||
|
||||
static StaticTask_t task_control_block;
|
||||
static StackType_t task_stack[TASK_STACK_DEPTH];
|
||||
|
||||
|
||||
static void test_task_entry(void *p_arg)
|
||||
{
|
||||
(void)p_arg;
|
||||
|
||||
/* Never scheduled at this priority while the test thread runs, but a task
|
||||
entry point must not return. */
|
||||
for(;;)
|
||||
{
|
||||
vTaskDelay(portMAX_DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void txfr_test_body(void)
|
||||
{
|
||||
BaseType_t result;
|
||||
TaskHandle_t task;
|
||||
TXFR_COUNTERS counters;
|
||||
|
||||
/* A successful creation takes both allocations, creates the notification
|
||||
semaphore and the thread, and releases nothing. */
|
||||
task = NULL;
|
||||
txfr_test_account_start(TXFR_INJECT_NONE, 0);
|
||||
result = xTaskCreate(test_task_entry, "ok", TASK_STACK_DEPTH, NULL, TASK_PRIORITY, &task);
|
||||
txfr_test_account_stop(&counters);
|
||||
txfr_test_check("xTaskCreate reports success", result == pdPASS);
|
||||
txfr_test_check("xTaskCreate returns a handle", task != NULL);
|
||||
txfr_test_check_counts("xTaskCreate accounting", &counters, 2, 0, 2, 0);
|
||||
|
||||
if(task != NULL)
|
||||
{
|
||||
vTaskDelete(task);
|
||||
}
|
||||
|
||||
/* The notification semaphore fails before anything else is created, so
|
||||
both allocations must come back and nothing needs deleting. */
|
||||
task = NULL;
|
||||
txfr_test_account_start(TXFR_INJECT_SEMAPHORE_CREATE, 1);
|
||||
result = xTaskCreate(test_task_entry, "sem", TASK_STACK_DEPTH, NULL, TASK_PRIORITY, &task);
|
||||
txfr_test_account_stop(&counters);
|
||||
txfr_test_check("xTaskCreate fails when the semaphore fails", result != pdPASS);
|
||||
txfr_test_check_counts("semaphore failure releases stack and task", &counters, 2, 2, 1, 0);
|
||||
|
||||
/* The thread creation fails with the semaphore already in place, so that
|
||||
semaphore must be deleted and both allocations released. */
|
||||
task = NULL;
|
||||
txfr_test_account_start(TXFR_INJECT_THREAD_CREATE, 1);
|
||||
result = xTaskCreate(test_task_entry, "thread", TASK_STACK_DEPTH, NULL, TASK_PRIORITY, &task);
|
||||
txfr_test_account_stop(&counters);
|
||||
txfr_test_check("xTaskCreate fails when the thread fails", result != pdPASS);
|
||||
txfr_test_check_counts("thread failure unwinds the semaphore too", &counters, 2, 2, 2, 1);
|
||||
|
||||
/* The static variant takes no allocations, and creates the semaphore and
|
||||
the thread from the caller's buffers. */
|
||||
txfr_test_account_start(TXFR_INJECT_NONE, 0);
|
||||
task = xTaskCreateStatic(test_task_entry, "static", TASK_STACK_DEPTH, NULL, TASK_PRIORITY,
|
||||
task_stack, &task_control_block);
|
||||
txfr_test_account_stop(&counters);
|
||||
txfr_test_check("xTaskCreateStatic returns a handle", task != NULL);
|
||||
txfr_test_check_counts("xTaskCreateStatic accounting", &counters, 0, 0, 2, 0);
|
||||
|
||||
if(task != NULL)
|
||||
{
|
||||
vTaskDelete(task);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,98 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2026 Eclipse ThreadX contributors
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the MIT License which is available at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*
|
||||
* AI Disclosure: This file was largely AI-generated by Claude Code (Opus 5).
|
||||
* The AI-generated portions may be considered public domain (CC0-1.0)
|
||||
* and not subject to the project's licence. The human contributor has
|
||||
* reviewed and verified that the code is correct.
|
||||
*
|
||||
* SPDX-License-Identifier: MIT and CC0-1.0
|
||||
**************************************************************************/
|
||||
|
||||
/**************************************************************************/
|
||||
/** */
|
||||
/** ThreadX Component */
|
||||
/** */
|
||||
/** FreeRTOS compatibility layer test harness */
|
||||
/** */
|
||||
/**************************************************************************/
|
||||
|
||||
/* This harness lets a test force a ThreadX object creation call to fail and
|
||||
then account for what the compatibility layer did with the resources it had
|
||||
already taken. The creation entry points of the layer take a byte pool
|
||||
allocation for the descriptor, sometimes a second one for backing storage,
|
||||
and then create one or more kernel objects; an error path that forgets to
|
||||
undo any of that is invisible to the caller, which only ever sees NULL.
|
||||
|
||||
Interception is done with the linker's --wrap option rather than with hooks
|
||||
in the layer itself, so tx_freertos.c is compiled exactly as it ships. Note
|
||||
that tx_api.h maps the public API onto the error checking entry points, so
|
||||
the symbols that exist at link time, and therefore the ones wrapped, are the
|
||||
_txe_ variants. The wrap options live in the test CMakeLists.
|
||||
|
||||
This mechanism is specific to GNU ld and lld, so the suite is Linux only. */
|
||||
|
||||
#ifndef TXFR_TEST_HARNESS_H
|
||||
#define TXFR_TEST_HARNESS_H
|
||||
|
||||
#include "FreeRTOS.h"
|
||||
|
||||
/* Identifies the kernel creation call a test wants to fail. */
|
||||
typedef enum TXFR_INJECT_TARGET_ENUM
|
||||
{
|
||||
TXFR_INJECT_NONE = 0,
|
||||
TXFR_INJECT_SEMAPHORE_CREATE,
|
||||
TXFR_INJECT_MUTEX_CREATE,
|
||||
TXFR_INJECT_EVENT_FLAGS_CREATE,
|
||||
TXFR_INJECT_TIMER_CREATE,
|
||||
TXFR_INJECT_THREAD_CREATE
|
||||
} TXFR_INJECT_TARGET;
|
||||
|
||||
/* Counts of the ThreadX primitives the layer reached for while accounting was
|
||||
active. The byte pool counts stand in for txfr_malloc() and txfr_free(),
|
||||
which cannot be wrapped: they are defined in tx_freertos.c and called from
|
||||
within it, so the compiler resolves those calls internally. */
|
||||
typedef struct TXFR_COUNTERS_STRUCT
|
||||
{
|
||||
int byte_allocate;
|
||||
int byte_release;
|
||||
int semaphore_create;
|
||||
int semaphore_delete;
|
||||
int mutex_create;
|
||||
int mutex_delete;
|
||||
int event_flags_create;
|
||||
int event_flags_delete;
|
||||
int timer_create;
|
||||
int timer_delete;
|
||||
int thread_create;
|
||||
int thread_delete;
|
||||
} TXFR_COUNTERS;
|
||||
|
||||
/* Start accounting. Pass TXFR_INJECT_NONE to only count, or a target together
|
||||
with the 1 based index of the call to fail, counted from this call onward. */
|
||||
void txfr_test_account_start(TXFR_INJECT_TARGET target, int fail_on_call);
|
||||
|
||||
/* Stop accounting and copy out what was counted. */
|
||||
void txfr_test_account_stop(TXFR_COUNTERS *p_counters);
|
||||
|
||||
/* Record the outcome of one expectation. Prints a line per check and remembers
|
||||
whether anything failed. */
|
||||
void txfr_test_check(const char *label, int condition);
|
||||
|
||||
/* As above, with the observed and expected counts printed on a failure. */
|
||||
void txfr_test_check_counts(const char *label, const TXFR_COUNTERS *p_counters,
|
||||
int expect_allocate, int expect_release,
|
||||
int expect_object_create, int expect_object_delete);
|
||||
|
||||
/* Number of failed checks so far. */
|
||||
int txfr_test_failures(void);
|
||||
|
||||
/* Each test file defines this. It runs in thread context, since the byte pool
|
||||
operations under test are not callable from initialization. */
|
||||
void txfr_test_body(void);
|
||||
|
||||
#endif /* TXFR_TEST_HARNESS_H */
|
||||
Reference in New Issue
Block a user