From a6a1360b24520dae321b6cb1820b0fa9d149a44b Mon Sep 17 00:00:00 2001 From: Sergio Rodriguez Date: Mon, 19 Dec 2016 17:47:08 -0800 Subject: [PATCH] tests: kernel: test_context: Porting legacy tests to unified kernel This is the port of the legacy/kernel/test_context test case to the unified kernel Change-Id: I344ac240eb3b48042477f8875115fdc3fca1154a Signed-off-by: Sergio Rodriguez Signed-off-by: Anas Nashif --- tests/kernel/context/Makefile | 4 + tests/kernel/context/README.txt | 125 ++++ tests/kernel/context/prj.conf | 1 + tests/kernel/context/src/Makefile | 3 + tests/kernel/context/src/context.c | 928 +++++++++++++++++++++++++++++ tests/kernel/context/testcase.ini | 2 + 6 files changed, 1063 insertions(+) create mode 100644 tests/kernel/context/Makefile create mode 100644 tests/kernel/context/README.txt create mode 100644 tests/kernel/context/prj.conf create mode 100644 tests/kernel/context/src/Makefile create mode 100644 tests/kernel/context/src/context.c create mode 100644 tests/kernel/context/testcase.ini diff --git a/tests/kernel/context/Makefile b/tests/kernel/context/Makefile new file mode 100644 index 00000000000..7adff3fb623 --- /dev/null +++ b/tests/kernel/context/Makefile @@ -0,0 +1,4 @@ +CONF_FILE ?= prj.conf +BOARD ?= qemu_x86 + +include $(ZEPHYR_BASE)/Makefile.inc diff --git a/tests/kernel/context/README.txt b/tests/kernel/context/README.txt new file mode 100644 index 00000000000..4927ef00a15 --- /dev/null +++ b/tests/kernel/context/README.txt @@ -0,0 +1,125 @@ +Title: Context and IRQ APIs + +Description: + +This test verifies that the kernel CPU and context APIs operate as expected. + + +APIs tested in this test set +============================ + +k_thread_spawn + - start a helper fiber to help with k_yield() tests + - start a fiber to test fiber related functionality + +k_yield + - Called by a higher priority fiber when there is another fiber + - Called by an equal priority fiber when there is another fiber + - Called by a lower priority fiber when there is another fiber + +k_current_get + - Called from an ISR (interrupted a task) + - Called from an ISR (interrupted a fiber) + - Called from a task + - Called from a fiber + +k_is_in_isr + - Called from an ISR that interrupted a task + - Called from an ISR that interrupted a fiber + - Called from a task + - Called from a fiber + +k_cpu_idle + - CPU to be woken up by tick timer. Thus, after each call, the tick count + should have advanced by one tick. + +irq_lock + - 1. Count the number of calls to _tick_get_32() before a tick expires. + - 2. Once determined, call _tick_get_32() many more times than that + with interrupts locked. Check that the tick count remains unchanged. + +irq_unlock + - Continuation irq_lock: unlock interrupts, loop and verify the tick + count changes. + +irq_offload + - Used when triggering an ISR to perform ISR context work. + +irq_enable +irq_disable + - Use these routines to disable and enable timer interrupts so that they can + be tested in the same way as irq_lock() and irq_unlock(). + +--------------------------------------------------------------------------- + +Building and Running Project: + +This project outputs to the console. It can be built and executed +on QEMU as follows: + + make qemu + +--------------------------------------------------------------------------- + +Troubleshooting: + +Problems caused by out-dated project information can be addressed by +issuing one of the following commands then rebuilding the project: + + make clean # discard results of previous builds + # but keep existing configuration info +or + make pristine # discard results of previous builds + # and restore pre-defined configuration info + +--------------------------------------------------------------------------- + +Sample Output: + +tc_start() - Test kernel CPU and thread routines +Initializing kernel objects +Testing k_cpu_idle() +Testing interrupt locking and unlocking +Testing irq_disable() and irq_enable() +Testing some kernel context routines +Testing k_current_get() from an ISR and task +Testing k_is_in_isr() from an ISR +Testing k_is_in_isr() from a preemtible thread +Spawning a thread from a task +Thread to test k_current_get() and k_is_in_isr() +Thread to test k_yield() +Testing k_busy_wait() +Thread busy waiting for 20000 usecs +Thread busy waiting completed +Testing k_sleep() + thread sleeping for 50 milliseconds + thread back from sleep +Testing k_thread_spawn() without cancellation + thread (q order: 2, t/o: 500) is running + got thread (q order: 2, t/o: 500) as expected + thread (q order: 3, t/o: 750) is running + got thread (q order: 3, t/o: 750) as expected + thread (q order: 0, t/o: 1000) is running + got thread (q order: 0, t/o: 1000) as expected + thread (q order: 6, t/o: 1250) is running + got thread (q order: 6, t/o: 1250) as expected + thread (q order: 1, t/o: 1500) is running + got thread (q order: 1, t/o: 1500) as expected + thread (q order: 4, t/o: 1750) is running + got thread (q order: 4, t/o: 1750) as expected + thread (q order: 5, t/o: 2000) is running + got thread (q order: 5, t/o: 2000) as expected +Testing k_thread_spawn() with cancellations + cancelling [q order: 0, t/o: 1000, t/o order: 0] + thread (q order: 3, t/o: 750) is running + got (q order: 3, t/o: 750, t/o order 1) as expected + thread (q order: 0, t/o: 1000) is running + got (q order: 0, t/o: 1000, t/o order 2) as expected + cancelling [q order: 3, t/o: 750, t/o order: 3] + cancelling [q order: 4, t/o: 1750, t/o order: 4] + thread (q order: 4, t/o: 1750) is running + got (q order: 4, t/o: 1750, t/o order 5) as expected + cancelling [q order: 6, t/o: 1250, t/o order: 6] +PASS - main. +=================================================================== +PROJECT EXECUTION SUCCESSFUL diff --git a/tests/kernel/context/prj.conf b/tests/kernel/context/prj.conf new file mode 100644 index 00000000000..44d4a391a4b --- /dev/null +++ b/tests/kernel/context/prj.conf @@ -0,0 +1 @@ +CONFIG_IRQ_OFFLOAD=y diff --git a/tests/kernel/context/src/Makefile b/tests/kernel/context/src/Makefile new file mode 100644 index 00000000000..10178c904df --- /dev/null +++ b/tests/kernel/context/src/Makefile @@ -0,0 +1,3 @@ +ccflags-y += -I${ZEPHYR_BASE}/tests/include + +obj-y = context.o diff --git a/tests/kernel/context/src/context.c b/tests/kernel/context/src/context.c new file mode 100644 index 00000000000..7501b802cd6 --- /dev/null +++ b/tests/kernel/context/src/context.c @@ -0,0 +1,928 @@ +/* context.c - test context and thread APIs */ + +/* + * Copyright (c) 2012-2015 Wind River Systems, Inc. + * + * 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. + */ + +/* + * DESCRIPTION + * This module tests the following CPU and thread related routines: + * k_thread_spawn, k_yield(), k_is_in_isr(), + * k_current_get(), k_cpu_idle(), + * irq_lock(), irq_unlock(), + * irq_offload(), irq_enable(), irq_disable(), + */ + +#include +#include +#include +#include + +#include + +/* + * Include board.h from platform to get IRQ number. + * NOTE: Cortex-M does not need IRQ numbers + */ +#if !defined(CONFIG_CPU_CORTEX_M) +#include +#endif + +#define THREAD_STACKSIZE 384 +#define THREAD_PRIORITY 4 + +#define THREAD_SELF_CMD 0 +#define EXEC_CTX_TYPE_CMD 1 + +#define UNKNOWN_COMMAND -1 + +/* + * Get the timer type dependent IRQ number. If timer type + * is not defined in platform, generate an error + */ +#if defined(CONFIG_HPET_TIMER) +#define TICK_IRQ CONFIG_HPET_TIMER_IRQ +#elif defined(CONFIG_LOAPIC_TIMER) +#if defined(CONFIG_LOAPIC) +#define TICK_IRQ CONFIG_LOAPIC_TIMER_IRQ +#else +/* MVIC case */ +#define TICK_IRQ CONFIG_MVIC_TIMER_IRQ +#endif +#elif defined(CONFIG_ALTERA_AVALON_TIMER) +#define TICK_IRQ TIMER_0_IRQ +#elif defined(CONFIG_ARCV2_TIMER) +#define TICK_IRQ IRQ_TIMER0 +#elif defined(CONFIG_CPU_CORTEX_M) +/* + * The Cortex-M use the SYSTICK exception for the system timer, which is + * not considered an IRQ by the irq_enable/Disable APIs. + */ +#else +/* generate an error */ +#error Timer type is not defined for this platform +#endif + +/* Nios II doesn't have a power saving instruction, so k_cpu_idle() + * returns immediately + */ +#if !defined(CONFIG_NIOS2) +#define HAS_POWERSAVE_INSTRUCTION +#endif + +extern uint32_t _tick_get_32(void); +extern int64_t _tick_get(void); + +typedef struct { + int command; /* command to process */ + int error; /* error value (if any) */ + union { + void *data; /* pointer to data to use or return */ + int value; /* value to be passed or returned */ + }; +} ISR_INFO; + +typedef int (*disable_int_func) (int); +typedef void (*enable_int_func) (int); + +static struct k_sem sem_thread; +static struct k_timer timer; +static struct k_sem reply_timeout; +struct k_fifo timeout_order_fifo; + +static int thread_detected_error; +static int thread_evidence; + +static char __stack thread_stack1[THREAD_STACKSIZE]; +static char __stack thread_stack2[THREAD_STACKSIZE]; + +static ISR_INFO isr_info; + +/** + * + * @brief Handler to perform various actions from within an ISR context + * + * This routine is the ISR handler for isr_handler_trigger(). It performs + * the command requested in . + * + * @return N/A + */ +static void isr_handler(void *data) +{ + ARG_UNUSED(data); + + switch (isr_info.command) { + case THREAD_SELF_CMD: + isr_info.data = (void *)k_current_get(); + break; + + case EXEC_CTX_TYPE_CMD: + if (k_is_in_isr()) { + isr_info.value = K_ISR; + break; + } + + if (_current->base.prio < 0) { + isr_info.value = K_COOP_THREAD; + break; + } + + isr_info.value = K_PREEMPT_THREAD; + + break; + + default: + isr_info.error = UNKNOWN_COMMAND; + break; + } +} + +static void isr_handler_trigger(void) +{ + irq_offload(isr_handler, NULL); +} + +/** + * + * @brief Initialize kernel objects + * + * This routine initializes the kernel objects used in this module's tests. + * + * @return TC_PASS + */ +static int kernel_init_objects(void) +{ + k_sem_init(&sem_thread, 0, UINT_MAX); + k_sem_init(&reply_timeout, 0, UINT_MAX); + k_timer_init(&timer, NULL, NULL); + k_fifo_init(&timeout_order_fifo); + + return TC_PASS; +} + +#ifdef HAS_POWERSAVE_INSTRUCTION +/** + * + * @brief Test the k_cpu_idle() routine + * + * This tests the k_cpu_idle() routine. The first thing it does is align to + * a tick boundary. The only source of interrupts while the test is running is + * expected to be the tick clock timer which should wake the CPU. Thus after + * each call to k_cpu_idle(), the tick count should be one higher. + * + * @return TC_PASS on success + * @return TC_FAIL on failure + */ +static int test_kernel_cpu_idle(void) +{ + int tick; /* current tick count */ + int i; /* loop variable */ + + /* Align to a "tick boundary". */ + tick = _tick_get_32(); + while (tick == _tick_get_32()) { + } + + tick = _tick_get_32(); + for (i = 0; i < 5; i++) { /* Repeat the test five times */ + k_cpu_idle(); + tick++; + if (_tick_get_32() != tick) { + return TC_FAIL; + } + } + return TC_PASS; +} +#endif + +/** + * + * @brief A wrapper for irq_lock() + * + * @return irq_lock() return value + */ +int irq_lock_wrapper(int unused) +{ + ARG_UNUSED(unused); + + return irq_lock(); +} + +/** + * + * @brief A wrapper for irq_unlock() + * + * @return N/A + */ +void irq_unlock_wrapper(int imask) +{ + irq_unlock(imask); +} + +/** + * + * @brief A wrapper for irq_disable() + * + * @return + */ +int irq_disable_wrapper(int irq) +{ + irq_disable(irq); + return irq; +} + +/** + * + * @brief A wrapper for irq_enable() + * + * @return N/A + */ +void irq_enable_wrapper(int irq) +{ + irq_enable(irq); +} + +/** + * + * @brief Test routines for disabling and enabling ints + * + * This routine tests the routines for disabling and enabling interrupts. + * These include irq_lock() and irq_unlock(), irq_disable() and irq_enable(). + * + * @return TC_PASS on success + * @return TC_FAIL on failure + */ +static int test_kernel_interrupts(disable_int_func disable_int, + enable_int_func enable_int, int irq) +{ + unsigned long long count = 0; + unsigned long long i = 0; + int tick; + int tick2; + int imask; + + /* Align to a "tick boundary" */ + tick = _tick_get_32(); + while (_tick_get_32() == tick) { + } + + tick++; + while (_tick_get_32() == tick) { + count++; + } + + /* + * Inflate so that when we loop later, many ticks should have + * elapsed during the loop. This later loop will not exactly match the + * previous loop, but it should be close enough in structure that when + * combined with the inflated count, many ticks will have passed. + */ + + count <<= 4; + + imask = disable_int(irq); + tick = _tick_get_32(); + for (i = 0; i < count; i++) { + _tick_get_32(); + } + + tick2 = _tick_get_32(); + + /* + * Re-enable interrupts before returning (for both success and failure + * cases). + */ + + enable_int(imask); + + if (tick2 != tick) { + return TC_FAIL; + } + + /* Now repeat with interrupts unlocked. */ + for (i = 0; i < count; i++) { + _tick_get_32(); + } + + return (tick == _tick_get_32()) ? TC_FAIL : TC_PASS; +} + +/** + * + * @brief Test some context routines from a preemptible thread + * + * This routines tests the k_current_get() and + * k_is_in_isr() routines from both a preemtible thread and an ISR (that + * interrupted a preemtible thread). Checking those routines with cooperative + * threads are done elsewhere. + * + * @return TC_PASS on success + * @return TC_FAIL on failure + */ +static int test_kernel_ctx_task(void) +{ + k_tid_t self_thread_id; + + TC_PRINT("Testing k_current_get() from an ISR and task\n"); + + self_thread_id = k_current_get(); + isr_info.command = THREAD_SELF_CMD; + isr_info.error = 0; + /* isr_info is modified by the isr_handler routine */ + isr_handler_trigger(); + if (isr_info.error || isr_info.data != (void *)self_thread_id) { + /* + * Either the ISR detected an error, or the ISR context ID + * does not match the interrupted task's thread ID. + */ + return TC_FAIL; + } + + TC_PRINT("Testing k_is_in_isr() from an ISR\n"); + isr_info.command = EXEC_CTX_TYPE_CMD; + isr_info.error = 0; + isr_handler_trigger(); + if (isr_info.error || isr_info.value != K_ISR) { + return TC_FAIL; + } + + TC_PRINT("Testing k_is_in_isr() from a preemtible thread\n"); + if (k_is_in_isr() || _current->base.prio < 0) { + return TC_FAIL; + } + + return TC_PASS; +} + +/** + * + * @brief Test the various context/thread routines from a cooperative thread + * + * This routines tests the k_current_get and + * k_is_in_isr() routines from both a thread and an ISR (that interrupted a + * cooperative thread). Checking those routines with preemptible threads are + * done elsewhere. + * + * This routine may set to the following values: + * 1 - if thread ID matches that of the task + * 2 - if thread ID taken during ISR does not match that of the thread + * 3 - k_is_in_isr() when called from an ISR is false + * 4 - k_is_in_isr() when called from a thread is true + * 5 - if thread is not a cooperative thread + * + * @return TC_PASS on success + * @return TC_FAIL on failure + */ +static int test_kernel_thread(k_tid_t task_thread_id) +{ + k_tid_t self_thread_id; + + self_thread_id = k_current_get(); + if (self_thread_id == task_thread_id) { + thread_detected_error = 1; + return TC_FAIL; + } + + isr_info.command = THREAD_SELF_CMD; + isr_info.error = 0; + isr_handler_trigger(); + if (isr_info.error || isr_info.data != (void *)self_thread_id) { + /* + * Either the ISR detected an error, or the ISR context ID + * does not match the interrupted thread's thread ID. + */ + thread_detected_error = 2; + return TC_FAIL; + } + + isr_info.command = EXEC_CTX_TYPE_CMD; + isr_info.error = 0; + isr_handler_trigger(); + if (isr_info.error || (isr_info.value != K_ISR)) { + thread_detected_error = 3; + return TC_FAIL; + } + + if (k_is_in_isr()) { + thread_detected_error = 4; + return TC_FAIL; + } + + if (_current->base.prio >= 0) { + thread_detected_error = 5; + return TC_FAIL; + } + + return TC_PASS; +} + +/** + * + * @brief Entry point to the thread's helper + * + * This routine is the entry point to the thread's helper thread. It is used to + * help test the behaviour of the k_yield() routine. + * + * @param arg1 unused + * @param arg2 unused + * + * @return N/A + */ + +static void thread_helper(void *arg1, void *arg2, void *arg3) +{ + k_tid_t self_thread_id; + + ARG_UNUSED(arg1); + ARG_UNUSED(arg2); + ARG_UNUSED(arg3); + + /* + * This thread starts off at a higher priority than thread_entry(). + * Thus, it should execute immediately. + */ + + thread_evidence++; + + /* Test that helper will yield to a thread of equal priority */ + self_thread_id = k_current_get(); + + /* Lower priority to that of thread_entry() */ + k_thread_priority_set(self_thread_id, self_thread_id->base.prio + 1); + + k_yield(); /* Yield to thread of equal priority */ + + thread_evidence++; + /* should now be 2 */ + +} + +/** + * + * @brief Test the k_yield() routine + * + * This routine tests the k_yield() routine. It starts another thread + * (thus also testing k_thread_spawn() and checks that behaviour of + * k_yield() against the cases of there being a higher priority thread, + * a lower priority thread, and another thread of equal priority. + * + * On error, it may set to one of the following values: + * 10 - helper thread ran prematurely + * 11 - k_yield() did not yield to a higher priority thread + * 12 - k_yield() did not yield to an equal prioirty thread + * 13 - k_yield() yielded to a lower priority thread + * + * @return TC_PASS on success + * @return TC_FAIL on failure + */ +static int test_k_yield(void) +{ + k_tid_t self_thread_id; + + /* + * Start a thread of higher priority. Note that since the new thread is + * being started from a thread, it will not automatically switch to the + * thread as it would if done from a task. + */ + + self_thread_id = k_current_get(); + thread_evidence = 0; + + k_thread_spawn(thread_stack2, THREAD_STACKSIZE, thread_helper, + NULL, NULL, NULL, + K_PRIO_COOP(THREAD_PRIORITY - 1), 0, 0); + + if (thread_evidence != 0) { + /* ERROR! Helper spawned at higher */ + thread_detected_error = 10; /* priority ran prematurely. */ + return TC_FAIL; + } + + /* + * Test that the thread will yield to the higher priority helper. + * is still 0. + */ + + k_yield(); + + if (thread_evidence == 0) { + /* ERROR! Did not yield to higher */ + thread_detected_error = 11; /* priority thread. */ + return TC_FAIL; + } + + if (thread_evidence > 1) { + /* ERROR! Helper did not yield to */ + thread_detected_error = 12; /* equal priority thread. */ + return TC_FAIL; + } + + /* + * Raise the priority of thread_entry(). Calling k_yield() should + * not result in switching to the helper. + */ + + k_thread_priority_set(self_thread_id, self_thread_id->base.prio - 1); + k_yield(); + + if (thread_evidence != 1) { + /* ERROR! Context switched to a lower */ + thread_detected_error = 13; /* priority thread! */ + return TC_FAIL; + } + + /* + * Block on . This will allow the helper thread to + * complete. The main task will wake this thread. + */ + + k_sem_take(&sem_thread, K_FOREVER); + + return TC_PASS; +} + +/** + * @brief Entry point to thread started by the task + * + * This routine is the entry point to the thread started by the task. + * + * @param task_thread_id thread ID of the spawning task + * @param arg1 unused + * @param arg2 unused + * + * @return N/A + */ +static void thread_entry(void *task_thread_id, void *arg1, void *arg2) +{ + int rv; + + ARG_UNUSED(arg1); + ARG_UNUSED(arg2); + + thread_evidence++; /* Prove to the task that the thread has run */ + k_sem_take(&sem_thread, K_FOREVER); + + rv = test_kernel_thread((k_tid_t) task_thread_id); + if (rv != TC_PASS) { + return; + } + + /* Allow the task to print any messages before the next test runs */ + k_sem_take(&sem_thread, K_FOREVER); + + rv = test_k_yield(); + if (rv != TC_PASS) { + return; + } +} + +/* + * Timeout tests + * + * Test the k_sleep() API, as well as the k_thread_spawn() ones. + */ + +struct timeout_order { + void *link_in_fifo; + int32_t timeout; + int timeout_order; + int q_order; +}; + +struct timeout_order timeouts[] = { + { 0, 1000, 2, 0 }, + { 0, 1500, 4, 1 }, + { 0, 500, 0, 2 }, + { 0, 750, 1, 3 }, + { 0, 1750, 5, 4 }, + { 0, 2000, 6, 5 }, + { 0, 1250, 3, 6 }, +}; + +#define NUM_TIMEOUT_THREADS ARRAY_SIZE(timeouts) +static char __stack timeout_stacks[NUM_TIMEOUT_THREADS][THREAD_STACKSIZE]; + +/* a thread busy waits, then reports through a fifo */ +static void test_busy_wait(void *mseconds, void *arg2, void *arg3) +{ + uint32_t usecs; + + ARG_UNUSED(arg2); + ARG_UNUSED(arg3); + + usecs = (int)mseconds * 1000; + + TC_PRINT("Thread busy waiting for %d usecs\n", usecs); + k_busy_wait(usecs); + TC_PRINT("Thread busy waiting completed\n"); + + /* + * Ideally the test should verify that the correct number of ticks + * have elapsed. However, when running under QEMU, the tick interrupt + * may be processed on a very irregular basis, meaning that far + * fewer than the expected number of ticks may occur for a given + * number of clock cycles vs. what would ordinarily be expected. + * + * Consequently, the best we can do for now to test busy waiting is + * to invoke the API and verify that it returns. (If it takes way + * too long, or never returns, the main test task may be able to + * time out and report an error.) + */ + + k_sem_give(&reply_timeout); +} + +/* a thread sleeps and times out, then reports through a fifo */ +static void test_thread_sleep(void *delta, void *arg2, void *arg3) +{ + int64_t timestamp; + int timeout = (int)delta; + + ARG_UNUSED(arg2); + ARG_UNUSED(arg3); + + TC_PRINT(" thread sleeping for %d milliseconds\n", timeout); + timestamp = k_uptime_get(); + k_sleep(timeout); + timestamp = k_uptime_get() - timestamp; + TC_PRINT(" thread back from sleep\n"); + + if (timestamp < timeout || timestamp > timeout + 10) { + return; + } + + k_sem_give(&reply_timeout); +} + +/* a thread is started with a delay, then it reports that it ran via a fifo */ +static void delayed_thread(void *num, void *arg2, void *arg3) +{ + struct timeout_order *timeout = &timeouts[(int)num]; + + ARG_UNUSED(arg2); + ARG_UNUSED(arg3); + + TC_PRINT(" thread (q order: %d, t/o: %d) is running\n", + timeout->q_order, timeout->timeout); + + k_fifo_put(&timeout_order_fifo, timeout); +} + +static int test_timeout(void) +{ + struct timeout_order *data; + int32_t timeout; + int rv; + int i; + + /* test k_busy_wait() */ + TC_PRINT("Testing k_busy_wait()\n"); + timeout = 20; /* in ms */ + + k_thread_spawn(timeout_stacks[0], THREAD_STACKSIZE, test_busy_wait, + (void *)(intptr_t) timeout, NULL, + NULL, K_PRIO_COOP(THREAD_PRIORITY), 0, 0); + + rv = k_sem_take(&reply_timeout, timeout * 2); + + if (rv) { + TC_ERROR(" *** task timed out waiting for " "k_busy_wait()\n"); + return TC_FAIL; + } + + /* test k_sleep() */ + + TC_PRINT("Testing k_sleep()\n"); + timeout = 50; + + k_thread_spawn(timeout_stacks[0], THREAD_STACKSIZE, test_thread_sleep, + (void *)(intptr_t) timeout, NULL, + NULL, K_PRIO_COOP(THREAD_PRIORITY), 0, 0); + + rv = k_sem_take(&reply_timeout, timeout * 2); + if (rv) { + TC_ERROR(" *** task timed out waiting for thread on " + "k_sleep().\n"); + return TC_FAIL; + } + + /* test k_thread_spawn() without cancellation */ + TC_PRINT("Testing k_thread_spawn() without cancellation\n"); + + for (i = 0; i < NUM_TIMEOUT_THREADS; i++) { + k_thread_spawn(timeout_stacks[i], THREAD_STACKSIZE, + delayed_thread, + (void *)i, + NULL, NULL, + K_PRIO_COOP(5), 0, timeouts[i].timeout); + } + for (i = 0; i < NUM_TIMEOUT_THREADS; i++) { + data = k_fifo_get(&timeout_order_fifo, 750); + if (!data) { + TC_ERROR + (" *** timeout while waiting for delayed thread\n"); + return TC_FAIL; + } + + if (data->timeout_order != i) { + TC_ERROR(" *** wrong delayed thread ran (got %d, " + "expected %d)\n", data->timeout_order, i); + return TC_FAIL; + } + + TC_PRINT(" got thread (q order: %d, t/o: %d) as expected\n", + data->q_order, data->timeout); + } + + /* ensure no more thread fire */ + data = k_fifo_get(&timeout_order_fifo, 750); + + if (data) { + TC_ERROR(" *** got something unexpected in the fifo\n"); + return TC_FAIL; + } + + /* test k_thread_spawn() with cancellation */ + TC_PRINT("Testing k_thread_spawn() with cancellations\n"); + + int cancellations[] = { 0, 3, 4, 6 }; + int num_cancellations = ARRAY_SIZE(cancellations); + int next_cancellation = 0; + + k_tid_t delayed_threads[NUM_TIMEOUT_THREADS]; + + for (i = 0; i < NUM_TIMEOUT_THREADS; i++) { + k_tid_t id; + + id = k_thread_spawn(timeout_stacks[i], THREAD_STACKSIZE, + delayed_thread, + (void *)i, NULL, NULL, + K_PRIO_COOP(5), 0, timeouts[i].timeout); + + delayed_threads[i] = id; + } + + for (i = 0; i < NUM_TIMEOUT_THREADS; i++) { + int j; + + if (i == cancellations[next_cancellation]) { + TC_PRINT(" cancelling " + "[q order: %d, t/o: %d, t/o order: %d]\n", + timeouts[i].q_order, timeouts[i].timeout, i); + + for (j = 0; j < NUM_TIMEOUT_THREADS; j++) { + if (timeouts[j].timeout_order == i) { + break; + } + } + k_thread_cancel(delayed_threads[j]); + ++next_cancellation; + continue; + } + + data = k_fifo_get(&timeout_order_fifo, 2750); + + if (!data) { + TC_ERROR + (" *** timeout while waiting for delayed thread\n"); + return TC_FAIL; + } + + if (data->timeout_order != i) { + TC_ERROR(" *** wrong delayed thread ran (got %d, " + "expected %d)\n", data->timeout_order, i); + return TC_FAIL; + } + + TC_PRINT(" got (q order: %d, t/o: %d, t/o order %d) " + "as expected\n", data->q_order, data->timeout, + data->timeout_order); + } + + if (num_cancellations != next_cancellation) { + TC_ERROR(" *** wrong number of cancellations (expected %d, " + "got %d\n", num_cancellations, next_cancellation); + return TC_FAIL; + } + + /* ensure no more thread fire */ + data = k_fifo_get(&timeout_order_fifo, 750); + if (data) { + TC_ERROR(" *** got something unexpected in the fifo\n"); + return TC_FAIL; + } + + return TC_PASS; +} + +/** + * @brief Entry point to timer tests + * + * This is the entry point to the CPU and thread tests. + * + * @return N/A + */ +void main(void) +{ + int rv; /* return value from tests */ + + thread_detected_error = 0; + thread_evidence = 0; + + TC_START("Test kernel CPU and thread routines"); + + TC_PRINT("Initializing kernel objects\n"); + rv = kernel_init_objects(); + if (rv != TC_PASS) { + goto tests_done; + } +#ifdef HAS_POWERSAVE_INSTRUCTION + TC_PRINT("Testing k_cpu_idle()\n"); + rv = test_kernel_cpu_idle(); + if (rv != TC_PASS) { + goto tests_done; + } +#endif + + TC_PRINT("Testing interrupt locking and unlocking\n"); + rv = test_kernel_interrupts(irq_lock_wrapper, irq_unlock_wrapper, -1); + if (rv != TC_PASS) { + goto tests_done; + } +#ifdef TICK_IRQ + /* Disable interrupts coming from the timer. */ + + TC_PRINT("Testing irq_disable() and irq_enable()\n"); + rv = test_kernel_interrupts(irq_disable_wrapper, irq_enable_wrapper, + TICK_IRQ); + if (rv != TC_PASS) { + goto tests_done; + } +#endif + + TC_PRINT("Testing some kernel context routines\n"); + rv = test_kernel_ctx_task(); + if (rv != TC_PASS) { + goto tests_done; + } + + TC_PRINT("Spawning a thread from a task\n"); + thread_evidence = 0; + + k_thread_spawn(thread_stack1, THREAD_STACKSIZE, thread_entry, + k_current_get(), NULL, + NULL, K_PRIO_COOP(THREAD_PRIORITY), 0, 0); + + if (thread_evidence != 1) { + rv = TC_FAIL; + TC_ERROR(" - thread did not execute as expected!\n"); + goto tests_done; + } + + /* + * The thread ran, now wake it so it can test k_current_get and + * k_is_in_isr. + */ + TC_PRINT("Thread to test k_current_get() and " "k_is_in_isr()\n"); + k_sem_give(&sem_thread); + + if (thread_detected_error != 0) { + rv = TC_FAIL; + TC_ERROR(" - failure detected in thread; " + "thread_detected_error = %d\n", thread_detected_error); + goto tests_done; + } + + TC_PRINT("Thread to test k_yield()\n"); + k_sem_give(&sem_thread); + + if (thread_detected_error != 0) { + rv = TC_FAIL; + TC_ERROR(" - failure detected in thread; " + "thread_detected_error = %d\n", thread_detected_error); + goto tests_done; + } + + k_sem_give(&sem_thread); + + rv = test_timeout(); + if (rv != TC_PASS) { + goto tests_done; + } + +tests_done: + TC_END_RESULT(rv); + TC_END_REPORT(rv); +} diff --git a/tests/kernel/context/testcase.ini b/tests/kernel/context/testcase.ini new file mode 100644 index 00000000000..fc3e56d847c --- /dev/null +++ b/tests/kernel/context/testcase.ini @@ -0,0 +1,2 @@ +[test] +tags = core bat_commit