Spdk/test/unit/lib/ftl/ftl_mngt/ftl_mngt_ut.c
paul luse a6dbe3721e update Intel copyright notices
per Intel policy to include file commit date using git cmd
below.  The policy does not apply to non-Intel (C) notices.

git log --follow -C90% --format=%ad --date default <file> | tail -1

and then pull just the 4 digit year from the result.

Intel copyrights were not added to files where Intel either had
no contribution ot the contribution lacked substance (ie license
header updates, formatting changes, etc).  Contribution date used
"--follow -C95%" to get the most accurate date.

Note that several files in this patch didn't end the license/(c)
block with a blank comment line so these were added as the vast
majority of files do have this last blank line.  Simply there for
consistency.

Signed-off-by: paul luse <paul.e.luse@intel.com>
Change-Id: Id5b7ce4f658fe87132f14139ead58d6e285c04d4
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/15192
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: Jim Harris <james.r.harris@intel.com>
Reviewed-by: Ben Walker <benjamin.walker@intel.com>
Community-CI: Mellanox Build Bot
2022-11-10 08:28:53 +00:00

1111 lines
26 KiB
C

/* SPDX-License-Identifier: BSD-3-Clause
* Copyright (C) 2022 Intel Corporation.
* All rights reserved.
*/
#include <sys/queue.h>
#include "spdk/stdinc.h"
#include "spdk_cunit.h"
#include "common/lib/test_env.c"
#include "ftl/mngt/ftl_mngt.c"
#define CALLER_CB_RET_VALUE 999
/* list for structure with results of tests from callbacks */
struct entry {
int data;
TAILQ_ENTRY(entry) entries;
};
TAILQ_HEAD(listhead, entry) g_head;
struct thread_send_msg_container {
spdk_msg_fn fn;
void *ctx;
} g_thread_send_msg_container;
struct spdk_ftl_dev g_dev;
int
spdk_thread_send_msg(const struct spdk_thread *thread, spdk_msg_fn fn, void *ctx)
{
g_thread_send_msg_container.fn = fn;
g_thread_send_msg_container.ctx = ctx;
return 0;
}
struct spdk_thread *
spdk_get_thread(void)
{
struct spdk_thread *thd = (struct spdk_thread *)0x1;
return thd;
}
static int
setup_test_list(void)
{
TAILQ_INIT(&g_head);
return 0;
}
static void
check_list_empty(void)
{
CU_ASSERT_TRUE(TAILQ_EMPTY(&g_head));
}
static void
add_elem_to_test_list(int data)
{
struct entry *en = calloc(1, sizeof(*en));
SPDK_CU_ASSERT_FATAL(en != NULL);
en->data = data;
TAILQ_INSERT_TAIL(&g_head, en, entries);
}
static void
check_elem_on_list_and_remove(int compared_elem)
{
struct entry *en = TAILQ_FIRST(&g_head);
if (en != NULL) {
CU_ASSERT_EQUAL(en->data, compared_elem);
TAILQ_REMOVE(&g_head, en, entries);
free(en);
} else {
CU_FAIL("not null value was expected");
}
}
static void
fn_finish(struct spdk_ftl_dev *dev, void *ctx, int status)
{
add_elem_to_test_list(CALLER_CB_RET_VALUE);
g_thread_send_msg_container.fn = NULL;
g_thread_send_msg_container.ctx = NULL;
}
typedef int (*ftl_execute_fn)(struct spdk_ftl_dev *dev,
const struct ftl_mngt_process_desc *process,
ftl_mngt_completion cb, void *cb_cntx);
static void
run_ftl_mngt_with_cb_cntx(ftl_execute_fn exec_fn,
const struct ftl_mngt_process_desc *process, void *cb_cntx)
{
int result = exec_fn(&g_dev, process, fn_finish, cb_cntx);
CU_ASSERT_EQUAL(result, 0);
while (g_thread_send_msg_container.fn != NULL) {
g_thread_send_msg_container.fn(g_thread_send_msg_container.ctx);
}
}
static void
run_ftl_mngt(ftl_execute_fn exec_fn,
const struct ftl_mngt_process_desc *process)
{
run_ftl_mngt_with_cb_cntx(exec_fn, process, NULL);
}
/*-
* test 1
* tests simple invoking next steps
* it is shown if ftl_mngt_process_execute and ftl_mngt_process_rollback invoke functions in proper order
* (functions call only ftl_mngt_next_step)
*/
static void
fn_1_1_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
add_elem_to_test_list(1);
ftl_mngt_next_step(mngt);
}
static void
fn_1_1_cleanup(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
add_elem_to_test_list(-1);
ftl_mngt_next_step(mngt);
}
static void
fn_1_2_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
add_elem_to_test_list(2);
ftl_mngt_next_step(mngt);
}
static void
fn_1_3_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
add_elem_to_test_list(3);
ftl_mngt_next_step(mngt);
}
static void
fn_1_3_cleanup(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
add_elem_to_test_list(-3);
ftl_mngt_next_step(mngt);
}
static struct ftl_mngt_process_desc pdesc_test_1 = {
.name = "process 1",
.steps = {
{
.name = "step 1",
.action = fn_1_1_action,
.cleanup = fn_1_1_cleanup
},
{
.name = "step 2",
.action = fn_1_2_action
},
{
.name = "step 3",
.action = fn_1_3_action,
.cleanup = fn_1_3_cleanup
},
{}
}
};
static void
test_next_step(void)
{
run_ftl_mngt(ftl_mngt_process_execute, &pdesc_test_1);
/* check proper order of action functions */
for (int i = 1; i <= 3; i++) {
check_elem_on_list_and_remove(i);
}
/* check if caller callback was invoked */
check_elem_on_list_and_remove(CALLER_CB_RET_VALUE);
run_ftl_mngt(ftl_mngt_process_rollback, &pdesc_test_1);
/* Check proper order of cleanup functions.
* Cleanup functions add to list opposite values to action functions.
* Cleanup functions are invoked in reverse order,
* moreover action 2 does not have cleanup,
* so expected values are -3, then -1 */
check_elem_on_list_and_remove(-3);
check_elem_on_list_and_remove(-1);
/* check if caller callback was invoked */
check_elem_on_list_and_remove(CALLER_CB_RET_VALUE);
check_list_empty();
}
/*-
* test 2
* tests action and cleanup function which invoke
* ftl_mngt_continue_step function
*/
static void
fn_2_common_part(struct ftl_mngt_process *mngt, int elem)
{
struct entry *en = TAILQ_LAST(&g_head, listhead);
if (en == NULL || en->data != elem) {
/* if function was invoked 1st time, make it once again */
add_elem_to_test_list(elem);
ftl_mngt_continue_step(mngt);
} else {
/* otherwise go to the next function */
add_elem_to_test_list(elem);
ftl_mngt_next_step(mngt);
}
}
static void
fn_2_1_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
fn_2_common_part(mngt, 1);
}
static void
fn_2_1_cleanup(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
fn_2_common_part(mngt, -1);
}
static void
fn_2_2_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
fn_2_common_part(mngt, 2);
}
static void
fn_2_2_cleanup(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
fn_2_common_part(mngt, -2);
}
static struct ftl_mngt_process_desc pdesc_test_2 = {
.name = "process 2",
.steps = {
{
.name = "step 1",
.action = fn_2_1_action,
.cleanup = fn_2_1_cleanup
},
{
.name = "step 2",
.action = fn_2_2_action,
.cleanup = fn_2_2_cleanup
},
{}
}
};
static void
test_continue_step(void)
{
run_ftl_mngt(ftl_mngt_process_execute, &pdesc_test_2);
/* check proper order of action functions */
check_elem_on_list_and_remove(1);
check_elem_on_list_and_remove(1);
check_elem_on_list_and_remove(2);
check_elem_on_list_and_remove(2);
/* check if caller callback was invoked */
check_elem_on_list_and_remove(CALLER_CB_RET_VALUE);
run_ftl_mngt(ftl_mngt_process_rollback, &pdesc_test_2);
/* check proper order of action functions */
check_elem_on_list_and_remove(-2);
check_elem_on_list_and_remove(-2);
check_elem_on_list_and_remove(-1);
check_elem_on_list_and_remove(-1);
/* check if caller callback was invoked */
check_elem_on_list_and_remove(CALLER_CB_RET_VALUE);
check_list_empty();
}
/*-
* test 3
* tests ftl_mngt_alloc_step_cntx and all ftl_mngt_get functions
*/
const int PROCESS_CNTX_TEST_VAL_0 = 21;
const int PROCESS_CNTX_TEST_VAL_1 = 37;
const int STEP_CNTX_TEST_VAL = 1;
static void
put_on_list(void)
{
struct entry *en = calloc(1, sizeof(*en));
SPDK_CU_ASSERT_FATAL(en != NULL);
TAILQ_INSERT_TAIL(&g_head, en, entries);
}
static bool
check_if_list_empty_and_clean(void)
{
struct entry *en = TAILQ_FIRST(&g_head);
if (en == NULL) {
return true;
} else {
TAILQ_REMOVE(&g_head, en, entries);
free(en);
return false;
}
}
static void
fn_3_1_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
int *step_cntx_ptr, *process_cntx_ptr;
char *caller_cntx_ptr;
int status;
step_cntx_ptr = ftl_mngt_get_step_ctx(mngt);
if (check_if_list_empty_and_clean()) {
/* In 1st run of this function test list is empty
* and 'if' is true, this part of function is done.
* That 'if' part ends with ftl_mngt_continue_step,
* so function will be called once again.
* Element is added to the test list
* to invoke 'else' in second run */
put_on_list();
/* this step descriptor does not locate any context
* at the beginning,
* so pointer should contain NULL */
CU_ASSERT_PTR_NULL(step_cntx_ptr);
status = ftl_mngt_alloc_step_ctx(mngt, sizeof(*step_cntx_ptr));
SPDK_CU_ASSERT_FATAL(status == 0);
step_cntx_ptr = ftl_mngt_get_step_ctx(mngt);
/* now pointer should point to allocated context */
CU_ASSERT_PTR_NOT_NULL(step_cntx_ptr);
/* this value should be retrieved in second run of function
* (in 'else' part) */
*step_cntx_ptr = STEP_CNTX_TEST_VAL;
ftl_mngt_continue_step(mngt);
} else {
/* In second run retrieved pointer is not empty.
* Moreover it should contain value allocated for this step
* in previous run of function */
CU_ASSERT_PTR_NOT_NULL(step_cntx_ptr);
CU_ASSERT_EQUAL(*step_cntx_ptr, STEP_CNTX_TEST_VAL);
/* check getting device */
CU_ASSERT_EQUAL(ftl_mngt_get_dev(mngt), dev);
CU_ASSERT_EQUAL(ftl_mngt_get_dev(mngt), &g_dev);
/* tests for process context */
process_cntx_ptr = ftl_mngt_get_process_ctx(mngt);
/* 1st get of process context, should be clear ('0' values) */
CU_ASSERT_EQUAL(process_cntx_ptr[0], 0);
CU_ASSERT_EQUAL(process_cntx_ptr[1], 0);
/* Random values put in process context.
* Should be retrieved in the next function
* (it is common space for the entire process) */
process_cntx_ptr[0] = PROCESS_CNTX_TEST_VAL_0;
process_cntx_ptr[1] = PROCESS_CNTX_TEST_VAL_1;
/* tests for caller context */
caller_cntx_ptr = ftl_mngt_get_caller_ctx(mngt);
/* check previously located values */
CU_ASSERT_EQUAL(caller_cntx_ptr[0], 'd');
CU_ASSERT_EQUAL(caller_cntx_ptr[1], 'a');
CU_ASSERT_EQUAL(caller_cntx_ptr[2], 'j');
/* insert new */
caller_cntx_ptr[0] = ' ';
caller_cntx_ptr[1] = 'k';
caller_cntx_ptr[2] = 'a';
ftl_mngt_next_step(mngt);
}
}
static void
fn_3_2_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
int *step_cntx_ptr, *process_cntx_ptr;
char *caller_cntx_ptr;
int status;
step_cntx_ptr = ftl_mngt_get_step_ctx(mngt);
/* context of this step descriptor is never empty
* so pointer cannot contain NULL */
CU_ASSERT_PTR_NOT_NULL(step_cntx_ptr);
if (check_if_list_empty_and_clean()) {
/* In 1st run of this function test list is empty
* and 'if' is true, this part of function is done.
* That 'if' part ends with ftl_mngt_continue_step,
* so function will be called once again.
* Element is added to the test list
* to invoke 'else' in second run */
put_on_list();
/* check getting device */
CU_ASSERT_EQUAL(ftl_mngt_get_dev(mngt), dev);
CU_ASSERT_EQUAL(ftl_mngt_get_dev(mngt), &g_dev);
/* tests for process context */
process_cntx_ptr = ftl_mngt_get_process_ctx(mngt);
/* check if it is possible to retrieve values located
* in process context by previous function */
CU_ASSERT_EQUAL(process_cntx_ptr[0], PROCESS_CNTX_TEST_VAL_0);
CU_ASSERT_EQUAL(process_cntx_ptr[1], PROCESS_CNTX_TEST_VAL_1);
/* tests for caller context */
caller_cntx_ptr = ftl_mngt_get_caller_ctx(mngt);
/* check previously located values */
CU_ASSERT_EQUAL(caller_cntx_ptr[0], ' ');
CU_ASSERT_EQUAL(caller_cntx_ptr[1], 'k');
CU_ASSERT_EQUAL(caller_cntx_ptr[2], 'a');
/* insert new */
caller_cntx_ptr[0] = 'm';
caller_cntx_ptr[1] = 'i';
caller_cntx_ptr[2] = 'e';
/* first run of step so reserved step context
* was never used before and should contain 0 */
CU_ASSERT_EQUAL(*step_cntx_ptr, 0);
/* this value should be retrieved in second run of function
* (in 'else' part) */
*step_cntx_ptr = STEP_CNTX_TEST_VAL;
ftl_mngt_continue_step(mngt);
} else {
/* In second run retrieved pointer should contain value
* allocated for this step in previous run of function */
CU_ASSERT_EQUAL(*step_cntx_ptr, STEP_CNTX_TEST_VAL);
status = ftl_mngt_alloc_step_ctx(mngt, sizeof(*step_cntx_ptr));
SPDK_CU_ASSERT_FATAL(status == 0);
step_cntx_ptr = ftl_mngt_get_step_ctx(mngt);
/* now pointer should point to newly allocated context
* and be cleaned up (should contain '0') */
CU_ASSERT_PTR_NOT_NULL(step_cntx_ptr);
CU_ASSERT_EQUAL(*step_cntx_ptr, 0);
ftl_mngt_next_step(mngt);
}
}
static void
fn_3_2_cleanup(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
int *step_cntx_ptr, *process_cntx_ptr;
char *caller_cntx_ptr;
int status;
step_cntx_ptr = ftl_mngt_get_step_ctx(mngt);
/* context of this step descriptor is never empty
* so pointer cannot contain NULL */
CU_ASSERT_PTR_NOT_NULL(step_cntx_ptr);
if (check_if_list_empty_and_clean()) {
/* In 1st run of this function test list is empty
* and 'if' is true, this part of function is done.
* That 'if' part ends with ftl_mngt_continue_step,
* so function will be called once again.
* Element is added to the test list
* to invoke 'else' in second run */
put_on_list();
/* first run of step so reserved step context
* was never used before and should contain 0 */
CU_ASSERT_EQUAL(*step_cntx_ptr, 0);
/* this value should be retrieved in second run of function
* (in 'else' part) */
*step_cntx_ptr = STEP_CNTX_TEST_VAL;
ftl_mngt_continue_step(mngt);
} else {
/* In second run retrieved pointer should contain value
* allocated for this step in previous run of function */
CU_ASSERT_EQUAL(*step_cntx_ptr, STEP_CNTX_TEST_VAL);
status = ftl_mngt_alloc_step_ctx(mngt, sizeof(*step_cntx_ptr));
SPDK_CU_ASSERT_FATAL(status == 0);
step_cntx_ptr = ftl_mngt_get_step_ctx(mngt);
/* now pointer should point to newly allocated context
* and be cleaned up (should contain '0') */
CU_ASSERT_PTR_NOT_NULL(step_cntx_ptr);
CU_ASSERT_EQUAL(*step_cntx_ptr, 0);
/* check getting device */
CU_ASSERT_EQUAL(ftl_mngt_get_dev(mngt), dev);
CU_ASSERT_EQUAL(ftl_mngt_get_dev(mngt), &g_dev);
/* tests for process context */
process_cntx_ptr = ftl_mngt_get_process_ctx(mngt);
/* 1st get of process context, should be clear ('0' values) */
CU_ASSERT_EQUAL(process_cntx_ptr[0], 0);
CU_ASSERT_EQUAL(process_cntx_ptr[1], 0);
/* Random values put in process context.
* Should be retrieved in the next function
* (it is common space for the entire process) */
process_cntx_ptr[0] = PROCESS_CNTX_TEST_VAL_0;
process_cntx_ptr[1] = PROCESS_CNTX_TEST_VAL_1;
/* tests for caller context */
caller_cntx_ptr = ftl_mngt_get_caller_ctx(mngt);
/* check previously located values */
CU_ASSERT_EQUAL(caller_cntx_ptr[0], 'm');
CU_ASSERT_EQUAL(caller_cntx_ptr[1], 'i');
CU_ASSERT_EQUAL(caller_cntx_ptr[2], 'e');
/* insert new */
caller_cntx_ptr[0] = 'n';
caller_cntx_ptr[1] = 'i';
caller_cntx_ptr[2] = 'a';
ftl_mngt_next_step(mngt);
}
}
static void
fn_3_1_cleanup(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
int *step_cntx_ptr, *process_cntx_ptr;
char *caller_cntx_ptr;
int status;
step_cntx_ptr = ftl_mngt_get_step_ctx(mngt);
if (check_if_list_empty_and_clean()) {
/* In 1st run of this function test list is empty
* and 'if' is true, this part of function is done.
* That 'if' part ends with ftl_mngt_continue_step,
* so function will be called once again.
* Element is added to the test list
* to invoke 'else' in second run */
put_on_list();
/* this step descriptor does not locate any context
* at the beginning,
* so pointer should contain NULL */
CU_ASSERT_PTR_NULL(step_cntx_ptr);
/* check getting device */
CU_ASSERT_EQUAL(ftl_mngt_get_dev(mngt), dev);
CU_ASSERT_EQUAL(ftl_mngt_get_dev(mngt), &g_dev);
/* tests for process context */
process_cntx_ptr = ftl_mngt_get_process_ctx(mngt);
/* check if it is possible to retrieve values located
* in process context by previous function */
CU_ASSERT_EQUAL(process_cntx_ptr[0], PROCESS_CNTX_TEST_VAL_0);
CU_ASSERT_EQUAL(process_cntx_ptr[1], PROCESS_CNTX_TEST_VAL_1);
/* tests for caller context */
caller_cntx_ptr = ftl_mngt_get_caller_ctx(mngt);
/* check previously located values */
CU_ASSERT_EQUAL(caller_cntx_ptr[0], 'n');
CU_ASSERT_EQUAL(caller_cntx_ptr[1], 'i');
CU_ASSERT_EQUAL(caller_cntx_ptr[2], 'a');
/* insert new */
caller_cntx_ptr[0] = '!';
caller_cntx_ptr[1] = '!';
caller_cntx_ptr[2] = '!';
status = ftl_mngt_alloc_step_ctx(mngt, sizeof(*step_cntx_ptr));
SPDK_CU_ASSERT_FATAL(status == 0);
step_cntx_ptr = ftl_mngt_get_step_ctx(mngt);
/* now pointer should point to allocated context */
CU_ASSERT_PTR_NOT_NULL(step_cntx_ptr);
/* this value should be retrieved in second run of function
* (in 'else' part) */
*step_cntx_ptr = STEP_CNTX_TEST_VAL;
ftl_mngt_continue_step(mngt);
} else {
/* In second run retrieved pointer is not empty.
* Moreover it should contain value allocated for this step
* in previous run of function */
CU_ASSERT_PTR_NOT_NULL(step_cntx_ptr);
CU_ASSERT_EQUAL(*step_cntx_ptr, STEP_CNTX_TEST_VAL);
ftl_mngt_next_step(mngt);
}
}
static struct ftl_mngt_process_desc pdesc_test_3 = {
.name = "process 3",
.ctx_size = 2 * sizeof(int),
.steps = {
{
.name = "step 1",
.action = fn_3_1_action,
.cleanup = fn_3_1_cleanup
},
{
.name = "step 2",
.ctx_size = sizeof(int),
.action = fn_3_2_action,
.cleanup = fn_3_2_cleanup
},
{}
}
};
static void
test_get_func_and_step_cntx_alloc(void)
{
char cb_cntx[4] = "daj";
run_ftl_mngt_with_cb_cntx(ftl_mngt_process_execute, &pdesc_test_3, cb_cntx);
/* check if caller callback was invoked */
check_elem_on_list_and_remove(CALLER_CB_RET_VALUE);
/* check if steps changed cb_cntx correctly */
CU_ASSERT_EQUAL(cb_cntx[0], 'm');
CU_ASSERT_EQUAL(cb_cntx[1], 'i');
CU_ASSERT_EQUAL(cb_cntx[2], 'e');
run_ftl_mngt_with_cb_cntx(ftl_mngt_process_rollback, &pdesc_test_3, cb_cntx);
/* check if caller callback was invoked */
check_elem_on_list_and_remove(CALLER_CB_RET_VALUE);
/* check if steps changed cb_cntx correctly */
CU_ASSERT_EQUAL(cb_cntx[0], '!');
CU_ASSERT_EQUAL(cb_cntx[1], '!');
CU_ASSERT_EQUAL(cb_cntx[2], '!');
check_list_empty();
}
/*-
* test 4
* tests ftl_mngt_fail_step function
*
* In that test one of the action functions fails (third one).
* Because of that expected result (saved on the test result list)
* are numbers of the next action function up to failing function.
* After that cleanup functions are invoked in reversed order.
*/
static void
fn_4_1_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
add_elem_to_test_list(1);
ftl_mngt_next_step(mngt);
}
static void
fn_4_1_cleanup(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
add_elem_to_test_list(-1);
ftl_mngt_next_step(mngt);
}
static void
fn_4_2_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
add_elem_to_test_list(2);
ftl_mngt_next_step(mngt);
}
static void
fn_4_2_cleanup(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
add_elem_to_test_list(-2);
ftl_mngt_next_step(mngt);
}
static void
fn_4_3_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
add_elem_to_test_list(3);
/* this action fails, so cleanup should begin now */
ftl_mngt_fail_step(mngt);
}
static void
fn_4_3_cleanup(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
add_elem_to_test_list(-3);
ftl_mngt_next_step(mngt);
}
static void
fn_4_4_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
CU_FAIL("failure cannot start another action");
ftl_mngt_next_step(mngt);
}
static struct ftl_mngt_process_desc pdesc_test_4 = {
.name = "process 4",
.steps = {
{
.name = "step 1",
.action = fn_4_1_action,
.cleanup = fn_4_1_cleanup
},
{
.name = "step 2",
.action = fn_4_2_action,
.cleanup = fn_4_2_cleanup
},
{
.name = "step 3",
.action = fn_4_3_action,
.cleanup = fn_4_3_cleanup
},
{
.name = "step 2",
.action = fn_4_4_action
},
{}
}
};
static void
test_fail_step(void)
{
run_ftl_mngt(ftl_mngt_process_execute, &pdesc_test_4);
/* check proper order of action functions */
for (int i = 1; i <= 3; i++) {
check_elem_on_list_and_remove(i);
}
/* 3rd action function fails, so now should be
* cleanup functions in reverse order */
for (int i = 3; i > 0; i--) {
check_elem_on_list_and_remove(-i);
}
/* check if caller callback was invoked */
check_elem_on_list_and_remove(CALLER_CB_RET_VALUE);
check_list_empty();
}
/*-
* test 5
* tests ftl_mngt_call_process and ftl_mngt_call_process_rollback functions
* tests only proper flow without failures
*/
static void
fn_5_2_1_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
add_elem_to_test_list(21);
ftl_mngt_next_step(mngt);
}
static void
fn_5_2_1_cleanup(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
add_elem_to_test_list(-21);
ftl_mngt_next_step(mngt);
}
static void
fn_5_2_2_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
add_elem_to_test_list(22);
ftl_mngt_next_step(mngt);
}
static void
fn_5_2_2_cleanup(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
add_elem_to_test_list(-22);
ftl_mngt_next_step(mngt);
}
static struct ftl_mngt_process_desc pdesc_test_5_2 = {
.name = "process nested inside step 2 from process 5",
.steps = {
{
.name = "step 2_1",
.action = fn_5_2_1_action,
.cleanup = fn_5_2_1_cleanup
},
{
.name = "step 2_2",
.action = fn_5_2_2_action,
.cleanup = fn_5_2_2_cleanup
},
{}
}
};
static void
fn_5_3_1_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
add_elem_to_test_list(31);
ftl_mngt_next_step(mngt);
}
static void
fn_5_3_1_cleanup(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
add_elem_to_test_list(-31);
ftl_mngt_next_step(mngt);
}
static void
fn_5_3_2_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
add_elem_to_test_list(32);
ftl_mngt_next_step(mngt);
}
static void
fn_5_3_2_cleanup(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
add_elem_to_test_list(-32);
ftl_mngt_next_step(mngt);
}
static struct ftl_mngt_process_desc pdesc_test_5_3 = {
.name = "process nested inside step 2 from process 5",
.steps = {
{
.name = "step 3_1",
.action = fn_5_3_1_action,
.cleanup = fn_5_3_1_cleanup
},
{
.name = "step 3_2",
.action = fn_5_3_2_action,
.cleanup = fn_5_3_2_cleanup
},
{}
}
};
static void
fn_5_1_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
add_elem_to_test_list(1);
ftl_mngt_next_step(mngt);
}
static void
fn_5_1_cleanup(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
add_elem_to_test_list(-1);
ftl_mngt_next_step(mngt);
}
static void
fn_5_2_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
add_elem_to_test_list(2);
ftl_mngt_call_process(mngt, &pdesc_test_5_2);
}
static void
fn_5_2_cleanup(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
add_elem_to_test_list(-2);
ftl_mngt_call_process_rollback(mngt, &pdesc_test_5_2);
}
static void
fn_5_3_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
add_elem_to_test_list(3);
ftl_mngt_call_process_rollback(mngt, &pdesc_test_5_3);
}
static void
fn_5_3_cleanup(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
add_elem_to_test_list(-3);
ftl_mngt_call_process(mngt, &pdesc_test_5_3);
}
static struct ftl_mngt_process_desc pdesc_test_5 = {
.name = "process 5 main",
.steps = {
{
.name = "step 1",
.action = fn_5_1_action,
.cleanup = fn_5_1_cleanup
},
{
.name = "step 2",
.action = fn_5_2_action,
.cleanup = fn_5_2_cleanup
},
{
.name = "step 3",
.action = fn_5_3_action,
.cleanup = fn_5_3_cleanup
},
{}
}
};
static void
test_mngt_call_and_call_rollback(void)
{
run_ftl_mngt(ftl_mngt_process_execute, &pdesc_test_5);
check_elem_on_list_and_remove(1);
check_elem_on_list_and_remove(2);
check_elem_on_list_and_remove(21);
check_elem_on_list_and_remove(22);
check_elem_on_list_and_remove(3);
check_elem_on_list_and_remove(-32);
check_elem_on_list_and_remove(-31);
/* check if caller callback was invoked */
check_elem_on_list_and_remove(CALLER_CB_RET_VALUE);
run_ftl_mngt(ftl_mngt_process_rollback, &pdesc_test_5);
check_elem_on_list_and_remove(-3);
check_elem_on_list_and_remove(31);
check_elem_on_list_and_remove(32);
check_elem_on_list_and_remove(-2);
check_elem_on_list_and_remove(-22);
check_elem_on_list_and_remove(-21);
check_elem_on_list_and_remove(-1);
/* check if caller callback was invoked */
check_elem_on_list_and_remove(CALLER_CB_RET_VALUE);
check_list_empty();
}
/*
* test 6
* tests failure inside nested process
*/
static void
fn_6_2_1_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
add_elem_to_test_list(21);
ftl_mngt_next_step(mngt);
}
static void
fn_6_2_1_cleanup(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
add_elem_to_test_list(-21);
ftl_mngt_next_step(mngt);
}
static void
fn_6_2_2_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
add_elem_to_test_list(22);
/* this action fails, so cleanup should begin now */
ftl_mngt_fail_step(mngt);
}
static void
fn_6_2_3_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
CU_FAIL("failure cannot start another action");
ftl_mngt_next_step(mngt);
}
static struct ftl_mngt_process_desc pdesc_test_6_2 = {
.name = "process nested inside step 2 from process 6",
.steps = {
{
.name = "step 6_1",
.action = fn_6_2_1_action,
.cleanup = fn_6_2_1_cleanup
},
{
.name = "step 6_2",
.action = fn_6_2_2_action
},
{
.name = "step 6_3",
.action = fn_6_2_3_action
},
{}
}
};
static void
fn_6_1_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
add_elem_to_test_list(1);
ftl_mngt_next_step(mngt);
}
static void
fn_6_2_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
add_elem_to_test_list(2);
ftl_mngt_call_process(mngt, &pdesc_test_6_2);
}
static void
fn_6_2_cleanup(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
add_elem_to_test_list(-2);
ftl_mngt_next_step(mngt);
}
static void
fn_6_3_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
CU_FAIL("failure cannot start another action");
ftl_mngt_next_step(mngt);
}
static struct ftl_mngt_process_desc pdesc_test_6 = {
.name = "process 6 main",
.steps = {
{
.name = "step 1",
.action = fn_6_1_action
},
{
.name = "step 2",
.action = fn_6_2_action,
.cleanup = fn_6_2_cleanup
},
{
.name = "step 3",
.action = fn_6_3_action
},
{}
}
};
static void
test_nested_process_failure(void)
{
run_ftl_mngt(ftl_mngt_process_execute, &pdesc_test_6);
check_elem_on_list_and_remove(1);
check_elem_on_list_and_remove(2);
check_elem_on_list_and_remove(21);
check_elem_on_list_and_remove(22);
check_elem_on_list_and_remove(-21);
check_elem_on_list_and_remove(-2);
/* check if caller callback was invoked */
check_elem_on_list_and_remove(CALLER_CB_RET_VALUE);
check_list_empty();
}
int
main(int argc, char **argv)
{
CU_pSuite suite = NULL;
unsigned int num_failures;
CU_set_error_action(CUEA_ABORT);
CU_initialize_registry();
suite = CU_add_suite("ftl_mngt", setup_test_list, NULL);
CU_ADD_TEST(suite, test_next_step);
CU_ADD_TEST(suite, test_continue_step);
CU_ADD_TEST(suite, test_get_func_and_step_cntx_alloc);
CU_ADD_TEST(suite, test_fail_step);
CU_ADD_TEST(suite, test_mngt_call_and_call_rollback);
CU_ADD_TEST(suite, test_nested_process_failure);
CU_basic_set_mode(CU_BRM_VERBOSE);
CU_basic_run_tests();
num_failures = CU_get_number_of_failures();
CU_cleanup_registry();
return num_failures;
}