diff options
Diffstat (limited to 'test/freertos/regression/txfr_test_harness.c')
| -rw-r--r-- | test/freertos/regression/txfr_test_harness.c | 384 |
1 files changed, 384 insertions, 0 deletions
diff --git a/test/freertos/regression/txfr_test_harness.c b/test/freertos/regression/txfr_test_harness.c new file mode 100644 index 00000000..b5bd45de --- /dev/null +++ b/test/freertos/regression/txfr_test_harness.c @@ -0,0 +1,384 @@ +/*************************************************************************** + * 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 */ +/** */ +/**************************************************************************/ + +#include "txfr_test_harness.h" + +#include <stdio.h> +#include <stdlib.h> + +/* Accounting state. Only the single test thread touches these. */ +static int txfr_accounting; +static TXFR_INJECT_TARGET txfr_inject_target; +static int txfr_inject_fail_on; +static int txfr_inject_call_index; +static TXFR_COUNTERS txfr_counters; +static int txfr_failures; + +/* The real entry points, supplied by the linker in response to --wrap. */ +UINT __real__txe_byte_allocate(TX_BYTE_POOL *pool_ptr, VOID **memory_ptr, ULONG memory_size, ULONG wait_option); +UINT __real__txe_byte_release(VOID *memory_ptr); +UINT __real__txe_semaphore_create(TX_SEMAPHORE *semaphore_ptr, TX_NAME_CONST CHAR *name_ptr, ULONG initial_count, UINT semaphore_control_block_size); +UINT __real__txe_semaphore_delete(TX_SEMAPHORE *semaphore_ptr); +UINT __real__txe_mutex_create(TX_MUTEX *mutex_ptr, TX_NAME_CONST CHAR *name_ptr, UINT inherit, UINT mutex_control_block_size); +UINT __real__txe_mutex_delete(TX_MUTEX *mutex_ptr); +UINT __real__txe_event_flags_create(TX_EVENT_FLAGS_GROUP *group_ptr, TX_NAME_CONST CHAR *name_ptr, UINT event_control_block_size); +UINT __real__txe_event_flags_delete(TX_EVENT_FLAGS_GROUP *group_ptr); +UINT __real__txe_timer_create(TX_TIMER *timer_ptr, TX_NAME_CONST CHAR *name_ptr, VOID (*expiration_function)(ULONG input), + ULONG expiration_input, ULONG initial_ticks, ULONG reschedule_ticks, + UINT auto_activate, UINT timer_control_block_size); +UINT __real__txe_timer_delete(TX_TIMER *timer_ptr); +UINT __real__txe_thread_create(TX_THREAD *thread_ptr, TX_NAME_CONST CHAR *name_ptr, VOID (*entry_function)(ULONG entry_input), + ULONG entry_input, VOID *stack_start, ULONG stack_size, UINT priority, + UINT preempt_threshold, ULONG time_slice, UINT auto_start, + UINT thread_control_block_size); +UINT __real__txe_thread_delete(TX_THREAD *thread_ptr); +UINT __real__txe_thread_resume(TX_THREAD *thread_ptr); + + +/* Determine whether the call now being made is the one the test asked to fail. + Only calls made while accounting is active are considered, which keeps the + objects created by tx_freertos_init() and by the harness out of the count. */ +static int txfr_should_fail(TXFR_INJECT_TARGET target) +{ + if((txfr_accounting == 0) || (txfr_inject_target != target)) + { + return 0; + } + + txfr_inject_call_index++; + + return (txfr_inject_call_index == txfr_inject_fail_on) ? 1 : 0; +} + + +UINT __wrap__txe_byte_allocate(TX_BYTE_POOL *pool_ptr, VOID **memory_ptr, ULONG memory_size, ULONG wait_option) +{ + if(txfr_accounting != 0) + { + txfr_counters.byte_allocate++; + } + + return __real__txe_byte_allocate(pool_ptr, memory_ptr, memory_size, wait_option); +} + + +UINT __wrap__txe_byte_release(VOID *memory_ptr) +{ + if(txfr_accounting != 0) + { + txfr_counters.byte_release++; + } + + return __real__txe_byte_release(memory_ptr); +} + + +UINT __wrap__txe_semaphore_create(TX_SEMAPHORE *semaphore_ptr, TX_NAME_CONST CHAR *name_ptr, ULONG initial_count, UINT semaphore_control_block_size) +{ + if(txfr_should_fail(TXFR_INJECT_SEMAPHORE_CREATE) != 0) + { + txfr_counters.semaphore_create++; + return TX_SEMAPHORE_ERROR; + } + + if(txfr_accounting != 0) + { + txfr_counters.semaphore_create++; + } + + return __real__txe_semaphore_create(semaphore_ptr, name_ptr, initial_count, semaphore_control_block_size); +} + + +UINT __wrap__txe_semaphore_delete(TX_SEMAPHORE *semaphore_ptr) +{ + if(txfr_accounting != 0) + { + txfr_counters.semaphore_delete++; + } + + return __real__txe_semaphore_delete(semaphore_ptr); +} + + +UINT __wrap__txe_mutex_create(TX_MUTEX *mutex_ptr, TX_NAME_CONST CHAR *name_ptr, UINT inherit, UINT mutex_control_block_size) +{ + if(txfr_should_fail(TXFR_INJECT_MUTEX_CREATE) != 0) + { + txfr_counters.mutex_create++; + return TX_MUTEX_ERROR; + } + + if(txfr_accounting != 0) + { + txfr_counters.mutex_create++; + } + + return __real__txe_mutex_create(mutex_ptr, name_ptr, inherit, mutex_control_block_size); +} + + +UINT __wrap__txe_mutex_delete(TX_MUTEX *mutex_ptr) +{ + if(txfr_accounting != 0) + { + txfr_counters.mutex_delete++; + } + + return __real__txe_mutex_delete(mutex_ptr); +} + + +UINT __wrap__txe_event_flags_create(TX_EVENT_FLAGS_GROUP *group_ptr, TX_NAME_CONST CHAR *name_ptr, UINT event_control_block_size) +{ + if(txfr_should_fail(TXFR_INJECT_EVENT_FLAGS_CREATE) != 0) + { + txfr_counters.event_flags_create++; + return TX_GROUP_ERROR; + } + + if(txfr_accounting != 0) + { + txfr_counters.event_flags_create++; + } + + return __real__txe_event_flags_create(group_ptr, name_ptr, event_control_block_size); +} + + +UINT __wrap__txe_event_flags_delete(TX_EVENT_FLAGS_GROUP *group_ptr) +{ + if(txfr_accounting != 0) + { + txfr_counters.event_flags_delete++; + } + + return __real__txe_event_flags_delete(group_ptr); +} + + +UINT __wrap__txe_timer_create(TX_TIMER *timer_ptr, TX_NAME_CONST CHAR *name_ptr, VOID (*expiration_function)(ULONG input), + ULONG expiration_input, ULONG initial_ticks, ULONG reschedule_ticks, + UINT auto_activate, UINT timer_control_block_size) +{ + if(txfr_should_fail(TXFR_INJECT_TIMER_CREATE) != 0) + { + txfr_counters.timer_create++; + return TX_TIMER_ERROR; + } + + if(txfr_accounting != 0) + { + txfr_counters.timer_create++; + } + + return __real__txe_timer_create(timer_ptr, name_ptr, expiration_function, expiration_input, + initial_ticks, reschedule_ticks, auto_activate, timer_control_block_size); +} + + +UINT __wrap__txe_timer_delete(TX_TIMER *timer_ptr) +{ + if(txfr_accounting != 0) + { + txfr_counters.timer_delete++; + } + + return __real__txe_timer_delete(timer_ptr); +} + + +UINT __wrap__txe_thread_create(TX_THREAD *thread_ptr, TX_NAME_CONST CHAR *name_ptr, VOID (*entry_function)(ULONG entry_input), + ULONG entry_input, VOID *stack_start, ULONG stack_size, UINT priority, + UINT preempt_threshold, ULONG time_slice, UINT auto_start, + UINT thread_control_block_size) +{ + if(txfr_should_fail(TXFR_INJECT_THREAD_CREATE) != 0) + { + txfr_counters.thread_create++; + return TX_THREAD_ERROR; + } + + if(txfr_accounting != 0) + { + txfr_counters.thread_create++; + } + + return __real__txe_thread_create(thread_ptr, name_ptr, entry_function, entry_input, stack_start, + stack_size, priority, preempt_threshold, time_slice, auto_start, + thread_control_block_size); +} + + +UINT __wrap__txe_thread_delete(TX_THREAD *thread_ptr) +{ + if(txfr_accounting != 0) + { + txfr_counters.thread_delete++; + } + + return __real__txe_thread_delete(thread_ptr); +} + + +UINT __wrap__txe_thread_resume(TX_THREAD *thread_ptr) +{ + if(txfr_should_fail(TXFR_INJECT_THREAD_RESUME) != 0) + { + txfr_counters.thread_resume++; + return TX_RESUME_ERROR; + } + + if(txfr_accounting != 0) + { + txfr_counters.thread_resume++; + } + + return __real__txe_thread_resume(thread_ptr); +} + + +void txfr_test_account_start(TXFR_INJECT_TARGET target, int fail_on_call) +{ + TX_MEMSET(&txfr_counters, 0, sizeof(txfr_counters)); + txfr_inject_target = target; + txfr_inject_fail_on = fail_on_call; + txfr_inject_call_index = 0; + txfr_accounting = 1; +} + + +void txfr_test_account_stop(TXFR_COUNTERS *p_counters) +{ + txfr_accounting = 0; + txfr_inject_target = TXFR_INJECT_NONE; + + if(p_counters != NULL) + { + *p_counters = txfr_counters; + } +} + + +void txfr_test_check(const char *label, int condition) +{ + printf("%-52s %s\n", label, (condition != 0) ? "PASS" : "FAIL"); + + if(condition == 0) + { + txfr_failures++; + } +} + + +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) +{ + int object_create; + int object_delete; + int ok; + + /* Only one kind of kernel object is exercised per check, so the totals + identify it unambiguously. */ + object_create = p_counters->semaphore_create + p_counters->mutex_create + + p_counters->event_flags_create + p_counters->timer_create + + p_counters->thread_create; + object_delete = p_counters->semaphore_delete + p_counters->mutex_delete + + p_counters->event_flags_delete + p_counters->timer_delete + + p_counters->thread_delete; + + ok = ((p_counters->byte_allocate == expect_allocate) && + (p_counters->byte_release == expect_release) && + (object_create == expect_object_create) && + (object_delete == expect_object_delete)) ? 1 : 0; + + printf("%-52s %s\n", label, (ok != 0) ? "PASS" : "FAIL"); + + if(ok == 0) + { + printf("%-52s got alloc=%d release=%d create=%d delete=%d\n", "", + p_counters->byte_allocate, p_counters->byte_release, object_create, object_delete); + printf("%-52s expected alloc=%d release=%d create=%d delete=%d\n", "", + expect_allocate, expect_release, expect_object_create, expect_object_delete); + txfr_failures++; + } +} + + +int txfr_test_failures(void) +{ + return txfr_failures; +} + + +/* Test thread. The byte pool operations under test require thread context. */ + +static TX_THREAD txfr_test_thread; +static ULONG txfr_test_stack[4096]; + + +static void txfr_test_thread_entry(ULONG id) +{ + (void)id; + + txfr_test_body(); + + printf("\n%s\n", (txfr_failures == 0) ? "ALL CHECKS PASSED" : "THERE WERE FAILED CHECKS"); + fflush(stdout); + + /* The kernel owns the calling thread, so returning here would simply idle. + Leave the process with a status ctest can read. */ + exit((txfr_failures == 0) ? 0 : 1); +} + + +void tx_application_define(void *first_unused_memory) +{ + UINT ret; + + (void)first_unused_memory; + + ret = tx_freertos_init(); + if(ret != TX_SUCCESS) + { + printf("tx_freertos_init() failed with %u\n", ret); + exit(2); + } + + ret = tx_thread_create(&txfr_test_thread, "txfr_test", txfr_test_thread_entry, 0u, + txfr_test_stack, sizeof(txfr_test_stack), 16u, 16u, 0u, TX_AUTO_START); + if(ret != TX_SUCCESS) + { + printf("tx_thread_create() failed with %u\n", ret); + exit(2); + } +} + + +int main(void) +{ + tx_kernel_enter(); + + return 0; +} |
