| /* pool.c - test microkernel memory pool APIs */ |
| |
| /* |
| * Copyright (c) 2012-2014 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 modules tests the following memory pool routines: |
| |
| task_mem_pool_alloc(), |
| task_mem_pool_free() |
| */ |
| |
| #include <zephyr.h> |
| #include <tc_util.h> |
| #include <misc/util.h> |
| |
| #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 |
| |
| typedef struct { |
| struct k_block *block; /* pointer to block data */ |
| kmemory_pool_t poolId; /* pool ID */ |
| int size; /* request size in bytes */ |
| int32_t timeout; /* # of ticks to wait */ |
| int rcode; /* expected return code */ |
| } TEST_CASE; |
| |
| typedef int (*poolBlockGetFunc_t)(struct k_block *, kmemory_pool_t, int, int32_t); |
| typedef int (*poolMoveBlockFunc_t)(struct k_block *, kmemory_pool_t); |
| |
| static volatile int evidence = 0; |
| |
| static struct k_block blockList[NUM_BLOCKS]; |
| static struct k_block helperBlock; |
| |
| static TEST_CASE getSet[] = { |
| {&blockList[0], POOL_ID, 0, 0, RC_OK}, |
| {&blockList[1], POOL_ID, 1, 0, RC_OK}, |
| {&blockList[2], POOL_ID, 32, 0, RC_OK}, |
| {&blockList[3], POOL_ID, 64, 0, RC_OK}, |
| {&blockList[4], POOL_ID, 128, 0, RC_OK}, |
| {&blockList[5], POOL_ID, 256, 0, RC_OK}, |
| {&blockList[6], POOL_ID, 512, 0, RC_OK}, |
| {&blockList[7], POOL_ID, 1024, 0, RC_OK}, |
| {&blockList[8], POOL_ID, 2048, 0, RC_FAIL}, |
| {&blockList[9], POOL_ID, 4096, 0, RC_FAIL} |
| }; |
| |
| static TEST_CASE getSet2[] = { |
| {&blockList[0], POOL_ID, 4096, 0, RC_OK}, |
| {&blockList[1], POOL_ID, 2048, 0, RC_FAIL}, |
| {&blockList[2], POOL_ID, 1024, 0, RC_FAIL}, |
| {&blockList[3], POOL_ID, 512, 0, RC_FAIL}, |
| {&blockList[4], POOL_ID, 256, 0, RC_FAIL} |
| }; |
| |
| static TEST_CASE getwtSet[] = { |
| {&blockList[0], POOL_ID, 4096, TENTH_SECOND, RC_OK}, |
| {&blockList[1], POOL_ID, 2048, TENTH_SECOND, RC_TIME}, |
| {&blockList[2], POOL_ID, 1024, TENTH_SECOND, RC_TIME}, |
| {&blockList[3], POOL_ID, 512, TENTH_SECOND, RC_TIME}, |
| {&blockList[4], POOL_ID, 256, TENTH_SECOND, RC_TIME} |
| }; |
| |
| static TEST_CASE defrag[] = { |
| {&blockList[0], POOL_ID, 64, 0, RC_OK}, |
| {&blockList[1], POOL_ID, 64, 0, RC_OK}, |
| {&blockList[2], POOL_ID, 64, 0, RC_OK}, |
| {&blockList[3], POOL_ID, 64, 0, RC_OK}, |
| {&blockList[4], POOL_ID, 256, 0, RC_OK}, |
| {&blockList[5], POOL_ID, 256, 0, RC_OK}, |
| {&blockList[6], POOL_ID, 256, 0, RC_OK}, |
| {&blockList[7], POOL_ID, 1024, 0, RC_OK}, |
| {&blockList[8], POOL_ID, 1024, 0, RC_OK}, |
| {&blockList[9], POOL_ID, 1024, 0, RC_OK} |
| }; |
| |
| /** |
| * |
| * @brief Compare the two blocks |
| * |
| * @return 0 if the same, non-zero if not the same |
| */ |
| |
| int blockCompare(struct k_block *b1, struct k_block *b2) |
| { |
| char *p1 = (char *) b1; |
| char *p2 = (char *) b2; |
| int i; |
| int diff; |
| |
| for (i = 0; i < sizeof(struct k_block); i++) { |
| diff = p2[i] - p1[i]; |
| if (diff != 0) { |
| break; |
| } |
| } |
| |
| return diff; |
| } |
| |
| /** |
| * |
| * @brief Wrapper for task_mem_pool_alloc() |
| * |
| * @return task_mem_pool_alloc() return value |
| */ |
| |
| int poolBlockGetFunc(struct k_block *block, kmemory_pool_t pool, int size, |
| int32_t unused) |
| { |
| ARG_UNUSED(unused); |
| |
| return task_mem_pool_alloc(block, pool, size, TICKS_NONE); |
| } |
| |
| /** |
| * |
| * @brief Wrapper for task_mem_pool_alloc(TICKS_UNLIMITED) |
| * |
| * @return task_mem_pool_alloc() return value |
| */ |
| |
| int poolBlockGetWFunc(struct k_block *block, kmemory_pool_t pool, int size, |
| int32_t unused) |
| { |
| ARG_UNUSED(unused); |
| |
| return task_mem_pool_alloc(block, pool, size, TICKS_UNLIMITED); |
| } |
| |
| /** |
| * |
| * @brief Wrapper for task_mem_pool_alloc(timeout) |
| * |
| * @return task_mem_pool_alloc(timeout) return value |
| */ |
| |
| int poolBlockGetWTFunc(struct k_block *block, kmemory_pool_t pool, |
| int size, int32_t timeout) |
| { |
| return task_mem_pool_alloc(block, pool, size, timeout); |
| } |
| |
| /** |
| * |
| * @brief Free any blocks allocated in the test set |
| * |
| * @return N/A |
| */ |
| |
| void freeBlocks(TEST_CASE *tests, int nTests) |
| { |
| int i; |
| |
| for (i = 0; i < nTests; i++) { |
| if (tests[i].rcode == RC_OK) { |
| task_mem_pool_free(tests[i].block); |
| } |
| } |
| } |
| |
| /** |
| * |
| * @brief Perform the work of getting blocks |
| * |
| * @return TC_PASS on success, TC_FAIL on failure |
| */ |
| |
| int poolBlockGetWork(char *string, poolBlockGetFunc_t func, |
| TEST_CASE *tests, int nTests) |
| { |
| int i; |
| int rv; |
| |
| for (i = 0; i < nTests; i++) { |
| rv = func(tests[i].block, tests[i].poolId, 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 task_mem_pool_alloc(TICKS_NONE) API |
| * |
| * The pool is 4 kB in size. |
| * |
| * @return TC_PASS on success, TC_FAIL on failure |
| */ |
| |
| int poolBlockGetTest(void) |
| { |
| int rv; /* return value from task_mem_pool_alloc() */ |
| int j; /* loop counter */ |
| |
| for (j = 0; j < 8; j++) { |
| rv = poolBlockGetWork("task_mem_pool_alloc", poolBlockGetFunc, |
| getSet, ARRAY_SIZE(getSet)); |
| if (rv != TC_PASS) { |
| return TC_FAIL; |
| } |
| |
| freeBlocks(getSet, ARRAY_SIZE(getSet)); |
| |
| rv = poolBlockGetWork("task_mem_pool_alloc", poolBlockGetFunc, |
| getSet2, ARRAY_SIZE(getSet2)); |
| if (rv != TC_PASS) { |
| return TC_FAIL; |
| } |
| |
| freeBlocks(getSet2, ARRAY_SIZE(getSet2)); |
| } |
| |
| return TC_PASS; |
| } |
| |
| /** |
| * |
| * @brief Helper task to poolBlockGetTimeoutTest() |
| * |
| * @return N/A |
| */ |
| |
| void HelperTask(void) |
| { |
| task_sem_take(HELPER_SEM, TICKS_UNLIMITED); |
| |
| task_sem_give(REGRESS_SEM); |
| task_mem_pool_free(&helperBlock); |
| } |
| |
| /** |
| * |
| * @brief Test task_mem_pool_alloc(timeout) |
| * |
| * @return TC_PASS on success, TC_FAIL on failure |
| */ |
| |
| int poolBlockGetTimeoutTest(void) |
| { |
| struct k_block block; |
| int rv; /* return value from task_mem_pool_alloc() */ |
| int j; /* loop counter */ |
| |
| for (j = 0; j < 8; j++) { |
| rv = poolBlockGetWork("task_mem_pool_alloc", poolBlockGetWTFunc, |
| getwtSet, ARRAY_SIZE(getwtSet)); |
| if (rv != TC_PASS) { |
| return TC_FAIL; |
| } |
| |
| freeBlocks(getwtSet, ARRAY_SIZE(getwtSet)); |
| } |
| |
| rv = task_mem_pool_alloc(&helperBlock, POOL_ID, 3148, 5); |
| if (rv != RC_OK) { |
| TC_ERROR("Failed to get size 3148 byte block from POOL_ID\n"); |
| return TC_FAIL; |
| } |
| |
| rv = task_mem_pool_alloc(&block, POOL_ID, 3148, TICKS_NONE); |
| if (rv != RC_FAIL) { |
| TC_ERROR("Unexpectedly got size 3148 byte block from POOL_ID\n"); |
| return TC_FAIL; |
| } |
| |
| task_sem_give(HELPER_SEM); /* Activate HelperTask */ |
| rv = task_mem_pool_alloc(&block, POOL_ID, 3148, 20); |
| if (rv != RC_OK) { |
| TC_ERROR("Failed to get size 3148 byte block from POOL_ID\n"); |
| return TC_FAIL; |
| } |
| |
| rv = task_sem_take(REGRESS_SEM, TICKS_NONE); |
| if (rv != RC_OK) { |
| TC_ERROR("Failed to get size 3148 byte block within 20 ticks\n"); |
| return TC_FAIL; |
| } |
| |
| task_mem_pool_free(&block); |
| |
| return TC_PASS; |
| } |
| |
| /** |
| * |
| * poolBlockGetWaitTest - |
| * |
| * @return TC_PASS on success, TC_FAIL on failure |
| */ |
| |
| int poolBlockGetWaitTest(void) |
| { |
| int rv; |
| |
| rv = task_mem_pool_alloc(&blockList[0], POOL_ID, 3000, TICKS_UNLIMITED); |
| if (rv != RC_OK) { |
| TC_ERROR("task_mem_pool_alloc(3000) expected %d, got %d\n", RC_OK, rv); |
| return TC_FAIL; |
| } |
| |
| task_sem_give(ALTERNATE_SEM); /* Wake AlternateTask */ |
| evidence = 0; |
| rv = task_mem_pool_alloc(&blockList[1], POOL_ID, 128, TICKS_UNLIMITED); |
| if (rv != RC_OK) { |
| TC_ERROR("task_mem_pool_alloc(128) expected %d, got %d\n", RC_OK, rv); |
| return TC_FAIL; |
| } |
| |
| switch (evidence) { |
| case 0: |
| TC_ERROR("task_mem_pool_alloc(128) did not block!\n"); |
| return TC_FAIL; |
| case 1: |
| break; |
| case 2: |
| default: |
| TC_ERROR("Rescheduling did not occur after task_mem_pool_free()\n"); |
| return TC_FAIL; |
| } |
| |
| task_mem_pool_free(&blockList[1]); |
| |
| return TC_PASS; |
| } |
| |
| /** |
| * |
| * @brief Task responsible for defragmenting the pool POOL_ID |
| * |
| * @return N/A |
| */ |
| |
| void DefragTask(void) |
| { |
| task_sem_take(DEFRAG_SEM, TICKS_UNLIMITED); /* Wait to be activated */ |
| |
| task_mem_pool_defragment(POOL_ID); |
| |
| task_sem_give(REGRESS_SEM); /* DefragTask is finished */ |
| } |
| |
| /** |
| * |
| * poolDefragTest - |
| * |
| * @return TC_PASS on success, TC_FAIL on failure |
| */ |
| |
| int poolDefragTest(void) |
| { |
| int rv; |
| struct k_block newBlock; |
| |
| /* Get a bunch of blocks */ |
| |
| rv = poolBlockGetWork("task_mem_pool_alloc", poolBlockGetFunc, |
| defrag, ARRAY_SIZE(defrag)); |
| if (rv != TC_PASS) { |
| return TC_FAIL; |
| } |
| |
| |
| task_sem_give(DEFRAG_SEM); /* Activate DefragTask */ |
| |
| /* |
| * Block on getting another block from the pool. |
| * This will allow DefragTask to execute so that we can get some |
| * better code coverage. 50 ticks is expected to more than sufficient |
| * time for DefragTask to finish. |
| */ |
| |
| rv = task_mem_pool_alloc(&newBlock, POOL_ID, DEFRAG_BLK_TEST, 50); |
| if (rv != RC_TIME) { |
| TC_ERROR("task_mem_pool_alloc() returned %d, not %d\n", rv, RC_TIME); |
| return TC_FAIL; |
| } |
| |
| rv = task_sem_take(REGRESS_SEM, TICKS_NONE); |
| if (rv != RC_OK) { |
| TC_ERROR("DefragTask did not finish in allotted time!\n"); |
| return TC_FAIL; |
| } |
| |
| /* Free the allocated blocks */ |
| |
| freeBlocks(defrag, ARRAY_SIZE(defrag)); |
| |
| return TC_PASS; |
| } |
| |
| /** |
| * |
| * @brief Alternate task in the test suite |
| * |
| * This routine runs at a lower priority than RegressionTask(). |
| * |
| * @return N/A |
| */ |
| |
| void AlternateTask(void) |
| { |
| task_sem_take(ALTERNATE_SEM, TICKS_UNLIMITED); |
| |
| evidence = 1; |
| |
| task_mem_pool_free(&blockList[0]); |
| |
| evidence = 2; |
| } |
| |
| /** |
| * |
| * @brief Main task in the test suite |
| * |
| * This is the entry point to the memory pool test suite. |
| * |
| * @return N/A |
| */ |
| |
| void RegressionTask(void) |
| { |
| int tcRC; /* test case return code */ |
| |
| TC_START("Test Microkernel Memory Pools"); |
| |
| TC_PRINT("Testing task_mem_pool_alloc(TICKS_NONE) ...\n"); |
| tcRC = poolBlockGetTest(); |
| if (tcRC != TC_PASS) { |
| goto doneTests; |
| } |
| |
| TC_PRINT("Testing task_mem_pool_alloc(timeout) ...\n"); |
| tcRC = poolBlockGetTimeoutTest(); |
| if (tcRC != TC_PASS) { |
| goto doneTests; |
| } |
| |
| TC_PRINT("Testing task_mem_pool_alloc(TICKS_UNLIMITED) ...\n"); |
| tcRC = poolBlockGetWaitTest(); |
| if (tcRC != TC_PASS) { |
| goto doneTests; |
| } |
| |
| TC_PRINT("Testing task_mem_pool_defragment() ...\n"); |
| tcRC = poolDefragTest(); |
| if (tcRC != TC_PASS) { |
| goto doneTests; |
| } |
| |
| doneTests: |
| TC_END_RESULT(tcRC); |
| TC_END_REPORT(tcRC); |
| } |