From 1eba370efd652b60f7542649b6766db8dd068fb2 Mon Sep 17 00:00:00 2001 From: Jithu Joseph Date: Wed, 25 Jan 2017 12:22:55 -0800 Subject: [PATCH] tests: kernel: import pool/heap tests to unified kernel Jira: ZEP-932 Change-Id: I4adf43f63db8dca7c252e40433f6ff0095dc1f26 Signed-off-by: Jithu Joseph Signed-off-by: Anas Nashif --- tests/kernel/mem_pool/test_mpool/Makefile | 4 + tests/kernel/mem_pool/test_mpool/README.txt | 44 ++ tests/kernel/mem_pool/test_mpool/prj.conf | 1 + tests/kernel/mem_pool/test_mpool/src/Makefile | 3 + tests/kernel/mem_pool/test_mpool/src/pool.c | 590 ++++++++++++++++++ tests/kernel/mem_pool/test_mpool/testcase.ini | 8 + 6 files changed, 650 insertions(+) create mode 100644 tests/kernel/mem_pool/test_mpool/Makefile create mode 100644 tests/kernel/mem_pool/test_mpool/README.txt create mode 100644 tests/kernel/mem_pool/test_mpool/prj.conf create mode 100644 tests/kernel/mem_pool/test_mpool/src/Makefile create mode 100644 tests/kernel/mem_pool/test_mpool/src/pool.c create mode 100644 tests/kernel/mem_pool/test_mpool/testcase.ini diff --git a/tests/kernel/mem_pool/test_mpool/Makefile b/tests/kernel/mem_pool/test_mpool/Makefile new file mode 100644 index 00000000000..e70a750a88d --- /dev/null +++ b/tests/kernel/mem_pool/test_mpool/Makefile @@ -0,0 +1,4 @@ +BOARD ?= qemu_x86 +CONF_FILE = prj.conf + +include ${ZEPHYR_BASE}/Makefile.test diff --git a/tests/kernel/mem_pool/test_mpool/README.txt b/tests/kernel/mem_pool/test_mpool/README.txt new file mode 100644 index 00000000000..947985313e8 --- /dev/null +++ b/tests/kernel/mem_pool/test_mpool/README.txt @@ -0,0 +1,44 @@ +Title: Memory Pool APIs + +Description: + +This test verifies that the memory pool and heap APIs operate as expected. + +-------------------------------------------------------------------------------- + +Building and Running Project: + +This project outputs to the console. It can be built and executed +on QEMU as follows: + + make run + +-------------------------------------------------------------------------------- + +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: + +***** BOOTING ZEPHYR OS xxxx - BUILD: xxxxxxx ***** +tc_start() - Test Memory Pool and Heap APIs +Testing k_mem_pool_alloc(K_NO_WAIT) ... +Testing k_mem_pool_alloc(timeout) ... +Testing k_mem_pool_alloc(K_FOREVER) ... +Testing k_mem_pool_defragment() ... +Testing k_malloc() and k_free() ... +=================================================================== +PASS - RegressionTask. +=================================================================== +PROJECT EXECUTION SUCCESSFUL + diff --git a/tests/kernel/mem_pool/test_mpool/prj.conf b/tests/kernel/mem_pool/test_mpool/prj.conf new file mode 100644 index 00000000000..26d52701286 --- /dev/null +++ b/tests/kernel/mem_pool/test_mpool/prj.conf @@ -0,0 +1 @@ +CONFIG_HEAP_MEM_POOL_SIZE=256 diff --git a/tests/kernel/mem_pool/test_mpool/src/Makefile b/tests/kernel/mem_pool/test_mpool/src/Makefile new file mode 100644 index 00000000000..e65f7322023 --- /dev/null +++ b/tests/kernel/mem_pool/test_mpool/src/Makefile @@ -0,0 +1,3 @@ +ccflags-y += -I${ZEPHYR_BASE}/tests/include + +obj-y = pool.o diff --git a/tests/kernel/mem_pool/test_mpool/src/pool.c b/tests/kernel/mem_pool/test_mpool/src/pool.c new file mode 100644 index 00000000000..a454dd36304 --- /dev/null +++ b/tests/kernel/mem_pool/test_mpool/src/pool.c @@ -0,0 +1,590 @@ +/* + * Copyright (c) 2012-2014 Wind River Systems, Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file test memory pool and heap APIs + * + * This modules tests the following memory pool routines: + * + * - k_mem_pool_alloc(), + * - k_mem_pool_free(), + * - k_malloc(), + * - k_free() + */ + +#include +#include +#include + +#define ONE_SECOND (sys_clock_ticks_per_sec) +#define TENTH_SECOND (sys_clock_ticks_per_sec / 10) + +#define NUM_BLOCKS 64 + +#define DEFRAG_BLK_TEST 2222 + +/* size of stack area used by each thread */ +#define STACKSIZE 512 + +K_SEM_DEFINE(ALTERNATE_SEM, 0, 1); +K_SEM_DEFINE(DEFRAG_SEM, 0, 1); +K_SEM_DEFINE(REGRESS_SEM, 0, 1); +K_SEM_DEFINE(HELPER_SEM, 0, 1); + +K_MEM_POOL_DEFINE(POOL_ID, 64, 4096, 1, 4); +K_MEM_POOL_DEFINE(SECOND_POOL_ID, 16, 1024, 5, 4); + +struct TEST_CASE { + struct k_mem_block *block; /* pointer to block data */ + struct k_mem_pool *pool_id; /* pool ID */ + int size; /* request size in bytes */ + int32_t timeout; /* # of ticks to wait */ + int rcode; /* expected return code */ +}; + +typedef int (*pool_block_get_func_t)(struct k_mem_block *, struct k_mem_pool *, + int, int32_t); +typedef int (*pool_move_block_func_t)(struct k_mem_block *, struct k_mem_pool *); + +static volatile int evidence; + +static struct k_mem_block block_list[NUM_BLOCKS]; +static struct k_mem_block helper_block; + +static struct TEST_CASE get_set[] = { + { &block_list[0], &POOL_ID, 0, 0, 0 }, + { &block_list[1], &POOL_ID, 1, 0, 0 }, + { &block_list[2], &POOL_ID, 32, 0, 0 }, + { &block_list[3], &POOL_ID, 64, 0, 0 }, + { &block_list[4], &POOL_ID, 128, 0, 0 }, + { &block_list[5], &POOL_ID, 256, 0, 0 }, + { &block_list[6], &POOL_ID, 512, 0, 0 }, + { &block_list[7], &POOL_ID, 1024, 0, 0 }, + { &block_list[8], &POOL_ID, 2048, 0, -ENOMEM }, + { &block_list[9], &POOL_ID, 4096, 0, -ENOMEM } +}; + +static struct TEST_CASE get_set2[] = { + { &block_list[0], &POOL_ID, 4096, 0, 0 }, + { &block_list[1], &POOL_ID, 2048, 0, -ENOMEM }, + { &block_list[2], &POOL_ID, 1024, 0, -ENOMEM }, + { &block_list[3], &POOL_ID, 512, 0, -ENOMEM }, + { &block_list[4], &POOL_ID, 256, 0, -ENOMEM } +}; + +static struct TEST_CASE getwt_set[] = { + { &block_list[0], &POOL_ID, 4096, TENTH_SECOND, 0 }, + { &block_list[1], &POOL_ID, 2048, TENTH_SECOND, -EAGAIN }, + { &block_list[2], &POOL_ID, 1024, TENTH_SECOND, -EAGAIN }, + { &block_list[3], &POOL_ID, 512, TENTH_SECOND, -EAGAIN }, + { &block_list[4], &POOL_ID, 256, TENTH_SECOND, -EAGAIN } +}; + +static struct TEST_CASE defrag[] = { + { &block_list[0], &POOL_ID, 64, 0, 0 }, + { &block_list[1], &POOL_ID, 64, 0, 0 }, + { &block_list[2], &POOL_ID, 64, 0, 0 }, + { &block_list[3], &POOL_ID, 64, 0, 0 }, + { &block_list[4], &POOL_ID, 256, 0, 0 }, + { &block_list[5], &POOL_ID, 256, 0, 0 }, + { &block_list[6], &POOL_ID, 256, 0, 0 }, + { &block_list[7], &POOL_ID, 1024, 0, 0 }, + { &block_list[8], &POOL_ID, 1024, 0, 0 }, + { &block_list[9], &POOL_ID, 1024, 0, 0 } +}; + +/** + * + * @brief Compare the two blocks + * + * @return 0 if the same, non-zero if not the same + */ + +int block_compare(struct k_mem_block *b1, struct k_mem_block *b2) +{ + char *p1 = (char *) b1; + char *p2 = (char *) b2; + int i; + int diff = 0; + + for (i = 0; i < sizeof(struct k_mem_block); i++) { + diff = p2[i] - p1[i]; + if (diff != 0) { + break; + } + } + + return diff; +} + +/** + * + * @brief Wrapper for k_mem_pool_alloc() + * + * @return k_mem_pool_alloc() return value + */ + +int pool_block_get_func(struct k_mem_block *block, struct k_mem_pool *pool, + int size, int32_t unused) +{ + ARG_UNUSED(unused); + + return k_mem_pool_alloc(pool, block, size, K_NO_WAIT); +} + +/** + * + * @brief Wrapper for k_mem_pool_alloc(K_FOREVER) + * + * @return k_mem_pool_alloc() return value + */ + +int pool_block_get_w_func(struct k_mem_block *block, struct k_mem_pool *pool, + int size, int32_t unused) +{ + ARG_UNUSED(unused); + + return k_mem_pool_alloc(pool, block, size, K_FOREVER); +} + +/** + * + * @brief Wrapper for k_mem_pool_alloc(timeout) + * + * @return k_mem_pool_alloc(timeout) return value + */ + +int pool_block_get_wt_func(struct k_mem_block *block, struct k_mem_pool *pool, + int size, int32_t timeout) +{ + return k_mem_pool_alloc(pool, block, size, timeout); +} + +/** + * + * @brief Free any blocks allocated in the test set + * + * @return N/A + */ + +void free_blocks(struct TEST_CASE *tests, int n_tests) +{ + int i; + + for (i = 0; i < n_tests; i++) { + if (tests[i].rcode == 0) { + k_mem_pool_free(tests[i].block); + } + } +} + +/** + * + * @brief Perform the work of getting blocks + * + * @return TC_PASS on success, TC_FAIL on failure + */ + +int pool_block_get_work(char *string, pool_block_get_func_t func, + struct TEST_CASE *tests, int n_tests) +{ + int i; + int rv; + + for (i = 0; i < n_tests; i++) { + rv = func(tests[i].block, tests[i].pool_id, tests[i].size, + tests[i].timeout); + if (rv != tests[i].rcode) { + TC_ERROR("%s() expected %d, got %d\n" + "size: %d, timeout: %d\n", string, tests[i].rcode, rv, + tests[i].size, tests[i].timeout); + return TC_FAIL; + } + } + + return TC_PASS; +} + +/** + * + * @brief Test the k_mem_pool_alloc(K_NO_WAIT) API + * + * The pool is 4 k_b in size. + * + * @return TC_PASS on success, TC_FAIL on failure + */ + +int pool_block_get_test(void) +{ + int rv; /* return value from k_mem_pool_alloc() */ + int j; /* loop counter */ + + for (j = 0; j < 8; j++) { + rv = pool_block_get_work("k_mem_pool_alloc", pool_block_get_func, + get_set, ARRAY_SIZE(get_set)); + if (rv != TC_PASS) { + return TC_FAIL; + } + + free_blocks(get_set, ARRAY_SIZE(get_set)); + + rv = pool_block_get_work("k_mem_pool_alloc", pool_block_get_func, + get_set2, ARRAY_SIZE(get_set2)); + if (rv != TC_PASS) { + return TC_FAIL; + } + + free_blocks(get_set2, ARRAY_SIZE(get_set2)); + } + + return TC_PASS; +} + +/** + * + * @brief Helper task to pool_block_get_timeout_test() + * + * @return N/A + */ + +void helper_task(void) +{ + k_sem_take(&HELPER_SEM, K_FOREVER); + + k_sem_give(®RESS_SEM); + k_mem_pool_free(&helper_block); +} + +/** + * + * @brief Test k_mem_pool_alloc(timeout) + * + * @return TC_PASS on success, TC_FAIL on failure + */ + +int pool_block_get_timeout_test(void) +{ + struct k_mem_block block; + int rv; /* return value from k_mem_pool_alloc() */ + int j; /* loop counter */ + + for (j = 0; j < 8; j++) { + rv = pool_block_get_work("k_mem_pool_alloc", pool_block_get_wt_func, + getwt_set, ARRAY_SIZE(getwt_set)); + if (rv != TC_PASS) { + return TC_FAIL; + } + + free_blocks(getwt_set, ARRAY_SIZE(getwt_set)); + } + + rv = k_mem_pool_alloc(&POOL_ID, &helper_block, 3148, 5); + if (rv != 0) { + TC_ERROR("Failed to get size 3148 byte block from POOL_ID\n"); + return TC_FAIL; + } + + rv = k_mem_pool_alloc(&POOL_ID, &block, 3148, K_NO_WAIT); + if (rv != -ENOMEM) { + TC_ERROR("Unexpectedly got size 3148 " + "byte block from POOL_ID\n"); + return TC_FAIL; + } + + k_sem_give(&HELPER_SEM); /* Activate helper_task */ + rv = k_mem_pool_alloc(&POOL_ID, &block, 3148, 20); + if (rv != 0) { + TC_ERROR("Failed to get size 3148 byte block from POOL_ID\n"); + return TC_FAIL; + } + + rv = k_sem_take(®RESS_SEM, K_NO_WAIT); + if (rv != 0) { + TC_ERROR("Failed to get size 3148 " + "byte block within 20 ticks\n"); + return TC_FAIL; + } + + k_mem_pool_free(&block); + + return TC_PASS; +} + +/** + * + * pool_block_get_wait_test - + * + * @return TC_PASS on success, TC_FAIL on failure + */ + +int pool_block_get_wait_test(void) +{ + int rv; + + rv = k_mem_pool_alloc(&POOL_ID, &block_list[0], 3000, K_FOREVER); + if (rv != 0) { + TC_ERROR("k_mem_pool_alloc(3000) expected %d, got %d\n", 0, rv); + return TC_FAIL; + } + + k_sem_give(&ALTERNATE_SEM); /* Wake alternate_task */ + evidence = 0; + rv = k_mem_pool_alloc(&POOL_ID, &block_list[1], 128, K_FOREVER); + if (rv != 0) { + TC_ERROR("k_mem_pool_alloc(128) expected %d, got %d\n", 0, rv); + return TC_FAIL; + } + + switch (evidence) { + case 0: + TC_ERROR("k_mem_pool_alloc(128) did not block!\n"); + return TC_FAIL; + case 1: + break; + case 2: + default: + TC_ERROR("Rescheduling did not occur " + "after k_mem_pool_free()\n"); + return TC_FAIL; + } + + k_mem_pool_free(&block_list[1]); + + return TC_PASS; +} + +/** + * + * @brief Task responsible for defragmenting the pool POOL_ID + * + * @return N/A + */ + +void defrag_task(void) +{ + k_sem_take(&DEFRAG_SEM, K_FOREVER); /* Wait to be activated */ + + k_mem_pool_defrag(&POOL_ID); + + k_sem_give(®RESS_SEM); /* defrag_task is finished */ +} + +/** + * + * pool_defrag_test - + * + * @return TC_PASS on success, TC_FAIL on failure + */ + +int pool_defrag_test(void) +{ + int rv; + struct k_mem_block new_block; + + /* Get a bunch of blocks */ + + rv = pool_block_get_work("k_mem_pool_alloc", pool_block_get_func, + defrag, ARRAY_SIZE(defrag)); + if (rv != TC_PASS) { + return TC_FAIL; + } + + + k_sem_give(&DEFRAG_SEM); /* Activate defrag_task */ + + /* + * Block on getting another block from the pool. + * This will allow defrag_task to execute so that we can get some + * better code coverage. 500 ms is expected to more than sufficient + * time for defrag_task to finish. + */ + + rv = k_mem_pool_alloc(&POOL_ID, &new_block, DEFRAG_BLK_TEST, 500); + if (rv != -EAGAIN) { + TC_ERROR("k_mem_pool_alloc() returned %d, not %d\n", rv, + -EAGAIN); + return TC_FAIL; + } + + rv = k_sem_take(®RESS_SEM, K_NO_WAIT); + if (rv != 0) { + TC_ERROR("defrag_task did not finish in allotted time!\n"); + return TC_FAIL; + } + + /* Free the allocated blocks */ + + free_blocks(defrag, ARRAY_SIZE(defrag)); + + return TC_PASS; +} + +/** + * + * @brief Alternate task in the test suite + * + * This routine runs at a lower priority than Regression_task(). + * + * @return N/A + */ + +void alternate_task(void) +{ + k_sem_take(&ALTERNATE_SEM, K_FOREVER); + + evidence = 1; + + k_mem_pool_free(&block_list[0]); + + evidence = 2; +} + +/** + * + * @brief Test the k_malloc() and k_free() APIs + * + * The heap memory pool is 256 bytes in size, and thus has only 4 blocks + * of 64 bytes or a single block of 256 bytes. (Each block has a lesser + * amount of usable space, due to the hidden block descriptor info the + * kernel adds at the start of any block allocated from this memory pool.) + * + * @return TC_PASS on success, TC_FAIL on failure + */ + +int pool_malloc_test(void) +{ + char *block[4]; + int j; /* loop counter */ + + TC_PRINT("Testing k_malloc() and k_free() ...\n"); + + /* allocate a large block (which consumes the entire pool buffer) */ + block[0] = k_malloc(150); + if (block[0] == NULL) { + TC_ERROR("150 byte allocation failed\n"); + return TC_FAIL; + } + + /* ensure a small block can no longer be allocated */ + block[1] = k_malloc(16); + if (block[1] != NULL) { + TC_ERROR("16 byte allocation did not fail\n"); + return TC_FAIL; + } + + /* return the large block */ + k_free(block[0]); + + /* allocate a small block (triggers block splitting)*/ + block[0] = k_malloc(16); + if (block[0] == NULL) { + TC_ERROR("16 byte allocation 0 failed\n"); + return TC_FAIL; + } + + /* ensure a large block can no longer be allocated */ + block[1] = k_malloc(80); + if (block[1] != NULL) { + TC_ERROR("80 byte allocation did not fail\n"); + return TC_FAIL; + } + + /* ensure all remaining small blocks can be allocated */ + for (j = 1; j < 4; j++) { + block[j] = k_malloc(16); + if (block[j] == NULL) { + TC_ERROR("16 byte allocation %d failed\n", j); + return TC_FAIL; + } + } + + /* ensure a small block can no longer be allocated */ + if (k_malloc(8) != NULL) { + TC_ERROR("8 byte allocation did not fail\n"); + return TC_FAIL; + } + + /* return the small blocks to pool in a "random" order */ + k_free(block[2]); + k_free(block[0]); + k_free(block[3]); + k_free(block[1]); + + /* allocate large block (triggers autodefragmentation) */ + block[0] = k_malloc(100); + if (block[0] == NULL) { + TC_ERROR("100 byte allocation failed\n"); + return TC_FAIL; + } + + /* ensure a small block can no longer be allocated */ + if (k_malloc(32) != NULL) { + TC_ERROR("32 byte allocation did not fail\n"); + return TC_FAIL; + } + + return TC_PASS; +} + +/** + * + * @brief Main task in the test suite + * + * This is the entry point to the memory pool test suite. + * + * @return N/A + */ + +void main(void) +{ + int tc_rC; /* test case return code */ + + TC_START("Test Memory Pool and Heap APIs"); + + TC_PRINT("Testing k_mem_pool_alloc(K_NO_WAIT) ...\n"); + tc_rC = pool_block_get_test(); + if (tc_rC != TC_PASS) { + goto done_tests; + } + + TC_PRINT("Testing k_mem_pool_alloc(timeout) ...\n"); + tc_rC = pool_block_get_timeout_test(); + if (tc_rC != TC_PASS) { + goto done_tests; + } + + TC_PRINT("Testing k_mem_pool_alloc(K_FOREVER) ...\n"); + tc_rC = pool_block_get_wait_test(); + if (tc_rC != TC_PASS) { + goto done_tests; + } + + TC_PRINT("Testing k_mem_pool_defragment() ...\n"); + tc_rC = pool_defrag_test(); + if (tc_rC != TC_PASS) { + goto done_tests; + } + + tc_rC = pool_malloc_test(); + if (tc_rC != TC_PASS) { + goto done_tests; + } + +done_tests: + TC_END_RESULT(tc_rC); + TC_END_REPORT(tc_rC); +} + + + +K_THREAD_DEFINE(t_alternate, STACKSIZE, alternate_task, NULL, NULL, NULL, + 6, 0, K_NO_WAIT); + +K_THREAD_DEFINE(t_defrag, STACKSIZE, defrag_task, NULL, NULL, NULL, + 7, 0, K_NO_WAIT); + +K_THREAD_DEFINE(t_helper, STACKSIZE, helper_task, NULL, NULL, NULL, + 7, 0, K_NO_WAIT); diff --git a/tests/kernel/mem_pool/test_mpool/testcase.ini b/tests/kernel/mem_pool/test_mpool/testcase.ini new file mode 100644 index 00000000000..8a054534dc5 --- /dev/null +++ b/tests/kernel/mem_pool/test_mpool/testcase.ini @@ -0,0 +1,8 @@ +[test] +tags = bat_commit core +filter = ( CONFIG_SRAM_SIZE > 32 or CONFIG_DCCM_SIZE > 32 or + CONFIG_RAM_SIZE > 32 ) + +[test_nios2] +tags = bat_commit core +arch_whitelist = nios2