summaryrefslogtreecommitdiff
path: root/test/freertos/regression/txfr_test_harness.c
diff options
context:
space:
mode:
Diffstat (limited to 'test/freertos/regression/txfr_test_harness.c')
-rw-r--r--test/freertos/regression/txfr_test_harness.c384
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;
+}