text: /*
   * Copyright (c) 2012-2014 Wind River Systems, Inc.
   *
   * SPDX-License-Identifier: Apache-2.0
   */
  
  /**
   * @file
   * @brief Test memory slab APIs
   *
   * This module tests the following memory slab routines:
   *
   *     k_mem_slab_alloc
   *     k_mem_slab_free
   *     k_mem_slab_num_used_get
   *
   * @note
   * One should ensure that the block is released to the same memory slab from
   * which it was allocated, and is only released once.  Using an invalid pointer
   * will have unpredictable side effects.
   */
  
  #include <tc_util.h>
  #include <stdbool.h>
  #include <zephyr.h>
  #include <ztest.h>
  
  /* size of stack area used by each thread */
  #define STACKSIZE (1024 + CONFIG_TEST_EXTRA_STACKSIZE)
  
  /* Number of memory blocks. The minimum number of blocks needed to run the
   * test is 2
   */
  #define NUMBLOCKS   2
  
  static int tc_rc = TC_PASS;     /* test case return code */
  
  int test_slab_get_all_blocks(void **P);
  int test_slab_free_all_blocks(void **P);
  
  
  K_SEM_DEFINE(SEM_HELPERDONE, 0, 1);
  K_SEM_DEFINE(SEM_REGRESSDONE, 0, 1);
  
  K_MEM_SLAB_DEFINE(map_lgblks, 1024, NUMBLOCKS, 4);
  
  
  /**
   *
   * @brief Verify return value
   *
   * This routine verifies current value against expected value
   * and returns true if they are the same.
   *
   * @param expect_ret_value     expect value
   * @param current_ret_current    current value
   *
   * @return  true, false
   */
  
  bool verify_ret_value(int expect_ret_value, int current_ret_current)
  {
  	return (expect_ret_value == current_ret_current);
  
  } /* verify_ret_value */
  
  /**
   *
   * @brief Helper task
   *
   * This routine gets all blocks from the memory slab.  It uses semaphores
   * SEM_REGRESDONE and SEM_HELPERDONE to synchronize between different parts
   * of the test.
   *
   * @return  N/A
   */
  
  void helper_thread(void)
  {
  	void *ptr[NUMBLOCKS];           /* Pointer to memory block */
  
  	memset(ptr, 0, sizeof(ptr));    /* keep static checkers happy */
  	/* Wait for part 1 to complete */
  	k_sem_take(&SEM_REGRESSDONE, K_FOREVER);
  
  	/* Part 2 of test */
  
  	TC_PRINT("Starts %s\n", __func__);
  	PRINT_LINE;
  	TC_PRINT("(2) - Allocate %d blocks in <%s>\n", NUMBLOCKS, __func__);
  	PRINT_LINE;
  
  	/* Test k_mem_slab_alloc */
  	tc_rc = test_slab_get_all_blocks(ptr);
  	if (tc_rc == TC_FAIL) {
  		TC_ERROR("Failed test_slab_get_all_blocks function\n");
  		goto exittest1;          /* terminate test */
  	}
  
  	k_sem_give(&SEM_HELPERDONE);  /* Indicate part 2 is complete */
  	/* Wait for part 3 to complete */
  	k_sem_take(&SEM_REGRESSDONE, K_FOREVER);
  
  	/*
  	 * Part 4 of test.
  	 * Free the first memory block.  RegressionTask is currently blocked
  	 * waiting (with a timeout) for a memory block.  Freeing the memory
  	 * block will unblock RegressionTask.
  	 */
  	PRINT_LINE;
  	TC_PRINT("(4) - Free a block in <%s> to unblock the other task "
  		 "from alloc timeout\n", __func__);
  	PRINT_LINE;
  
  	TC_PRINT("%s: About to free a memory block\n", __func__);
  	k_mem_slab_free(&map_lgblks, &ptr[0]);
  	k_sem_give(&SEM_HELPERDONE);
  
  	/* Part 5 of test */
  	k_sem_take(&SEM_REGRESSDONE, K_FOREVER);
  	PRINT_LINE;
  	TC_PRINT("(5) <%s> freeing the next block\n", __func__);
  	PRINT_LINE;
  	TC_PRINT("%s: About to free another memory block\n", __func__);
  	k_mem_slab_free(&map_lgblks, &ptr[1]);
  
  	/*
  	 * Free all the other blocks.  The first 2 blocks are freed by this task
  	 */
  	for (int i = 2; i < NUMBLOCKS; i++) {
  		k_mem_slab_free(&map_lgblks, &ptr[i]);
  	}
  	TC_PRINT("%s: freed all blocks allocated by this task\n", __func__);
  
  exittest1:
  
  	TC_END_RESULT(tc_rc);
  	k_sem_give(&SEM_HELPERDONE);
  }  /* helper thread */
  
  
  /**
   *
   * @brief Get all blocks from the memory slab
   *
   * Get all blocks from the memory slab.  It also tries to get one more block
   * from the map after the map is empty to verify the error return code.
   *
   * This routine tests the following:
   *
   *   k_mem_slab_alloc(), k_mem_slab_num_used_get()
   *
   * @param p    pointer to pointer of allocated blocks
   *
   * @return  TC_PASS, TC_FAIL
   */
  
  int test_slab_get_all_blocks(void **p)
  {
  	int ret_value;  /* task_mem_map_xxx interface return value */
  	void *errptr;   /* Pointer to block */
  
  	TC_PRINT("Function %s\n", __func__);
  
  	for (int i = 0; i < NUMBLOCKS; i++) {
  		/* Verify number of used blocks in the map */
  		ret_value = k_mem_slab_num_used_get(&map_lgblks);
  		if (verify_ret_value(i, ret_value)) {
  			TC_PRINT("MAP_LgBlks used %d blocks\n", ret_value);
  		} else {
  			TC_ERROR("Failed task_mem_map_used_get for "
  				 "MAP_LgBlks, i=%d, retValue=%d\n",
  				 i, ret_value);
  			return TC_FAIL;
  		}
  
  		/* Get memory block */
  		ret_value = k_mem_slab_alloc(&map_lgblks, &p[i], K_NO_WAIT);
  		if (verify_ret_value(0, ret_value)) {
  			TC_PRINT("  k_mem_slab_alloc OK, p[%d] = %p\n",
  				 i, p[i]);
  		} else {
  			TC_ERROR("Failed k_mem_slab_alloc, i=%d, "
  				 "ret_value %d\n", i, ret_value);
  			return TC_FAIL;
  		}
  
  	} /* for */
  
  	/* Verify number of used blocks in the map - expect all blocks are
  	 * used
  	 */
  	ret_value = k_mem_slab_num_used_get(&map_lgblks);
  	if (verify_ret_value(NUMBLOCKS, ret_value)) {
  		TC_PRINT("MAP_LgBlks used %d blocks\n", ret_value);
  	} else {
  		TC_ERROR("Failed task_mem_map_used_get for MAP_LgBlks, "
  			 "retValue %d\n", ret_value);
  		return TC_FAIL;
  	}
  
  	/* Try to get one more block and it should fail */
  	ret_value = k_mem_slab_alloc(&map_lgblks, &errptr, K_NO_WAIT);
  	if (verify_ret_value(-ENOMEM, ret_value)) {
  		TC_PRINT("  k_mem_slab_alloc RC_FAIL expected as all (%d) "
  			 "blocks are used.\n", NUMBLOCKS);
  	} else {
  		TC_ERROR("Failed k_mem_slab_alloc, expect RC_FAIL, got %d\n",
  			 ret_value);
  		return TC_FAIL;
  	}
  
  	PRINT_LINE;
  
  	return TC_PASS;
  }  /* test_slab_get_all_blocks */
  
  /**
   *
   * @brief Free all memory blocks
   *
   * This routine frees all memory blocks and also verifies that the number of
   * blocks used are correct.
   *
   * This routine tests the following:
   *
   *   k_mem_slab_free(&), k_mem_slab_num_used_get(&)
   *
   * @param p    pointer to pointer of allocated blocks
   *
   * @return  TC_PASS, TC_FAIL
   */
  
  int test_slab_free_all_blocks(void **p)
  {
  	int ret_value;     /* task_mem_map_xxx interface return value */
  
  	TC_PRINT("Function %s\n", __func__);
  
  	for (int i = 0; i < NUMBLOCKS; i++) {
  		/* Verify number of used blocks in the map */
  		ret_value = k_mem_slab_num_used_get(&map_lgblks);
  		if (verify_ret_value(NUMBLOCKS - i, ret_value)) {
  			TC_PRINT("MAP_LgBlks used %d blocks\n", ret_value);
  		} else {
  			TC_ERROR("Failed task_mem_map_used_get for "
  				 "MAP_LgBlks, expect %d, got %d\n",
  				 NUMBLOCKS - i, ret_value);
  			return TC_FAIL;
  		}
  
  		TC_PRINT("  block ptr to free p[%d] = %p\n", i, p[i]);
  		/* Free memory block */
  		k_mem_slab_free(&map_lgblks, &p[i]);
  
  		TC_PRINT("MAP_LgBlks freed %d block\n", i + 1);
  
  	} /* for */
  
  	/*
  	 * Verify number of used blocks in the map
  	 *  - should be 0 as no blocks are used
  	 */
  
  	ret_value = k_mem_slab_num_used_get(&map_lgblks);
  	if (verify_ret_value(0, ret_value)) {
  		TC_PRINT("MAP_LgBlks used %d blocks\n", ret_value);
  	} else {
  		TC_ERROR("Failed task_mem_map_used_get for MAP_LgBlks, "
  			 "retValue %d\n", ret_value);
  		return TC_FAIL;
  	}
  
  	PRINT_LINE;
  	return TC_PASS;
  }   /* testSlabFreeAllBlocks */
  
  /**
   *
   * @brief Print the pointers
   *
   * This routine prints out the pointers.
   *
   * @param pointer    pointer to pointer of allocated blocks
   *
   * @return  N/A
   */
  void print_pointers(void **pointer)
  {
  	TC_PRINT("%s: ", __func__);
  	for (int i = 0; i < NUMBLOCKS; i++) {
  		TC_PRINT("p[%d] = %p, ", i, pointer[i]);
  	}
  
  	TC_PRINT("\n");
  	PRINT_LINE;
  
  }  /* print_pointers */
  
  /**
   *
   * @brief Main task to test memory slab interfaces
   *
   * This routine calls test_slab_get_all_blocks() to get all memory blocks from
   * the map and calls test_slab_free_all_blocks() to free all memory blocks.
   * It also tries to wait (with and without timeout) for a memory block.
   *
   * This routine tests the following:
   *
   *   k_mem_slab_alloc
   *
   * @return  N/A
   */
  
  void testing_mslab(void)
  {
  	int ret_value;                  /* task_mem_map_xxx interface return value */
  	void *b;                        /* Pointer to memory block */
  	void *ptr[NUMBLOCKS];           /* Pointer to memory block */
  
  	/* not strictly necessary, but keeps coverity checks happy */
  	memset(ptr, 0, sizeof(ptr));
  
  	/* Part 1 of test */
  
  	TC_PRINT("Starts %s\n", __func__);
  	PRINT_LINE;
  	TC_PRINT("(1) - Allocate and free %d blocks "
  		 "in <%s>\n", NUMBLOCKS, __func__);
  	PRINT_LINE;
  
  	/* Test k_mem_slab_alloc */
  	tc_rc = test_slab_get_all_blocks(ptr);
  	zassert_not_equal(tc_rc, TC_FAIL,
  			  "Failed test_slab_get_all_blocks function\n");
  
  	print_pointers(ptr);
  	/* Test task_mem_map_free */
  	tc_rc = test_slab_free_all_blocks(ptr);
  	zassert_not_equal(tc_rc, TC_FAIL,
  			  "Failed testalab_freeall_blocks function\n");
  
  	k_sem_give(&SEM_REGRESSDONE);   /* Allow helper thread to run */
  	/* Wait for helper thread to finish */
  	k_sem_take(&SEM_HELPERDONE, K_FOREVER);
  
  	/*
  	 * Part 3 of test.
  	 *
  	 * helper thread got all memory blocks.  There is no free block left.
  	 * The call will timeout.  Note that control does not switch back to
  	 * helper thread as it is waiting for SEM_REGRESSDONE.
  	 */
  
  	PRINT_LINE;
  	TC_PRINT("(3) - Further allocation results in  timeout "
  		 "in <%s>\n", __func__);
  	PRINT_LINE;
  
  	ret_value = k_mem_slab_alloc(&map_lgblks, &b, 20);
  	zassert_true(verify_ret_value(-EAGAIN, ret_value),
  		     "Failed k_mem_slab_alloc, retValue %d\n",
  		     ret_value);
  
  	TC_PRINT("%s: start to wait for block\n", __func__);
  	k_sem_give(&SEM_REGRESSDONE);    /* Allow helper thread to run part 4 */
  	ret_value = k_mem_slab_alloc(&map_lgblks, &b, 50);
  	zassert_true(verify_ret_value(0, ret_value),
  		     "Failed k_mem_slab_alloc, ret_value %d\n", ret_value);
  
  	/* Wait for helper thread to complete */
  	k_sem_take(&SEM_HELPERDONE, K_FOREVER);
  
  	TC_PRINT("%s: start to wait for block\n", __func__);
  	k_sem_give(&SEM_REGRESSDONE);    /* Allow helper thread to run part 5 */
  	ret_value = k_mem_slab_alloc(&map_lgblks, &b, K_FOREVER);
  	zassert_true(verify_ret_value(0, ret_value),
  		     "Failed k_mem_slab_alloc, ret_value %d\n", ret_value);
  
  	/* Wait for helper thread to complete */
  	k_sem_take(&SEM_HELPERDONE, K_FOREVER);
  
  
  	/* Free memory block */
  	TC_PRINT("%s: Used %d block\n", __func__,
  		 k_mem_slab_num_used_get(&map_lgblks));
  	k_mem_slab_free(&map_lgblks, &b);
  	TC_PRINT("%s: 1 block freed, used %d block\n",
  		 __func__,  k_mem_slab_num_used_get(&map_lgblks));
  }
  
  K_THREAD_DEFINE(HELPER, STACKSIZE, helper_thread, NULL, NULL, NULL,
  		7, 0, K_NO_WAIT);
  
  /*test case main entry*/
  void test_main(void)
  {
  	ztest_test_suite(test_mslab, ztest_unit_test(testing_mslab));
  	ztest_run_test_suite(test_mslab);
  }
  