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 <sergio.sf.rodriguez@intel.com>
Signed-off-by: Anas Nashif <anas.nashif@intel.com>
diff --git a/tests/kernel/context/Makefile b/tests/kernel/context/Makefile
new file mode 100644
index 0000000..7adff3f
--- /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 0000000..4927ef00
--- /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 0000000..44d4a39
--- /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 0000000..10178c9
--- /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 0000000..7501b80
--- /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 <tc_util.h>
+#include <kernel_structs.h>
+#include <arch/cpu.h>
+#include <irq_offload.h>
+
+#include <util_test_common.h>
+
+/*
+ * Include board.h from platform to get IRQ number.
+ * NOTE: Cortex-M does not need IRQ numbers
+ */
+#if !defined(CONFIG_CPU_CORTEX_M)
+#include <board.h>
+#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 <isr_info.command>.
+ *
+ * @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 <irq>
+ */
+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 <count> 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 <thread_detected_error> 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++;
+	/* <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 <thread_detected_error> 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.
+	 * <thread_evidence> 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 <sem_thread>.  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 0000000..fc3e56d
--- /dev/null
+++ b/tests/kernel/context/testcase.ini
@@ -0,0 +1,2 @@
+[test]
+tags = core bat_commit