tests: bluetooth: mesh: Check bt_mesh_rpl_reset with unit test
Check RPL when rescheduling happens during the reset operation started by bt_mesh_rpl_reset. Signed-off-by: Pavel Vasilyev <pavel.vasilyev@nordicsemi.no>
This commit is contained in:
parent
1fdf283531
commit
da7e555a99
21
tests/bluetooth/mesh/rpl/CMakeLists.txt
Normal file
21
tests/bluetooth/mesh/rpl/CMakeLists.txt
Normal file
|
@ -0,0 +1,21 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
cmake_minimum_required(VERSION 3.20.0)
|
||||
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
|
||||
project(bluetooth_mesh_rpl)
|
||||
|
||||
FILE(GLOB app_sources src/*.c)
|
||||
target_sources(app
|
||||
PRIVATE
|
||||
${app_sources}
|
||||
${ZEPHYR_BASE}/subsys/bluetooth/mesh/rpl.c)
|
||||
|
||||
target_include_directories(app
|
||||
PRIVATE
|
||||
${ZEPHYR_BASE}/subsys/bluetooth/mesh)
|
||||
|
||||
target_compile_options(app
|
||||
PRIVATE
|
||||
-DCONFIG_BT_MESH_CRPL=10
|
||||
-DCONFIG_BT_MESH_RPL_STORE_TIMEOUT=1
|
||||
-DCONFIG_BT_SETTINGS)
|
3
tests/bluetooth/mesh/rpl/prj.conf
Normal file
3
tests/bluetooth/mesh/rpl/prj.conf
Normal file
|
@ -0,0 +1,3 @@
|
|||
CONFIG_ZTEST=y
|
||||
CONFIG_ZTEST_NEW_API=y
|
||||
CONFIG_ZTEST_MOCKING=y
|
471
tests/bluetooth/mesh/rpl/src/main.c
Normal file
471
tests/bluetooth/mesh/rpl/src/main.c
Normal file
|
@ -0,0 +1,471 @@
|
|||
/*
|
||||
* Copyright (c) 2023 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/ztest.h>
|
||||
#include <zephyr/net/buf.h>
|
||||
#include <zephyr/bluetooth/mesh.h>
|
||||
|
||||
#include "settings.h"
|
||||
#include "net.h"
|
||||
#include "rpl.h"
|
||||
|
||||
#define EMPTY_ENTRIES_CNT (CONFIG_BT_MESH_CRPL - ARRAY_SIZE(test_vector))
|
||||
|
||||
/* Used for cleaning RPL without checking it. */
|
||||
static bool skip_delete;
|
||||
|
||||
/* Test function that should call bt_mesh_rpl_check(). */
|
||||
static enum {
|
||||
SETTINGS_SAVE_ONE = 1,
|
||||
SETTINGS_DELETE
|
||||
} settings_func;
|
||||
/* Number of `settings_func` calls before calling bt_mesh_rpl_check() (1 means first call). */
|
||||
static int settings_func_cnt;
|
||||
/* Received message context. */
|
||||
static struct bt_mesh_net_rx recv_msg;
|
||||
|
||||
/* We will change test vector during the test as it is convenient to do so. Therefore, we need
|
||||
* to keep default values separately.
|
||||
*/
|
||||
static const struct test_rpl_entry {
|
||||
char *name;
|
||||
uint16_t src;
|
||||
bool old_iv;
|
||||
uint32_t seq;
|
||||
} test_vector_default[] = {
|
||||
{ .name = "bt/mesh/RPL/1", .src = 0x1, .old_iv = false, .seq = 10, },
|
||||
{ .name = "bt/mesh/RPL/17", .src = 0x17, .old_iv = true, .seq = 32, },
|
||||
{ .name = "bt/mesh/RPL/7c", .src = 0x7c, .old_iv = false, .seq = 20, },
|
||||
{ .name = "bt/mesh/RPL/2c", .src = 0x2c, .old_iv = true, .seq = 5, },
|
||||
{ .name = "bt/mesh/RPL/5a", .src = 0x5a, .old_iv = true, .seq = 12, },
|
||||
};
|
||||
static struct test_rpl_entry test_vector[ARRAY_SIZE(test_vector_default)];
|
||||
|
||||
/**** Helper functions ****/
|
||||
|
||||
static void prepare_rpl_and_start_reset(void)
|
||||
{
|
||||
/* Add test vector to RPL. */
|
||||
for (int i = 0; i < ARRAY_SIZE(test_vector); i++) {
|
||||
struct bt_mesh_net_rx msg = {
|
||||
.local_match = true,
|
||||
.ctx.addr = test_vector[i].src,
|
||||
.old_iv = test_vector[i].old_iv,
|
||||
.seq = test_vector[i].seq,
|
||||
};
|
||||
|
||||
ztest_expect_value(bt_mesh_settings_store_schedule, flag,
|
||||
BT_MESH_SETTINGS_RPL_PENDING);
|
||||
zassert_false(bt_mesh_rpl_check(&msg, NULL));
|
||||
}
|
||||
|
||||
/* settings_save_one() will be triggered for all new entries when
|
||||
* bt_mesh_rpl_pending_store() is called.
|
||||
*/
|
||||
for (int i = 0; i < ARRAY_SIZE(test_vector); i++) {
|
||||
ztest_expect_data(settings_save_one, name, test_vector[i].name);
|
||||
}
|
||||
bt_mesh_rpl_pending_store(BT_MESH_ADDR_ALL_NODES);
|
||||
|
||||
/* Check that all added entries are in RPL. */
|
||||
for (int i = 0; i < ARRAY_SIZE(test_vector); i++) {
|
||||
struct bt_mesh_net_rx msg = {
|
||||
.local_match = true,
|
||||
.ctx.addr = test_vector[i].src,
|
||||
.old_iv = test_vector[i].old_iv,
|
||||
.seq = test_vector[i].seq,
|
||||
};
|
||||
|
||||
zassert_true(bt_mesh_rpl_check(&msg, NULL));
|
||||
}
|
||||
|
||||
/* Simulate IVI Update. This should only flip flags. The actual storing will happen
|
||||
* when bt_mesh_rpl_pending_store() is called.
|
||||
*/
|
||||
ztest_expect_value(bt_mesh_settings_store_schedule, flag, BT_MESH_SETTINGS_RPL_PENDING);
|
||||
bt_mesh_rpl_reset();
|
||||
}
|
||||
|
||||
/* Should be called after the reset operation is finished. */
|
||||
static void check_entries_from_test_vector(void)
|
||||
{
|
||||
for (int i = 0; i < ARRAY_SIZE(test_vector); i++) {
|
||||
struct bt_mesh_net_rx msg = {
|
||||
.local_match = true,
|
||||
.ctx.addr = test_vector[i].src,
|
||||
/* Entries with old_iv == true should have been deleted. old_iv in entries
|
||||
* is flipped, so to check this we can try to add the removed entries
|
||||
* again. RPL should accept them.
|
||||
*/
|
||||
.old_iv = !test_vector[i].old_iv,
|
||||
.seq = test_vector[i].seq
|
||||
};
|
||||
|
||||
/* Removed entries can now be added again. */
|
||||
if (test_vector[i].old_iv) {
|
||||
ztest_expect_value(bt_mesh_settings_store_schedule, flag,
|
||||
BT_MESH_SETTINGS_RPL_PENDING);
|
||||
zassert_false(bt_mesh_rpl_check(&msg, NULL));
|
||||
} else {
|
||||
zassert_true(bt_mesh_rpl_check(&msg, NULL));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void check_empty_entries(int cnt)
|
||||
{
|
||||
/* Check that RPL has the specified amount of empty entries. */
|
||||
for (int i = 0; i < cnt; i++) {
|
||||
struct bt_mesh_net_rx msg = {
|
||||
.local_match = true,
|
||||
.ctx.addr = 0x7fff - i,
|
||||
.old_iv = false,
|
||||
.seq = i,
|
||||
};
|
||||
|
||||
ztest_expect_value(bt_mesh_settings_store_schedule, flag,
|
||||
BT_MESH_SETTINGS_RPL_PENDING);
|
||||
zassert_false(bt_mesh_rpl_check(&msg, NULL));
|
||||
}
|
||||
|
||||
/* Check that there are no more empty entries in RPL. */
|
||||
struct bt_mesh_net_rx msg = {
|
||||
.local_match = true,
|
||||
.ctx.addr = 0x1024,
|
||||
.old_iv = false,
|
||||
.seq = 1024,
|
||||
};
|
||||
zassert_true(bt_mesh_rpl_check(&msg, NULL));
|
||||
}
|
||||
|
||||
static void check_op(int op)
|
||||
{
|
||||
if (settings_func == op) {
|
||||
if (settings_func_cnt > 1) {
|
||||
settings_func_cnt--;
|
||||
} else {
|
||||
ztest_expect_value(bt_mesh_settings_store_schedule, flag,
|
||||
BT_MESH_SETTINGS_RPL_PENDING);
|
||||
zassert_false(bt_mesh_rpl_check(&recv_msg, NULL));
|
||||
|
||||
settings_func_cnt--;
|
||||
settings_func = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void call_rpl_check_on(int func, int cnt, struct test_rpl_entry *entry)
|
||||
{
|
||||
settings_func = func;
|
||||
settings_func_cnt = cnt;
|
||||
|
||||
recv_msg.local_match = true;
|
||||
recv_msg.ctx.addr = entry->src;
|
||||
recv_msg.seq = entry->seq;
|
||||
recv_msg.old_iv = entry->old_iv;
|
||||
}
|
||||
|
||||
static void expect_pending_store(void)
|
||||
{
|
||||
/* Entries with old_iv == true should be removed, others should be stored. */
|
||||
for (int i = 0; i < ARRAY_SIZE(test_vector); i++) {
|
||||
if (test_vector[i].old_iv) {
|
||||
ztest_expect_value(settings_delete, name, test_vector[i].name);
|
||||
} else {
|
||||
ztest_expect_data(settings_save_one, name, test_vector[i].name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool is_rpl_check_called(void)
|
||||
{
|
||||
return settings_func_cnt == 0;
|
||||
}
|
||||
|
||||
static void verify_rpl(void)
|
||||
{
|
||||
check_entries_from_test_vector();
|
||||
check_empty_entries(EMPTY_ENTRIES_CNT);
|
||||
}
|
||||
|
||||
static void setup(void *f)
|
||||
{
|
||||
/* Restore test vector. */
|
||||
memcpy(test_vector, test_vector_default, sizeof(test_vector));
|
||||
|
||||
/* Clear RPL before every test. */
|
||||
skip_delete = true;
|
||||
ztest_expect_value(bt_mesh_settings_store_schedule, flag, BT_MESH_SETTINGS_RPL_PENDING);
|
||||
bt_mesh_rpl_clear();
|
||||
bt_mesh_rpl_pending_store(BT_MESH_ADDR_ALL_NODES);
|
||||
skip_delete = false;
|
||||
|
||||
settings_func = 0;
|
||||
settings_func_cnt = 0;
|
||||
}
|
||||
|
||||
/**** Mocked functions ****/
|
||||
|
||||
void bt_mesh_settings_store_schedule(enum bt_mesh_settings_flag flag)
|
||||
{
|
||||
ztest_check_expected_value(flag);
|
||||
}
|
||||
|
||||
void bt_mesh_settings_store_cancel(enum bt_mesh_settings_flag flag)
|
||||
{
|
||||
}
|
||||
|
||||
int settings_save_one(const char *name, const void *value, size_t val_len)
|
||||
{
|
||||
ztest_check_expected_data(name, strlen(name));
|
||||
|
||||
check_op(SETTINGS_SAVE_ONE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int settings_delete(const char *name)
|
||||
{
|
||||
if (skip_delete) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ztest_check_expected_data(name, strlen(name));
|
||||
|
||||
check_op(SETTINGS_DELETE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**** Tests ****/
|
||||
|
||||
ZTEST_SUITE(bt_mesh_rpl_reset, NULL, NULL, setup, NULL, NULL);
|
||||
|
||||
/** Test that entries with old_iv == true are removed after the reset operation finished. */
|
||||
ZTEST(bt_mesh_rpl_reset, test_reset_normal)
|
||||
{
|
||||
prepare_rpl_and_start_reset();
|
||||
expect_pending_store();
|
||||
|
||||
bt_mesh_rpl_pending_store(BT_MESH_ADDR_ALL_NODES);
|
||||
|
||||
verify_rpl();
|
||||
}
|
||||
|
||||
/** Test that RPL accepts and stores a valid entry that was just deleted. The entry should be
|
||||
* stored after the reset operation is finished.
|
||||
*/
|
||||
ZTEST(bt_mesh_rpl_reset, test_rpl_check_on_delete_same_entry)
|
||||
{
|
||||
prepare_rpl_and_start_reset();
|
||||
expect_pending_store();
|
||||
|
||||
/* Take the first entry with old_iv == true and simulate msg reception with same src
|
||||
* address and correct IVI after the entry was deleted.
|
||||
*/
|
||||
struct test_rpl_entry *entry = &test_vector[1];
|
||||
|
||||
zassert_true(entry->old_iv);
|
||||
entry->old_iv = false;
|
||||
call_rpl_check_on(SETTINGS_DELETE, 1, entry);
|
||||
|
||||
bt_mesh_rpl_pending_store(BT_MESH_ADDR_ALL_NODES);
|
||||
zassert_true(is_rpl_check_called());
|
||||
|
||||
/* Call bt_mesh_rpl_pending_store() to store new entry. */
|
||||
ztest_expect_data(settings_save_one, name, entry->name);
|
||||
bt_mesh_rpl_pending_store(BT_MESH_ADDR_ALL_NODES);
|
||||
|
||||
verify_rpl();
|
||||
}
|
||||
|
||||
/** Test that RPL accepts and stores a valid entry that was just stored. The entry should be stored
|
||||
* after the reset operation is finished.
|
||||
*/
|
||||
ZTEST(bt_mesh_rpl_reset, test_rpl_check_on_save_same_entry)
|
||||
{
|
||||
prepare_rpl_and_start_reset();
|
||||
expect_pending_store();
|
||||
|
||||
/* Take the first entry with old_iv == false and simulate msg reception with same src
|
||||
* address and correct IVI after the entry was stored.
|
||||
*/
|
||||
struct test_rpl_entry *entry = &test_vector[0];
|
||||
|
||||
zassert_false(entry->old_iv);
|
||||
call_rpl_check_on(SETTINGS_SAVE_ONE, 1, entry);
|
||||
|
||||
bt_mesh_rpl_pending_store(BT_MESH_ADDR_ALL_NODES);
|
||||
zassert_true(is_rpl_check_called());
|
||||
|
||||
/* Call bt_mesh_rpl_pending_store() to store new entry. */
|
||||
ztest_expect_data(settings_save_one, name, entry->name);
|
||||
bt_mesh_rpl_pending_store(BT_MESH_ADDR_ALL_NODES);
|
||||
|
||||
verify_rpl();
|
||||
}
|
||||
|
||||
/** Test that RPL accepts and stores a valid entry that has not yet been deleted. The entry should
|
||||
* be stored during the reset operation.
|
||||
*/
|
||||
ZTEST(bt_mesh_rpl_reset, test_rpl_check_on_delete_other_entry)
|
||||
{
|
||||
prepare_rpl_and_start_reset();
|
||||
|
||||
/* Take the non-first entry with old_iv == true and simulate msg reception with same src
|
||||
* address and correct IVI before the entry is deleted.
|
||||
*
|
||||
* Should be done before calling ztest_expect_data() because the expectation changes.
|
||||
*/
|
||||
struct test_rpl_entry *entry = &test_vector[3];
|
||||
|
||||
zassert_true(entry->old_iv);
|
||||
entry->old_iv = false;
|
||||
call_rpl_check_on(SETTINGS_DELETE, 1, entry);
|
||||
|
||||
expect_pending_store();
|
||||
|
||||
bt_mesh_rpl_pending_store(BT_MESH_ADDR_ALL_NODES);
|
||||
zassert_true(is_rpl_check_called());
|
||||
|
||||
/* The entry should have been deleted in previous bt_mesh_rpl_pending_store() call. Another
|
||||
* call should not do anything.
|
||||
*/
|
||||
bt_mesh_rpl_pending_store(BT_MESH_ADDR_ALL_NODES);
|
||||
|
||||
verify_rpl();
|
||||
}
|
||||
|
||||
/* Test that RPL accepts and stores a valid entry that has not yet been stored. The entry should be
|
||||
* stored during the reset operation.
|
||||
*/
|
||||
ZTEST(bt_mesh_rpl_reset, test_rpl_check_on_save_other_entry)
|
||||
{
|
||||
prepare_rpl_and_start_reset();
|
||||
|
||||
/* Take RPL entry from test vector that has old_iv == false and is not stored yet after
|
||||
* bt_mesh_reset() call and try to store it again. RPL has such entry with flipped old_iv,
|
||||
* so this one can be accepted as is.
|
||||
*
|
||||
* Should be done before calling ztest_expect_data() because the expectation changes.
|
||||
*/
|
||||
struct test_rpl_entry *entry = &test_vector[2];
|
||||
|
||||
zassert_false(entry->old_iv);
|
||||
call_rpl_check_on(SETTINGS_SAVE_ONE, 1, entry);
|
||||
|
||||
expect_pending_store();
|
||||
|
||||
bt_mesh_rpl_pending_store(BT_MESH_ADDR_ALL_NODES);
|
||||
zassert_true(is_rpl_check_called());
|
||||
|
||||
/* The entry should have been stored in previous bt_mesh_rpl_pending_store() call. Another
|
||||
* call should not do anything.
|
||||
*/
|
||||
bt_mesh_rpl_pending_store(BT_MESH_ADDR_ALL_NODES);
|
||||
|
||||
verify_rpl();
|
||||
}
|
||||
|
||||
/** Test that RPL accepts and stores a valid entry that has been deleted during the reset operation.
|
||||
* The entry will be added at the end of RPL, therefore it should be stored during the reset
|
||||
* operation.
|
||||
*/
|
||||
ZTEST(bt_mesh_rpl_reset, test_rpl_check_on_delete_deleted_entry)
|
||||
{
|
||||
prepare_rpl_and_start_reset();
|
||||
expect_pending_store();
|
||||
|
||||
/* Take the first entry with old_iv == true, wait until bt_mesh_rpl_pending_store() takes
|
||||
* another entry after that one and simulate msg reception.
|
||||
*/
|
||||
struct test_rpl_entry *entry = &test_vector[1];
|
||||
|
||||
zassert_true(entry->old_iv);
|
||||
entry->old_iv = false;
|
||||
call_rpl_check_on(SETTINGS_DELETE, 2, entry);
|
||||
/* The entry will be stored during the reset operation as it will be added to the end of
|
||||
* the RPL.
|
||||
*/
|
||||
ztest_expect_data(settings_save_one, name, entry->name);
|
||||
|
||||
bt_mesh_rpl_pending_store(BT_MESH_ADDR_ALL_NODES);
|
||||
zassert_true(is_rpl_check_called());
|
||||
|
||||
/* The new entry should have been stored already. Another bt_mesh_rpl_pending_store() call
|
||||
* should not do anything.
|
||||
*/
|
||||
bt_mesh_rpl_pending_store(BT_MESH_ADDR_ALL_NODES);
|
||||
|
||||
verify_rpl();
|
||||
}
|
||||
|
||||
/** Test that RPL accepts and stores a valid entry that has been stored during the reset operation.
|
||||
* Since the entry has been already in the list, it should be stored again after the reset
|
||||
* operation is finished.
|
||||
*/
|
||||
ZTEST(bt_mesh_rpl_reset, test_rpl_check_on_store_stored_entry)
|
||||
{
|
||||
prepare_rpl_and_start_reset();
|
||||
expect_pending_store();
|
||||
|
||||
/* Take the first entry with old_iv == false, wait until bt_mesh_rpl_pending_store() takes
|
||||
* another entry after that one and simulate msg reception.
|
||||
*/
|
||||
struct test_rpl_entry *entry = &test_vector[0];
|
||||
|
||||
zassert_false(entry->old_iv);
|
||||
entry->old_iv = true;
|
||||
entry->seq++;
|
||||
call_rpl_check_on(SETTINGS_SAVE_ONE, 2, entry);
|
||||
|
||||
bt_mesh_rpl_pending_store(BT_MESH_ADDR_ALL_NODES);
|
||||
zassert_true(is_rpl_check_called());
|
||||
|
||||
/* The entry was updated after bt_mesh_rpl_pending_store() checked it. So it should be
|
||||
* stored again.
|
||||
*/
|
||||
ztest_expect_data(settings_save_one, name, entry->name);
|
||||
bt_mesh_rpl_pending_store(BT_MESH_ADDR_ALL_NODES);
|
||||
|
||||
verify_rpl();
|
||||
}
|
||||
|
||||
/** Test that RPL accepts and stores a new entry when the reset operation is not yet finished. */
|
||||
ZTEST(bt_mesh_rpl_reset, test_rpl_check_on_save_new_entry)
|
||||
{
|
||||
prepare_rpl_and_start_reset();
|
||||
expect_pending_store();
|
||||
|
||||
/* Add a new entry to RPL during the reset operation. */
|
||||
struct test_rpl_entry entry = {
|
||||
.name = "bt/mesh/RPL/2b",
|
||||
.src = 43,
|
||||
.old_iv = false,
|
||||
.seq = 32,
|
||||
};
|
||||
ztest_expect_data(settings_save_one, name, entry.name);
|
||||
call_rpl_check_on(SETTINGS_SAVE_ONE, 1, &entry);
|
||||
|
||||
bt_mesh_rpl_pending_store(BT_MESH_ADDR_ALL_NODES);
|
||||
zassert_true(is_rpl_check_called());
|
||||
|
||||
/* The entry should have been stored in previous bt_mesh_rpl_pending_store() call. Another
|
||||
* call should not do anything.
|
||||
*/
|
||||
bt_mesh_rpl_pending_store(BT_MESH_ADDR_ALL_NODES);
|
||||
|
||||
check_entries_from_test_vector();
|
||||
/* Check that added entry in the RPL. */
|
||||
struct bt_mesh_net_rx msg = {
|
||||
.local_match = true,
|
||||
.ctx.addr = entry.src,
|
||||
.old_iv = entry.old_iv,
|
||||
.seq = entry.seq
|
||||
};
|
||||
zassert_true(bt_mesh_rpl_check(&msg, NULL));
|
||||
check_empty_entries(EMPTY_ENTRIES_CNT - 1);
|
||||
}
|
6
tests/bluetooth/mesh/rpl/testcase.yaml
Normal file
6
tests/bluetooth/mesh/rpl/testcase.yaml
Normal file
|
@ -0,0 +1,6 @@
|
|||
tests:
|
||||
bluetooth.mesh.rpl:
|
||||
platform_allow: native_posix
|
||||
tags: bluetooth mesh
|
||||
integration_platforms:
|
||||
- native_posix
|
Loading…
Reference in a new issue