1 /*
2  * Copyright (c) 2024 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 #include <stdlib.h>
7 
8 #include "settings_priv.h"
9 #include <zephyr/ztest.h>
10 #include <zephyr/settings/settings.h>
11 #include <zephyr/bluetooth/bluetooth.h>
12 
13 /* This is a test suite for performance testing of settings subsystem by writing
14  * many small setting values repeatedly. Ideally, this should consume as small
15  * amount of time as possible for best possible UX.
16  */
17 
18 static struct k_work_q settings_work_q;
19 static K_THREAD_STACK_DEFINE(settings_work_stack, 2024);
20 static struct k_work_delayable pending_store;
21 
22 #define TEST_SETTINGS_COUNT (128)
23 #define TEST_STORE_ITR (5)
24 #define TEST_TIMEOUT_SEC (60)
25 #define TEST_SETTINGS_WORKQ_PRIO (1)
26 
bt_scan_cb(const bt_addr_le_t * addr,int8_t rssi,uint8_t adv_type,struct net_buf_simple * buf)27 static void bt_scan_cb(const bt_addr_le_t *addr, int8_t rssi, uint8_t adv_type,
28 		struct net_buf_simple *buf)
29 {
30 	printk("len %u\n", buf->len);
31 }
32 
33 struct test_setting {
34 	uint32_t val;
35 } test_settings[TEST_SETTINGS_COUNT];
36 
37 K_SEM_DEFINE(waitfor_work, 0, 1);
38 
store_pending(struct k_work * work)39 static void store_pending(struct k_work *work)
40 {
41 	int err;
42 	char path[20];
43 	struct test_stats {
44 		uint32_t total_calculated;
45 		uint32_t total_measured;
46 		uint32_t single_entry_max;
47 		uint32_t single_entry_min;
48 	} stats = {0, 0, 0, UINT32_MAX};
49 
50 	int64_t ts1 = k_uptime_get();
51 
52 	/* benchmark storage performance */
53 	for (int j = 0; j < TEST_STORE_ITR; j++) {
54 		for (int i = 0; i < TEST_SETTINGS_COUNT; i++) {
55 			test_settings[i].val = TEST_SETTINGS_COUNT*j + i;
56 
57 			int64_t ts2 = k_uptime_get();
58 
59 			snprintk(path, sizeof(path), "ab/cdef/ghi/%04x", i);
60 			err = settings_save_one(path, &test_settings[i],
61 						sizeof(struct test_setting));
62 			zassert_equal(err, 0, "settings_save_one failed %d", err);
63 
64 			int64_t delta2 = k_uptime_delta(&ts2);
65 
66 			if (stats.single_entry_max < delta2) {
67 				stats.single_entry_max = delta2;
68 			}
69 			if (stats.single_entry_min > delta2) {
70 				stats.single_entry_min = delta2;
71 			}
72 			stats.total_calculated += delta2;
73 		}
74 	}
75 
76 	int64_t delta1 = k_uptime_delta(&ts1);
77 
78 	stats.total_measured = delta1;
79 
80 	printk("*** storing of %u entries completed ***\n", ARRAY_SIZE(test_settings));
81 	printk("total calculated: %u, total measured: %u\n", stats.total_calculated,
82 	       stats.total_measured);
83 	printk("entry max: %u, entry min: %u\n", stats.single_entry_max,
84 	       stats.single_entry_min);
85 
86 	k_sem_give(&waitfor_work);
87 }
88 
89 ZTEST_SUITE(settings_perf, NULL, NULL, NULL, NULL, NULL);
90 
ZTEST(settings_perf,test_performance)91 ZTEST(settings_perf, test_performance)
92 {
93 	int err;
94 
95 	if (IS_ENABLED(CONFIG_NVS)) {
96 		printk("Testing with NVS\n");
97 	} else if (IS_ENABLED(CONFIG_ZMS)) {
98 		printk("Testing with ZMS\n");
99 	}
100 
101 	k_work_queue_start(&settings_work_q, settings_work_stack,
102 		K_THREAD_STACK_SIZEOF(settings_work_stack),
103 		K_PRIO_COOP(TEST_SETTINGS_WORKQ_PRIO), NULL);
104 	k_thread_name_set(&settings_work_q.thread, "Settings workq");
105 	k_work_init_delayable(&pending_store, store_pending);
106 
107 	if (IS_ENABLED(CONFIG_BT)) {
108 		/* enable one of the major subsystems, and start scanning. */
109 		err = bt_enable(NULL);
110 		zassert_equal(err, 0, "Bluetooth init failed (err %d)\n", err);
111 
112 		err = bt_le_scan_start(BT_LE_SCAN_ACTIVE, bt_scan_cb);
113 		zassert_equal(err, 0, "Scanning failed to start (err %d)\n", err);
114 	}
115 
116 	err = settings_subsys_init();
117 	zassert_equal(err, 0, "settings_backend_init failed %d", err);
118 
119 	/* fill with values */
120 	for (int i = 0; i < TEST_SETTINGS_COUNT; i++) {
121 		test_settings[i].val = i;
122 	}
123 
124 	k_work_reschedule_for_queue(&settings_work_q, &pending_store, K_NO_WAIT);
125 
126 	err = k_sem_take(&waitfor_work, K_SECONDS(TEST_TIMEOUT_SEC));
127 	zassert_equal(err, 0, "k_sem_take failed %d", err);
128 
129 	if (IS_ENABLED(CONFIG_BT)) {
130 		err = bt_le_scan_stop();
131 		zassert_equal(err, 0, "Scanning failed to stop (err %d)\n", err);
132 	}
133 }
134