1 /*
2  * Copyright (c) 2022 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #if defined(CONFIG_BUILD_DATE_TIME_TEST)
8 
9 #include <stdlib.h>
10 #include <zephyr/ztest.h>
11 #include <zephyr/net_buf.h>
12 #include <zephyr/mgmt/mcumgr/mgmt/mgmt.h>
13 #include <zephyr/mgmt/mcumgr/transport/smp_dummy.h>
14 #include <zephyr/mgmt/mcumgr/mgmt/callbacks.h>
15 #include <zephyr/mgmt/mcumgr/grp/os_mgmt/os_mgmt.h>
16 #include <os_mgmt_processor.h>
17 #include <zcbor_common.h>
18 #include <zcbor_decode.h>
19 #include <zcbor_encode.h>
20 #include <mgmt/mcumgr/util/zcbor_bulk.h>
21 #include <zephyr/version.h>
22 #include <smp_internal.h>
23 #include "smp_test_util.h"
24 
25 #define SMP_RESPONSE_WAIT_TIME 3
26 #define ZCBOR_BUFFER_SIZE 256
27 #define OUTPUT_BUFFER_SIZE 256
28 #define ZCBOR_HISTORY_ARRAY_SIZE 4
29 
30 static struct net_buf *nb;
31 
32 /* Responses to commands */
33 extern uint8_t *test_date_time;
34 const uint8_t response_all_board_revision_left[] = "Zephyr unknown " STRINGIFY(BUILD_VERSION) " "
35 						   KERNEL_VERSION_STRING " ";
36 const uint8_t response_all_board_revision_right[] = " " CONFIG_ARCH " " PROCESSOR_NAME " "
37 						   CONFIG_BOARD "@" CONFIG_BOARD_REVISION
38 						   " Zephyr";
39 const uint8_t response_all_left[] = "Zephyr unknown " STRINGIFY(BUILD_VERSION) " "
40 				    KERNEL_VERSION_STRING " ";
41 const uint8_t response_all_right[] = " " CONFIG_ARCH " " PROCESSOR_NAME " " CONFIG_BOARD " Zephyr";
42 
43 const uint8_t query_build_date[] = "b";
44 const uint8_t query_all[] = "a";
45 
46 #define DATE_CHECK_LEFT_CHARS 11
47 #define DATE_CHECK_RIGHT_CHARS 5
48 #define TIME_CHECK_HH_START_CHAR 11
49 
50 #define TIME_HH_OFFSET 0
51 #define TIME_MM_OFFSET 3
52 #define TIME_SS_OFFSET 6
53 
54 #define SECONDS_PER_HOUR 3600
55 #define SECONDS_PER_MINUTE 60
56 
57 #define TIME_DIFFERENCE_ALLOWANCE 60
58 
time_string_to_seconds(const uint8_t * time_string)59 static int32_t time_string_to_seconds(const uint8_t *time_string)
60 {
61 	uint8_t time_hh;
62 	uint8_t time_mm;
63 	uint8_t time_ss;
64 
65 	/* Convert times to separate fields and then to timestamps which can be compared */
66 	time_hh = ((time_string[TIME_HH_OFFSET] - '0') * 10) +
67 		  (time_string[TIME_HH_OFFSET + 1] - '0');
68 	time_mm = ((time_string[TIME_MM_OFFSET] - '0') * 10) +
69 		  (time_string[TIME_MM_OFFSET + 1] - '0');
70 	time_ss = ((time_string[TIME_SS_OFFSET] - '0') * 10) +
71 		  (time_string[TIME_SS_OFFSET + 1] - '0');
72 
73 	return (time_hh * SECONDS_PER_HOUR) + (time_mm * SECONDS_PER_MINUTE) + time_ss;
74 }
75 
ZTEST(os_mgmt_info_build_date,test_info_build_date_1_build_date)76 ZTEST(os_mgmt_info_build_date, test_info_build_date_1_build_date)
77 {
78 	uint8_t buffer[ZCBOR_BUFFER_SIZE];
79 	uint8_t buffer_out[OUTPUT_BUFFER_SIZE];
80 	bool ok;
81 	uint16_t buffer_size;
82 	zcbor_state_t zse[ZCBOR_HISTORY_ARRAY_SIZE] = { 0 };
83 	zcbor_state_t zsd[ZCBOR_HISTORY_ARRAY_SIZE] = { 0 };
84 	bool received;
85 	struct zcbor_string output = { 0 };
86 	size_t decoded = 0;
87 	int32_t expected_time_seconds;
88 	int32_t received_time_seconds;
89 
90 	struct zcbor_map_decode_key_val output_decode[] = {
91 		ZCBOR_MAP_DECODE_KEY_DECODER("output", zcbor_tstr_decode, &output),
92 	};
93 
94 	memset(buffer, 0, sizeof(buffer));
95 	memset(buffer_out, 0, sizeof(buffer_out));
96 	buffer_size = 0;
97 	memset(zse, 0, sizeof(zse));
98 	memset(zsd, 0, sizeof(zsd));
99 
100 	zcbor_new_encode_state(zse, 2, buffer, ARRAY_SIZE(buffer), 0);
101 
102 	ok = create_mcumgr_format_packet(zse, query_build_date, buffer, buffer_out, &buffer_size);
103 	zassert_true(ok, "Expected packet creation to be successful\n");
104 
105 	/* Enable dummy SMP backend and ready for usage */
106 	smp_dummy_enable();
107 	smp_dummy_clear_state();
108 
109 	/* Send test echo command to dummy SMP backend */
110 	(void)smp_dummy_tx_pkt(buffer_out, buffer_size);
111 	smp_dummy_add_data();
112 
113 	/* For a short duration to see if response has been received */
114 	received = smp_dummy_wait_for_data(SMP_RESPONSE_WAIT_TIME);
115 
116 	zassert_true(received, "Expected to receive data but timed out\n");
117 
118 	/* Retrieve response buffer and ensure validity */
119 	nb = smp_dummy_get_outgoing();
120 	smp_dummy_disable();
121 
122 	/* Process received data by removing header */
123 	(void)net_buf_pull(nb, sizeof(struct smp_hdr));
124 	zcbor_new_decode_state(zsd, 3, nb->data, nb->len, 1, NULL, 0);
125 
126 	ok = zcbor_map_decode_bulk(zsd, output_decode, ARRAY_SIZE(output_decode), &decoded) == 0;
127 
128 	zassert_true(ok, "Expected decode to be successful\n");
129 	zassert_equal(decoded, 1, "Expected to receive 1 decoded zcbor element\n");
130 
131 	zassert_equal(strlen(test_date_time), output.len,
132 		      "Expected to receive %d bytes but got %d\n",
133 		      strlen(test_date_time), output.len);
134 
135 	/* Check left and right sides of date which should match */
136 	zassert_mem_equal(test_date_time, output.value, DATE_CHECK_LEFT_CHARS,
137 			  "Expected received data mismatch");
138 	zassert_mem_equal(&test_date_time[(strlen(test_date_time) - DATE_CHECK_RIGHT_CHARS)],
139 			  &output.value[(strlen(test_date_time) - DATE_CHECK_RIGHT_CHARS)],
140 			  DATE_CHECK_RIGHT_CHARS, "Expected received data mismatch");
141 
142 	/* Extract time strings into timestamps */
143 	expected_time_seconds = time_string_to_seconds(&test_date_time[TIME_CHECK_HH_START_CHAR]);
144 	received_time_seconds = time_string_to_seconds(&output.value[TIME_CHECK_HH_START_CHAR]);
145 
146 	zassert_within(expected_time_seconds, received_time_seconds, TIME_DIFFERENCE_ALLOWANCE,
147 		       "Expected times to be within %d seconds but got %d",
148 		       TIME_DIFFERENCE_ALLOWANCE,
149 		       abs(expected_time_seconds - received_time_seconds));
150 }
151 
ZTEST(os_mgmt_info_build_date,test_info_build_date_2_all)152 ZTEST(os_mgmt_info_build_date, test_info_build_date_2_all)
153 {
154 	uint8_t buffer[ZCBOR_BUFFER_SIZE];
155 	uint8_t buffer_out[OUTPUT_BUFFER_SIZE];
156 	bool ok;
157 	uint16_t buffer_size;
158 	zcbor_state_t zse[ZCBOR_HISTORY_ARRAY_SIZE] = { 0 };
159 	zcbor_state_t zsd[ZCBOR_HISTORY_ARRAY_SIZE] = { 0 };
160 	bool received;
161 	struct zcbor_string output = { 0 };
162 	size_t decoded = 0;
163 	int32_t expected_time_seconds;
164 	int32_t received_time_seconds;
165 
166 	struct zcbor_map_decode_key_val output_decode[] = {
167 		ZCBOR_MAP_DECODE_KEY_DECODER("output", zcbor_tstr_decode, &output),
168 	};
169 
170 	memset(buffer, 0, sizeof(buffer));
171 	memset(buffer_out, 0, sizeof(buffer_out));
172 	buffer_size = 0;
173 	memset(zse, 0, sizeof(zse));
174 	memset(zsd, 0, sizeof(zsd));
175 
176 	zcbor_new_encode_state(zse, 2, buffer, ARRAY_SIZE(buffer), 0);
177 
178 	ok = create_mcumgr_format_packet(zse, query_all, buffer, buffer_out, &buffer_size);
179 	zassert_true(ok, "Expected packet creation to be successful\n");
180 
181 	/* Enable dummy SMP backend and ready for usage */
182 	smp_dummy_enable();
183 	smp_dummy_clear_state();
184 
185 	/* Send test echo command to dummy SMP backend */
186 	(void)smp_dummy_tx_pkt(buffer_out, buffer_size);
187 	smp_dummy_add_data();
188 
189 	/* For a short duration to see if response has been received */
190 	received = smp_dummy_wait_for_data(SMP_RESPONSE_WAIT_TIME);
191 
192 	zassert_true(received, "Expected to receive data but timed out\n");
193 
194 	/* Retrieve response buffer and ensure validity */
195 	nb = smp_dummy_get_outgoing();
196 	smp_dummy_disable();
197 
198 	/* Process received data by removing header */
199 	(void)net_buf_pull(nb, sizeof(struct smp_hdr));
200 	zcbor_new_decode_state(zsd, 3, nb->data, nb->len, 1, NULL, 0);
201 
202 	ok = zcbor_map_decode_bulk(zsd, output_decode, ARRAY_SIZE(output_decode), &decoded) == 0;
203 
204 	zassert_true(ok, "Expected decode to be successful\n");
205 	zassert_equal(decoded, 1, "Expected to receive 1 decoded zcbor element\n");
206 
207 	if (sizeof(CONFIG_BOARD_REVISION) > 1) {
208 		/* Check with board revision */
209 		zassert_equal((strlen(test_date_time) + strlen(response_all_board_revision_left) +
210 			       strlen(response_all_board_revision_right)), output.len,
211 			      "Expected to receive %d bytes but got %d\n",
212 			      (strlen(test_date_time) + strlen(response_all_board_revision_left) +
213 			       strlen(response_all_board_revision_right)), output.len);
214 
215 		zassert_mem_equal(response_all_board_revision_left, output.value,
216 				  strlen(response_all_board_revision_left),
217 				  "Expected received data mismatch");
218 		zassert_mem_equal(response_all_board_revision_right,
219 				  &output.value[strlen(response_all_board_revision_left) +
220 				  strlen(test_date_time)],
221 				  strlen(response_all_board_revision_right),
222 				  "Expected received data mismatch");
223 	} else {
224 		/* Check without board revision */
225 		zassert_equal((strlen(test_date_time) + strlen(response_all_left) +
226 			       strlen(response_all_right)), output.len,
227 			      "Expected to receive %d bytes but got %d\n",
228 			      (strlen(test_date_time) + strlen(response_all_left) +
229 			       strlen(response_all_right)), output.len);
230 
231 		zassert_mem_equal(response_all_left, output.value, strlen(response_all_left),
232 				  "Expected received data mismatch");
233 		zassert_mem_equal(response_all_right, &output.value[strlen(response_all_left) +
234 				  strlen(test_date_time)], strlen(response_all_right),
235 				  "Expected received data mismatch");
236 	}
237 
238 	/* Extract time strings into timestamps */
239 	expected_time_seconds = time_string_to_seconds(&test_date_time[TIME_CHECK_HH_START_CHAR]);
240 	received_time_seconds = time_string_to_seconds(&output.value[(strlen(response_all_left) +
241 				TIME_CHECK_HH_START_CHAR)]);
242 
243 	zassert_within(expected_time_seconds, received_time_seconds, TIME_DIFFERENCE_ALLOWANCE,
244 		       "Expected times to be within %d seconds but got %d",
245 		       TIME_DIFFERENCE_ALLOWANCE,
246 		       abs(expected_time_seconds - received_time_seconds));
247 }
248 
cleanup_test(void * p)249 static void cleanup_test(void *p)
250 {
251 	if (nb != NULL) {
252 		net_buf_unref(nb);
253 		nb = NULL;
254 	}
255 }
256 
257 /* Build date/time test set */
258 ZTEST_SUITE(os_mgmt_info_build_date, NULL, NULL, NULL, cleanup_test, NULL);
259 
260 #endif
261